Web Maps from Scratch

Ohio URISA GIS Education Series 2017 Workshop


"An Introduction to Leaflet, GitHub & Jekyll"


Malcolm Meyer, OVRDC

Jesse Glascock, Pirme 3SG

Jon Woyame, Prime 3SG


Leaflet JS

Publishing with GitHub & Jekyll

What is Leaflet?

JavaScript web mapping API created in 2011 by Vladimir Agafonkin of Cloudmade (now with Mapbox).


Leaflet 1.0.3 was released in January 2017


Open Source

Small & Simple

Hundreds of Plugins

Large Online Community

Prime 3SG Examples

Commerce City plow tracker https://snowtrooper.c3gov.com/

Franklin County election results

Middlesex County demographics

Facebook Live Example

HTML, CSS & JavaScript





Limitations for Rendered Data (GeoJSON, TopoJSON, etc)

Layer Indexing, Layering & Layer Control Limitations


Develop Templates Jekyll/Node JS

Expand Internal Knowledge

Focus Web Apps or use Hosted Data (Esri, Mapbox, CARTO)

Use a Plugin or use a Custom Layer Control


Programming Language for the Web and Beyond

What JavaScript looks like

 { "type": "FeatureCollection",
    "features": [
      { "type": "Feature",
         "geometry": {
           "type": "Polygon",
           "coordinates": [
             [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
               [100.0, 1.0], [100.0, 0.0] ]
         "properties": {
           "prop0": "value0",
           "prop1": {"this": "that"}


Let's Make a MAP!!

    <title>Leaflet Map Intro</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- Leaflet -->
    <link rel="stylesheet" href="_leaflet/plugins/leaflet/leaflet.css" />
    <script src="_leaflet/plugins/leaflet/leaflet.js"></script>
    <!-- Plugins -->
    body,html {height:100%;width:100%;margin:0;}
    #map {width:100%;height:100%;background:whitesmoke;}
<div id="map"></div>
/*Our Leaflet Map Here*/

Fullscreen Map

var map = L.map("map");

map.setView([40.3,-96.6], 5);

L.hash(map); //plugin

L.control.scale().addTo(map); //built-in function

//built-in function
var layerControl = L.control.layers().addTo(map);

Basic Leaflet Map


Basic Leaflet Map


/*Easily add basemaps or baselayers with L.tileLayer 
WMS layers can also be added with L.tileLayer.wms */

var tileUrl = 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';
var attribution = '© OpenStreetMap, Tiles courtesy of Humanitarian OpenStreetMap Team';

var OpenStreetMap_HOT = L.tileLayer(tileUrl, {
  maxZoom: 19,
  attribution: attribution

layerControl.addBaseLayer(OpenStreetMap_HOT, "Streets");

Adding Tile Layers (Basemaps)

Adding Tile Layers (Basemaps)

// Load data with leaflet-omnivore, then add it to 
// our empty counties layer

var url = "tutorial-data/counties.topojson";

var counties = L.geoJson().addTo(map);

layerControl.addOverlay(counties, "Counties");

var cData = omnivore.topojson(url, null, counties);

Add Data Layers

Add Data Layers

var counties = L.geoJson(null, {
    style: function(feature) {
	switch(feature.properties.winner) {
	    case "Trump": return {fillColor: "red"}
	    default	: return {fillColor: "blue"}
    onEachFeature: function(feature, county) {
	var info = county.feature.properties.NAME + 
        "<br>" + county.feature.properties.winner;

Styling & Interaction

Styling & Interaction

var url2= "tutorial-data/airports.geojson"

var aStyle= {
    color: "black",
    radius: 5,
    weight: 0, 
    fillOpacity: 1, 
    pane: "markerPane" };

var airports = L.geoJson(null, {
    pointToLayer: function(feature, latlng) {
	return L.circleMarker(latlng, aStyle)

var aData= omnivore.geojson(url2, null, airports);

Styling Points

Styling Points

Leaflet Plugins

// Once our county data is loaded,
// add search functionality using
// leaflet-search with default settings

cData.on("ready", function() {

    var searchControl = new L.Control.Search({
	layer: counties,
	propertyName: 'NAME'

Searching Layers

Searching Layers

var choropleth;

cData.on("ready", function() {
    choropleth = L.choropleth(cData.toGeoJSON(), {
	valueProperty: "POP_SQMI",
	scale: ["white","#006d2c"],
	steps: 7,
        mode: "q",
	style: {color: "#fff",weight: 1,fillOpacity: 0.9},
        onEachFeature: function(feature, county) {
            var name = feature.properties.NAME;



*legend code not shown in previous page

var airportJson;

aData.on('ready', function() {
    airportJson = aData.toGeoJSON();

var highlightStyle = { 
    color: "goldenrod", 
    fillColor: "goldenrod", 
    opacity: 1, 
    fillOpacity: 1, 
    pane: "markerPane"

var highlight = new L.geoJson(null, {
    pointToLayer: function(feature, latlng) {
	return new L.circleMarker(latlng, highlightStyle)

counties.on("mouseover", function(e) {
    var selLayer = new L.geoJson(e.layer.toGeoJSON());
    var within = turf.within(airportJson, selLayer.toGeoJSON());

Turf GIS Analysis

Turf GIS Analysis

hovering over a county highlights the points inside that county

Part II 

Building a Web Map Publishing Platform with GitHub & Jekyll

GitHub is a code hosting platform for version control and collaboration. It offers free web hosting for small projects and has built-in support for Jekyll and GeoJSON.

Jekyll is a "simple, blog aware, static site generator", based on Ruby, with Markdown support. Jekyll uses the Liquid templating language and yaml for config files.

How and Why use GitHub & Jekyll?

  • Page Templates
  • Code Backup
  • Simple Hosting
  • No Databases to Manage
  • Static Sites are AWESOME!
  • Example

GitHub Web Publishing


Free Hosting!!

Raw HTML, CSS & JavaScript

Jekyll Sites

Built-In Themes

Third-Party Integration

Editable by Multiple Users




Limited Size & Capacity on Free Accounts

Free Repositories are Public 

Choosing & Building the Map Template




Is This the Best We Can Do?

Why Reinvent the Wheel?

Templates from Esri & Google

Material Maps

  • Simple Structure
  • One Map Layout
  • Gallery Layout
  • Maps are a Jekyll 'Collection'
  • Similar template in use at OVRDC

Material Maps Live


Malcolm Meyer



Jesse Glascock



Jon Woyame