maplibre-preview 1.4.3 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +13 -0
- data/docs/README_RU.md +13 -0
- data/lib/maplibre-preview/public/js/filters.js +14 -2
- data/lib/maplibre-preview/public/js/tilegrid.js +4 -5
- data/lib/maplibre-preview/version.rb +1 -1
- data/lib/maplibre-preview/views/maplibre_layout.slim +91 -17
- data/lib/maplibre-preview/views/maplibre_map.slim +273 -55
- data/spec/maplibre_preview_spec.rb +31 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 17ba0e8d475ee1bb1a213790388e255df8eab1f4a3670382c0c77b3e2967f660
|
|
4
|
+
data.tar.gz: 4091e3a89317f63690f447167f68c5bdc887d442c33aad1dc418f7ed850a8abc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43cb047740512a57492d2e8582e704f3b1a45c61bcc7add1fb8845eea75bf66e05e67e1d86f73003ca4a50aad59204857b6dd816789f68ddc0475cc99a0da4a6
|
|
7
|
+
data.tar.gz: 8188dbf8a672afd1f42ad70d51315b4741ce1006a2111f37b84ab71c8dfc8353ac34fb8920f72111ddc50ce7bdf038c90fccabcfe66a17d598fa2589bd84ba52
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.6.0] - 2026-05-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Basemap opacity control** - added a Map Settings slider for changing preview basemap transparency without changing the tested style
|
|
7
|
+
- **Terrain exaggeration control** - added a terrain-only slider that updates `map.setTerrain({ source, exaggeration })`
|
|
8
|
+
- **MapLibre debug controls** - added Collision Boxes, Overdraw, and Raster Fade toggles for label placement, dense style, and raster/DEM diagnostics
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Tile Boundaries naming** - renamed the Tile Grid UI to Tile Boundaries to clarify that it uses MapLibre `showTileBoundaries`
|
|
12
|
+
- **Map Settings layout** - added compact setting rows and range styling for view-mode map controls
|
|
13
|
+
|
|
14
|
+
## [1.5.1] - 2026-05-13
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **Map control layout** - split map settings and style controls into independent panels with separate collapse controls
|
|
18
|
+
- **Panel sizing** - adjusted map settings, filters, and layers panels to size from their visible content
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Filter and layer scrolling** - restored internal scrolling for large filter and layer lists after the panel layout split
|
|
22
|
+
|
|
23
|
+
## [1.5.0] - 2026-05-13
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- **Map request cache toggle** - added a development mode that disables browser HTTP cache for style and MapLibre resource requests
|
|
27
|
+
|
|
28
|
+
## [1.4.4] - 2026-05-13
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- **Filter labels** - normalize object-based localized labels to display `title`, `name`, or `label` instead of `[object Object]`
|
|
32
|
+
|
|
3
33
|
## [1.4.2] - 2026-04-23
|
|
4
34
|
|
|
5
35
|
### Changed
|
data/README.md
CHANGED
|
@@ -125,6 +125,19 @@ The gem uses fixed configurations for optimal compatibility:
|
|
|
125
125
|
|
|
126
126
|
**Style URL**: Pass via URL parameter `?style_url=https://example.com/style.json`
|
|
127
127
|
|
|
128
|
+
## Map Settings
|
|
129
|
+
|
|
130
|
+
The preview UI keeps focused MapLibre controls that are useful for tile, style, and terrain services:
|
|
131
|
+
|
|
132
|
+
- **Basemap opacity**: changes the injected OpenStreetMap preview layer opacity while preserving the tested style.
|
|
133
|
+
- **Terrain exaggeration**: appears for styles with `terrain` and updates `map.setTerrain({ source, exaggeration })`.
|
|
134
|
+
- **Antialias**: reloads the WebGL context with antialiasing enabled or disabled.
|
|
135
|
+
- **Cache**: disables browser cache for style and map requests when comparing freshly generated tiles/styles.
|
|
136
|
+
- **Tile Boundaries**: toggles MapLibre tile boundary overlay, including tile coordinate/zoom diagnostics in pitched views, and visible tile count diagnostics.
|
|
137
|
+
- **Collision Boxes**: toggles MapLibre symbol collision boxes for label/icon debugging.
|
|
138
|
+
- **Overdraw**: toggles MapLibre overdraw inspection for dense mixed styles and expensive layers.
|
|
139
|
+
- **Raster Fade**: toggles raster tile fade duration for raster/DEM cache and reconstruction checks.
|
|
140
|
+
|
|
128
141
|
## API Reference
|
|
129
142
|
|
|
130
143
|
### Sinatra Extension
|
data/docs/README_RU.md
CHANGED
|
@@ -125,6 +125,19 @@ Gem использует фиксированные настройки:
|
|
|
125
125
|
|
|
126
126
|
**URL стиля**: Передается через параметр URL `?style_url=https://example.com/style.json`
|
|
127
127
|
|
|
128
|
+
## Настройки карты
|
|
129
|
+
|
|
130
|
+
Интерфейс preview оставляет только те настройки MapLibre, которые полезны для сервисов тайлов, стилей и рельефа:
|
|
131
|
+
|
|
132
|
+
- **Basemap opacity**: меняет прозрачность добавленной preview-подложки OpenStreetMap, не меняя тестируемый стиль.
|
|
133
|
+
- **Terrain exaggeration**: появляется для стилей с `terrain` и обновляет `map.setTerrain({ source, exaggeration })`.
|
|
134
|
+
- **Antialias**: перезагружает WebGL context с включенным или выключенным antialiasing.
|
|
135
|
+
- **Cache**: отключает browser cache для style и map requests при проверке свежесгенерированных тайлов/стилей.
|
|
136
|
+
- **Tile Boundaries**: включает MapLibre overlay границ тайлов, включая диагностику координат/зумов тайлов при наклоне карты, и счетчик видимых тайлов.
|
|
137
|
+
- **Collision Boxes**: включает MapLibre symbol collision boxes для отладки подписей и иконок.
|
|
138
|
+
- **Overdraw**: включает MapLibre overdraw inspection для плотных mixed styles и дорогих слоев.
|
|
139
|
+
- **Raster Fade**: включает или выключает fade duration у raster tiles для проверки raster/DEM cache и reconstruction.
|
|
140
|
+
|
|
128
141
|
## Справочник API
|
|
129
142
|
|
|
130
143
|
### Расширение Sinatra
|
|
@@ -37,16 +37,28 @@ class Filters {
|
|
|
37
37
|
const languagePriority = ['en-US', 'en', 'ru'];
|
|
38
38
|
|
|
39
39
|
for (const lang of languagePriority) {
|
|
40
|
-
|
|
40
|
+
const label = this.normalizeLocalizedFilterName(locale[lang]?.[filterId]);
|
|
41
|
+
if (label) return label;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
for (const lang in locale) {
|
|
44
|
-
|
|
45
|
+
const label = this.normalizeLocalizedFilterName(locale[lang]?.[filterId]);
|
|
46
|
+
if (label) return label;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
return filterId;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
normalizeLocalizedFilterName(value) {
|
|
53
|
+
if (!value) return null;
|
|
54
|
+
if (typeof value === 'string') return value;
|
|
55
|
+
if (typeof value === 'object') {
|
|
56
|
+
return value.title || value.name || value.label || null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return String(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
50
62
|
applyFilterMode() {
|
|
51
63
|
if (this.isUpdating) return;
|
|
52
64
|
this.isUpdating = true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TileGridManager - manages tile
|
|
2
|
+
* TileGridManager - manages MapLibre tile boundary overlay and tile statistics
|
|
3
3
|
*/
|
|
4
4
|
class TileGridManager {
|
|
5
5
|
constructor(options) {
|
|
@@ -27,7 +27,7 @@ class TileGridManager {
|
|
|
27
27
|
|
|
28
28
|
this.panelContainer.innerHTML = `
|
|
29
29
|
<div class="tilegrid-header">
|
|
30
|
-
<span class="tilegrid-title">Tile
|
|
30
|
+
<span class="tilegrid-title">Tile Boundaries</span>
|
|
31
31
|
<button class="tilegrid-close" onclick="tileGridManager.toggle()">×</button>
|
|
32
32
|
</div>
|
|
33
33
|
<div class="tilegrid-stats">
|
|
@@ -36,7 +36,7 @@ class TileGridManager {
|
|
|
36
36
|
</div>
|
|
37
37
|
<div class="tilegrid-controls">
|
|
38
38
|
<label class="tilegrid-checkbox-label">
|
|
39
|
-
<span>Show
|
|
39
|
+
<span>Show boundary overlay</span>
|
|
40
40
|
<input type="checkbox" id="tilegrid-borders-checkbox" checked onchange="tileGridManager.toggleBorders(this.checked)">
|
|
41
41
|
<span class="tilegrid-checkbox-custom"></span>
|
|
42
42
|
</label>
|
|
@@ -118,7 +118,7 @@ class TileGridManager {
|
|
|
118
118
|
|
|
119
119
|
const btn = document.getElementById('tilegrid-mode-btn');
|
|
120
120
|
if (btn) {
|
|
121
|
-
btn.textContent = this.isVisible ? 'Hide Tile
|
|
121
|
+
btn.textContent = this.isVisible ? 'Hide Tile Boundaries' : 'Tile Boundaries';
|
|
122
122
|
btn.className = `control-button ${this.isVisible ? 'active' : ''}`;
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -171,4 +171,3 @@ class TileGridManager {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
|
-
|
|
@@ -175,37 +175,80 @@ html
|
|
|
175
175
|
| .controls a:hover { text-decoration: underline; }
|
|
176
176
|
| .layer-controls-wrapper {
|
|
177
177
|
| position: absolute; top: 50%; left: 10px; transform: translateY(-50%); z-index: 1000;
|
|
178
|
-
| display: flex; align-items:
|
|
178
|
+
| display: flex; align-items: flex-start; gap: 0;
|
|
179
179
|
| }
|
|
180
180
|
| .layer-controls {
|
|
181
|
-
| background:
|
|
182
|
-
| border-radius: 4px; color: #a9b7c6; display: flex; flex-direction: column; gap:
|
|
183
|
-
| max-width:
|
|
184
|
-
| overflow: hidden;
|
|
181
|
+
| background: transparent; border: none; padding: 0;
|
|
182
|
+
| border-radius: 4px; color: #a9b7c6; display: flex; flex-direction: column; gap: 12px;
|
|
183
|
+
| width: fit-content; max-width: min(90vw, 420px); max-height: 80vh; transition: max-width 0.2s;
|
|
184
|
+
| overflow: hidden; order: 1;
|
|
185
185
|
| }
|
|
186
|
-
| .
|
|
187
|
-
|
|
|
186
|
+
| .control-section-wrapper {
|
|
187
|
+
| display: flex; align-items: center; width: max-content; max-width: min(90vw, 420px);
|
|
188
188
|
| }
|
|
189
|
-
| .
|
|
189
|
+
| .control-section-wrapper + .control-section-wrapper {
|
|
190
|
+
| margin-top: 4px;
|
|
191
|
+
| }
|
|
192
|
+
| .control-section {
|
|
193
|
+
| display: flex; flex-direction: column; gap: 8px; min-height: 0; width: max-content;
|
|
194
|
+
| max-width: min(90vw, 420px);
|
|
195
|
+
| background: rgba(60, 63, 65, 0.95); border: 1px solid #555555; padding: 12px;
|
|
196
|
+
| border-radius: 4px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
197
|
+
| }
|
|
198
|
+
| .control-section-wrapper:not(.collapsed) .control-section {
|
|
199
|
+
| border-right: none; border-radius: 4px 0 0 4px;
|
|
200
|
+
| }
|
|
201
|
+
| .control-section.collapsed {
|
|
202
|
+
| gap: 0;
|
|
203
|
+
| }
|
|
204
|
+
| .control-section-header {
|
|
205
|
+
| display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
206
|
+
| }
|
|
207
|
+
| .control-section-title {
|
|
208
|
+
| color: #ffc66d; font-size: 11px; font-weight: bold; line-height: 1.2;
|
|
209
|
+
| text-transform: uppercase;
|
|
210
|
+
| }
|
|
211
|
+
| .control-section-toggle-external {
|
|
190
212
|
| background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
|
|
191
213
|
| border-left: none; border-radius: 0 4px 4px 0; padding: 8px 4px;
|
|
192
214
|
| color: #a9b7c6; cursor: pointer; display: flex; align-items: center; justify-content: center;
|
|
193
|
-
| width: 18px; height: auto; min-height: 60px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
215
|
+
| width: 18px; height: auto; min-height: 60px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); flex-shrink: 0;
|
|
194
216
|
| }
|
|
195
|
-
| .
|
|
217
|
+
| .control-section-toggle-external:hover {
|
|
196
218
|
| background: rgba(75, 77, 79, 0.95); color: #ffc66d; border-color: #666666;
|
|
197
219
|
| }
|
|
198
|
-
| .
|
|
199
|
-
|
|
|
220
|
+
| .control-section-wrapper.collapsed .control-section-toggle-external {
|
|
221
|
+
| display: none;
|
|
200
222
|
| }
|
|
201
|
-
| .
|
|
202
|
-
|
|
|
223
|
+
| .control-section-toggle-inline {
|
|
224
|
+
| background: #4b4d4f; color: #a9b7c6; border: 1px solid #555555;
|
|
225
|
+
| border-radius: 3px; cursor: pointer; font-size: 10px; line-height: 1;
|
|
226
|
+
| padding: 0; width: 22px; height: 22px; transition: all 0.2s ease; flex-shrink: 0;
|
|
227
|
+
| display: none; align-items: center; justify-content: center;
|
|
228
|
+
| }
|
|
229
|
+
| .control-section-toggle-inline:hover {
|
|
230
|
+
| background: #5a5d5f; border-color: #666666;
|
|
231
|
+
| }
|
|
232
|
+
| .control-section.collapsed .control-section-toggle-inline {
|
|
233
|
+
| display: flex;
|
|
203
234
|
| }
|
|
204
235
|
| .toggle-icon {
|
|
205
236
|
| font-size: 12px; font-weight: bold; transition: transform 0.2s;
|
|
206
237
|
| }
|
|
238
|
+
| .control-section-body {
|
|
239
|
+
| display: flex; flex-direction: column; gap: 8px; min-height: 0;
|
|
240
|
+
| }
|
|
241
|
+
| .control-section.collapsed .control-section-body {
|
|
242
|
+
| display: none;
|
|
243
|
+
| }
|
|
244
|
+
| .map-settings-section {
|
|
245
|
+
| flex-shrink: 0;
|
|
246
|
+
| }
|
|
247
|
+
| .style-controls-section {
|
|
248
|
+
| overflow: hidden; min-width: 160px; max-height: calc(80vh - 120px);
|
|
249
|
+
| }
|
|
207
250
|
| .mode-switcher {
|
|
208
|
-
| display: flex; gap: 2px; margin-bottom: 10px; background: #3c3f41;
|
|
251
|
+
| display: flex; gap: 2px; margin-bottom: 10px; background: #3c3f41; width: 100%;
|
|
209
252
|
| border-radius: 3px; padding: 2px; flex-shrink: 0;
|
|
210
253
|
| }
|
|
211
254
|
| .mode-button {
|
|
@@ -218,6 +261,27 @@ html
|
|
|
218
261
|
| display: none; flex-direction: column; gap: 6px;
|
|
219
262
|
| max-height: calc(80vh - 200px); overflow-y: auto;
|
|
220
263
|
| }
|
|
264
|
+
| .settings-panel {
|
|
265
|
+
| max-height: none; overflow: visible; width: 220px;
|
|
266
|
+
| }
|
|
267
|
+
| .setting-group {
|
|
268
|
+
| display: flex; flex-direction: column; gap: 6px; padding-bottom: 8px;
|
|
269
|
+
| border-bottom: 1px solid #464749;
|
|
270
|
+
| }
|
|
271
|
+
| .setting-label {
|
|
272
|
+
| display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
273
|
+
| color: #a9b7c6; font-size: 11px; font-weight: bold;
|
|
274
|
+
| }
|
|
275
|
+
| .setting-value {
|
|
276
|
+
| color: #6897bb; font-family: 'Courier New', monospace; font-weight: bold;
|
|
277
|
+
| }
|
|
278
|
+
| .setting-range {
|
|
279
|
+
| width: 100%; accent-color: #6897bb; cursor: pointer;
|
|
280
|
+
| }
|
|
281
|
+
| .style-panel {
|
|
282
|
+
| flex: 1; min-height: 0; max-height: calc(80vh - 245px); width: max-content; max-width: 100%;
|
|
283
|
+
| overflow: hidden;
|
|
284
|
+
| }
|
|
221
285
|
| .control-panel::-webkit-scrollbar { width: 8px; }
|
|
222
286
|
| .control-panel::-webkit-scrollbar-track { background: #3c3f41; border-radius: 4px; }
|
|
223
287
|
| .control-panel::-webkit-scrollbar-thumb {
|
|
@@ -231,16 +295,26 @@ html
|
|
|
231
295
|
| display: flex; flex-direction: column; gap: 6px;
|
|
232
296
|
| }
|
|
233
297
|
| .control-panel-content {
|
|
234
|
-
| flex: 1; overflow-y: auto;
|
|
298
|
+
| flex: 1; min-height: 0; max-height: calc(80vh - 325px); overflow-y: auto;
|
|
299
|
+
| width: max-content; max-width: 100%;
|
|
300
|
+
| }
|
|
301
|
+
| #filter-buttons, #layer-buttons {
|
|
302
|
+
| display: inline-grid; grid-template-columns: max-content; gap: 2px; width: max-content;
|
|
303
|
+
| justify-items: stretch;
|
|
235
304
|
| }
|
|
236
305
|
| .control-button {
|
|
237
306
|
| background: #4b4d4f; color: #a9b7c6; border: 1px solid #555555;
|
|
238
307
|
| padding: 6px 12px; border-radius: 3px; cursor: pointer; font-size: 11px;
|
|
239
|
-
| transition: all 0.2s ease; width: 100%; box-sizing: border-box;
|
|
308
|
+
| transition: all 0.2s ease; width: 100%; box-sizing: border-box; white-space: nowrap;
|
|
309
|
+
| }
|
|
310
|
+
| #filter-buttons .control-button, #layer-buttons .control-button {
|
|
311
|
+
| width: 100%; text-align: center;
|
|
240
312
|
| }
|
|
241
313
|
| .control-button:hover { background: #5a5d5f; border-color: #666666; }
|
|
242
314
|
| .control-button.active { background: #6a9955; color: #ffffff; border-color: #7bb366; }
|
|
243
315
|
| .control-button.inactive { background: #3c3f41; color: #808080; border-color: #464749; }
|
|
316
|
+
| .control-button.cache-off { background: #7a2f35; color: #ffffff; border-color: #9b3f47; font-weight: bold; }
|
|
317
|
+
| .control-button.cache-off:hover { background: #8f3841; border-color: #b64b55; }
|
|
244
318
|
| .filter-group {
|
|
245
319
|
| margin-bottom: 8px;
|
|
246
320
|
| border: 1px solid #555555;
|
|
@@ -12,29 +12,67 @@ ruby:
|
|
|
12
12
|
- if style_name || external_style_url || options[:style_url]
|
|
13
13
|
.layer-controls-wrapper
|
|
14
14
|
.layer-controls id="layer-controls"
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
15
|
+
.control-section-wrapper.map-settings-wrapper id="map-settings-wrapper"
|
|
16
|
+
.control-section.map-settings-section id="map-settings-section"
|
|
17
|
+
.control-section-header
|
|
18
|
+
.control-section-title Map Settings
|
|
19
|
+
button.control-section-toggle-inline id="map-settings-toggle-inline" onclick="toggleControlSection('map-settings')" title="Expand Map Settings"
|
|
20
|
+
span.toggle-icon ▶
|
|
21
|
+
.control-section-body
|
|
22
|
+
.mode-switcher id="settings-mode-switcher"
|
|
23
|
+
button.mode-button.active id="settings-mode-view" onclick="switchSettingsMode(this, 'view')" View
|
|
24
|
+
button.mode-button id="settings-mode-debug" onclick="switchSettingsMode(this, 'debug')" Debug
|
|
25
|
+
|
|
26
|
+
#settings-view-panel.control-panel.settings-panel.active
|
|
27
|
+
.setting-group
|
|
28
|
+
.setting-label
|
|
29
|
+
span Basemap opacity
|
|
30
|
+
span.setting-value id="basemap-opacity-value" 80%
|
|
31
|
+
input.setting-range id="basemap-opacity-slider" type="range" min="0" max="100" step="5" value="80" oninput="setBasemapOpacity(this.value)"
|
|
32
|
+
.setting-group.terrain-setting id="terrain-exaggeration-setting" style="display: none;"
|
|
33
|
+
.setting-label
|
|
34
|
+
span Terrain exaggeration
|
|
35
|
+
span.setting-value id="terrain-exaggeration-value" 1.0x
|
|
36
|
+
input.setting-range id="terrain-exaggeration-slider" type="range" min="0" max="3" step="0.1" value="1" oninput="setTerrainExaggeration(this.value)"
|
|
37
|
+
button.control-button onclick="toggleAntialias()" id="antialias-btn" Antialias
|
|
38
|
+
|
|
39
|
+
#settings-debug-panel.control-panel.settings-panel
|
|
40
|
+
button.control-button onclick="toggleMapCache()" id="map-cache-btn" Cache
|
|
41
|
+
button.control-button onclick="toggleTileGrid()" id="tilegrid-mode-btn" Tile Boundaries
|
|
42
|
+
button.control-button onclick="toggleCollisionBoxes()" id="collision-boxes-btn" Collision Boxes
|
|
43
|
+
button.control-button onclick="toggleOverdrawInspector()" id="overdraw-inspector-btn" Overdraw
|
|
44
|
+
button.control-button onclick="toggleTileFade()" id="tile-fade-btn" Raster Fade
|
|
45
|
+
button.control-button onclick="toggleHoverMode()" id="hover-mode-btn" Hover Mode
|
|
46
|
+
button.control-button onclick="toggleProfileMode()" id="profile-mode-btn" style="display: none;" Elevation Profile
|
|
47
|
+
button.control-section-toggle-external id="map-settings-toggle" onclick="toggleControlSection('map-settings')" title="Collapse Map Settings"
|
|
48
|
+
span.toggle-icon ◀
|
|
49
|
+
|
|
50
|
+
.control-section-wrapper.style-controls-wrapper id="style-controls-wrapper"
|
|
51
|
+
.control-section.style-controls-section id="style-controls-section"
|
|
52
|
+
.control-section-header
|
|
53
|
+
.control-section-title Style Controls
|
|
54
|
+
button.control-section-toggle-inline id="style-controls-toggle-inline" onclick="toggleControlSection('style-controls')" title="Expand Style Controls"
|
|
55
|
+
span.toggle-icon ▶
|
|
56
|
+
.control-section-body
|
|
57
|
+
.mode-switcher id="style-mode-switcher"
|
|
58
|
+
button.mode-button.active id="mode-filters" onclick="switchMode(this, 'filters')" Filters
|
|
59
|
+
button.mode-button id="mode-layers" onclick="switchMode(this, 'layers')" Layers
|
|
60
|
+
|
|
61
|
+
#filters-panel.control-panel.style-panel.active
|
|
62
|
+
.control-panel-header
|
|
63
|
+
button.control-button onclick="toggleAllFilters()" Toggle All Filters
|
|
64
|
+
button.control-button onclick="toggleBasemap()" id="basemap-filters-btn" Show/Hide Basemap
|
|
65
|
+
.control-panel-content
|
|
66
|
+
#filter-buttons
|
|
67
|
+
|
|
68
|
+
#layers-panel.control-panel.style-panel
|
|
69
|
+
.control-panel-header
|
|
70
|
+
button.control-button onclick="toggleAllLayers()" Toggle All Layers
|
|
71
|
+
button.control-button onclick="toggleBasemap()" id="basemap-layers-btn" Show/Hide Basemap
|
|
72
|
+
.control-panel-content
|
|
73
|
+
#layer-buttons
|
|
74
|
+
button.control-section-toggle-external id="style-controls-toggle" onclick="toggleControlSection('style-controls')" title="Collapse Style Controls"
|
|
75
|
+
span.toggle-icon ◀
|
|
38
76
|
|
|
39
77
|
#map-container
|
|
40
78
|
#map.map-layer data-style-url="#{style_url}"
|
|
@@ -77,11 +115,17 @@ ruby:
|
|
|
77
115
|
javascript:
|
|
78
116
|
const mapEl = document.getElementById('map');
|
|
79
117
|
const style_url = mapEl?.dataset?.styleUrl || null;
|
|
80
|
-
let
|
|
118
|
+
let showBasemap = true, currentMode = 'filters';
|
|
81
119
|
let hoverMode = 'click';
|
|
82
120
|
let layerStates = {};
|
|
83
121
|
let map = null;
|
|
84
122
|
let antialiasEnabled = localStorage.getItem('antialiasEnabled') !== 'false';
|
|
123
|
+
let mapCacheDisabled = localStorage.getItem('mapCacheDisabled') === 'true';
|
|
124
|
+
let basemapOpacity = Number.parseFloat(localStorage.getItem('basemapOpacity') || '0.8');
|
|
125
|
+
let terrainExaggeration = Number.parseFloat(localStorage.getItem('terrainExaggeration') || '1');
|
|
126
|
+
let collisionBoxesEnabled = localStorage.getItem('collisionBoxesEnabled') === 'true';
|
|
127
|
+
let overdrawInspectorEnabled = localStorage.getItem('overdrawInspectorEnabled') === 'true';
|
|
128
|
+
let tileFadeEnabled = localStorage.getItem('tileFadeEnabled') !== 'false';
|
|
85
129
|
let styleLoaded = false, resourcesLoaded = 0, totalResources = 0;
|
|
86
130
|
let tilesLoaded = 0, tilesTotal = 0;
|
|
87
131
|
let currentStyle = null;
|
|
@@ -95,12 +139,14 @@ javascript:
|
|
|
95
139
|
let tileGridManager = null;
|
|
96
140
|
|
|
97
141
|
const toDomId = (prefix, id) => `${prefix}-${String(id).replace(/[^a-zA-Z0-9_-]/g, '_')}`;
|
|
142
|
+
const noCacheRequestOptions = () => mapCacheDisabled ? {cache: 'no-store'} : undefined;
|
|
143
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
98
144
|
|
|
99
145
|
const getRealTerrainElevation = (lngLat) => {
|
|
100
146
|
const elevation = map.queryTerrainElevation(lngLat);
|
|
101
147
|
if (elevation == null) return null;
|
|
102
148
|
|
|
103
|
-
const exaggeration = currentStyle?.terrain?.exaggeration || 1.0;
|
|
149
|
+
const exaggeration = terrainExaggeration || currentStyle?.terrain?.exaggeration || 1.0;
|
|
104
150
|
return elevation / exaggeration;
|
|
105
151
|
};
|
|
106
152
|
|
|
@@ -136,7 +182,7 @@ javascript:
|
|
|
136
182
|
const target = isStringArg ? null : (arg1?.currentTarget || arg1);
|
|
137
183
|
currentMode = mode;
|
|
138
184
|
|
|
139
|
-
document.querySelectorAll('.mode-button').forEach(btn => btn.classList.remove('active'));
|
|
185
|
+
document.querySelectorAll('#style-mode-switcher .mode-button').forEach(btn => btn.classList.remove('active'));
|
|
140
186
|
(target || document.getElementById(`mode-${mode}`))?.classList.add('active');
|
|
141
187
|
|
|
142
188
|
document.getElementById('filters-panel').classList.toggle('active', mode === 'filters');
|
|
@@ -147,6 +193,17 @@ javascript:
|
|
|
147
193
|
updateBasemapButton();
|
|
148
194
|
};
|
|
149
195
|
|
|
196
|
+
const switchSettingsMode = (arg1, arg2) => {
|
|
197
|
+
const isStringArg = typeof arg1 === 'string';
|
|
198
|
+
const mode = isStringArg ? arg1 : arg2;
|
|
199
|
+
const target = isStringArg ? null : (arg1?.currentTarget || arg1);
|
|
200
|
+
document.querySelectorAll('#settings-mode-switcher .mode-button').forEach(btn => btn.classList.remove('active'));
|
|
201
|
+
(target || document.getElementById(`settings-mode-${mode}`))?.classList.add('active');
|
|
202
|
+
|
|
203
|
+
document.querySelectorAll('.settings-panel').forEach(panel => panel.classList.remove('active'));
|
|
204
|
+
document.getElementById(`settings-${mode}-panel`)?.classList.add('active');
|
|
205
|
+
};
|
|
206
|
+
|
|
150
207
|
const applyLayerMode = () => {
|
|
151
208
|
Object.keys(layerStates).forEach(layerId => {
|
|
152
209
|
if (map.getLayer(layerId)) {
|
|
@@ -156,8 +213,7 @@ javascript:
|
|
|
156
213
|
};
|
|
157
214
|
|
|
158
215
|
const applyBasemapVisibility = () => {
|
|
159
|
-
|
|
160
|
-
map?.getLayer?.('preview-basemap-layer') && map.setLayoutProperty('preview-basemap-layer', 'visibility', isVisible ? 'visible' : 'none');
|
|
216
|
+
map?.getLayer?.('preview-basemap-layer') && map.setLayoutProperty('preview-basemap-layer', 'visibility', showBasemap ? 'visible' : 'none');
|
|
161
217
|
};
|
|
162
218
|
|
|
163
219
|
const checkStyleResources = (style) => {
|
|
@@ -196,7 +252,13 @@ javascript:
|
|
|
196
252
|
zoom,
|
|
197
253
|
attributionControl: false,
|
|
198
254
|
validateStyle: false,
|
|
199
|
-
antialias: antialiasEnabled
|
|
255
|
+
antialias: antialiasEnabled,
|
|
256
|
+
canvasContextAttributes: {antialias: antialiasEnabled},
|
|
257
|
+
fadeDuration: tileFadeEnabled ? 300 : 0,
|
|
258
|
+
transformRequest: (url) => ({
|
|
259
|
+
url,
|
|
260
|
+
...noCacheRequestOptions()
|
|
261
|
+
})
|
|
200
262
|
});
|
|
201
263
|
};
|
|
202
264
|
|
|
@@ -222,7 +284,7 @@ javascript:
|
|
|
222
284
|
type: 'raster',
|
|
223
285
|
source: 'preview-basemap',
|
|
224
286
|
layout: {visibility: 'visible'},
|
|
225
|
-
paint: {'raster-opacity':
|
|
287
|
+
paint: {'raster-opacity': basemapOpacity, 'raster-fade-duration': tileFadeEnabled ? 300 : 0}
|
|
226
288
|
});
|
|
227
289
|
|
|
228
290
|
return modifiedStyle;
|
|
@@ -249,7 +311,7 @@ javascript:
|
|
|
249
311
|
const emptyStyle = {version: 8, sources: {}, layers: []};
|
|
250
312
|
|
|
251
313
|
style_url
|
|
252
|
-
? fetch(style_url)
|
|
314
|
+
? fetch(style_url, noCacheRequestOptions())
|
|
253
315
|
.then(response => response.json())
|
|
254
316
|
.then(originalStyle => createMapWithStyle(addBasemapToStyle(originalStyle)))
|
|
255
317
|
.catch(error => {
|
|
@@ -319,13 +381,7 @@ javascript:
|
|
|
319
381
|
|
|
320
382
|
const onStyleReady = () => {
|
|
321
383
|
const hasTerrain = currentStyle?.terrain;
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
try {
|
|
325
|
-
map.setProjection({type: projectionType});
|
|
326
|
-
} catch (e) {
|
|
327
|
-
console.warn('Projection setting failed:', e);
|
|
328
|
-
}
|
|
384
|
+
applyDefaultProjection();
|
|
329
385
|
|
|
330
386
|
try {
|
|
331
387
|
map.addControl(new maplibregl.GlobeControl(), 'top-right');
|
|
@@ -336,14 +392,20 @@ javascript:
|
|
|
336
392
|
if (currentStyle?.terrain) {
|
|
337
393
|
const terrainSourceName = currentStyle.terrain.source;
|
|
338
394
|
map.addControl(new maplibregl.TerrainControl({
|
|
339
|
-
source: terrainSourceName
|
|
395
|
+
source: terrainSourceName,
|
|
396
|
+
exaggeration: terrainExaggeration
|
|
340
397
|
}), 'top-right');
|
|
341
398
|
|
|
342
399
|
initializeContourManager();
|
|
400
|
+
applyTerrainExaggeration();
|
|
343
401
|
}
|
|
344
402
|
|
|
345
403
|
initializeTileGridManager();
|
|
404
|
+
applyBasemapOpacity();
|
|
405
|
+
applyDebugRenderSettings();
|
|
406
|
+
applyTileFadeSetting();
|
|
346
407
|
updateTerrainIndicator();
|
|
408
|
+
updateMapSettingsControls();
|
|
347
409
|
};
|
|
348
410
|
|
|
349
411
|
const setupMapInteractions = () => {
|
|
@@ -415,20 +477,20 @@ javascript:
|
|
|
415
477
|
|
|
416
478
|
const setupPerformanceMonitoring = () => {
|
|
417
479
|
[startPerformanceMonitoring, () => requestAnimationFrame(countFrame),
|
|
418
|
-
updateHoverModeButton, updateBasemapButton, updateAntialiasButton
|
|
480
|
+
updateHoverModeButton, updateBasemapButton, updateAntialiasButton, updateMapCacheButton,
|
|
481
|
+
updateMapSettingsControls].forEach(fn => fn());
|
|
419
482
|
};
|
|
420
483
|
|
|
421
484
|
const toggleBasemap = () => {
|
|
422
|
-
|
|
485
|
+
showBasemap = !showBasemap;
|
|
423
486
|
applyBasemapVisibility();
|
|
424
487
|
updateBasemapButton();
|
|
425
488
|
};
|
|
426
489
|
|
|
427
490
|
const updateBasemapButton = () => {
|
|
428
|
-
const isVisible = currentMode === 'filters' ? showBasemapFilters : showBasemapLayers;
|
|
429
491
|
document.querySelectorAll('button[onclick="toggleBasemap()"]').forEach(btn => {
|
|
430
|
-
btn.textContent =
|
|
431
|
-
btn.className = `control-button ${
|
|
492
|
+
btn.textContent = showBasemap ? 'Hide Basemap' : 'Show Basemap';
|
|
493
|
+
btn.className = `control-button ${showBasemap ? 'active' : 'inactive'}`;
|
|
432
494
|
});
|
|
433
495
|
};
|
|
434
496
|
|
|
@@ -436,6 +498,7 @@ javascript:
|
|
|
436
498
|
const terrainRow = document.getElementById('terrain-row');
|
|
437
499
|
const terrainElement = document.getElementById('terrain-status-value');
|
|
438
500
|
const profileBtn = document.getElementById('profile-mode-btn');
|
|
501
|
+
const terrainSetting = document.getElementById('terrain-exaggeration-setting');
|
|
439
502
|
|
|
440
503
|
if (!terrainRow || !terrainElement) return;
|
|
441
504
|
|
|
@@ -445,9 +508,11 @@ javascript:
|
|
|
445
508
|
terrainElement.textContent = 'is detected';
|
|
446
509
|
terrainElement.className = 'metric-value success';
|
|
447
510
|
profileBtn && (profileBtn.style.display = 'block');
|
|
511
|
+
terrainSetting && (terrainSetting.style.display = 'flex');
|
|
448
512
|
} else {
|
|
449
513
|
terrainRow.style.display = 'none';
|
|
450
514
|
profileBtn && (profileBtn.style.display = 'none');
|
|
515
|
+
terrainSetting && (terrainSetting.style.display = 'none');
|
|
451
516
|
}
|
|
452
517
|
};
|
|
453
518
|
|
|
@@ -704,7 +769,7 @@ javascript:
|
|
|
704
769
|
});
|
|
705
770
|
|
|
706
771
|
if (map.getLayer('preview-basemap-layer')) {
|
|
707
|
-
|
|
772
|
+
showBasemap = newState;
|
|
708
773
|
}
|
|
709
774
|
|
|
710
775
|
updateLayerButtons();
|
|
@@ -720,7 +785,7 @@ javascript:
|
|
|
720
785
|
}
|
|
721
786
|
|
|
722
787
|
if (layerId === 'preview-basemap-layer') {
|
|
723
|
-
|
|
788
|
+
showBasemap = layerStates[layerId];
|
|
724
789
|
updateBasemapButton();
|
|
725
790
|
}
|
|
726
791
|
|
|
@@ -871,19 +936,165 @@ javascript:
|
|
|
871
936
|
btn.className = `control-button ${antialiasEnabled ? 'active' : 'inactive'}`;
|
|
872
937
|
};
|
|
873
938
|
|
|
874
|
-
|
|
939
|
+
const toggleMapCache = () => {
|
|
940
|
+
mapCacheDisabled = !mapCacheDisabled;
|
|
941
|
+
localStorage.setItem('mapCacheDisabled', mapCacheDisabled.toString());
|
|
942
|
+
window.location.reload();
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
const updateMapCacheButton = () => {
|
|
946
|
+
const btn = document.getElementById('map-cache-btn');
|
|
947
|
+
if (!btn) return;
|
|
948
|
+
|
|
949
|
+
btn.textContent = `Cache: ${mapCacheDisabled ? 'OFF' : 'ON'}`;
|
|
950
|
+
btn.className = `control-button ${mapCacheDisabled ? 'cache-off' : 'active'}`;
|
|
951
|
+
btn.title = mapCacheDisabled ? 'Browser cache is disabled for map requests' : 'Browser cache is enabled for map requests';
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
const getDefaultProjectionType = () => {
|
|
955
|
+
return currentStyle?.terrain ? 'mercator' : 'globe';
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
const applyDefaultProjection = () => {
|
|
959
|
+
if (!map) return;
|
|
960
|
+
|
|
961
|
+
try {
|
|
962
|
+
map.setProjection({type: getDefaultProjectionType()});
|
|
963
|
+
} catch (e) {
|
|
964
|
+
console.warn('Projection setting failed:', e);
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const setBasemapOpacity = (value) => {
|
|
969
|
+
basemapOpacity = clamp(Number.parseFloat(value) / 100, 0, 1);
|
|
970
|
+
localStorage.setItem('basemapOpacity', basemapOpacity.toString());
|
|
971
|
+
applyBasemapOpacity();
|
|
972
|
+
updateBasemapOpacityControl();
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
const applyBasemapOpacity = () => {
|
|
976
|
+
if (map?.getLayer?.('preview-basemap-layer')) {
|
|
977
|
+
map.setPaintProperty('preview-basemap-layer', 'raster-opacity', basemapOpacity);
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
const updateBasemapOpacityControl = () => {
|
|
982
|
+
const slider = document.getElementById('basemap-opacity-slider');
|
|
983
|
+
const value = document.getElementById('basemap-opacity-value');
|
|
984
|
+
const percent = Math.round(clamp(basemapOpacity, 0, 1) * 100);
|
|
985
|
+
slider && (slider.value = percent);
|
|
986
|
+
value && (value.textContent = `${percent}%`);
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
const setTerrainExaggeration = (value) => {
|
|
990
|
+
terrainExaggeration = clamp(Number.parseFloat(value), 0, 3);
|
|
991
|
+
localStorage.setItem('terrainExaggeration', terrainExaggeration.toString());
|
|
992
|
+
applyTerrainExaggeration();
|
|
993
|
+
updateTerrainExaggerationControl();
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
const applyTerrainExaggeration = () => {
|
|
997
|
+
if (!map || !currentStyle?.terrain?.source) return;
|
|
998
|
+
|
|
999
|
+
try {
|
|
1000
|
+
map.setTerrain({
|
|
1001
|
+
source: currentStyle.terrain.source,
|
|
1002
|
+
exaggeration: terrainExaggeration
|
|
1003
|
+
});
|
|
1004
|
+
} catch (e) {
|
|
1005
|
+
console.warn('Terrain exaggeration setting failed:', e);
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
const updateTerrainExaggerationControl = () => {
|
|
1010
|
+
const slider = document.getElementById('terrain-exaggeration-slider');
|
|
1011
|
+
const value = document.getElementById('terrain-exaggeration-value');
|
|
1012
|
+
slider && (slider.value = terrainExaggeration);
|
|
1013
|
+
value && (value.textContent = `${terrainExaggeration.toFixed(1)}x`);
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
const toggleCollisionBoxes = () => {
|
|
1017
|
+
collisionBoxesEnabled = !collisionBoxesEnabled;
|
|
1018
|
+
localStorage.setItem('collisionBoxesEnabled', collisionBoxesEnabled.toString());
|
|
1019
|
+
applyDebugRenderSettings();
|
|
1020
|
+
updateDebugRenderButtons();
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
const toggleOverdrawInspector = () => {
|
|
1024
|
+
overdrawInspectorEnabled = !overdrawInspectorEnabled;
|
|
1025
|
+
localStorage.setItem('overdrawInspectorEnabled', overdrawInspectorEnabled.toString());
|
|
1026
|
+
applyDebugRenderSettings();
|
|
1027
|
+
updateDebugRenderButtons();
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
const applyDebugRenderSettings = () => {
|
|
1031
|
+
if (!map) return;
|
|
1032
|
+
map.showCollisionBoxes = collisionBoxesEnabled;
|
|
1033
|
+
map.showOverdrawInspector = overdrawInspectorEnabled;
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
const updateDebugRenderButtons = () => {
|
|
1037
|
+
const collisionBtn = document.getElementById('collision-boxes-btn');
|
|
1038
|
+
const overdrawBtn = document.getElementById('overdraw-inspector-btn');
|
|
1039
|
+
if (collisionBtn) {
|
|
1040
|
+
collisionBtn.textContent = `Collision Boxes: ${collisionBoxesEnabled ? 'ON' : 'OFF'}`;
|
|
1041
|
+
collisionBtn.className = `control-button ${collisionBoxesEnabled ? 'active' : 'inactive'}`;
|
|
1042
|
+
}
|
|
1043
|
+
if (overdrawBtn) {
|
|
1044
|
+
overdrawBtn.textContent = `Overdraw: ${overdrawInspectorEnabled ? 'ON' : 'OFF'}`;
|
|
1045
|
+
overdrawBtn.className = `control-button ${overdrawInspectorEnabled ? 'active' : 'inactive'}`;
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
const toggleTileFade = () => {
|
|
1050
|
+
tileFadeEnabled = !tileFadeEnabled;
|
|
1051
|
+
localStorage.setItem('tileFadeEnabled', tileFadeEnabled.toString());
|
|
1052
|
+
applyTileFadeSetting();
|
|
1053
|
+
updateTileFadeButton();
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
const applyTileFadeSetting = () => {
|
|
1057
|
+
if (!map?.getStyle()?.layers) return;
|
|
1058
|
+
const fadeDuration = tileFadeEnabled ? 300 : 0;
|
|
1059
|
+
|
|
1060
|
+
map.getStyle().layers.forEach(layer => {
|
|
1061
|
+
if (layer.type === 'raster' && map.getLayer(layer.id)) {
|
|
1062
|
+
map.setPaintProperty(layer.id, 'raster-fade-duration', fadeDuration);
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
const updateTileFadeButton = () => {
|
|
1068
|
+
const btn = document.getElementById('tile-fade-btn');
|
|
1069
|
+
if (!btn) return;
|
|
1070
|
+
btn.textContent = `Raster Fade: ${tileFadeEnabled ? 'ON' : 'OFF'}`;
|
|
1071
|
+
btn.className = `control-button ${tileFadeEnabled ? 'active' : 'inactive'}`;
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
const updateMapSettingsControls = () => {
|
|
1075
|
+
updateBasemapOpacityControl();
|
|
1076
|
+
updateTerrainExaggerationControl();
|
|
1077
|
+
updateDebugRenderButtons();
|
|
1078
|
+
updateTileFadeButton();
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
const toggleControlSection = (sectionName) => {
|
|
1082
|
+
const wrapper = document.getElementById(`${sectionName}-wrapper`);
|
|
1083
|
+
const section = document.getElementById(`${sectionName}-section`);
|
|
1084
|
+
const externalToggle = document.getElementById(`${sectionName}-toggle`);
|
|
1085
|
+
const inlineToggle = document.getElementById(`${sectionName}-toggle-inline`);
|
|
1086
|
+
if (!wrapper || !section) return;
|
|
875
1087
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
const controls = document.getElementById('layer-controls');
|
|
879
|
-
if (!wrapper || !controls) return;
|
|
1088
|
+
const isCollapsed = section.classList.toggle('collapsed');
|
|
1089
|
+
wrapper.classList.toggle('collapsed', isCollapsed);
|
|
880
1090
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1091
|
+
const sectionTitle = section.querySelector('.control-section-title')?.textContent || 'section';
|
|
1092
|
+
externalToggle && (externalToggle.title = `Collapse ${sectionTitle}`);
|
|
1093
|
+
inlineToggle && (inlineToggle.title = `Expand ${sectionTitle}`);
|
|
884
1094
|
};
|
|
885
1095
|
|
|
886
1096
|
window.switchMode = switchMode;
|
|
1097
|
+
window.switchSettingsMode = switchSettingsMode;
|
|
887
1098
|
window.toggleBasemap = toggleBasemap;
|
|
888
1099
|
window.toggleHoverMode = toggleHoverMode;
|
|
889
1100
|
window.toggleAllFilters = () => filters?.toggleAllFilters();
|
|
@@ -891,7 +1102,14 @@ javascript:
|
|
|
891
1102
|
window.togglePerformancePanel = togglePerformancePanel;
|
|
892
1103
|
window.toggleProfileMode = toggleProfileMode;
|
|
893
1104
|
window.toggleAntialias = toggleAntialias;
|
|
894
|
-
window.
|
|
1105
|
+
window.toggleMapCache = toggleMapCache;
|
|
1106
|
+
window.setBasemapOpacity = setBasemapOpacity;
|
|
1107
|
+
window.setTerrainExaggeration = setTerrainExaggeration;
|
|
1108
|
+
window.toggleCollisionBoxes = toggleCollisionBoxes;
|
|
1109
|
+
window.toggleOverdrawInspector = toggleOverdrawInspector;
|
|
1110
|
+
window.toggleTileFade = toggleTileFade;
|
|
1111
|
+
window.toggleControlSection = toggleControlSection;
|
|
1112
|
+
window.toggleLayerControls = () => toggleControlSection('style-controls');
|
|
895
1113
|
window.hideProfile = hideProfile;
|
|
896
1114
|
window.toggleTileGrid = toggleTileGrid;
|
|
897
1115
|
window.tileGridManager = null;
|
|
@@ -16,6 +16,37 @@ RSpec.describe MapLibrePreview do
|
|
|
16
16
|
expect(last_response.body).to include('d3')
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
it 'renders map cache toggle wiring' do
|
|
20
|
+
get '/?style_url=https://example.com/style.json'
|
|
21
|
+
expect(last_response).to be_ok
|
|
22
|
+
|
|
23
|
+
expect(last_response.body).to include('Map Settings')
|
|
24
|
+
expect(last_response.body).to include('Style Controls')
|
|
25
|
+
expect(last_response.body).to include('id="settings-mode-switcher"')
|
|
26
|
+
expect(last_response.body).to include('id="style-mode-switcher"')
|
|
27
|
+
expect(last_response.body).to include('id="map-settings-toggle"')
|
|
28
|
+
expect(last_response.body).to include('id="style-controls-toggle"')
|
|
29
|
+
expect(last_response.body).to include('toggleControlSection')
|
|
30
|
+
expect(last_response.body).to include('id="map-cache-btn"')
|
|
31
|
+
expect(last_response.body).to include('id="basemap-opacity-slider"')
|
|
32
|
+
expect(last_response.body).to include('id="terrain-exaggeration-slider"')
|
|
33
|
+
expect(last_response.body).to include('id="collision-boxes-btn"')
|
|
34
|
+
expect(last_response.body).to include('id="overdraw-inspector-btn"')
|
|
35
|
+
expect(last_response.body).to include('id="tile-fade-btn"')
|
|
36
|
+
expect(last_response.body).to include('mapCacheDisabled')
|
|
37
|
+
expect(last_response.body).to include('basemapOpacity')
|
|
38
|
+
expect(last_response.body).to include('terrainExaggeration')
|
|
39
|
+
expect(last_response.body).to include('showCollisionBoxes')
|
|
40
|
+
expect(last_response.body).to include('showOverdrawInspector')
|
|
41
|
+
expect(last_response.body).to include("cache: 'no-store'")
|
|
42
|
+
expect(last_response.body).to include('cache-off')
|
|
43
|
+
expect(last_response.body).to include('transformRequest')
|
|
44
|
+
expect(last_response.body).to include('canvasContextAttributes')
|
|
45
|
+
expect(last_response.body).to include('raster-fade-duration')
|
|
46
|
+
expect(last_response.body).to include('window.toggleMapCache')
|
|
47
|
+
expect(last_response.body).to include('window.switchSettingsMode')
|
|
48
|
+
end
|
|
49
|
+
|
|
19
50
|
it 'serves all required JavaScript modules' do
|
|
20
51
|
%w[/js/filters.js /js/contour.js /js/tilegrid.js /vendor/maplibre-gl/maplibre-gl.js /vendor/maplibre-contour/index.min.js /vendor/d3/d3.v7.min.js].each do |js_file|
|
|
21
52
|
get js_file
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: maplibre-preview
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Ludov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|