Recently, I was part of an initiative that was looking into how to leverage Free and Open Source Software For Geospatial (FOSS4G) to create a robust and scalable pipeline for working with geospatial data. While a rather complicated sentence, it was also a rather complicated task. I did not have any geospatial knowledge, but I did have experience with web technologies, so I focused on how to present the result of such a pipeline onto a map.
I set off to research and get familiar with three different front-end mapping frameworks: Leaflet.js, OpenLayers, and Mapbox GL JS. In order to get hands-on experience with each framework, I built a demo application that would house all three frameworks side-by-side. The demo served as a great way to see how each framework differed visually in their presentation and performance while also exploring their APIs.
Front-End Mapping Framework Demo
The demo application allows selection between all three of the frameworks in a variety of use cases including:
- The simplest case of rendering a map.
- Drawing shapes to the map.
- Displaying popups at a particular location.
- Working with GeoJSON.
- Exploring the excellent geospatial analysis library Turf.js.
- Weather radar.
Feel free to spend some time exploring the demo to see how each framework differs!
Try the Demo Application
Front-End Mapping Framework Breakdown
Leaflet.js
Out of the frameworks, Leaflet.js is by far the easiest to use. Its API is extremely concise and the most common use cases are intuitive to work with.
// Create a Leaflet map that corresponds to the div with id "map"
const mymap = L.map("map").setView([37.71859, -92.007813], 4);
// Use OpenStreetMap as the map's tileset
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: '&<a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
}).addTo(mymap);
// Set a popup to open at the coordinates where a mouse click occurrs
mymap.on("click", (e) => {
L.popup()
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(mymap);
});
Leaflet’s documentation is well done. Not only are all parameters and methods documented, there are also copy/paste code examples for most things. Leaflet also has an active online community. There are third-party plugins available that add in additional functionality that Leaflet cannot do out of the box. That being said, bringing in third party plugins may not always be a good idea. Packages can lead to security concerns if they’re no longer maintained, performance problems that cannot be easily addressed, or even unknown behavior. If Leaflet does not lend itself well to the task at hand, consider taking a look at OpenLayers.
OpenLayers
OpenLayers is an extremely powerful framework. It supports nearly everything right out of the box - no plugins needed. The downside is that OpenLayers is extremely cumbersome to use.
// Obtain reference to the HTMLElements that will house the popup content
const container = document.getElementById("popup");
const content = document.getElementById("popup-content");
const closer = document.getElementById("popup-closer");
// Create an overlay to anchor the popup to the map
const overlay = new Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250,
},
});
// Add a click handler to hide the popup
closer.onclick = function () {
overlay.setPosition(undefined);
closer.blur();
return false;
};
// Construct a new map object that uses OpenStreetMap as it's base layer and attach the above overlay
const map = new Map({
target: "map",
layers: [
new TileLayer({
source: new XYZ({
url: "https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png",
}),
}),
],
overlays: [overlay],
view: new View({
center: [0, 0],
zoom: 2,
}),
});
// Add a click handler to the map to render the popup
map.on("singleclick", function (evt) {
const coordinate = evt.coordinate;
// conver the cordinate to a 'readable' string
const hdms = toStringHDMS(olProj.toLonLat(coordinate));
content.innerHTML = "<p>You clicked here:</p><code>" + hdms + "</code>";
overlay.setPosition(coordinate);
});
Importing the correct thing to configure an object is very frustrating. The documentation for OpenLayers is often incomplete and leads to multiple Google searches to get the bottom of what the behavior is. Despite all of this, complex mapping needs are best paired with OpenLayers. If the API is a dealbreaker, consider checking out ol-kit. ol-kit is a React-based UI toolkit built on top of OpenLayers. Extensible React components, powerful utility methods, and more - all for OpenLayers. ol-kit is developed and maintained by a collaboration between 1904labs and Bayer Crop Science.
import React from "react";
import {
Map,
BasemapContainer,
ContextMenu,
Controls,
LayerPanel,
Popup,
loadDataLayer,
} from "@bayer/ol-kit";
// This code example provides all the code needed to get popups, map controls (zooming, panning, etc.), and a layers panel.
class App extends React.Component {
onMapInit = async (map) => {
console.log("we got a map!", map);
// nice to have map set on the window while debugging
window.map = map;
// find a geojson or kml dataset (url or file) to load on the map
const data =
"https://data.nasa.gov/api/geospatial/7zbq-j77a?method=export&format=KML";
const dataLayer = await loadDataLayer(map, data);
// set the title on the layer to show in LayerPanel
dataLayer.set("title", "NASA Data");
console.log("data layer:", dataLayer);
};
render() {
return (
<Map onMapInit={this.onMapInit} fullScreen>
<BasemapContainer />
<ContextMenu />
<Controls />
<LayerPanel />
<Popup />
</Map>
);
}
}
export default App;
Mapbox GL JS
Mapbox GL JS falls somewhere between OpenLayers and Leaflet. Its API is fairly easy to use, but it’s not super versatile in functionality. Where Mapbox GL JS shines is with its integration into the Mapbox ecosystem. Being able to leverage Mapbox’s geospatial offerings with extremely minimal lines of code is very enticing, but leaves a lot to be desired for a general purpose mapping framework.
// Construct a new map object.
// NOTE: Be sure to set the mapbox.accessToken before calling this!
const map = new mapbox.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v11",
center: [-90, 40],
zoom: 3,
});
map.addControl(new mapbox.NavigationControl(), "top-left");
map.on("click", function (e) {
const lngLat = e.lngLat;
const htmlStr = `<p>You clicked at ${JSON.stringify(lngLat)}</p>`;
new mapbox.Popup().setLngLat(lngLat).setHTML(htmlStr).addTo(map);
});
The Takeaway
Despite being the most complex, OpenLayers is by far the best of the front-end mapping frameworks. Its unrivaled functionality far outweighs its cumbersome API. Leaflet lies on the other end of the spectrum. Its ease of use makes developing map-based applications fun and engaging, but super complex interactions might be harder to develop. Consider looking into ol-kit for all the power of OpenLayers with a much simpler API interface.