|
63 | 63 | margin-left: 5px; |
64 | 64 | font-weight: normal; |
65 | 65 | } |
| 66 | + |
| 67 | + .section-divider { |
| 68 | + border: none; |
| 69 | + border-top: 2px solid #ccc; |
| 70 | + margin: 12px 0 10px; |
| 71 | + } |
| 72 | + |
| 73 | + .section-title { |
| 74 | + font-weight: bold; |
| 75 | + font-size: 13px; |
| 76 | + margin-bottom: 8px; |
| 77 | + color: #333; |
| 78 | + text-transform: uppercase; |
| 79 | + letter-spacing: 0.5px; |
| 80 | + } |
66 | 81 | </style> |
67 | 82 | </head> |
68 | 83 |
|
69 | 84 | <body> |
70 | 85 | <div id='map'></div> |
71 | 86 | <div id='controls'> |
| 87 | + <div class="section-title">Layer 1 — Flight Lines</div> |
| 88 | + |
72 | 89 | <div class="control-group"> |
73 | 90 | <label> |
74 | 91 | <input type="checkbox" id="blendModeEnabled" checked onchange="toggleBlendMode(this)"> |
|
80 | 97 | <label for="blendModeSelect">Blend Mode:</label> |
81 | 98 | <select id="blendModeSelect" onchange="updateBlendMode()"> |
82 | 99 | <option value="multiply">multiply</option> |
83 | | - <option value="additive">additive</option> |
| 100 | + <option value="additive" selected>additive</option> |
84 | 101 | </select> |
85 | 102 | </div> |
86 | 103 |
|
87 | 104 | <div class="control-group"> |
88 | 105 | <label for="opacitySlider"> |
89 | | - Opacity: <span class="slider-value" id="opacityValue">0.01</span> |
| 106 | + Opacity: <span class="slider-value" id="opacityValue">1.00</span> |
90 | 107 | </label> |
91 | | - <input type="range" id="opacitySlider" min="0" max="1" step="0.01" value="0.01" oninput="updateOpacity(this)"> |
| 108 | + <input type="range" id="opacitySlider" min="0" max="1" step="0.001" value="1" oninput="updateOpacity(this)"> |
| 109 | + </div> |
| 110 | + |
| 111 | + <div class="control-group"> |
| 112 | + <label for="additiveClampSlider"> |
| 113 | + Additive Clamp: <span class="slider-value" id="additiveClampValue">0 (auto)</span> |
| 114 | + </label> |
| 115 | + <input type="range" id="additiveClampSlider" min="0" max="200" step="1" value="0" oninput="updateAdditiveClamp(this)"> |
92 | 116 | </div> |
93 | 117 |
|
94 | 118 | <div class="control-group"> |
95 | 119 | <label for="basemapSelect">Basemap & Line Color:</label> |
96 | 120 | <select id="basemapSelect" onchange="updateBasemap()"> |
97 | 121 | <option value="light">Light (Blue Lines)</option> |
98 | | - <option value="dark">Dark (Yellow Lines)</option> |
| 122 | + <option value="dark" selected>Dark (Yellow Lines)</option> |
99 | 123 | </select> |
100 | 124 | </div> |
101 | 125 |
|
|
104 | 128 | <input type="text" id="geojsonUrl" value="https://docs.mapbox.com/mapbox-gl-js/assets/flights-200k.geojson"> |
105 | 129 | <button onclick="loadGeoJSON()" style="margin-top: 5px; width: 100%; padding: 4px;">Load GeoJSON</button> |
106 | 130 | </div> |
| 131 | + |
| 132 | + <hr class="section-divider"> |
| 133 | + <div class="section-title">Layer 2 — Star Shape</div> |
| 134 | + |
| 135 | + <div class="control-group"> |
| 136 | + <label> |
| 137 | + <input type="checkbox" id="polygonBlendModeEnabled" checked onchange="togglePolygonBlendMode(this)"> |
| 138 | + Enable Blend Mode |
| 139 | + </label> |
| 140 | + </div> |
| 141 | + |
| 142 | + <div class="control-group"> |
| 143 | + <label for="polygonBlendModeSelect">Blend Mode:</label> |
| 144 | + <select id="polygonBlendModeSelect" onchange="updatePolygonBlendMode()"> |
| 145 | + <option value="multiply">multiply</option> |
| 146 | + <option value="additive">additive</option> |
| 147 | + </select> |
| 148 | + </div> |
| 149 | + |
| 150 | + <div class="control-group"> |
| 151 | + <label for="polygonColorSelect">Line Color:</label> |
| 152 | + <select id="polygonColorSelect" onchange="updatePolygonColor()"> |
| 153 | + <option value="#0080ff">Blue</option> |
| 154 | + <option value="#ff4400">Red</option> |
| 155 | + <option value="#00cc44">Green</option> |
| 156 | + <option value="#ffcc00">Yellow</option> |
| 157 | + <option value="#aa00ff">Purple</option> |
| 158 | + </select> |
| 159 | + </div> |
| 160 | + |
| 161 | + <div class="control-group"> |
| 162 | + <label for="maineOpacitySlider"> |
| 163 | + Opacity: <span class="slider-value" id="maineOpacityValue">0.00</span> |
| 164 | + </label> |
| 165 | + <input type="range" id="maineOpacitySlider" min="0" max="1" step="0.01" value="0" |
| 166 | + oninput="updateStarOpacity(this)"> |
| 167 | + </div> |
107 | 168 | </div> |
108 | 169 |
|
109 | 170 | <script type="module"> |
110 | 171 | import mapboxgl from '../dist/esm-dev/mapbox-gl.js'; |
111 | 172 | import { getAccessToken } from './access_token_generated.js'; |
112 | 173 | mapboxgl.accessToken = getAccessToken(); |
113 | 174 |
|
114 | | - let currentOpacity = 0.01; |
115 | | - let currentBlendMode = 'multiply'; |
| 175 | + let currentOpacity = 1.0; |
| 176 | + let currentBlendMode = 'additive'; |
116 | 177 | let blendModeEnabled = true; |
| 178 | + let additiveClamp = 0; |
| 179 | + |
| 180 | + let polygonBlendModeEnabled = true; |
| 181 | + let polygonBlendMode = 'multiply'; |
| 182 | + let polygonLineColor = '#0080ff'; |
| 183 | + let starOpacity = 0; |
117 | 184 |
|
118 | 185 | const basemapConfig = { |
119 | 186 | light: { |
|
126 | 193 | } |
127 | 194 | }; |
128 | 195 |
|
129 | | - let currentBasemap = 'light'; |
| 196 | + let currentBasemap = 'dark'; |
| 197 | + |
| 198 | + // Abstract 6-pointed star centred on Munich [11.576, 48.137] |
| 199 | + const cx = 11.576, cy = 48.137; |
| 200 | + const starCoordinates = [ |
| 201 | + [cx + 0.0, cy + 16.0], |
| 202 | + [cx + 5.0, cy + 9.0], |
| 203 | + [cx + 16.0, cy + 9.0], |
| 204 | + [cx + 7.0, cy + 1.0], |
| 205 | + [cx + 11.0, cy - 10.0], |
| 206 | + [cx + 0.0, cy - 4.0], |
| 207 | + [cx - 11.0, cy - 10.0], |
| 208 | + [cx - 7.0, cy + 1.0], |
| 209 | + [cx - 16.0, cy + 9.0], |
| 210 | + [cx - 5.0, cy + 9.0], |
| 211 | + [cx + 0.0, cy + 16.0] |
| 212 | + ]; |
| 213 | + |
| 214 | + const starGeoJSON = { |
| 215 | + type: 'Feature', |
| 216 | + geometry: { |
| 217 | + type: 'Polygon', |
| 218 | + coordinates: [starCoordinates] |
| 219 | + } |
| 220 | + }; |
130 | 221 |
|
131 | 222 | const map = window.map = new mapboxgl.Map({ |
132 | 223 | container: 'map', |
|
135 | 226 | imports: [ |
136 | 227 | { |
137 | 228 | id: 'basemap', |
138 | | - url: basemapConfig.light.url |
| 229 | + url: basemapConfig.dark.url |
139 | 230 | } |
140 | 231 | ], |
141 | 232 | version: 8, |
|
144 | 235 | type: 'geojson', |
145 | 236 | data: document.getElementById('geojsonUrl').value, |
146 | 237 | }, |
| 238 | + 'star': { |
| 239 | + type: 'geojson', |
| 240 | + data: starGeoJSON |
| 241 | + } |
147 | 242 | }, |
148 | 243 | layers: [ |
| 244 | + { |
| 245 | + id: 'star-outline', |
| 246 | + type: 'line', |
| 247 | + source: 'star', |
| 248 | + paint: { |
| 249 | + 'line-color': polygonLineColor, |
| 250 | + 'line-width': 200, |
| 251 | + 'line-opacity': starOpacity, |
| 252 | + 'line-blend-mode': polygonBlendMode |
| 253 | + } |
| 254 | + }, |
149 | 255 | { |
150 | 256 | id: 'overlay', |
151 | 257 | type: 'line', |
152 | 258 | source: 'overlay', |
153 | 259 | paint: { |
154 | | - 'line-color': `rgba(${basemapConfig.light.lineColor.join(',')}, ${currentOpacity})`, |
| 260 | + 'line-color': `rgba(${basemapConfig.dark.lineColor.join(',')}, ${currentOpacity})`, |
155 | 261 | 'line-blend-mode': currentBlendMode, |
| 262 | + 'line-blend-additive-clamp': additiveClamp, |
156 | 263 | 'line-width': 1 |
157 | 264 | } |
158 | 265 | } |
159 | 266 | ] |
160 | 267 | }, |
161 | | - center: [-73.985, 40.748], |
162 | | - zoom: 6, |
| 268 | + center: [cx, cy], |
| 269 | + zoom: 4, |
163 | 270 | pitch: 0, |
164 | 271 | hash: true |
165 | 272 | }); |
|
172 | 279 | function updateLayer() { |
173 | 280 | if (map.getLayer('overlay')) { |
174 | 281 | map.setPaintProperty('overlay', 'line-color', getLineColor()); |
175 | | - map.setPaintProperty('overlay', 'line-blend-mode', blendModeEnabled ? currentBlendMode : undefined); |
| 282 | + map.setPaintProperty('overlay', 'line-blend-mode', blendModeEnabled ? currentBlendMode : 'default'); |
| 283 | + map.setPaintProperty('overlay', 'line-blend-additive-clamp', additiveClamp); |
176 | 284 | } |
177 | 285 | } |
178 | 286 |
|
| 287 | + function updateAdditiveClamp(slider) { |
| 288 | + additiveClamp = parseFloat(slider.value); |
| 289 | + document.getElementById('additiveClampValue').textContent = additiveClamp === 0 ? '0 (auto)' : additiveClamp.toFixed(0); |
| 290 | + updateLayer(); |
| 291 | + } |
| 292 | + |
179 | 293 | function updateOpacity(slider) { |
180 | 294 | currentOpacity = parseFloat(slider.value); |
181 | 295 | document.getElementById('opacityValue').textContent = currentOpacity.toFixed(2); |
|
210 | 324 | type: 'geojson', |
211 | 325 | data: document.getElementById('geojsonUrl').value, |
212 | 326 | }, |
| 327 | + 'star': { |
| 328 | + type: 'geojson', |
| 329 | + data: starGeoJSON |
| 330 | + } |
213 | 331 | }, |
214 | 332 | layers: [ |
| 333 | + { |
| 334 | + id: 'star-outline', |
| 335 | + type: 'line', |
| 336 | + source: 'star', |
| 337 | + paint: { |
| 338 | + 'line-color': polygonLineColor, |
| 339 | + 'line-width': 200, |
| 340 | + 'line-opacity': starOpacity, |
| 341 | + 'line-blend-mode': polygonBlendModeEnabled ? polygonBlendMode : 'default' |
| 342 | + } |
| 343 | + }, |
215 | 344 | { |
216 | 345 | id: 'overlay', |
217 | 346 | type: 'line', |
218 | 347 | source: 'overlay', |
219 | 348 | paint: { |
220 | 349 | 'line-color': getLineColor(), |
221 | | - 'line-blend-mode': blendModeEnabled ? currentBlendMode : 'normal', |
| 350 | + 'line-blend-mode': blendModeEnabled ? currentBlendMode : 'default', |
| 351 | + 'line-blend-additive-clamp': additiveClamp, |
222 | 352 | 'line-width': 1 |
223 | 353 | } |
224 | 354 | } |
|
235 | 365 | } |
236 | 366 | } |
237 | 367 |
|
| 368 | + function updateStarOpacity(slider) { |
| 369 | + starOpacity = parseFloat(slider.value); |
| 370 | + document.getElementById('maineOpacityValue').textContent = starOpacity.toFixed(2); |
| 371 | + if (map.getLayer('star-outline')) { |
| 372 | + map.setPaintProperty('star-outline', 'line-opacity', starOpacity); |
| 373 | + } |
| 374 | + } |
| 375 | + |
| 376 | + function togglePolygonBlendMode(checkbox) { |
| 377 | + polygonBlendModeEnabled = checkbox.checked; |
| 378 | + document.getElementById('polygonBlendModeSelect').disabled = !polygonBlendModeEnabled; |
| 379 | + if (map.getLayer('star-outline')) { |
| 380 | + map.setPaintProperty('star-outline', 'line-blend-mode', polygonBlendModeEnabled ? polygonBlendMode : 'default'); |
| 381 | + } |
| 382 | + } |
| 383 | + |
| 384 | + function updatePolygonBlendMode() { |
| 385 | + polygonBlendMode = document.getElementById('polygonBlendModeSelect').value; |
| 386 | + if (map.getLayer('star-outline') && polygonBlendModeEnabled) { |
| 387 | + map.setPaintProperty('star-outline', 'line-blend-mode', polygonBlendMode); |
| 388 | + } |
| 389 | + } |
| 390 | + |
| 391 | + function updatePolygonColor() { |
| 392 | + polygonLineColor = document.getElementById('polygonColorSelect').value; |
| 393 | + if (map.getLayer('star-outline')) { |
| 394 | + map.setPaintProperty('star-outline', 'line-color', polygonLineColor); |
| 395 | + } |
| 396 | + } |
| 397 | + |
238 | 398 | window.updateOpacity = updateOpacity; |
| 399 | + window.updateAdditiveClamp = updateAdditiveClamp; |
239 | 400 | window.updateBlendMode = updateBlendMode; |
240 | 401 | window.toggleBlendMode = toggleBlendMode; |
241 | 402 | window.updateBasemap = updateBasemap; |
242 | 403 | window.loadGeoJSON = loadGeoJSON; |
| 404 | + window.updateStarOpacity = updateStarOpacity; |
| 405 | + window.togglePolygonBlendMode = togglePolygonBlendMode; |
| 406 | + window.updatePolygonBlendMode = updatePolygonBlendMode; |
| 407 | + window.updatePolygonColor = updatePolygonColor; |
243 | 408 | </script> |
244 | 409 | </body> |
245 | 410 |
|
|
0 commit comments