Fullscreen Leaflet Map



<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <link rel="stylesheet" href="/assets/leaflet.css" />
  <script src="/assets/leaflet.js"></script>
  <style>
  #map {position:absolute;top:0;bottom:0;left:0;width:100%;}
  </style>
</head>
<body>
  <div id='map'></div>
  <script src="map.js"></script>
</body>

  
var map = new L.Map('map');
map.setView([41.5, -81.7], 10)

var layerControl = new L.control.layers().addTo(map);

var scale = L.control.scale().addTo(map);
    

We Forgot the Layers!

Adding Basemaps

Leaflet Providers


var fire = L.tileLayer('https://{s}.tile.thunderforest.com/spinal-map/{z}/{x}/{y}.png', {
    attribution: '© Thunderforest, © OpenStreetMap'
});

var basemaps = {
  'Fire': fire,//html allowed here
};

var layerControl = new L.control.layers(basemaps).addTo(map);

Adding Layers

Simplification & Topojson via Mapshaper

Original GeoJSON: 3.7mb

Simplified Topojson: 324kb


var blockGroups = L.geoJson(null, {
  onEachFeature: function(feature, layer) {
    lmi = (layer.feature.properties.lowmodpct * 100).toFixed(2);
    layer.bindPopup('LMI: ' + lmi + '%');
  }
});
    

leaflet-omnivore


    var data = omnivore.topojson('/assets/blockgroups.json', null, blockGroups);
    

Alternative Methods

(Topojson Support Must be Added)

leaflet-ajax


/*Load data directly but dependent on jQuery*/
var blockGroups = new L.GeoJSON.AJAX("/assets/blockgroups.geojson");
    

jQuery


/*Or just use jQuery*/
$.getJSON("/assets/blockgroups.geojson", function(data) {
  /*add geojson formatted data to our L.geoJson() feature*/
  blockGroups.addData(data);
});
    

Block Groups (Topojson)

What about those pretty colors?

Choropleth Made Simple

Choropleth Plugin


var choropleth = L.choropleth(data.toGeoJSON(), {
    valueProperty: 'lowmodpct',
    scale: ['white', 'orange', 'red'],
    steps: 7,
    mode: 'q',
    style: {
        color: '#fff', // border color
        weight: 2,
        fillOpacity: 0.8
    },
    onEachFeature: function(feature, layer) {
      lmi = layer.feature.properties.lowmodpct * 100;
      layer.bindPopup('LMI: ' + lmi + '%')
    }
}).addTo(map)

Analysis with Turf - Within

Turf Documentation


data.on('ready', function() {
  var selBridges = new L.geoJson().addTo(map);
  blockGroups.on('click', function(e) {
    selBridges.clearLayers();
    var selLayer = new L.geoJson(e.layer.toGeoJSON());
    var within = turf.within(bridges.toGeoJSON(), selLayer.toGeoJSON());
    selBridges.addData(within);
  })
});
    

Turf Collect



//Function to analyze data with turf.collect - note differences from turf documentation
function analyze(poly, points, fieldA, fieldB, callback) {
  ga = turf.collect(poly.toGeoJSON(), points.toGeoJSON(), fieldA, fieldB);
  console.log('analyze complete');
  callback()
}

//Analyze the data, then use the data to build the map
analyze(data, bridges, 'garating', 'values', function() {
  console.log('build layers');
  console.log(ga.features[1].properties.values.length);
  if (ga.features[1].properties.values.length <= 0) {
    alert('Turfjs hit a snag. Please reload the page and try again.');
  }else{

  //Layer for block groups with no bridges inside
  var noData = L.geoJson(ga, {
    filter: function(feature, layer) {
      if (feature.properties.values.length == 0) {
        return true
      }
    },
    style: {
      color: 'gray',
      weight: 1,
      fillColor: 'gray',
      fillOpacity: 0.6
    },
    onEachFeature: function(feature, layer) {
      layer.bindPopup('

No Data

') } }).addTo(map); //Filter data out to only get those features with bridges inside var gaFeature = L.geoJson(ga, { filter: function(feature, layer) { if (feature.properties.values.length > 0) { return true } }, onEachFeature: function(feature, layer) { var garating = 0; //loop through values array to get total - turf.average() may be deprecated?? var v = layer.feature.properties.values; for (i = 0; i < v.length; i++) { garating += v[i] } //then get the avg bridge rating layer.feature.properties.avgga = (garating/v.length).toFixed(2); layer.feature.properties.count = (v.length).toString(); } }); /*Then build the choropleth layer based on the previous geojson layer. This could probably be done inside value property which can take a function that returns a value.*/ choropleth = new L.choropleth(gaFeature.toGeoJSON(), { valueProperty: 'avgga', scale: ['red', 'orange', 'white'], steps: 7, mode: 'q', style: { color: '#fff', weight: 1, fillOpacity: 0.6 }, onEachFeature: function(feature, layer) { var p = layer.feature.properties; layer.bindPopup('

Avg Bridge Rating: ' + (p.avgga).toString() + '

Total Bridges: ' + p.count + '
*A Rating of 4 or below is deficient'); } }).addTo(map);

Simplify Production with Jekyll

Creating
a Jekyll _site/

  • _data/
  • _includes/
  • _layouts/
  • _posts/
  • _config.yml

Default Layout

_layouts/default.html



<html lang="en">
  
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <link rel="stylesheet" href="/assets/leaflet.css" />
    <script src="/assets/leaflet.js"></script>
    {% if page.layout == 'map' %}<style>
    #map {position:absolute;top:0;bottom:0;left:0;width:100%;}
    </style>{% endif %}
  </head>
  <body>
  {{ content }}
  </body>
</html>

  

Map Layout

_layouts/maps.html


---
layout: default
---
<div id='map'></div>
<script>
var map = new L.Map('map');
map.setView({{page.center}}, {{page.zoom}})//values from the post frontmatter

var scale = L.control.scale().addTo(map);

//Load your basemaps here
var ortho = L.tileLayer();
var streets = L.tileLayer();
var topo = L.tileLayer();

//Add them to the layerControl
var basemaps = {
  "Ortho": ortho,
  "Streets": streets,
  "Topo": topo
};

var layerControl = new L.control.layers(basemaps).addTo(map);

//Then add a basemap based on your post frontmatter
{{page.basemap}}.addTo(map);

{{ content }}//your map post content
</script>
  

Map Post

_posts/YYYY-MM-DD-my-post-title.js


    ---
    layout: maps
    title: My New Map
    center: "[41.5, -81.7]"
    zoom: 10
    basemap: ortho
    ---
    //Add code here such as layers and interaction