Leaflet is the leading open-source JavaScript library for mobile-friendly interactive maps. Weighing just about 39 KB of JS, it has all the mapping features most developers ever need.
Let's use the assignment 4 starter as a base template.
We'll also need the SG GeoJSON dataset.
We're going to add Leaflet CSS and JS via a CDN.
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
Ignore / comment out the D3 code for now.
You'll need to create a map container for Leaflet to hook to.
Make sure you can get Leaflet and the base map tiles to load.
#map {
width: 1000px;
height: 600px;
}
<div id="map"></div>
let tiles = new L.TileLayer("http://{s}.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);
Try adding in parameters like maxZoom and minZoom. Find out more on the Leaflet docs.
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 Leaflet providers
You can already 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.34128961848499, 103.96340773679763]).addTo(map);
marker.bindPopup("SUTD campus");
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 the 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 isn't a true 3D solution, say unlike Cesium.
Comparison here.
Please go get your own mapbox API key - it's free for low amount of usage.
We're going to run this example from mapbox.
All 2.5D mapbox code will be run using your own API key!
We're going to embed a 3D object. We'll use three.js to load it, then render it using mapboxGL canvas renderer.
We'll run this example from mapbox.
Extra: Will demo an example locally, using three.js code to load animation.
deck.gl and Mapbox GL JS: Better Together, Xiao Ji Chen
Chi-Loong | V/R