mapbox-gl-shadow-simulator 0.61.1 → 0.62.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -1
- package/dist/mapbox-gl-shadow-simulator.d.ts +3 -0
- package/dist/mapbox-gl-shadow-simulator.esm.js +2 -2
- package/dist/mapbox-gl-shadow-simulator.umd.min.js +2 -2
- package/examples/map.html +246 -196
- package/examples/maplibre.html +257 -214
- package/examples/markers-maplibre.html +304 -0
- package/examples/markers.html +197 -0
- package/package.json +1 -1
- package/dist/leaflet-shademap/leaflet-shademap/dist/ShadeMap.d.ts +0 -122
- package/dist/leaflet-shademap/leaflet-shademap/dist/ShadeMapLeaflet.d.ts +0 -17
- package/dist/leaflet-shademap/leaflet-shademap/dist/ShadeMapMapbox.d.ts +0 -29
- package/dist/leaflet-shademap/leaflet-shademap/dist/buildings/BuildingRasterizer.d.ts +0 -42
- package/dist/leaflet-shademap/leaflet-shademap/dist/buildings/fetch-buildings.d.ts +0 -12
- package/dist/leaflet-shademap/leaflet-shademap/dist/buildings/normalize-buildings.d.ts +0 -8
- package/dist/leaflet-shademap/leaflet-shademap/dist/components/CanvasOverlay.d.ts +0 -11
- package/dist/leaflet-shademap/leaflet-shademap/dist/index.d.ts +0 -7
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/EventEmitter.d.ts +0 -10
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/TileMerger.d.ts +0 -49
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/heightMap.d.ts +0 -33
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/helpers.d.ts +0 -33
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/image.d.ts +0 -13
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/projection.d.ts +0 -3
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/shadowMap.d.ts +0 -17
- package/dist/leaflet-shademap/leaflet-shademap/dist/lib/webgl.d.ts +0 -13
- package/dist/leaflet-shademap/leaflet-shademap/dist/map/geometryLeaflet.d.ts +0 -11
- package/dist/leaflet-shademap/leaflet-shademap/dist/map/umap.d.ts +0 -26
- package/dist/leaflet-shademap/leaflet-shademap/dist/shader/gpu.d.ts +0 -35
- package/dist/leaflet-shademap/leaflet-shademap/dist/shader/kernel.d.ts +0 -72
- package/dist/leaflet-shademap/leaflet-shademap/dist/types/color.d.ts +0 -5
- package/dist/leaflet-shademap/leaflet-shademap/dist/types/constants.d.ts +0 -3
- package/dist/leaflet-shademap/leaflet-shademap/dist/types/quality.d.ts +0 -5
- package/dist/leaflet-shademap/leaflet-shademap/dist/types/shadeMapOptions.d.ts +0 -46
- package/dist/leaflet-shademap/leaflet-shademap/dist/types/shadow3DData.d.ts +0 -21
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
|
8
|
+
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.1.0/dist/maplibre-gl.css" />
|
|
9
|
+
<script src="https://unpkg.com/maplibre-gl@4.1.0/dist/maplibre-gl.js"></script>
|
|
10
|
+
<script src="https://www.unpkg.com/suncalc@1.9.0/suncalc.js"></script>
|
|
11
|
+
<script src="https://unpkg.com/protomaps-themes-base@4.3.0/dist/protomaps-themes-base.js"></script>
|
|
12
|
+
<script src="../dist/mapbox-gl-shadow-simulator.umd.min.js"></script>
|
|
13
|
+
<style>
|
|
14
|
+
body {
|
|
15
|
+
padding: 0px;
|
|
16
|
+
margin: 0px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#mapid {
|
|
20
|
+
height: 100vh;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.maplibregl-control-time {
|
|
24
|
+
padding: 20px;
|
|
25
|
+
background-color: white;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#exposure-gradient-container {
|
|
29
|
+
display: none;
|
|
30
|
+
background-color: white;
|
|
31
|
+
padding: 0 10px 5px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#exposure-gradient {
|
|
35
|
+
height: 20px;
|
|
36
|
+
background-image: linear-gradient(to right,
|
|
37
|
+
rgb(0 0 255/ 0.5),
|
|
38
|
+
rgb(0 255 0 / 0.5),
|
|
39
|
+
rgb(255 0 0 / 0.5));
|
|
40
|
+
display: flex;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
#exposure-gradient>div {
|
|
44
|
+
flex: 1;
|
|
45
|
+
border: 1px solid white;
|
|
46
|
+
text-align: center;
|
|
47
|
+
font-weight: bold;
|
|
48
|
+
}
|
|
49
|
+
</style>
|
|
50
|
+
<title>mapbox-gl-shadow-simulator Maplibre Markers Example</title>
|
|
51
|
+
</head>
|
|
52
|
+
|
|
53
|
+
<body>
|
|
54
|
+
<div id="mapid">
|
|
55
|
+
<div class="maplibregl-control-container" style="z-index: 2000; pointer-events: auto">
|
|
56
|
+
<div class="maplibregl-ctrl-top-left">
|
|
57
|
+
<div class="maplibregl-control-time" style="pointer-events: auto">
|
|
58
|
+
<button id="decrement">-1 hour</button>
|
|
59
|
+
<button id="increment">+1 hour</button>
|
|
60
|
+
<button id="play">Play</button>
|
|
61
|
+
<button id="stop">Stop</button>
|
|
62
|
+
<label><input id="exposure" type="checkbox" autocomplete="off" />Full-day sun exposure</label>
|
|
63
|
+
<button>
|
|
64
|
+
<a href="https://shademap.app/about" target="_blank">Get API key</a>
|
|
65
|
+
</button>
|
|
66
|
+
<span id="loader" style="padding: 3px"></span>
|
|
67
|
+
</div>
|
|
68
|
+
<div id="exposure-gradient-container">
|
|
69
|
+
<div>Hours of sunlight</div>
|
|
70
|
+
<div id="exposure-gradient"></div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<script>
|
|
76
|
+
const mapLoaded = (map) => {
|
|
77
|
+
return new Promise((res, rej) => {
|
|
78
|
+
function cb() {
|
|
79
|
+
if (!map.loaded()) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
map.off("render", cb);
|
|
83
|
+
res();
|
|
84
|
+
}
|
|
85
|
+
map.on("render", cb);
|
|
86
|
+
cb();
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
/* Maplibregl setup */
|
|
90
|
+
maplibregl.setRTLTextPlugin(
|
|
91
|
+
"https://unpkg.com/@mapbox/mapbox-gl-rtl-text@0.2.3/mapbox-gl-rtl-text.min.js",
|
|
92
|
+
true // Lazy load the plugin
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const map = new maplibregl.Map({
|
|
96
|
+
container: "mapid",
|
|
97
|
+
style: {
|
|
98
|
+
version: 8,
|
|
99
|
+
glyphs:
|
|
100
|
+
"https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf",
|
|
101
|
+
sources: {
|
|
102
|
+
protomaps: {
|
|
103
|
+
type: "vector",
|
|
104
|
+
tiles: ["https://cfw.shademap.app/planet/{z}/{x}/{y}.pbf"],
|
|
105
|
+
attribution:
|
|
106
|
+
'<a href="https://protomaps.com">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',
|
|
107
|
+
maxzoom: 15,
|
|
108
|
+
},
|
|
109
|
+
buildings: {
|
|
110
|
+
type: "vector",
|
|
111
|
+
tiles: [`https://cfw.shademap.app/buildings/{z}/{x}/{y}.mvt`],
|
|
112
|
+
minzoom: 14,
|
|
113
|
+
maxzoom: 14,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
layers: [
|
|
117
|
+
...protomaps_themes_base.default("protomaps", "light", "en"),
|
|
118
|
+
{
|
|
119
|
+
id: "building",
|
|
120
|
+
source: "buildings",
|
|
121
|
+
"source-layer": "building",
|
|
122
|
+
type: "line",
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
maplibreLogo: true,
|
|
127
|
+
center: { lng: -122.15675711631776, lat: 47.75510439397995 },
|
|
128
|
+
zoom: 16, // starting zoom
|
|
129
|
+
hash: true,
|
|
130
|
+
});
|
|
131
|
+
/* End Maplibregl setup */
|
|
132
|
+
|
|
133
|
+
/* Add markers for cafes */
|
|
134
|
+
const cafes = [
|
|
135
|
+
{ lat: 47.75479062611596, lng: -122.15534138695534 },
|
|
136
|
+
{ lat: 47.75337697202738, lng: -122.16319480178332 },
|
|
137
|
+
{ lat: 47.751941595291726, lng: -122.15921456808333 },
|
|
138
|
+
{ lat: 47.753477542718706, lng: -122.15083544800348 },
|
|
139
|
+
{ lat: 47.754992309138885, lng: -122.15102829347933 },
|
|
140
|
+
{ lat: 47.757776865447056, lng: -122.1563603726317 },
|
|
141
|
+
];
|
|
142
|
+
const markers = cafes.map((coords) => {
|
|
143
|
+
return new maplibregl.Marker().setLngLat(coords).addTo(map);
|
|
144
|
+
});
|
|
145
|
+
/* End add markers for cafes */
|
|
146
|
+
|
|
147
|
+
/* ShadeMap setup */
|
|
148
|
+
const loaderEl = document.getElementById("loader");
|
|
149
|
+
let now = new Date(
|
|
150
|
+
SunCalc.getTimes(
|
|
151
|
+
new Date(),
|
|
152
|
+
47.694878957368815,
|
|
153
|
+
-122.18578164139899
|
|
154
|
+
).sunrise.getTime() +
|
|
155
|
+
60 * 60 * 1000
|
|
156
|
+
);
|
|
157
|
+
let shadeMap;
|
|
158
|
+
map.on("load", () => {
|
|
159
|
+
shadeMap = new ShadeMap({
|
|
160
|
+
apiKey:
|
|
161
|
+
"eyJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InRwcGlvdHJvd3NraUBzaGFkZW1hcC5hcHAiLCJjcmVhdGVkIjoxNjYyNDkzMDY2Nzk0LCJpYXQiOjE2NjI0OTMwNjZ9.ovCrLTYsdKFTF6TW3DuODxCaAtGQ3qhcmqj3DWcol5g",
|
|
162
|
+
date: now,
|
|
163
|
+
color: "#01112f",
|
|
164
|
+
opacity: 0.7,
|
|
165
|
+
terrainSource: {
|
|
166
|
+
maxZoom: 15,
|
|
167
|
+
tileSize: 256,
|
|
168
|
+
getSourceUrl: ({ x, y, z }) =>
|
|
169
|
+
`https://s3.amazonaws.com/elevation-tiles-prod/terrarium/${z}/${x}/${y}.png`,
|
|
170
|
+
getElevation: ({ r, g, b, a }) => r * 256 + g + b / 256 - 32768,
|
|
171
|
+
_overzoom: 18,
|
|
172
|
+
},
|
|
173
|
+
getFeatures: async () => {
|
|
174
|
+
if (map.getZoom() >= 12) {
|
|
175
|
+
await mapLoaded(map);
|
|
176
|
+
const buildingData = map.querySourceFeatures("buildings", {
|
|
177
|
+
sourceLayer: "building",
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
buildingData.forEach((feature) => {
|
|
181
|
+
feature.properties.height = feature.properties.height || 3.1;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Buildings segments must be rasterized from bottom to top. If not, the base of a building
|
|
185
|
+
// could rasterize over the top tower of the building
|
|
186
|
+
buildingData.sort((a, b) => {
|
|
187
|
+
return a.properties.height - b.properties.height;
|
|
188
|
+
});
|
|
189
|
+
return buildingData;
|
|
190
|
+
}
|
|
191
|
+
return [];
|
|
192
|
+
},
|
|
193
|
+
debug: (msg) => {
|
|
194
|
+
console.log(new Date().toISOString(), msg);
|
|
195
|
+
},
|
|
196
|
+
}).addTo(map);
|
|
197
|
+
|
|
198
|
+
shadeMap.on("tileloaded", (loadedTiles, totalTiles) => {
|
|
199
|
+
loaderEl.innerText = `Loading: ${(
|
|
200
|
+
(loadedTiles / totalTiles) *
|
|
201
|
+
100
|
|
202
|
+
).toFixed(0)}%`;
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
shadeMap.on("idle", async () => {
|
|
206
|
+
console.log('idle')
|
|
207
|
+
const promises = markers.map((marker) => {
|
|
208
|
+
const latlng = marker.getLngLat();
|
|
209
|
+
const { x, y } = map.project(latlng);
|
|
210
|
+
return shadeMap.isPositionInSun(x, y);
|
|
211
|
+
});
|
|
212
|
+
const results = await Promise.all(promises);
|
|
213
|
+
markers.forEach((marker, i) => {
|
|
214
|
+
const inTheSun = results[i] === true;
|
|
215
|
+
const element = marker.getElement();
|
|
216
|
+
const svg = element.getElementsByTagName("svg")[0];
|
|
217
|
+
const path = svg.getElementsByTagName("path")[0];
|
|
218
|
+
path.setAttribute("fill", inTheSun ? "#ffcc00" : "#ccf");
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
/* End ShadeMap setup */
|
|
223
|
+
|
|
224
|
+
/* Controls setup */
|
|
225
|
+
let intervalTimer;
|
|
226
|
+
|
|
227
|
+
const increment = document.getElementById("increment");
|
|
228
|
+
const decrement = document.getElementById("decrement");
|
|
229
|
+
const play = document.getElementById("play");
|
|
230
|
+
const stop = document.getElementById("stop");
|
|
231
|
+
const exposure = document.getElementById("exposure");
|
|
232
|
+
const exposureGradientContainer = document.getElementById(
|
|
233
|
+
"exposure-gradient-container"
|
|
234
|
+
);
|
|
235
|
+
const exposureGradient = document.getElementById("exposure-gradient");
|
|
236
|
+
|
|
237
|
+
increment.addEventListener(
|
|
238
|
+
"click",
|
|
239
|
+
() => {
|
|
240
|
+
now = new Date(now.getTime() + 3600000);
|
|
241
|
+
shadeMap && shadeMap.setDate(now);
|
|
242
|
+
},
|
|
243
|
+
false
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
decrement.addEventListener(
|
|
247
|
+
"click",
|
|
248
|
+
() => {
|
|
249
|
+
now = new Date(now.getTime() - 3600000);
|
|
250
|
+
shadeMap && shadeMap.setDate(now);
|
|
251
|
+
},
|
|
252
|
+
false
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
play.addEventListener("click", () => {
|
|
256
|
+
intervalTimer = setInterval(() => {
|
|
257
|
+
now = new Date(now.getTime() + 60000);
|
|
258
|
+
shadeMap && shadeMap.setDate(now);
|
|
259
|
+
}, 100);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
stop.addEventListener("click", () => {
|
|
263
|
+
clearInterval(intervalTimer);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
exposure.addEventListener("click", (e) => {
|
|
267
|
+
clearInterval(intervalTimer);
|
|
268
|
+
const target = e.target;
|
|
269
|
+
if (!target.checked) {
|
|
270
|
+
shadeMap && shadeMap.setSunExposure(false);
|
|
271
|
+
increment.disabled = false;
|
|
272
|
+
decrement.disabled = false;
|
|
273
|
+
play.disabled = false;
|
|
274
|
+
stop.disabled = false;
|
|
275
|
+
exposureGradientContainer.style.display = "none";
|
|
276
|
+
} else {
|
|
277
|
+
const { lat, lng } = map.getCenter();
|
|
278
|
+
const { sunrise, sunset } = SunCalc.getTimes(now, lat, lng);
|
|
279
|
+
shadeMap &&
|
|
280
|
+
shadeMap.setSunExposure(true, {
|
|
281
|
+
startDate: sunrise,
|
|
282
|
+
endDate: sunset,
|
|
283
|
+
});
|
|
284
|
+
increment.disabled = true;
|
|
285
|
+
decrement.disabled = true;
|
|
286
|
+
play.disabled = true;
|
|
287
|
+
stop.disabled = true;
|
|
288
|
+
|
|
289
|
+
const hours = (sunset - sunrise) / 1000 / 3600;
|
|
290
|
+
const partial = hours - Math.floor(hours);
|
|
291
|
+
const html = [];
|
|
292
|
+
for (let i = 0; i < hours; i++) {
|
|
293
|
+
html.push(`<div>${i + 1}</div>`);
|
|
294
|
+
}
|
|
295
|
+
html.push(`<div style="flex: ${partial}"></div>`);
|
|
296
|
+
exposureGradientContainer.style.display = "block";
|
|
297
|
+
exposureGradient.innerHTML = html.join("");
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
/* End controls setup */
|
|
301
|
+
</script>
|
|
302
|
+
</body>
|
|
303
|
+
|
|
304
|
+
</html>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
|
|
7
|
+
<link
|
|
8
|
+
href="https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.css"
|
|
9
|
+
rel="stylesheet"
|
|
10
|
+
/>
|
|
11
|
+
<script src="https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.js"></script>
|
|
12
|
+
<script src="https://www.unpkg.com/suncalc@1.9.0/suncalc.js"></script>
|
|
13
|
+
<script src="../dist/mapbox-gl-shadow-simulator.umd.min.js"></script>
|
|
14
|
+
<style>
|
|
15
|
+
body {
|
|
16
|
+
padding: 0px;
|
|
17
|
+
margin: 0px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#mapid {
|
|
21
|
+
height: 100vh;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.mapbox-control-time {
|
|
25
|
+
padding: 20px;
|
|
26
|
+
background-color: white;
|
|
27
|
+
}
|
|
28
|
+
</style>
|
|
29
|
+
<title>mapbox-gl-shadow-simulator Mapbox Markers Example</title>
|
|
30
|
+
</head>
|
|
31
|
+
|
|
32
|
+
<body>
|
|
33
|
+
<div id="mapid">
|
|
34
|
+
<div
|
|
35
|
+
class="mapboxgl-control-container"
|
|
36
|
+
style="z-index: 2000; pointer-events: auto"
|
|
37
|
+
>
|
|
38
|
+
<div class="mapboxgl-ctrl-top-left">
|
|
39
|
+
<div class="mapbox-control-time" style="pointer-events: auto">
|
|
40
|
+
<button id="decrement">-1 hour</button>
|
|
41
|
+
<button id="increment">+1 hour</button>
|
|
42
|
+
<button id="play">Play</button>
|
|
43
|
+
<button id="stop">Stop</button>
|
|
44
|
+
<button>
|
|
45
|
+
<a href="https://shademap.app/about" target="_blank"
|
|
46
|
+
>Get API key</a
|
|
47
|
+
>
|
|
48
|
+
</button>
|
|
49
|
+
<span id="loader" style="padding: 3px"></span>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<script>
|
|
55
|
+
const mapLoaded = (map) => {
|
|
56
|
+
return new Promise((res, rej) => {
|
|
57
|
+
function cb() {
|
|
58
|
+
if (!map.loaded()) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
map.off("render", cb);
|
|
62
|
+
res();
|
|
63
|
+
}
|
|
64
|
+
map.on("render", cb);
|
|
65
|
+
cb();
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
/* Mapbox setup */
|
|
69
|
+
mapboxgl.accessToken =
|
|
70
|
+
"pk.eyJ1IjoidHBwaW90cm93c2tpIiwiYSI6ImNrbGtkMXZtNTA3NmUydXJ5Z2xxNXExcmEifQ.Anj5VuaYC5d595Pde-Xp3w";
|
|
71
|
+
const map = new mapboxgl.Map({
|
|
72
|
+
container: "mapid",
|
|
73
|
+
style: "mapbox://styles/mapbox/streets-v11",
|
|
74
|
+
center: { lng: -122.15675711631776, lat: 47.75510439397995 },
|
|
75
|
+
zoom: 16, // starting zoom
|
|
76
|
+
hash: true,
|
|
77
|
+
});
|
|
78
|
+
/* End Mapbox setup */
|
|
79
|
+
|
|
80
|
+
/* Add markers for cafes */
|
|
81
|
+
const cafes = [
|
|
82
|
+
{ lat: 47.75479062611596, lng: -122.15534138695534 },
|
|
83
|
+
{ lat: 47.75337697202738, lng: -122.16319480178332 },
|
|
84
|
+
{ lat: 47.751941595291726, lng: -122.15921456808333 },
|
|
85
|
+
{ lat: 47.753477542718706, lng: -122.15083544800348 },
|
|
86
|
+
{ lat: 47.754992309138885, lng: -122.15102829347933 },
|
|
87
|
+
{ lat: 47.757776865447056, lng: -122.1563603726317 },
|
|
88
|
+
];
|
|
89
|
+
const markers = cafes.map((coords) => {
|
|
90
|
+
return new mapboxgl.Marker().setLngLat(coords).addTo(map);
|
|
91
|
+
});
|
|
92
|
+
/* End add markers for cafes */
|
|
93
|
+
|
|
94
|
+
/* ShadeMap setup */
|
|
95
|
+
const loaderEl = document.getElementById("loader");
|
|
96
|
+
let now = new Date(1633358583454);
|
|
97
|
+
let shadeMap;
|
|
98
|
+
map.on("load", () => {
|
|
99
|
+
shadeMap = new ShadeMap({
|
|
100
|
+
apiKey:
|
|
101
|
+
"eyJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6InRwcGlvdHJvd3NraUBzaGFkZW1hcC5hcHAiLCJjcmVhdGVkIjoxNjYyNDkzMDY2Nzk0LCJpYXQiOjE2NjI0OTMwNjZ9.ovCrLTYsdKFTF6TW3DuODxCaAtGQ3qhcmqj3DWcol5g",
|
|
102
|
+
date: now,
|
|
103
|
+
color: "#01112f",
|
|
104
|
+
opacity: 0.7,
|
|
105
|
+
terrainSource: {
|
|
106
|
+
maxZoom: 15,
|
|
107
|
+
tileSize: 256,
|
|
108
|
+
getSourceUrl: ({ x, y, z }) =>
|
|
109
|
+
`https://s3.amazonaws.com/elevation-tiles-prod/terrarium/${z}/${x}/${y}.png`,
|
|
110
|
+
getElevation: ({ r, g, b, a }) => r * 256 + g + b / 256 - 32768,
|
|
111
|
+
_overzoom: 18,
|
|
112
|
+
},
|
|
113
|
+
getFeatures: async () => {
|
|
114
|
+
await mapLoaded(map);
|
|
115
|
+
const buildingData = map
|
|
116
|
+
.querySourceFeatures("composite", { sourceLayer: "building" })
|
|
117
|
+
.filter((feature) => {
|
|
118
|
+
return (
|
|
119
|
+
feature.properties &&
|
|
120
|
+
feature.properties.underground !== "true" &&
|
|
121
|
+
(feature.properties.height ||
|
|
122
|
+
feature.properties.render_height)
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
return buildingData;
|
|
126
|
+
},
|
|
127
|
+
debug: (msg) => {
|
|
128
|
+
console.log(new Date().toISOString(), msg);
|
|
129
|
+
},
|
|
130
|
+
}).addTo(map);
|
|
131
|
+
|
|
132
|
+
shadeMap.on("tileloaded", (loadedTiles, totalTiles) => {
|
|
133
|
+
loaderEl.innerText = `Loading: ${(
|
|
134
|
+
(loadedTiles / totalTiles) *
|
|
135
|
+
100
|
|
136
|
+
).toFixed(0)}%`;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
shadeMap.on("idle", async () => {
|
|
140
|
+
console.log("idle");
|
|
141
|
+
const promises = markers.map((marker) => {
|
|
142
|
+
const latlng = marker.getLngLat();
|
|
143
|
+
const { x, y } = map.project(latlng);
|
|
144
|
+
return shadeMap.isPositionInSun(x, y);
|
|
145
|
+
});
|
|
146
|
+
const results = await Promise.all(promises);
|
|
147
|
+
markers.forEach((marker, i) => {
|
|
148
|
+
const inTheSun = results[i] === true;
|
|
149
|
+
const element = marker.getElement();
|
|
150
|
+
const svg = element.getElementsByTagName("svg")[0];
|
|
151
|
+
const path = svg.getElementsByTagName("path")[0];
|
|
152
|
+
path.setAttribute("fill", inTheSun ? "#ffcc00" : "#ccf");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
/* End ShadeMap setup */
|
|
157
|
+
|
|
158
|
+
/* Controls setup */
|
|
159
|
+
let intervalTimer;
|
|
160
|
+
|
|
161
|
+
const increment = document.getElementById("increment");
|
|
162
|
+
const decrement = document.getElementById("decrement");
|
|
163
|
+
const play = document.getElementById("play");
|
|
164
|
+
const stop = document.getElementById("stop");
|
|
165
|
+
|
|
166
|
+
increment.addEventListener(
|
|
167
|
+
"click",
|
|
168
|
+
() => {
|
|
169
|
+
now = new Date(now.getTime() + 3600000);
|
|
170
|
+
shadeMap && shadeMap.setDate(now);
|
|
171
|
+
},
|
|
172
|
+
false
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
decrement.addEventListener(
|
|
176
|
+
"click",
|
|
177
|
+
() => {
|
|
178
|
+
now = new Date(now.getTime() - 3600000);
|
|
179
|
+
shadeMap && shadeMap.setDate(now);
|
|
180
|
+
},
|
|
181
|
+
false
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
play.addEventListener("click", () => {
|
|
185
|
+
intervalTimer = setInterval(() => {
|
|
186
|
+
now = new Date(now.getTime() + 60000);
|
|
187
|
+
shadeMap && shadeMap.setDate(now);
|
|
188
|
+
}, 100);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
stop.addEventListener("click", () => {
|
|
192
|
+
clearInterval(intervalTimer);
|
|
193
|
+
});
|
|
194
|
+
/* End controls setup */
|
|
195
|
+
</script>
|
|
196
|
+
</body>
|
|
197
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mapbox-gl-shadow-simulator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"description": "Display terrain and structure shadows cast by the sun in a custom style layer",
|
|
5
5
|
"main": "dist/mapbox-gl-shadow-simulator.umd.min.js",
|
|
6
6
|
"browser": "dist/mapbox-gl-shadow-simulator.umd.min.js",
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { LatLngLiteral, Map as LeafletMap } from "leaflet";
|
|
2
|
-
import { Map, MapboxGeoJSONFeature } from "mapbox-gl";
|
|
3
|
-
import { BuildingRasterizer } from "./buildings/BuildingRasterizer";
|
|
4
|
-
import { EventEmitter } from "./lib/EventEmitter";
|
|
5
|
-
import { HeightMap } from "./lib/heightMap";
|
|
6
|
-
import { TileMerger } from "./lib/TileMerger";
|
|
7
|
-
import { LatLngBounds } from "./map/geometryLeaflet";
|
|
8
|
-
import { UMap } from "./map/umap";
|
|
9
|
-
import { CompiledKernel } from "./shader/kernel";
|
|
10
|
-
import { Color } from "./types/color";
|
|
11
|
-
import { DSMSource, SunExposureOptions, TerrainSource } from "./types/shadeMapOptions";
|
|
12
|
-
export declare class ShadeMap extends EventEmitter {
|
|
13
|
-
_canvas: HTMLCanvasElement;
|
|
14
|
-
_gl?: WebGLRenderingContext;
|
|
15
|
-
_map?: Map | LeafletMap;
|
|
16
|
-
_compiledKernel?: CompiledKernel;
|
|
17
|
-
_color: Color;
|
|
18
|
-
_bounds?: LatLngBounds;
|
|
19
|
-
_buildingRasterizer?: BuildingRasterizer;
|
|
20
|
-
_tileMerger?: TileMerger;
|
|
21
|
-
_heightMap?: HeightMap;
|
|
22
|
-
options: {
|
|
23
|
-
date: Date;
|
|
24
|
-
color: string;
|
|
25
|
-
opacity: number;
|
|
26
|
-
sunExposure: {
|
|
27
|
-
enabled: boolean;
|
|
28
|
-
startDate: Date;
|
|
29
|
-
endDate: Date;
|
|
30
|
-
iterations: number;
|
|
31
|
-
};
|
|
32
|
-
apiKey: string;
|
|
33
|
-
terrainSource: {
|
|
34
|
-
maxZoom: number;
|
|
35
|
-
tileSize: number;
|
|
36
|
-
_overzoom: number;
|
|
37
|
-
getSourceUrl: (params: {
|
|
38
|
-
x: number;
|
|
39
|
-
y: number;
|
|
40
|
-
z: number;
|
|
41
|
-
}) => string;
|
|
42
|
-
getElevation: (params: {
|
|
43
|
-
r: number;
|
|
44
|
-
g: number;
|
|
45
|
-
b: number;
|
|
46
|
-
a: number;
|
|
47
|
-
}) => number;
|
|
48
|
-
};
|
|
49
|
-
dsmSource: {
|
|
50
|
-
bounds: {
|
|
51
|
-
lat: number;
|
|
52
|
-
lng: number;
|
|
53
|
-
}[];
|
|
54
|
-
data: Uint8ClampedArray;
|
|
55
|
-
width: number;
|
|
56
|
-
height: number;
|
|
57
|
-
maxHeight: number;
|
|
58
|
-
};
|
|
59
|
-
belowCanopy: boolean;
|
|
60
|
-
getFeatures: () => Promise<MapboxGeoJSONFeature[]>;
|
|
61
|
-
getSize: () => {
|
|
62
|
-
width: number;
|
|
63
|
-
height: number;
|
|
64
|
-
};
|
|
65
|
-
debug: (msg: string) => void;
|
|
66
|
-
};
|
|
67
|
-
constructor(...args: any[]);
|
|
68
|
-
onRemove(): this;
|
|
69
|
-
setDate(date: Date): this;
|
|
70
|
-
_setDateForTimezone(date: Date, tzId?: string): void;
|
|
71
|
-
setColor(color: string): this;
|
|
72
|
-
setOpacity(opacity: number): this;
|
|
73
|
-
setBelowCanopy(belowCanopy: boolean): this;
|
|
74
|
-
setTerrainSource(terrainSource: TerrainSource): this;
|
|
75
|
-
setDSMSource(dsmSource: DSMSource): this;
|
|
76
|
-
setSunExposure(enabled?: boolean, options?: SunExposureOptions): Promise<this>;
|
|
77
|
-
_lngLatToTextureCoords(lngLat: LatLngLiteral[]): number[][];
|
|
78
|
-
_getBounds(map: UMap, demZoom: number): LatLngBounds;
|
|
79
|
-
_getDEMZoom(map: UMap): number;
|
|
80
|
-
_reset(): Promise<this>;
|
|
81
|
-
_draw(heightMap: HeightMap): Promise<this>;
|
|
82
|
-
readPixel(x: number, y: number): Uint8Array;
|
|
83
|
-
readPixels(x: number, y: number, width: number, height: number): Uint8Array;
|
|
84
|
-
toGeoTiff(): {
|
|
85
|
-
data: Uint8Array;
|
|
86
|
-
metadata: {
|
|
87
|
-
width: number;
|
|
88
|
-
height: number;
|
|
89
|
-
ModelTiepoint: any[];
|
|
90
|
-
ModelPixelScale: number[];
|
|
91
|
-
GeographicTypeGeoKey: number;
|
|
92
|
-
GeogCitationGeoKey: string;
|
|
93
|
-
};
|
|
94
|
-
} | null;
|
|
95
|
-
_generateShadeProfile(params: {
|
|
96
|
-
locations: LatLngLiteral[];
|
|
97
|
-
dates: Date[];
|
|
98
|
-
sunColor: number[];
|
|
99
|
-
shadeColor: number[];
|
|
100
|
-
}): Uint8Array;
|
|
101
|
-
_generateLocationShadeProfile(params: {
|
|
102
|
-
location: LatLngLiteral;
|
|
103
|
-
startDate: Date;
|
|
104
|
-
endDate: Date;
|
|
105
|
-
sunColor: number[];
|
|
106
|
-
shadeColor: number[];
|
|
107
|
-
tzId: string;
|
|
108
|
-
}): {
|
|
109
|
-
data: Uint8Array;
|
|
110
|
-
width: number;
|
|
111
|
-
height: number;
|
|
112
|
-
};
|
|
113
|
-
getHoursOfSun(x: number, y: number): number;
|
|
114
|
-
_repositionCanvas(bounds: LatLngBounds): void;
|
|
115
|
-
_flush(): void;
|
|
116
|
-
flushSync(): void;
|
|
117
|
-
_parseColor(color: string): {
|
|
118
|
-
r: number;
|
|
119
|
-
g: number;
|
|
120
|
-
b: number;
|
|
121
|
-
};
|
|
122
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Map } from "leaflet";
|
|
2
|
-
import { CanvasOverlay } from "./components/CanvasOverlay";
|
|
3
|
-
import { LatLngBounds } from "./map/geometryLeaflet";
|
|
4
|
-
import { ShadeMap } from "./ShadeMap";
|
|
5
|
-
import { ShadeMapOptions } from "./types/shadeMapOptions";
|
|
6
|
-
declare class ShadeMapLeaflet extends ShadeMap {
|
|
7
|
-
_canvasOverlay?: CanvasOverlay;
|
|
8
|
-
constructor(options: ShadeMapOptions);
|
|
9
|
-
addTo(map: Map): this;
|
|
10
|
-
onAdd(map: Map): this;
|
|
11
|
-
_repositionCanvas(bounds: LatLngBounds): void;
|
|
12
|
-
_flush(): void;
|
|
13
|
-
}
|
|
14
|
-
declare module "leaflet" {
|
|
15
|
-
function shadeMap(options: ShadeMapOptions): ShadeMapLeaflet;
|
|
16
|
-
}
|
|
17
|
-
export default ShadeMapLeaflet;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Map } from "mapbox-gl";
|
|
2
|
-
import { LatLngBounds } from "./map/geometryLeaflet";
|
|
3
|
-
import { ShadeMap } from "./ShadeMap";
|
|
4
|
-
import { ShadeMapOptions } from "./types/shadeMapOptions";
|
|
5
|
-
export default class extends ShadeMap {
|
|
6
|
-
id: string;
|
|
7
|
-
type: string;
|
|
8
|
-
canvasSourceId: string;
|
|
9
|
-
attributionSourceId: string;
|
|
10
|
-
canvasLayerId: string;
|
|
11
|
-
attributionLayerId: string;
|
|
12
|
-
_map?: Map;
|
|
13
|
-
_refreshing: number;
|
|
14
|
-
_raf: number;
|
|
15
|
-
_moveEndHandler: () => void;
|
|
16
|
-
_framebuffer?: WebGLFramebuffer;
|
|
17
|
-
constructor(options: ShadeMapOptions);
|
|
18
|
-
render(gl: WebGLRenderingContext, matrix: number[]): void;
|
|
19
|
-
addTo(map: Map): this;
|
|
20
|
-
onAdd(map: Map): this;
|
|
21
|
-
onRemove(): this;
|
|
22
|
-
_getHeightMapCoords(x: number, y: number): any;
|
|
23
|
-
getHoursOfSun(x: number, y: number): number;
|
|
24
|
-
remove(): void;
|
|
25
|
-
readPixel(x: number, y: number): Uint8Array;
|
|
26
|
-
readPixels(x: number, y: number, width: number, height: number): Uint8Array;
|
|
27
|
-
_flush(): void;
|
|
28
|
-
_repositionCanvas(bounds: LatLngBounds): this;
|
|
29
|
-
}
|