Making a geoJSON powered Leaflet map

DATE: 2018-04-18

AUTHOR: John L. Godlee

I made a website for my labgroup using Jekyll and Github-pages. It can be seen here at seosaw.github.io[1]. The bit I want to show off about is the interactive map I made for the page on data and sites, which can be seen here[2].

1: https://seosaw.github.io

2: https://seosaw.github.io/data.html

The map is powered using leaflet[3], which is a Javascript library specifically for making maps.

3: http://leafletjs.com

This is how I got our data to run on the map.

First I have to generate the geoJSON files for the location of our sites as points, and also for a polygon layer showing the extent of savannas in Africa. I used R to generate these files because that is the platform I have the most experience with GIS stuff in. This is a stripped down version of the script I used to transform the original data .csv and .shp into geoJSON outputs:

For the plot data:

# Packages ----
library(dplyr)
library(geojsonio)
library(readr)


# Read in plot data 
plots <- read.csv("plots.csv")

# Create intermediate data frame ----
plots_export <- plots %>%
    filter(!is.na(longitude_of_centre) & !is.na(latitude_of_centre)) %>%
    mutate(name = paste(name, "-", plotcode),
    			 lon = as.numeric(as.character(longitude_of_centre)),
    			 lat = as.numeric(as.character(latitude_of_centre)),
    			 area_of_plot = round(as.numeric(area_of_plot) * 10000, digits = 2)) %>%
    select(name, country, 
    			 area_of_plot, 
    			 lon, lat) %>%
    filter(!is.na(lon), !is.na(lat))
    
# Export to json ----
geojson_write(input = plots_export,
    lat = "lat",
    lon = "lon",
    file = "plots_export")

And for the polygon data:

# Packages ----
library(rgdal)
library(rgeos)
library(geojsonio)

# Read shapefile ----
white_veg <- readOGR(dsn="whitesveg", 
    layer="Whites vegetation")

# Attempting to transform CRS to wgs84 ---- 
## No CRS given
proj4string(white_veg)
white_veg <- spTransform(white_veg,
    CRS("+proj=longlat +datum=WGS84"))

white_veg_sub <- white_veg[which((white_veg$DESCRIPTIO == "Moist-infertile savanna") | 
    (white_veg$DESCRIPTIO == "Mosaics of forest") | 
    (white_veg$DESCRIPTIO == "Mopane savanna") | 
    (white_veg$DESCRIPTIO == "Montane Forest") | 
    (white_veg$DESCRIPTIO == "Hydropmorphic grassland") | 
    (white_veg$DESCRIPTIO == "Arid-fertile savanna") | 
    (white_veg$DESCRIPTIO == "Sedge and reed swamp")),]


white_woodlands_poly <- gUnaryUnion(white_veg_sub)

# Output to geojson ----
# Export to json ----
geojson_write(input = white_woodlands_poly,
    file = "miombo_po")

The polygon data comes from White's 1983 vegetation map of Africa, which remains the best approximation of the biomes of Africa, even though it was constructed in a very non-systematic fashion.

Next I have to convert these geoJSON files to .js files and name them as variables by adding a variable designation at the top of the files like this:

var miombo = [
    {"type": "FeatureCollection",
  "features": [{
    "type": "Feature",

...
var locations = [
{ "type": "Feature", "id": 1, "properties": { "name": "Kruger Skukuza - SSM-001", "country": "South Africa", "area_of_plot": 40000.0, "lon": 31.4970563, "lat": -25.0197541 }, "geometry": { "type": "Point", "coordinates": [ 31.4970563, -25.0197541 ] } },
{ "type": "Feature", "id": 2, "properties": { "name": "Hwange-Farm41 - ZHH-001", "country": "Zimbabwe", "area_of_plot": 1000.0, "lon": 27.90663333, "lat": -18.62653333 }, "geometry": { "type": "Point", "coordinates": [ 27.90663333, -18.62653333 ] } },

...

Then, I need to construct the javascript file which makes the map, which calls the map tiles (L.tileLayer), and gives the plot locations and polygons various aesthetic attributes:

var mymap = L.map('leaf-map').setView([-16.5, 24.7], 4);

L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
  maxZoom: 18,
  id: 'mapbox.streets'
}).addTo(mymap);

var plotLocOptions = {
  fillOpacity: 0.5,
  weight: 0.2,
  radius: 5,
  fillColor: "#7A0099",
  color: "#7A0099"
};

function miomboOptions(feature) {
  return {
    fillOpacity: 0.9,
    fillColor: "#179600",
    color: "#179600",
    weight: 0.1
};
}

L.geoJSON(miombo, {style: miomboOptions}).addTo(mymap);

L.geoJSON(locations, {
  pointToLayer: function(feature, latlng) {
    return L.circleMarker(latlng, plotLocOptions);
  },
    onEachFeature: function(feature, layer){
    	layer.bindPopup("Name: " + feature.properties.name + "<br>" +
    	"Country: " + feature.properties.country + "<br>" +
      "Plot Area: " + feature.properties.area_of_plot + " m<sup>2</sup>");
    }
}).addTo(mymap);

The only thing left to do is make sure all of these scripts are called in the right order in the HTML file:


<script src="/scripts/plot_loc.js"></script> <!plot locations>
<script src="/scripts/miombo_po.js"></script> <!polygon locations>
<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"
    integrity="sha512-lInM/apFSqyy1o6s89K4iQUKg6ppXEgsVxT35HbzUupEVRh2Eu9Wdl4tHj7dZO0s1uvplcYGmt3498TtHq+log=="
    crossorigin="">
</script> <!leaflet script>
<script src="/scripts/leaf-map.js"></script> <!map construction script>

Leaflet map