maplibre-preview 1.7.2 → 1.9.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 +28 -0
- data/lib/maplibre-preview/public/css/temporal_picker.css +164 -0
- data/lib/maplibre-preview/public/js/overlay_layout.js +415 -0
- data/lib/maplibre-preview/public/js/temporal_picker.js +247 -0
- data/lib/maplibre-preview/public/js/tilegrid.js +9 -0
- data/lib/maplibre-preview/version.rb +1 -1
- data/lib/maplibre-preview/views/maplibre_layout.slim +77 -10
- data/lib/maplibre-preview/views/maplibre_map.slim +238 -26
- data/spec/maplibre_preview_spec.rb +32 -5
- metadata +4 -1
|
@@ -35,6 +35,7 @@ ruby:
|
|
|
35
35
|
span.setting-value id="terrain-exaggeration-value" 1.0x
|
|
36
36
|
input.setting-range id="terrain-exaggeration-slider" type="range" min="0" max="3" step="0.1" value="1" oninput="setTerrainExaggeration(this.value)"
|
|
37
37
|
button.control-button onclick="toggleAntialias()" id="antialias-btn" Antialias
|
|
38
|
+
button.control-button onclick="resetOverlayLayout()" id="overlay-layout-reset-btn" Reset window layout
|
|
38
39
|
|
|
39
40
|
#settings-debug-panel.control-panel.settings-panel
|
|
40
41
|
button.control-button onclick="toggleMapCache()" id="map-cache-btn" Cache
|
|
@@ -78,12 +79,13 @@ ruby:
|
|
|
78
79
|
#map.map-layer data-style-url="#{style_url}"
|
|
79
80
|
|
|
80
81
|
#style-parameters-panel.style-parameters-overlay.collapsed style="display: none;"
|
|
81
|
-
|
|
82
|
+
.style-parameters-header
|
|
82
83
|
span.style-parameters-title Style parameters
|
|
83
84
|
span.style-parameters-summary
|
|
84
85
|
span#style-parameters-count 0
|
|
85
86
|
span params
|
|
86
|
-
|
|
87
|
+
button.style-parameters-toggle type="button" onclick="toggleStyleParametersPanel()" id="style-parameters-toggle" aria-expanded="false" title="Collapse/Expand Style parameters"
|
|
88
|
+
span.style-parameters-toggle-icon ▲
|
|
87
89
|
.style-parameters-body
|
|
88
90
|
#style-parameter-fields.style-parameter-fields
|
|
89
91
|
.style-parameter-actions
|
|
@@ -150,10 +152,11 @@ javascript:
|
|
|
150
152
|
let currentProfile = null;
|
|
151
153
|
let contourManager = null;
|
|
152
154
|
let tileGridManager = null;
|
|
155
|
+
let overlayLayoutManager = null;
|
|
153
156
|
let originalStyle = null;
|
|
154
157
|
let styleParameterDefinitions = new Map();
|
|
155
158
|
let styleParameterValues = {};
|
|
156
|
-
let
|
|
159
|
+
let parameterizedUrlRules = [];
|
|
157
160
|
|
|
158
161
|
const toDomId = (prefix, id) => `${prefix}-${String(id).replace(/[^a-zA-Z0-9_-]/g, '_')}`;
|
|
159
162
|
const noCacheRequestOptions = () => mapCacheDisabled ? {cache: 'no-store'} : undefined;
|
|
@@ -166,7 +169,8 @@ javascript:
|
|
|
166
169
|
return [];
|
|
167
170
|
};
|
|
168
171
|
|
|
169
|
-
const
|
|
172
|
+
const isTemporalParameter = (name) => /(^|_|-)(time|date|datetime)($|_|-)/i.test(name);
|
|
173
|
+
const inferParameterInputType = (name) => isTemporalParameter(name) ? 'datetime-local' : 'text';
|
|
170
174
|
|
|
171
175
|
const parameterInputToQueryValue = (name, value) => {
|
|
172
176
|
if (!value) return '';
|
|
@@ -208,9 +212,24 @@ javascript:
|
|
|
208
212
|
return prefix ? new URL(prefix, window.location.href).toString() : null;
|
|
209
213
|
};
|
|
210
214
|
|
|
211
|
-
const rememberParameterizedUrl = (template) => {
|
|
215
|
+
const rememberParameterizedUrl = (template, parameterNames) => {
|
|
216
|
+
const names = normalizeQueryParams(parameterNames);
|
|
217
|
+
if (!names.length) return;
|
|
218
|
+
|
|
212
219
|
const prefix = urlPrefixFromTemplate(template);
|
|
213
|
-
|
|
220
|
+
if (!prefix) return;
|
|
221
|
+
|
|
222
|
+
const existing = parameterizedUrlRules.find(rule => rule.prefix === prefix);
|
|
223
|
+
if (existing) {
|
|
224
|
+
names.forEach(name => existing.parameterNames.add(name));
|
|
225
|
+
} else {
|
|
226
|
+
parameterizedUrlRules.push({prefix, parameterNames: new Set(names)});
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const rememberParameterizedSourceUrls = (sourceDef, parameterNames) => {
|
|
231
|
+
[sourceDef?.url, sourceDef?.meta_url, sourceDef?.data].forEach(url => rememberParameterizedUrl(url, parameterNames));
|
|
232
|
+
(sourceDef?.tiles || []).forEach(tileUrl => rememberParameterizedUrl(tileUrl, parameterNames));
|
|
214
233
|
};
|
|
215
234
|
|
|
216
235
|
const appendStyleParametersToUrl = (resourceUrl, values = styleParameterValues, parameterNames = Object.keys(values)) => {
|
|
@@ -239,16 +258,13 @@ javascript:
|
|
|
239
258
|
const params = sourceDeclaredParameters(sourceDef);
|
|
240
259
|
params.forEach(name => mergeStyleParameterDefinition(name, sourceName));
|
|
241
260
|
|
|
242
|
-
|
|
243
|
-
[sourceDef.url, sourceDef.meta_url, sourceDef.data].forEach(rememberParameterizedUrl);
|
|
244
|
-
(sourceDef.tiles || []).forEach(rememberParameterizedUrl);
|
|
245
|
-
}
|
|
261
|
+
rememberParameterizedSourceUrls(sourceDef, params);
|
|
246
262
|
});
|
|
247
263
|
};
|
|
248
264
|
|
|
249
265
|
const fetchSourceMetadata = async (url) => {
|
|
250
266
|
try {
|
|
251
|
-
const response = await fetch(
|
|
267
|
+
const response = await fetch(url, noCacheRequestOptions());
|
|
252
268
|
return response.ok ? response.json() : null;
|
|
253
269
|
} catch (e) {
|
|
254
270
|
console.warn('Could not inspect source metadata:', url, e);
|
|
@@ -269,7 +285,9 @@ javascript:
|
|
|
269
285
|
|
|
270
286
|
const params = normalizeQueryParams(metadata.query_params || metadata.queryParams);
|
|
271
287
|
params.forEach(name => mergeStyleParameterDefinition(name, sourceName));
|
|
272
|
-
|
|
288
|
+
const parameterNames = getSourceParameterNames(sourceName, sourceDef);
|
|
289
|
+
rememberParameterizedSourceUrls(sourceDef, parameterNames);
|
|
290
|
+
(metadata.tiles || []).forEach(tileUrl => rememberParameterizedUrl(tileUrl, parameterNames));
|
|
273
291
|
}
|
|
274
292
|
}));
|
|
275
293
|
};
|
|
@@ -294,6 +312,53 @@ javascript:
|
|
|
294
312
|
return values;
|
|
295
313
|
};
|
|
296
314
|
|
|
315
|
+
const normalizeLocalizedLabel = (value) => {
|
|
316
|
+
if (!value) return null;
|
|
317
|
+
if (typeof value === 'string') return value;
|
|
318
|
+
if (typeof value === 'object') return value.title || value.name || value.label || null;
|
|
319
|
+
return String(value);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const getLocalizedMetadataLabel = (id, style = originalStyle) => {
|
|
323
|
+
const locale = style?.metadata?.locale;
|
|
324
|
+
if (!locale || !id) return id;
|
|
325
|
+
|
|
326
|
+
for (const lang of ['en-US', 'en', 'ru']) {
|
|
327
|
+
const label = normalizeLocalizedLabel(locale[lang]?.[id]);
|
|
328
|
+
if (label) return label;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
for (const lang in locale) {
|
|
332
|
+
const label = normalizeLocalizedLabel(locale[lang]?.[id]);
|
|
333
|
+
if (label) return label;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return id;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const compactList = (items, limit = 3) => {
|
|
340
|
+
const unique = [...new Set(items.filter(Boolean))];
|
|
341
|
+
return unique.length > limit
|
|
342
|
+
? `${unique.slice(0, limit).join(', ')} +${unique.length - limit}`
|
|
343
|
+
: unique.join(', ');
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const getStyleParameterContext = (definition) => {
|
|
347
|
+
const style = originalStyle || currentStyle;
|
|
348
|
+
const sources = [...definition.sources].sort();
|
|
349
|
+
const layers = (style?.layers || []).filter(layer => sources.includes(layer.source));
|
|
350
|
+
const filterIds = [...new Set(layers.map(layer => layer.metadata?.filter_id).filter(Boolean))].sort();
|
|
351
|
+
const filterLabels = filterIds.map(id => getLocalizedMetadataLabel(id, style));
|
|
352
|
+
|
|
353
|
+
return {
|
|
354
|
+
sources,
|
|
355
|
+
layers: layers.map(layer => layer.id).sort(),
|
|
356
|
+
filterLabels: filterLabels.length ? filterLabels : sources,
|
|
357
|
+
sourceSummary: compactList(sources),
|
|
358
|
+
filterSummary: compactList(filterLabels.length ? filterLabels : sources)
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
|
|
297
362
|
const renderStyleParameterControls = () => {
|
|
298
363
|
const panel = document.getElementById('style-parameters-panel');
|
|
299
364
|
const fields = document.getElementById('style-parameter-fields');
|
|
@@ -306,28 +371,84 @@ javascript:
|
|
|
306
371
|
fields.innerHTML = '';
|
|
307
372
|
|
|
308
373
|
definitions.forEach(definition => {
|
|
309
|
-
const row = document.createElement('
|
|
374
|
+
const row = document.createElement('div');
|
|
310
375
|
row.className = 'style-parameter-row';
|
|
311
|
-
|
|
376
|
+
const context = getStyleParameterContext(definition);
|
|
312
377
|
|
|
313
|
-
const
|
|
378
|
+
const header = document.createElement('div');
|
|
379
|
+
header.className = 'style-parameter-header';
|
|
380
|
+
const label = document.createElement('label');
|
|
314
381
|
label.className = 'style-parameter-label';
|
|
382
|
+
label.htmlFor = `style-param-${definition.name}`;
|
|
315
383
|
label.textContent = definition.name;
|
|
384
|
+
const counts = document.createElement('span');
|
|
385
|
+
counts.className = 'style-parameter-counts';
|
|
386
|
+
counts.textContent = `${context.sources.length} src / ${context.layers.length} lyr`;
|
|
387
|
+
counts.title = `${context.sources.length} sources, ${context.layers.length} layers`;
|
|
388
|
+
header.appendChild(label);
|
|
389
|
+
header.appendChild(counts);
|
|
316
390
|
|
|
317
391
|
const input = document.createElement('input');
|
|
318
392
|
input.className = 'style-parameter-input';
|
|
319
393
|
input.id = `style-param-${definition.name}`;
|
|
320
|
-
input.type = inferParameterInputType(definition.name);
|
|
394
|
+
input.type = isTemporalParameter(definition.name) ? 'text' : inferParameterInputType(definition.name);
|
|
321
395
|
input.value = queryValueToParameterInput(definition.name, styleParameterValues[definition.name]);
|
|
322
396
|
input.dataset.parameterName = definition.name;
|
|
323
|
-
input.title = [
|
|
397
|
+
input.title = [
|
|
398
|
+
`Sources: ${context.sources.join(', ')}`,
|
|
399
|
+
`Layers: ${context.layers.join(', ')}`
|
|
400
|
+
].join('\n');
|
|
401
|
+
if (isTemporalParameter(definition.name)) {
|
|
402
|
+
input.classList.add('temporal');
|
|
403
|
+
input.readOnly = true;
|
|
404
|
+
input.placeholder = 'Select date and time';
|
|
405
|
+
input.setAttribute('aria-haspopup', 'dialog');
|
|
406
|
+
input.addEventListener('click', () => window.TemporalPicker.open(input));
|
|
407
|
+
input.addEventListener('focus', () => window.TemporalPicker.open(input));
|
|
408
|
+
input.addEventListener('keydown', event => {
|
|
409
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
410
|
+
event.preventDefault();
|
|
411
|
+
window.TemporalPicker.open(input);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
}
|
|
324
415
|
|
|
325
|
-
|
|
326
|
-
|
|
416
|
+
const parameterControl = document.createElement('div');
|
|
417
|
+
parameterControl.className = isTemporalParameter(definition.name)
|
|
418
|
+
? 'style-parameter-input-group temporal'
|
|
419
|
+
: 'style-parameter-input-group';
|
|
420
|
+
parameterControl.appendChild(input);
|
|
421
|
+
|
|
422
|
+
const contextBlock = document.createElement('div');
|
|
423
|
+
contextBlock.className = 'style-parameter-context';
|
|
424
|
+
contextBlock.title = input.title;
|
|
425
|
+
|
|
426
|
+
const usedBy = document.createElement('span');
|
|
427
|
+
usedBy.className = 'style-parameter-context-row';
|
|
428
|
+
const usedByKey = document.createElement('span');
|
|
429
|
+
usedByKey.className = 'style-parameter-context-key';
|
|
430
|
+
usedByKey.textContent = 'Used by';
|
|
431
|
+
usedBy.appendChild(usedByKey);
|
|
432
|
+
usedBy.appendChild(document.createTextNode(` ${context.filterSummary || 'No linked layers'}`));
|
|
433
|
+
contextBlock.appendChild(usedBy);
|
|
434
|
+
|
|
435
|
+
const sourceInfo = document.createElement('span');
|
|
436
|
+
sourceInfo.className = 'style-parameter-context-row';
|
|
437
|
+
const sourceKey = document.createElement('span');
|
|
438
|
+
sourceKey.className = 'style-parameter-context-key';
|
|
439
|
+
sourceKey.textContent = 'Sources';
|
|
440
|
+
sourceInfo.appendChild(sourceKey);
|
|
441
|
+
sourceInfo.appendChild(document.createTextNode(` ${context.sourceSummary || 'None'}`));
|
|
442
|
+
contextBlock.appendChild(sourceInfo);
|
|
443
|
+
|
|
444
|
+
row.appendChild(header);
|
|
445
|
+
row.appendChild(parameterControl);
|
|
446
|
+
row.appendChild(contextBlock);
|
|
327
447
|
fields.appendChild(row);
|
|
328
448
|
});
|
|
329
449
|
|
|
330
450
|
layoutBottomOverlays();
|
|
451
|
+
overlayLayoutManager?.refreshPanel('style-parameters');
|
|
331
452
|
};
|
|
332
453
|
|
|
333
454
|
const toggleStyleParametersPanel = () => {
|
|
@@ -338,12 +459,13 @@ javascript:
|
|
|
338
459
|
const isCollapsed = panel.classList.toggle('collapsed');
|
|
339
460
|
toggle?.setAttribute('aria-expanded', String(!isCollapsed));
|
|
340
461
|
layoutBottomOverlays();
|
|
462
|
+
overlayLayoutManager?.refreshPanel('style-parameters');
|
|
341
463
|
};
|
|
342
464
|
|
|
343
465
|
const initializeStyleParameters = async (style) => {
|
|
344
466
|
styleParameterDefinitions = new Map();
|
|
345
467
|
styleParameterValues = {};
|
|
346
|
-
|
|
468
|
+
parameterizedUrlRules = [];
|
|
347
469
|
|
|
348
470
|
collectStyleSourceParameters(style);
|
|
349
471
|
styleParameterDefinitions.forEach((definition, name) => {
|
|
@@ -384,15 +506,27 @@ javascript:
|
|
|
384
506
|
return modifiedStyle;
|
|
385
507
|
};
|
|
386
508
|
|
|
387
|
-
const
|
|
388
|
-
if (!Object.values(styleParameterValues).some(value => value !== undefined && value !== null && value !== '')) return false;
|
|
509
|
+
const parameterizedUrlRuleFor = (resourceUrl) => {
|
|
389
510
|
const absolute = new URL(resourceUrl, window.location.href).toString();
|
|
390
|
-
return
|
|
511
|
+
return parameterizedUrlRules
|
|
512
|
+
.filter(rule => absolute.startsWith(rule.prefix))
|
|
513
|
+
.sort((a, b) => b.prefix.length - a.prefix.length)[0] || null;
|
|
391
514
|
};
|
|
392
515
|
|
|
393
|
-
const patchRequestUrl = (resourceUrl) =>
|
|
516
|
+
const patchRequestUrl = (resourceUrl) => {
|
|
517
|
+
const rule = parameterizedUrlRuleFor(resourceUrl);
|
|
518
|
+
if (!rule) return resourceUrl;
|
|
519
|
+
|
|
520
|
+
const parameterNames = [...rule.parameterNames];
|
|
521
|
+
if (!parameterNames.some(name => {
|
|
522
|
+
const value = styleParameterValues[name];
|
|
523
|
+
return value !== undefined && value !== null && value !== '';
|
|
524
|
+
})) return resourceUrl;
|
|
525
|
+
|
|
526
|
+
return appendStyleParametersToUrl(resourceUrl, styleParameterValues, parameterNames);
|
|
527
|
+
};
|
|
394
528
|
|
|
395
|
-
const BOTTOM_OVERLAY_IDS = ['
|
|
529
|
+
const BOTTOM_OVERLAY_IDS = ['loading-indicator'];
|
|
396
530
|
const BOTTOM_OVERLAY_BASE_OFFSET = 20;
|
|
397
531
|
const BOTTOM_OVERLAY_GAP = 12;
|
|
398
532
|
|
|
@@ -419,6 +553,69 @@ javascript:
|
|
|
419
553
|
element.style.bottom = `${bottomOffset}px`;
|
|
420
554
|
bottomOffset += element.offsetHeight + BOTTOM_OVERLAY_GAP;
|
|
421
555
|
});
|
|
556
|
+
overlayLayoutManager?.refreshBounds();
|
|
557
|
+
});
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const getSystemReservedBottom = () => {
|
|
561
|
+
const loading = document.getElementById('loading-indicator');
|
|
562
|
+
return isVisibleBottomOverlay(loading) ? loading.offsetHeight + BOTTOM_OVERLAY_BASE_OFFSET + BOTTOM_OVERLAY_GAP : 0;
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const leftControlStackOffset = (sectionName) => {
|
|
566
|
+
const mapSettings = document.getElementById('map-settings-wrapper');
|
|
567
|
+
const styleControls = document.getElementById('style-controls-wrapper');
|
|
568
|
+
const gap = 12;
|
|
569
|
+
const mapHeight = mapSettings?.getBoundingClientRect().height || mapSettings?.offsetHeight || 0;
|
|
570
|
+
const styleHeight = styleControls?.getBoundingClientRect().height || styleControls?.offsetHeight || 0;
|
|
571
|
+
|
|
572
|
+
return {
|
|
573
|
+
x: 0,
|
|
574
|
+
y: sectionName === 'map-settings' ? -((styleHeight + gap) / 2) : ((mapHeight + gap) / 2)
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
const initializeOverlayLayout = () => {
|
|
579
|
+
if (!window.OverlayLayoutManager) return;
|
|
580
|
+
|
|
581
|
+
overlayLayoutManager?.destroy();
|
|
582
|
+
overlayLayoutManager = new OverlayLayoutManager({
|
|
583
|
+
storageKey: 'maplibre-preview:overlay-layout:v3',
|
|
584
|
+
snapThreshold: 32,
|
|
585
|
+
mobileBreakpoint: 768,
|
|
586
|
+
edgeGap: 10,
|
|
587
|
+
getReservedBounds: () => ({bottom: getSystemReservedBottom()})
|
|
588
|
+
});
|
|
589
|
+
window.overlayLayoutManager = overlayLayoutManager;
|
|
590
|
+
window.resetOverlayLayout = () => overlayLayoutManager.resetLayout();
|
|
591
|
+
|
|
592
|
+
overlayLayoutManager.registerPanel({
|
|
593
|
+
id: 'map-settings',
|
|
594
|
+
element: '#map-settings-wrapper',
|
|
595
|
+
handleSelector: '.control-section-title',
|
|
596
|
+
defaultAnchor: 'top-left',
|
|
597
|
+
defaultOffset: {x: 0, y: 0}
|
|
598
|
+
});
|
|
599
|
+
overlayLayoutManager.registerPanel({
|
|
600
|
+
id: 'style-controls',
|
|
601
|
+
element: '#style-controls-wrapper',
|
|
602
|
+
handleSelector: '.control-section-title',
|
|
603
|
+
defaultAnchor: 'left',
|
|
604
|
+
defaultOffset: () => leftControlStackOffset('style-controls')
|
|
605
|
+
});
|
|
606
|
+
overlayLayoutManager.registerPanel({
|
|
607
|
+
id: 'style-parameters',
|
|
608
|
+
element: '#style-parameters-panel',
|
|
609
|
+
handleSelector: '.style-parameters-title',
|
|
610
|
+
defaultAnchor: 'bottom',
|
|
611
|
+
defaultOffset: {x: 0, y: 0}
|
|
612
|
+
});
|
|
613
|
+
overlayLayoutManager.registerPanel({
|
|
614
|
+
id: 'performance',
|
|
615
|
+
element: '#performance-panel',
|
|
616
|
+
handleSelector: '.performance-content',
|
|
617
|
+
defaultAnchor: 'top',
|
|
618
|
+
defaultOffset: {x: 0, y: -10}
|
|
422
619
|
});
|
|
423
620
|
};
|
|
424
621
|
|
|
@@ -648,6 +845,7 @@ javascript:
|
|
|
648
845
|
});
|
|
649
846
|
filters.init();
|
|
650
847
|
createLayerButtons();
|
|
848
|
+
overlayLayoutManager?.refreshBounds();
|
|
651
849
|
} catch (e) {
|
|
652
850
|
console.warn('Filter initialization failed:', e);
|
|
653
851
|
}
|
|
@@ -947,7 +1145,19 @@ javascript:
|
|
|
947
1145
|
|
|
948
1146
|
[header, stats, chart].forEach(el => overlay.appendChild(el));
|
|
949
1147
|
document.getElementById('map-container').appendChild(overlay);
|
|
950
|
-
|
|
1148
|
+
overlayLayoutManager?.registerPanel({
|
|
1149
|
+
id: 'profile',
|
|
1150
|
+
element: overlay,
|
|
1151
|
+
handleSelector: '.profile-title',
|
|
1152
|
+
defaultAnchor: 'bottom',
|
|
1153
|
+
defaultOffset: {x: 0, y: -16},
|
|
1154
|
+
snap: false,
|
|
1155
|
+
lockSizeOnDrag: true
|
|
1156
|
+
});
|
|
1157
|
+
setTimeout(() => {
|
|
1158
|
+
drawSimpleProfileChart(profile);
|
|
1159
|
+
overlayLayoutManager?.refreshPanel('profile');
|
|
1160
|
+
}, 10);
|
|
951
1161
|
layoutBottomOverlays();
|
|
952
1162
|
};
|
|
953
1163
|
|
|
@@ -1025,6 +1235,7 @@ javascript:
|
|
|
1025
1235
|
};
|
|
1026
1236
|
|
|
1027
1237
|
const hideProfile = () => {
|
|
1238
|
+
overlayLayoutManager?.unregisterPanel('profile');
|
|
1028
1239
|
document.getElementById('profile-overlay')?.remove();
|
|
1029
1240
|
layoutBottomOverlays();
|
|
1030
1241
|
map.getLayer('profile-line') && (map.removeLayer('profile-line'), map.removeSource('profile-line'));
|
|
@@ -1491,4 +1702,5 @@ javascript:
|
|
|
1491
1702
|
window.tileGridManager = null;
|
|
1492
1703
|
window.addEventListener('resize', layoutBottomOverlays);
|
|
1493
1704
|
|
|
1705
|
+
initializeOverlayLayout();
|
|
1494
1706
|
initializeMap();
|
|
@@ -14,6 +14,7 @@ RSpec.describe MapLibrePreview do
|
|
|
14
14
|
expect(last_response.body).to include('maplibre-gl')
|
|
15
15
|
expect(last_response.body).to include('maplibre-contour')
|
|
16
16
|
expect(last_response.body).to include('d3')
|
|
17
|
+
expect(last_response.body).to include('overlay_layout')
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
it 'renders map cache toggle wiring' do
|
|
@@ -38,13 +39,34 @@ RSpec.describe MapLibrePreview do
|
|
|
38
39
|
expect(last_response.body).to include('id="style-parameter-fields"')
|
|
39
40
|
expect(last_response.body).to include('id="style-parameters-apply"')
|
|
40
41
|
expect(last_response.body).to include('id="style-parameters-reset"')
|
|
42
|
+
expect(last_response.body).to include('OverlayLayoutManager')
|
|
43
|
+
expect(last_response.body).to include('overlayLayoutManager')
|
|
44
|
+
expect(last_response.body).to include('resetOverlayLayout')
|
|
45
|
+
expect(last_response.body).to include('id="overlay-layout-reset-btn"')
|
|
46
|
+
expect(last_response.body).to include('Reset window layout')
|
|
47
|
+
expect(last_response.body).to include('maplibre-preview:overlay-layout:v3')
|
|
48
|
+
expect(last_response.body).not_to include('overlay-panel-dock-actions')
|
|
41
49
|
expect(last_response.body).to include('mapCacheDisabled')
|
|
42
50
|
expect(last_response.body).to include('basemapOpacity')
|
|
43
51
|
expect(last_response.body).to include('terrainExaggeration')
|
|
44
52
|
expect(last_response.body).to include('styleParameterDefinitions')
|
|
53
|
+
expect(last_response.body).to include('parameterizedUrlRules')
|
|
45
54
|
expect(last_response.body).to include('sourceDeclaredParameters')
|
|
46
55
|
expect(last_response.body).to include('collectSourceMetadataParameters')
|
|
47
56
|
expect(last_response.body).to include('applyStyleParametersToStyle')
|
|
57
|
+
expect(last_response.body).to include('rememberParameterizedSourceUrls')
|
|
58
|
+
expect(last_response.body).to include('parameterizedUrlRuleFor')
|
|
59
|
+
expect(last_response.body).to include('getStyleParameterContext')
|
|
60
|
+
expect(last_response.body).to include('isTemporalParameter')
|
|
61
|
+
expect(last_response.body).to include('/css/temporal_picker.css')
|
|
62
|
+
expect(last_response.body).to include('/js/temporal_picker.js')
|
|
63
|
+
expect(last_response.body).to include('window.TemporalPicker.open')
|
|
64
|
+
expect(last_response.body).to include('style-parameter-input-group')
|
|
65
|
+
expect(last_response.body).to include('style-parameter-counts')
|
|
66
|
+
expect(last_response.body).to include('style-parameter-context')
|
|
67
|
+
expect(last_response.body).to include('Used by')
|
|
68
|
+
expect(last_response.body).to include('Sources:')
|
|
69
|
+
expect(last_response.body).not_to include("absolute.includes('/rb_tiles/')")
|
|
48
70
|
expect(last_response.body).to include('layoutBottomOverlays')
|
|
49
71
|
expect(last_response.body).to include('showCollisionBoxes')
|
|
50
72
|
expect(last_response.body).to include('showOverdrawInspector')
|
|
@@ -60,7 +82,7 @@ RSpec.describe MapLibrePreview do
|
|
|
60
82
|
end
|
|
61
83
|
|
|
62
84
|
it 'serves all required JavaScript modules' do
|
|
63
|
-
%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|
|
|
85
|
+
%w[/js/overlay_layout.js /js/filters.js /js/contour.js /js/tilegrid.js /js/temporal_picker.js /vendor/maplibre-gl/maplibre-gl.js /vendor/maplibre-contour/index.min.js /vendor/d3/d3.v7.min.js].each do |js_file|
|
|
64
86
|
get js_file
|
|
65
87
|
expect(last_response).to be_ok
|
|
66
88
|
expect(last_response.content_type).to include('javascript')
|
|
@@ -69,11 +91,13 @@ RSpec.describe MapLibrePreview do
|
|
|
69
91
|
end
|
|
70
92
|
|
|
71
93
|
it 'serves required stylesheets' do
|
|
72
|
-
|
|
94
|
+
%w[/vendor/maplibre-gl/maplibre-gl.css /css/temporal_picker.css].each do |css_file|
|
|
95
|
+
get css_file
|
|
73
96
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
97
|
+
expect(last_response).to be_ok
|
|
98
|
+
expect(last_response.content_type).to include('text/css')
|
|
99
|
+
expect(last_response.body).not_to be_empty
|
|
100
|
+
end
|
|
77
101
|
end
|
|
78
102
|
end
|
|
79
103
|
|
|
@@ -85,9 +109,12 @@ RSpec.describe MapLibrePreview do
|
|
|
85
109
|
body = last_response.body
|
|
86
110
|
|
|
87
111
|
expect(body).to include('/vendor/maplibre-gl/maplibre-gl.css')
|
|
112
|
+
expect(body).to include('/css/temporal_picker.css')
|
|
88
113
|
expect(body).to include('/vendor/maplibre-gl/maplibre-gl.js')
|
|
89
114
|
expect(body).to include('/vendor/maplibre-contour/index.min.js')
|
|
90
115
|
expect(body).to include('/vendor/d3/d3.v7.min.js')
|
|
116
|
+
expect(body).to include('/js/overlay_layout.js')
|
|
117
|
+
expect(body).to include('/js/temporal_picker.js')
|
|
91
118
|
expect(body).not_to include('unpkg.com')
|
|
92
119
|
expect(body).not_to include('d3js.org')
|
|
93
120
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: maplibre-preview
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Ludov
|
|
@@ -201,8 +201,11 @@ files:
|
|
|
201
201
|
- bin/maplibre-preview
|
|
202
202
|
- docs/README_RU.md
|
|
203
203
|
- lib/maplibre-preview.rb
|
|
204
|
+
- lib/maplibre-preview/public/css/temporal_picker.css
|
|
204
205
|
- lib/maplibre-preview/public/js/contour.js
|
|
205
206
|
- lib/maplibre-preview/public/js/filters.js
|
|
207
|
+
- lib/maplibre-preview/public/js/overlay_layout.js
|
|
208
|
+
- lib/maplibre-preview/public/js/temporal_picker.js
|
|
206
209
|
- lib/maplibre-preview/public/js/tilegrid.js
|
|
207
210
|
- lib/maplibre-preview/public/vendor/d3/d3.v7.min.js
|
|
208
211
|
- lib/maplibre-preview/public/vendor/maplibre-contour/index.min.js
|