Leveraging Leaflet.js With Drupal 8

Presentation by Kyle Levitan | @kalevitan

NOAA logo

A little about me

  • Web developer for NOAA’s data collection center here in Asheville, NC, known as NCEI
  • Lead developer for Climate.gov and the systems administrator for Toolkit.climate.gov

Why is web mapping important?

  • We like to tell stories with visualizations
  • We like reading stories with visualizations
  • It's how we associate things spatially

What is Leaflet.js?

  • Open source JavaScript library
  • Comparable to Google Maps API or Bing maps API, but it’s free!
    • No API keys needed
  • Light-weight:
    • Weighs just about 33 KB
  • Framework’s infrastructure is comprised of modern web programming, such as current features of JavaScript, HTML5, and CSS3
  • Optimized for mobile devices
  • Designed for retina or 4k displays

Integrating with Drupal

  1. Need a custom theme
  2. Leaflet library
  3. Install GeoPHP dependency via Composer *
  4. 
    $ composer config repositories.drupal composer https://packages.drupal.org/8
    $ composer require "phayes/geophp"
    									
    *More info about Composer

Integrating with Drupal (cont.)

  1. Contrib modules:
  2. Core modules:
    • Views
    • REST
    • Serialize

Attach Leaflet to your Theme

  • Reference the library in your theme’s libraries.yml file.
  • 
    global:
      version: VERSION
      css:
        base:
          libraries/leaflet/leaflet.css: {}
          css/THEME_NAME.css: {}
      js:
        libraries/leaflet/leaflet.js: {}
        js/THEME_NAME.js: {}
      dependencies:
        - core/jquery
    									
  • Include it in your theme’s info.yml file.
  • 
    libraries:
     - leaflet_dcavl/global
    								

Setting up Drupal components

  • Create a content type with GeoField field.
  • Drupal content field

Setting up Drupal components

  • Create a View to act as your data end point.
  • Drupal view end point

Setting up Drupal components

  • Set your REST export fields.
  • Drupal end point settings

Configuring your View

  • Change the format to GeoJSON and add additional fields.
  • Drupal view display

Defining your map element

  • Add the map container to a template file.
  • 
    {{ page.content }}
    
  • Define a map container height.
  • 
    #map {
      height: 500px;
    }
    								

Initializing your map

  • Define your map settings within your custom JS file.

/**
 * Declare a layer variable for your basemap.
 */
var baselayer = L.tileLayer ('https://MAP_TILE_URL/{z}/{x}/{y}', {
  attribution: '© Mapbox',
  maxZoom: 12,
});

/**
 * Declare your map variable.
 */
var map = L.map('map', {
  scrollWheelZoom: false,
  center: [35.5951, -82.5515],
});

/**
 * Bind the baselayer to your map.
 */
map.addLayer(baselayer);
							
Leaflet Provider Demo

Fetching your map data

  • Use the $.getJSON jQuery method to load our geoJSON view.

/**
 * Fetch geoJSON data, then fit contents within viewing area before adding to the map.
 */
$.getJSON('/us-cities').then(function(data) {
  var events = L.geoJson(data);
  map.fitBounds(events.getBounds(), {
    padding: [50, 50]
  });
  map.addLayer(events);
});
							

Altering your map data (partial)

Leaflet allows you to pass a variety of callbacks as
options to the L.geoJson layer.


/*...*/
filter: function(feature, layer) {
  return feature.properties.type === 'city';
},
pointToLayer: function(feature, latlng) {
  return L.marker(latlng, {icon: [PRE_DEF_VARIABLE]});
},
onEachFeature: function(feature, layer) {
  layer.bindPopup('
'+feature.properties.name+'
'); }, /*...*/

Altering your map data (full)

Leaflet allows you to pass a variety of callbacks as
options to the L.geoJson layer.


$.getJSON('/us-cities').then(function(data) {
 var events = L.geoJson(data, {
   // Filter feature data.
   filter: function(feature, layer) {
     return feature.properties.type === 'city';
   },
   // Define layer settings.
   pointToLayer: function(feature, latlng) {
     return L.marker(latlng, {icon: [PRE_DEF_VARIABLE]});
   },
   // Bind popup properties.
   onEachFeature: function(feature, layer) {
     layer.bindPopup('
'+feature.properties.name+'
'); }, }); // Fit points to viewing area. map.fitBounds(events.getBounds(), { padding: [50, 50] }); // Add the events array to the map. map.addLayer(events); });

The Whole Enchilada


/**
 * Declare a layer variable for your basemap.
 */
var baselayer = L.tileLayer ('https://MAP_TILE_URL/{z}/{x}/{y}', {
  attribution: '© Mapbox',
  maxZoom: 12,
});

/**
 * Declare your map variable.
 */
var map = L.map('map', {
  scrollWheelZoom: false,
  center: [35.5951, -82.5515],
});

/**
 * Bind the baselayer to your map.
 */
map.addLayer(baselayer);

$.getJSON('/us-cities').then(function(data) {
 var events = L.geoJson(data, {
   // Filter feature data.
   filter: function(feature, layer) {
     return feature.properties.type === 'city';
   },
   // Define layer settings.
   pointToLayer: function(feature, latlng) {
     return L.marker(latlng, {icon: [PRE_DEF_VARIABLE]});
   },
   // Bind popup properties.
   onEachFeature: function(feature, layer) {
     layer.bindPopup('
'+feature.properties.name+'
'); }, }); // Fit points to viewing area. map.fitBounds(events.getBounds(), { padding: [50, 50] }); // Add the events array to the map. map.addLayer(events); });

Thank you

questions?

kylelevitan.com | @kalevitan