|
2 | 2 | <head> |
3 | 3 | <script src="./node_modules/maplibre-gl/dist/maplibre-gl.js"></script> |
4 | 4 | <link href="./node_modules/maplibre-gl/dist/maplibre-gl.css" rel="stylesheet" /> |
| 5 | + <link href="./index.css" rel="stylesheet" /> |
5 | 6 | </head> |
6 | 7 | <body> |
7 | | -<div id="map" style="width:100%;height:100%"></div> |
| 8 | +<div id="map"></div> |
| 9 | +<div id="popup"> |
| 10 | + <div><strong>Style:</strong> <span id="s-name"></span></div> |
| 11 | + <div><strong>Style URL:</strong> <span id="s-url"></span></div> |
| 12 | + <hr/> |
| 13 | + <div id="layers"></div> |
| 14 | + <button id="close" class="close">Close</button> |
| 15 | +</div> |
8 | 16 |
|
9 | 17 | <script type="module"> |
10 | 18 | import StyleFlipperControl from "./node_modules/maplibre-gl-style-flipper/index.js"; |
|
31 | 39 | }, |
32 | 40 | }; |
33 | 41 |
|
34 | | - var map = new maplibregl.Map({ |
| 42 | + let map = new maplibregl.Map({ |
35 | 43 | container: 'map', |
36 | 44 | style: mapStyles["historical"].url, |
37 | 45 | center: [-74.033, 40.6259], |
|
40 | 48 | }) |
41 | 49 |
|
42 | 50 | // Create an instance of StyleFlipperControl |
43 | | - const styleFlipperControl = new StyleFlipperControl(mapStyles); |
| 51 | + const flipper = new StyleFlipperControl(mapStyles); |
44 | 52 |
|
45 | 53 | // Set the initial style code |
46 | | - styleFlipperControl.setCurrentStyleCode("historical"); |
| 54 | + flipper.setCurrentStyleCode("historical"); |
47 | 55 |
|
48 | 56 | // Add the control to the map |
49 | | - map.addControl(styleFlipperControl, "bottom-right"); |
| 57 | + map.addControl(flipper, "bottom-right"); |
| 58 | + |
| 59 | + const |
| 60 | + p = document.getElementById("popup"), |
| 61 | + sName = document.getElementById("s-name"), |
| 62 | + sUrl = document.getElementById("s-url"), |
| 63 | + layersEl = document.getElementById("layers"), |
| 64 | + closeBtn = document.getElementById("close") |
| 65 | + ; |
| 66 | + |
| 67 | + map.on("load", () => { |
| 68 | + map.on("click", (e) => { |
| 69 | + e.originalEvent.stopPropagation(); |
| 70 | + const cur = flipper.currentStyleCode || "historical"; |
| 71 | + sName.textContent = cur; |
| 72 | + sUrl.textContent = mapStyles[cur].url || ""; |
| 73 | + |
| 74 | + layersEl.innerHTML = ""; |
| 75 | + const tol = 2; |
| 76 | + const bbox = [[e.point.x - tol, e.point.y - tol], [e.point.x + tol, e.point.y + tol]]; |
| 77 | + const features = map.queryRenderedFeatures(bbox); |
| 78 | + |
| 79 | + if (!features || features.length === 0) { |
| 80 | + layersEl.textContent = "No rendered features at this point."; |
| 81 | + } else { |
| 82 | + const byLayer = {}; |
| 83 | + features.forEach(f => { |
| 84 | + const id = (f.layer && f.layer.id) ? f.layer.id : "(no-layer)"; |
| 85 | + (byLayer[id] || (byLayer[id] = [])).push(f); |
| 86 | + }); |
| 87 | + for (const lid of Object.keys(byLayer)) { |
| 88 | + const wrapper = document.createElement("div"); |
| 89 | + const title = document.createElement("div"); |
| 90 | + title.className = "layer-title"; |
| 91 | + |
| 92 | + const left = document.createElement("div"); |
| 93 | + left.className = "layer-left"; |
| 94 | + const nameSpan = document.createElement("span"); |
| 95 | + nameSpan.textContent = `${lid} — ${byLayer[lid].length} feature(s)`; |
| 96 | + left.appendChild(nameSpan); |
| 97 | + |
| 98 | + const copyBtn = document.createElement("button"); |
| 99 | + copyBtn.className = "copy-btn"; |
| 100 | + copyBtn.type = "button"; |
| 101 | + copyBtn.innerText = "📋"; |
| 102 | + copyBtn.title = "Copy layer id"; |
| 103 | + copyBtn.dataset.layer = lid; |
| 104 | + |
| 105 | + // copy handler |
| 106 | + copyBtn.addEventListener("click", async (ev) => { |
| 107 | + ev.stopPropagation(); // avoid closing popup |
| 108 | + try { |
| 109 | + await navigator.clipboard.writeText(lid); |
| 110 | + const prev = copyBtn.innerText; |
| 111 | + copyBtn.innerText = "Copied!"; |
| 112 | + setTimeout(() => copyBtn.innerText = prev, 1200); |
| 113 | + } catch (err) { |
| 114 | + copyBtn.innerText = "Err"; |
| 115 | + setTimeout(() => copyBtn.innerText = "📋", 1200); |
| 116 | + } |
| 117 | + }); |
| 118 | + |
| 119 | + left.appendChild(copyBtn); |
| 120 | + title.appendChild(left); |
| 121 | + |
| 122 | + // append features props (only properties) below |
| 123 | + const propsDiv = document.createElement("div"); |
| 124 | + byLayer[lid].forEach((feat, i) => { |
| 125 | + const pre = document.createElement("pre"); |
| 126 | + pre.textContent = `feature ${i} id:${feat.id ?? "n/a"}\n` + JSON.stringify(feat.properties || {}, null, 2); |
| 127 | + propsDiv.appendChild(pre); |
| 128 | + }); |
| 129 | + |
| 130 | + title.style.marginBottom = "6px"; |
| 131 | + wrapper.appendChild(title); |
| 132 | + wrapper.appendChild(propsDiv); |
| 133 | + layersEl.appendChild(wrapper); |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + p.style.left = (e.originalEvent.clientX + 10) + "px"; |
| 138 | + p.style.top = (e.originalEvent.clientY + 10) + "px"; |
| 139 | + p.style.display = "block"; |
| 140 | + }); |
| 141 | + }); |
| 142 | + |
| 143 | + // close handling: ignore clicks inside popup |
| 144 | + document.addEventListener("click", (ev) => { |
| 145 | + if (!p.contains(ev.target)) p.style.display = "none"; |
| 146 | + }); |
| 147 | + closeBtn.addEventListener("click", () => p.style.display = "none"); |
50 | 148 |
|
51 | 149 | </script> |
52 | 150 |
|
|
0 commit comments