Leaflet JS Shadow Simulator
Yesterday I published the first version 0.1.0 of leaflet-shadow-simulator
on NPM. Today I will show you how to simulate shadows on some popular online maps. I will use a Bookmarklet to inject leaflet-shadow-simulator
into these maps. Let’s begin.
Sun hits Trail Camp around 7am
A favorite newcomer in the online mapping scene is Felt. Felt allows you to build data-rich collaborative maps and best of all, it’s based on Leaflet JS. Once you create an account, you are given some example maps and one of them is the Mount Whitney summit hike. It outlines the trail and a camping location where you will spend the first night. If you’re like me and have a hard time using your fingers when they’re cold, you probably don’t want to get up until the sun warms your tent. But when will that be? We can find out by injecting leaflet-shadow-simulator
into the map.
The following 50 lines of code
- load the
leaflet-shadow-simulator
library - configure the shadow color and terrain source
- display a time slider on the screen you can use to change the time
javascript: (() => {
// load leaflet-shadow-simulator
var script = document.createElement('script');
script.src = 'https://unpkg.com/leaflet-shadow-simulator@0.1.0/dist/leaflet-shadow-simulator.umd.min.js';
document.body.appendChild(script);
script.addEventListener('load', () => {
// configure leaflet-shadow-simulator
const today = new Date();
const shadeMap = L.shadeMap({
apiKey: "eyJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InRwcGlvdHJvd3NraUBzaGFkZW1hcC5hcHAiLCJjcmVhdGVkIjoxNjYyNDkzMDY2Nzk0LCJpYXQiOjE2NjI0OTMwNjZ9.ovCrLTYsdKFTF6TW3DuODxCaAtGQ3qhcmqj3DWcol5g",
date: today,
color: '#01112f',
opacity: 0.7,
terrainSource: {
maxZoom: 15,
tileSize: 256,
getSourceUrl: ({ x, y, z }) => `https://s3.amazonaws.com/elevation-tiles-prod/terrarium/${z}/${x}/${y}.png`,
getElevation: ({ r, g, b, a }) => (r * 256 + g + b / 256) - 32768,
}
}).addTo(window.felt.leafletMap);
// Create time slider control
const div = document.createElement('div');
div.innerHTML = `
<div style="position:absolute; bottom:125px; left:50%; width:400px; margin-left:-200px; background-color:white; padding:10px;border-radius:16px;">
<input id="shadow-slider" type="range" min="0" max="1440" style="width:100%" value="${today.getHours() * 60 + today.getMinutes()}" />
<span id="shadow-date">${today.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', timeZoneName: 'long' })}</span>
<span id="shadow-loader" style="float:right">0%</span>
</div>`;
document.body.append(div);
const shadowLoader = document.getElementById('shadow-loader');
shadeMap.on('tileloaded', (loadedTiles, totalTiles) => {
const percent = Math.floor((loadedTiles / totalTiles) * 100);
shadowLoader.innerText = (percent >= 100) ? `${totalTiles} tiles loaded` : `loading: ${percent}%`;
})
const shadowDate = document.getElementById('shadow-date');
const shadowSlider = document.getElementById('shadow-slider');
shadowSlider.addEventListener('input', (e) => {
const value = parseInt(e.target.value);
const newDate = new Date(today);
newDate.setMinutes(value - (Math.floor(value / 60) * 60));
newDate.setHours(Math.floor(value / 60));
newDate.setSeconds(0);
shadowDate.innerText = newDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', timeZoneName: 'long' });
shadeMap.setDate(newDate);
});
})
})();
One thing to note is that Felt exposes their Leaflet map object under the variable window.felt.leafletMap
. The shadow simulator needs access to the map object in order to add the shadow layer to the map.
Firefox bookmarklet
For best results you’ll want to minify this code. I used this minifier. Then, create a bookmark in your browser and copy the minified code into the URL field. Finally, open any Felt map and click the bookmark to simulate terrain shadows on the map.
I did notice that Felt incorrectly fires the moveend
event while a user is still dragging the map, causing ShadeMap to glitch occasionally :(. Fortunately, there is another Leaflet JS based map I use to plan outdoor adventures and that’s CalTopo. CalTopo exposes their Leaflet map object under the variable window.map.map
so you just need to replace window.felt.leafletMap
with window.map.map
in the example above and you should see this:
Leaflet Shadow Simulator and CalTopo
For updates or answers to questions, find me on Twitter.
If you just want to play with the shadow simulator check out shademap.app.