mirror of
https://github.com/noerw/sentinel_fire
synced 2025-03-13 02:30:27 +01:00
153 lines
5.5 KiB
Text
153 lines
5.5 KiB
Text
<!DOCTYPE html>
|
|
<html lang="en" dir="ltr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
|
|
<style>
|
|
html, body, #map {
|
|
margin: 0;
|
|
padding: 0;
|
|
height: 100%;
|
|
}
|
|
</style>
|
|
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css" crossorigin=""/>
|
|
<link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" type="text/css">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/GuillaumeAmat/leaflet-overpass-layer/dist/OverPassLayer.css" type="text/css">
|
|
</head>
|
|
|
|
<body>
|
|
<div id="map"></div>
|
|
|
|
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet-src.js" crossorigin=""></script>
|
|
<!-- OSM overlay -->
|
|
<script src="https://cdn.jsdelivr.net/gh/GuillaumeAmat/leaflet-overpass-layer/dist/OverPassLayer.bundle.js" crossorigin=""></script>
|
|
<!-- geotiff visualization -->
|
|
<script src="https://d3js.org/d3.v4.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/1.3.4/chroma.min.js"></script>
|
|
<script src="https://npmcdn.com/geotiff@0.3.6/dist/geotiff.js"></script>
|
|
<script src="https://unpkg.com/leaflet-canvaslayer-field@1.4.1/dist/leaflet.canvaslayer.field.js" crossorigin=""></script>
|
|
<!-- for the time slider -->
|
|
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
|
<script src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/gh/dwilhelm89/LeafletSlider/dist/leaflet.SliderControl.min.js" crossorigin=""></script>
|
|
|
|
<!-- the variables below are inserted via %%templating%% -->
|
|
<!-- IMPORTANT! TIFs must be in EPGS:4326! -->
|
|
<script type="application/javascript">
|
|
const aoi = %%AOI%%
|
|
const tifs = [%%TIFS%%].sort()
|
|
</script>
|
|
|
|
<script type="application/javascript">
|
|
const initialZoom = 12
|
|
const bgOsm = L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}.png', {
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
|
})
|
|
const bgSat = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png', {
|
|
attribution: 'Sources: Esri, DigitalGlobe, GeoEye, i-cubed, USDA FSA, USGS, AEX, Getmapping, Aerogrid, IGN, IGP, swisstopo, and the GIS User Community'
|
|
})
|
|
const aoiLayer = L.geoJSON(aoi, {
|
|
style: {
|
|
interactive: false,
|
|
fillColor: '#aaa',
|
|
opacity: 0,
|
|
}
|
|
})
|
|
|
|
const map = L.map('map', { scale: true })
|
|
.addLayer(bgSat)
|
|
.addLayer(aoiLayer)
|
|
.setView(aoiLayer.getBounds().getCenter(), initialZoom)
|
|
.setMaxBounds(aoiLayer.getBounds())
|
|
|
|
// load village / city placemarks from OSM
|
|
const villagesLayer = new L.OverPassLayer({
|
|
loadedBounds: [aoiLayer.getBounds()],
|
|
minZoom: initialZoom,
|
|
minZoomIndicatorEnabled: false,
|
|
endPoint: 'https://overpass-api.de/api/',
|
|
query: 'node [place~"(village|town|suburb|city)"]({{bbox}}); out;',
|
|
|
|
// customize marker size & popup content
|
|
onSuccess(data) {
|
|
for (let i = 0; i < data.elements.length; i++) {
|
|
const e = data.elements[i]
|
|
if (e.id in this._ids) continue
|
|
this._ids[e.id] = true
|
|
|
|
const marker = L.circle(L.latLng(e.lat, e.lon), 100, {
|
|
stroke: false,
|
|
fillColor: 'lightblue',
|
|
fillOpacity: 0.8,
|
|
})
|
|
|
|
const popupDiv = document.createElement('div');
|
|
popupDiv.innerHTML = `${e.tags['place']}: <b>${e.tags['name']}</b> (${e.tags['name:en']})`
|
|
const popup = L.popup().setContent(popupDiv)
|
|
marker.bindPopup(popup);
|
|
|
|
this._markers.addLayer(marker);
|
|
}
|
|
},
|
|
}).addTo(map)
|
|
|
|
L.control.scale().addTo(map)
|
|
L.control.layers({
|
|
'OpenStreetMap Dark': bgOsm,
|
|
'ESRI World Imagery': bgSat,
|
|
}, {
|
|
'area of interest': aoiLayer,
|
|
// 'dNBR timeseries': timeseriesLayer, // buggy
|
|
'OSM villages': villagesLayer,
|
|
}).addTo(map);
|
|
|
|
function tif2Layer (url) {
|
|
// extract date from filename
|
|
const time = url.split('/').pop().split('.')[0]
|
|
console.log(`fetching TIF for ${time} at ${url}`)
|
|
|
|
return fetch(url)
|
|
.then(res => res.arrayBuffer())
|
|
.then(tifData => {
|
|
console.log(`parsing TIF at ${url}`)
|
|
const scalarField = L.ScalarField.fromGeoTIFF(tifData, bandIndex = 0)
|
|
|
|
console.log(`creating layer for TIF at ${url}`)
|
|
const layer = L.canvasLayer.scalarField(scalarField, {
|
|
time, // time is used as display name in timeslider plugin
|
|
color: chroma.scale(['#ddc739', '#e59f27', '#f21602']).domain([1, 3]),
|
|
opacity: 0.7
|
|
})
|
|
|
|
layer.setFilter(v => v >= 1)
|
|
|
|
layer.on('click', function (e) {
|
|
if (e.value !== null) {
|
|
const dnbr = e.value.toFixed(0)
|
|
const popup = L.popup()
|
|
.setLatLng(e.latlng)
|
|
.setContent(`<span class="popupText">dNBR: ${dnbr}</span>`)
|
|
.openOn(map)
|
|
}
|
|
})
|
|
return layer
|
|
})
|
|
}
|
|
|
|
// load all TIF files
|
|
Promise.all(tifs.map(url => tif2Layer(url)))
|
|
// create a layer group for the time slider plugin with all TIFs
|
|
.then(layers => {
|
|
const sliderControl = L.control.sliderControl({
|
|
layer: L.layerGroup(layers),
|
|
follow: true
|
|
})
|
|
|
|
map.addControl(sliderControl)
|
|
sliderControl.startSlider()
|
|
})
|
|
.catch(console.error)
|
|
</script>
|
|
</body>
|
|
</html>
|