200 lines
No EOL
6.4 KiB
HTML
200 lines
No EOL
6.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="it">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>Basemap Protomaps + PMTiles (locale)</title>
|
|
<!-- CSS MapLibre -->
|
|
<link rel="stylesheet" href="/assets/maplibre/maplibre-gl.css">
|
|
<style>
|
|
html,body,#map{height:100%;margin:0}
|
|
.ctrl{
|
|
position:absolute;z-index:10;top:8px;left:8px;
|
|
background:rgba(255,255,255,.85);padding:6px 8px;border-radius:6px;
|
|
font:13px/1.2 system-ui,-apple-system,Segoe UI,Roboto,sans-serif
|
|
}
|
|
.ctrl label{display:inline-flex;gap:.4rem;align-items:center;cursor:pointer}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="map"></div>
|
|
<div class="ctrl">
|
|
<label title="Amplificazione verticale del terreno">
|
|
Exaggeration:
|
|
<input id="exag" type="range" min="0.8" max="2.5" step="0.1" value="1.6">
|
|
<span id="exagv">1.6</span>x
|
|
</label>
|
|
</div>
|
|
|
|
<!-- Librerie locali -->
|
|
<script src="/assets/maplibre/maplibre-gl.js"></script>
|
|
<script src="/assets/pmtiles/pmtiles.js"></script>
|
|
<script src="/assets/basemaps/basemaps.js" crossorigin="anonymous"></script>
|
|
<!-- Plugin per curve di livello (on-the-fly) -->
|
|
<script src="https://unpkg.com/maplibre-contour@0.1.0/dist/index.min.js"></script>
|
|
|
|
<script>
|
|
// 1) PMTiles protocol
|
|
const protocol = new pmtiles.Protocol({ metadata: true });
|
|
maplibregl.addProtocol("pmtiles", protocol.tile);
|
|
|
|
// 2) Basemap vettoriale (MVT) in PMTiles sotto /tiles
|
|
const PMTILES_URL = "tiles/planet.pmtiles"; // basemap MVT
|
|
protocol.add(new pmtiles.PMTiles(PMTILES_URL));
|
|
|
|
// 3) Asset locali
|
|
const origin = window.location.origin;
|
|
const spriteUrl = `${origin}/assets/sprites/v4/light`;
|
|
const glyphsUrl = `${origin}/assets/fonts/{fontstack}/{range}.pbf`;
|
|
//const glyphsUrl = 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf';
|
|
const terrainDEM = `${origin}/data/terrain.pmtiles`; // DEM Terrarium (PMTiles locale)
|
|
|
|
// 4) Mappa
|
|
const map = window.map = new maplibregl.Map({
|
|
container: "map",
|
|
style: {
|
|
version: 8,
|
|
glyphs: glyphsUrl,
|
|
sprite: spriteUrl,
|
|
sources: {
|
|
protomaps: {
|
|
type: "vector",
|
|
url: "pmtiles://" + PMTILES_URL,
|
|
attribution: 'Protomaps © OpenStreetMap'
|
|
}
|
|
},
|
|
layers: basemaps.layers("protomaps", basemaps.namedFlavor("light"), { lang: "en" })
|
|
},
|
|
center: [12.05, 44.22],
|
|
zoom: 8,
|
|
pitch: 60,
|
|
maxPitch: 85
|
|
});
|
|
|
|
// Fallback icone mancanti nello sprite (es. "townhall")
|
|
map.on('styleimagemissing', (e) => {
|
|
const id = e.id;
|
|
if (!map.hasImage(id)) {
|
|
const empty = new Uint8ClampedArray([0,0,0,0]); // 1x1 trasparente
|
|
map.addImage(id, { width:1, height:1, data: empty });
|
|
}
|
|
});
|
|
|
|
map.on('load', () => {
|
|
// === A) Terreno 3D locale (PMTiles) + hillshade su sorgente separata ===
|
|
map.addSource('terrain-dem', {
|
|
type: 'raster-dem',
|
|
url: 'pmtiles://' + terrainDEM,
|
|
encoding: 'terrarium',
|
|
tileSize: 256
|
|
});
|
|
map.addSource('hillshade-dem', {
|
|
type: 'raster-dem',
|
|
url: 'pmtiles://' + terrainDEM,
|
|
encoding: 'terrarium',
|
|
tileSize: 256
|
|
});
|
|
map.setTerrain({ source: 'terrain-dem', exaggeration: 1.6 });
|
|
|
|
const labelLayerId = map.getStyle().layers.find(l => l.type === 'symbol')?.id;
|
|
|
|
map.addLayer({
|
|
id: 'hillshade',
|
|
type: 'hillshade',
|
|
source: 'hillshade-dem',
|
|
paint: {
|
|
'hillshade-exaggeration': 0.5,
|
|
'hillshade-illumination-direction': 315
|
|
}
|
|
}, labelLayerId);
|
|
|
|
// === B) Curve di livello on-the-fly (DEM online Z/X/Y) ===
|
|
// - Usa AWS Joerd Terrarium per test immediato (Z/X/Y).
|
|
// - maplibre-contour genera isoipse in WebWorker e le espone come vector tiles.
|
|
const demSource = new mlcontour.DemSource({
|
|
url: 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png',
|
|
encoding: 'terrarium',
|
|
maxzoom: 12,
|
|
worker: true
|
|
});
|
|
demSource.setupMaplibre(maplibregl);
|
|
|
|
// (facoltativo) hillshade che riusa la cache del plugin:
|
|
map.addSource('hills-contour-cache', {
|
|
type: 'raster-dem',
|
|
tiles: [demSource.sharedDemProtocolUrl],
|
|
tileSize: 512,
|
|
maxzoom: 12
|
|
});
|
|
|
|
// Sorgente vettoriale delle curve (fornita dal plugin)
|
|
map.addSource('contourSource', {
|
|
type: 'vector',
|
|
tiles: [
|
|
demSource.contourProtocolUrl({
|
|
multiplier: 1, // 1 = metri (3.28084 per piedi)
|
|
thresholds: {
|
|
11: [200, 1000],
|
|
12: [100, 500],
|
|
13: [50, 200],
|
|
14: [25, 100],
|
|
15: [10, 50]
|
|
},
|
|
contourLayer: 'contours',
|
|
elevationKey: 'ele',
|
|
levelKey: 'level' // 0 = minore, 1 = maggiore
|
|
})
|
|
],
|
|
maxzoom: 15
|
|
});
|
|
|
|
// Linee isoipse
|
|
map.addLayer({
|
|
id: 'contours',
|
|
type: 'line',
|
|
source: 'contourSource',
|
|
'source-layer': 'contours',
|
|
paint: {
|
|
'line-color': '#6b6b6b',
|
|
'line-opacity': 0.6,
|
|
'line-width': ['match', ['get', 'level'], 1, 1.2, 0.6]
|
|
}
|
|
}, labelLayerId);
|
|
|
|
// Etichette solo sulle maggiori
|
|
map.addLayer({
|
|
id: 'contour-labels',
|
|
type: 'symbol',
|
|
source: 'contourSource',
|
|
'source-layer': 'contours',
|
|
filter: ['==', ['get', 'level'], 1],
|
|
layout: {
|
|
'symbol-placement': 'line',
|
|
'text-field': ['number-format', ['get', 'ele'], {}],
|
|
'text-size': 11,
|
|
'text-font': ['Noto Sans Bold']
|
|
},
|
|
paint: { 'text-halo-color': 'white', 'text-halo-width': 1 }
|
|
}, labelLayerId);
|
|
|
|
// Slider con THROTTLE (riduce ricalcoli durante il drag)
|
|
const exIn = document.getElementById('exag');
|
|
const exSp = document.getElementById('exagv');
|
|
let tId = null, pending = null;
|
|
const APPLY_EVERY_MS = 150;
|
|
|
|
const applyExag = (v) => {
|
|
exSp.textContent = v.toFixed(1);
|
|
map.setTerrain({ source: 'terrain-dem', exaggeration: v });
|
|
};
|
|
exIn.addEventListener('input', (e) => {
|
|
pending = parseFloat(e.target.value);
|
|
if (tId) return;
|
|
tId = setTimeout(() => { applyExag(pending); tId = null; }, APPLY_EVERY_MS);
|
|
});
|
|
exIn.addEventListener('change', (e) => applyExag(parseFloat(e.target.value)));
|
|
});
|
|
|
|
map.addControl(new maplibregl.NavigationControl({ visualizePitch: true }), "top-right");
|
|
</script>
|
|
</body>
|
|
</html> |