Leaflet is the leading open-source JavaScript library for mobile-friendly interactive maps. Weighing just about 42 KB of JS, it has all the mapping features most developers ever need.
We're going to add Leaflet CSS and JS via a CDN.
<html>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" />
<style></style>
</head>
<body>
<div id="map"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
<script></script>
</body>
</html>
We're using openstreetmap tiles here.
Note that Leaflet is hooked onto the map container defined previously.
#map {
width: 1000px;
height: 600px;
}
let tiles = new L.TileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: 'Tiles © OpenStreeMaps'
});
let map = new L.Map("map", {
center: [1.347833, 103.809357],
zoom: 11,
})
.addLayer(tiles);
let map = new L.Map("map", {
center: [1.347833, 103.809357],
zoom: 11,
maxBounds: L.latLngBounds(L.latLng(1.1, 103.5), L.latLng(1.5, 104.3))
})
.addLayer(tiles);
More on zoom levels
Try adding in parameters like maxZoom and minZoom
Add in the Leaflet scale control so we can see the zoom.
L.control.scale().addTo(map);
You can control the zoom / pan using code rather than using interface
Example: Exploring the lungs of Asia, Kontinentalist
setTimeout(function(){
map.flyTo([1.34141372, 103.963757], 15)
}, 2000);
let tiles = new L.tileLayer('http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
subdomains:['mt0','mt1','mt2','mt3'],
attribution: 'Tiles © Google'
});
let tiles = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: 'Tiles © Esri'
});
For many base maps you need to register or install their script.
Here's a small list of map providers
You can do a lot of things within Leaflet and base map tiles.
Can you add a marker location at the SUTD campus on your map?
let marker = L.marker([1.34128961, 103.96340773]).addTo(map);
marker.bindPopup("SUTD campus");
Try adding lines and circles to the Leaflet map!
Useful reading: Leaflet GeoJSON tutorial.
Let's go through this snippet of code.
You can do Assignment 4 using Leaflet instead.
Useful reading: Leaflet Choropleth tutorial.
Bootcamp Day 3 final project is assignment 4 done in Leaflet instead.
Of course you can!
Hook a SVG layer into Leaflet's overlayPane.
let svg = d3.select(map.getPanes().overlayPane)
.append("svg")
.attr("width", 1000)
.attr("height", 600)
.append("g")
.attr("id","svgLayer")
.attr("class", "leaflet-zoom-hide");
The leaflet-zoom-hide class is so that when you use Leaflet zoom, it turns of the layer temporarily during the zooming animation.
You can try what happens when you don't include this in the final instance.
You cannot use D3's Mercator projection. The projection needs to be the same as Leaflet's.
Leaflet uses its own geo projection, and we're going to make D3 sync with that.
Recall that D3 projection is simply taking a lat/lon coordinate and turning it into a SVG x, y point.
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new L.LatLng(y, x));
this.stream.point(point.x, point.y);
}
let projection = d3.geoTransform({point: projectPoint})
let geopath = d3.geoPath().projection(projection);
Looks good. But zooming and scaling breaks the visualization.
We're going to write a function recalculate the bounds and redraw the SVG when moved...
function redrawLeafletLayer() {
// d3.geo.bounds takes a single Feature or a FeatureCollection as argument
let bounds = geopath.bounds(data[0]),
topLeft = bounds[0],
bottomRight = bounds[1];
let svg = d3.select(map.getPanes().overlayPane).select("svg");
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
svg.select("g").attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
d3.select("#map svg g#districts").selectAll("path")
.attr("d", geopath);
}
...Then we're going to call this function every time Leaflet is zoomed.
redrawLeafletLayer();
// reset whenever map is moved
map.on('zoomend', redrawLeafletLayer);
ObservableHQ on how to use D3 with Leaflet.
Leaflet turns off mouse events for all overlay SVG panes by default.
Add the "leaflet-interactive" class to every SVG tag (the appended paths in the case of districts) that you want to make interactive.
Mapbox (2.5D) isn't a true 3D solution, say unlike Cesium (3D).
Comparison here.
MapLibre is a free open-source fork of MapboxGL
We're going to embed a 3D model onto a basemap, using this snippet of code.
Please download it and try to run it.
The code loads a 3D object using three.js and places it on a map using MapLibre.
If you want a glassy, fast basemap, you have to pay for it. E.g. Maptiller, Mapbox, etc.
The API code given in the code snippet for maptiller will not work (of course). Let's just use openstreemaps.
style: {
version: 8,
sources: {
osm: {
type: 'raster',
tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap Contributors',
}
},
layers: [
{
id: 'osm',
type: 'raster',
source: 'osm'
}
]
},
SG elections, D3 + OSM + mapLibre
Viz.gl, Uber viz open-source framework
Cesium Sandcastle examples
deck.gl examples
Chi-Loong | V/R