const TREES_MIN_ZOOM = 17; var map = L.map('map').setView([51.05, 13.75], TREES_MIN_ZOOM, { maxZoom: 20, }); var tiles = L.tileLayer('https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg', { attribution: '© OpenStreetMap contributors', maxZoom: 17, }).addTo(map); var HeatmapLayer = L.TileLayer.extend({ getTileUrl: function (coords) { var cs = this._tileCoordsToNwSe(coords); var x1 = Math.min(cs[0].lng, cs[1].lng); var y1 = Math.min(cs[0].lat, cs[1].lat); var x2 = Math.max(cs[0].lng, cs[1].lng); var y2 = Math.max(cs[0].lat, cs[1].lat); return ["", "heatmap", x1, y1, x2, y2].join("/"); }, }); L.tileLayer('/heatmap/{z}/{x}/{y}/tile.png', { maxZoom: TREES_MIN_ZOOM - 1, opacity: 0.8, }).addTo(map); var treeIcon = L.icon({ iconUrl: "tree.png", iconSize: [12, 12], iconAnchor: [6, 6], popupAnchor: [0, -6], }); function treePopup(coords, entry) { var popup = L.popup() .setLatLng(coords); popup.on('add', function() { var info = {}; function update(newInfo) { Object.keys(newInfo).forEach(function(k) { info[k] = newInfo[k]; }); popup.setContent(function() { var div = document.createElement("div"); var h2 = document.createElement("h2"); h2.textContent = entry.german; if (entry.age) h2.textContent += " (" + entry.age + ")"; div.appendChild(h2); var p = document.createElement("p"); p.textContent = entry.botanic; div.appendChild(p); if (info.area) { p = document.createElement("p"); p.textContent = JSON.stringify(info.area); div.appendChild(p); } if (info.tree) { p = document.createElement("p"); p.style.maxHeight = "10em"; p.style.overflow = "scroll"; p.textContent = JSON.stringify(info.tree); div.appendChild(p); } return div; }); } update({}); fetch(["", "area", coords[1], coords[0]].join("/")) .then(res => res.json()) .then(data => { update({ area: data }); }); fetch(["", "tree", entry.id].join("/")) .then(res => res.json()) .then(data => { console.log("tree data", data); update({ tree: data }); }); }); return popup; } var trees; var trees_pending = false; var visible_trees; function updateTrees() { if (map.getZoom() < TREES_MIN_ZOOM) { if (trees) { trees.remove(); trees = null; } return; } var bounds = map.getBounds(); if (!trees) { trees = L.layerGroup().addTo(map); visible_trees = {}; } else { trees.eachLayer(function(marker) { var ll = marker.getLatLng(); if (ll.lng < bounds.getWest() || ll.lng > bounds.getEast() || ll.lat < bounds.getSouth() || ll.lat > bounds.getNorth()) { delete visible_trees[marker.options.id]; trees.removeLayer(marker); } }); } if (trees_pending) return; trees_pending = true; fetch(["", "trees", bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()].join("/")) .then(res => res.json()) .then(data => { if (map.getZoom() < TREES_MIN_ZOOM) { return; } data.forEach(function(entry) { var coords = [entry.coords[1], entry.coords[0]]; if (entry.planted) { entry.age = Math.round((Date.now() - Date.parse(entry.planted)) / (365.25 * 24 * 60 * 60 * 1000)); } if (!visible_trees[entry.id]) { visible_trees[entry.id] = true; var marker = L.marker(coords, { id: entry.id, title: entry.german, icon: treeIcon, }).bindPopup(treePopup(coords, entry)); trees.addLayer(marker); } }); trees_pending = false; }) .catch (error => { console.error('Error:' + error) trees_pending = false; }); } map.on('moveend', updateTrees); map.on('zoomend', updateTrees); updateTrees();