maplibre-preview 1.3.10 → 1.4.1
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c77ead5ccf5ff35082ce55f81de3ac6edc8cfe20b468b5bd96120fc1c3e1a3ea
|
|
4
|
+
data.tar.gz: 45e3418367216b9beed3c90ba0972bd52f1be2272ea0430a4a14d81d088660fd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42b45e964f3935c8e8a351ab504b357aec1794f365c19b8d8fffb6d26dd9e2b37e08675400eac1ff1d438ea8e393b4d5dc4d563d431dcd456c48aee68140817a
|
|
7
|
+
data.tar.gz: 62dd94272a2ec97175bddc53308eb4f36dc0de7a21cb086c0220fab40029d400556cda5b9c9a833fda106408d38c75705b3f50689ae325ee9b9cf7f073a4747e
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.1] - 2025-12-17
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Static assets paths** - fixed paths to favicon and JS files to support non-root application mounting using `request.script_name`
|
|
7
|
+
|
|
8
|
+
## [1.4.0] - 2025-12-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Tile Grid visualization** - TileGridManager class for displaying tile boundaries and statistics
|
|
12
|
+
- **Tile count tracking** - real-time count of loaded tiles with fallback estimation
|
|
13
|
+
- **Tile Grid panel** - UI panel showing number of loaded tiles with toggle for tile borders
|
|
14
|
+
- **Tile Grid button** - control button in layer controls to show/hide tile grid visualization
|
|
15
|
+
|
|
16
|
+
### Technical Changes
|
|
17
|
+
- **tilegrid.js** - new JavaScript module for tile grid management
|
|
18
|
+
- **MapLibre showTileBoundaries integration** - uses native debug feature for tile boundary visualization
|
|
19
|
+
- **Internal API tile counting** - attempts to count tiles via sourceCaches with fallback estimation
|
|
20
|
+
|
|
3
21
|
## [1.3.10] - 2025-11-19
|
|
4
22
|
|
|
5
23
|
### Changed
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TileGridManager - manages tile boundaries visualization and tile statistics
|
|
3
|
+
*/
|
|
4
|
+
class TileGridManager {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.map = options.map;
|
|
7
|
+
this.panelContainer = null;
|
|
8
|
+
this.isVisible = false;
|
|
9
|
+
this.showBorders = true;
|
|
10
|
+
this.tilesLoaded = 0;
|
|
11
|
+
this.updateInterval = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
init() {
|
|
15
|
+
if (!this.map) return;
|
|
16
|
+
|
|
17
|
+
this.createPanel();
|
|
18
|
+
this.setupEventListeners();
|
|
19
|
+
this.startTileTracking();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
createPanel() {
|
|
23
|
+
this.panelContainer = document.createElement('div');
|
|
24
|
+
this.panelContainer.id = 'tilegrid-panel';
|
|
25
|
+
this.panelContainer.className = 'tilegrid-panel';
|
|
26
|
+
this.panelContainer.style.display = 'none';
|
|
27
|
+
|
|
28
|
+
this.panelContainer.innerHTML = `
|
|
29
|
+
<div class="tilegrid-header">
|
|
30
|
+
<span class="tilegrid-title">Tile Grid</span>
|
|
31
|
+
<button class="tilegrid-close" onclick="tileGridManager.toggle()">×</button>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="tilegrid-stats">
|
|
34
|
+
<div class="tilegrid-stat-label">NO. OF TILES LOADED</div>
|
|
35
|
+
<div class="tilegrid-stat-value" id="tilegrid-count">0</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="tilegrid-controls">
|
|
38
|
+
<label class="tilegrid-checkbox-label">
|
|
39
|
+
<span>Show tile borders</span>
|
|
40
|
+
<input type="checkbox" id="tilegrid-borders-checkbox" checked onchange="tileGridManager.toggleBorders(this.checked)">
|
|
41
|
+
<span class="tilegrid-checkbox-custom"></span>
|
|
42
|
+
</label>
|
|
43
|
+
</div>
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
document.getElementById('map-container').appendChild(this.panelContainer);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setupEventListeners() {
|
|
50
|
+
this.map.on('sourcedata', () => this.updateTileCount());
|
|
51
|
+
this.map.on('data', () => this.updateTileCount());
|
|
52
|
+
this.map.on('moveend', () => this.updateTileCount());
|
|
53
|
+
this.map.on('zoomend', () => this.updateTileCount());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
startTileTracking() {
|
|
57
|
+
this.updateInterval = setInterval(() => {
|
|
58
|
+
if (this.isVisible) {
|
|
59
|
+
this.updateTileCount();
|
|
60
|
+
}
|
|
61
|
+
}, 1000);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
updateTileCount() {
|
|
65
|
+
if (!this.map || !this.isVisible) return;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const style = this.map.getStyle();
|
|
69
|
+
if (!style || !style.sources) return;
|
|
70
|
+
|
|
71
|
+
let totalTiles = 0;
|
|
72
|
+
|
|
73
|
+
// Count tiles from all raster and vector sources
|
|
74
|
+
Object.keys(style.sources).forEach(sourceName => {
|
|
75
|
+
const sourceCache = this.map.style?.sourceCaches?.[sourceName];
|
|
76
|
+
if (sourceCache) {
|
|
77
|
+
const tiles = sourceCache.getVisibleCoordinates?.() || [];
|
|
78
|
+
totalTiles += tiles.length;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Fallback: estimate tiles based on zoom and viewport
|
|
83
|
+
if (totalTiles === 0) {
|
|
84
|
+
totalTiles = this.estimateTileCount();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.tilesLoaded = totalTiles;
|
|
88
|
+
const countElement = document.getElementById('tilegrid-count');
|
|
89
|
+
if (countElement) {
|
|
90
|
+
countElement.textContent = this.tilesLoaded;
|
|
91
|
+
}
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.warn('TileGridManager: could not count tiles', e);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
estimateTileCount() {
|
|
98
|
+
const bounds = this.map.getBounds();
|
|
99
|
+
const nw = this.map.project(bounds.getNorthWest());
|
|
100
|
+
const se = this.map.project(bounds.getSouthEast());
|
|
101
|
+
|
|
102
|
+
const viewportWidth = Math.abs(se.x - nw.x);
|
|
103
|
+
const viewportHeight = Math.abs(se.y - nw.y);
|
|
104
|
+
|
|
105
|
+
// Fallback estimation, assume standard 256px tiles
|
|
106
|
+
const tilesX = Math.ceil(viewportWidth / 256) + 1;
|
|
107
|
+
const tilesY = Math.ceil(viewportHeight / 256) + 1;
|
|
108
|
+
|
|
109
|
+
return tilesX * tilesY;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
toggle() {
|
|
113
|
+
this.isVisible = !this.isVisible;
|
|
114
|
+
|
|
115
|
+
if (this.panelContainer) {
|
|
116
|
+
this.panelContainer.style.display = this.isVisible ? 'block' : 'none';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const btn = document.getElementById('tilegrid-mode-btn');
|
|
120
|
+
if (btn) {
|
|
121
|
+
btn.textContent = this.isVisible ? 'Hide Tile Grid' : 'Tile Grid';
|
|
122
|
+
btn.className = `control-button ${this.isVisible ? 'active' : ''}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (this.isVisible) {
|
|
126
|
+
this.updateTileCount();
|
|
127
|
+
this.toggleBorders(true);
|
|
128
|
+
} else {
|
|
129
|
+
this.toggleBorders(false);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
toggleBorders(show) {
|
|
134
|
+
this.showBorders = show;
|
|
135
|
+
|
|
136
|
+
if (this.map) {
|
|
137
|
+
this.map.showTileBoundaries = show;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Update checkbox state
|
|
141
|
+
const checkbox = document.getElementById('tilegrid-borders-checkbox');
|
|
142
|
+
if (checkbox && checkbox.checked !== show) {
|
|
143
|
+
checkbox.checked = show;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
show() {
|
|
148
|
+
if (!this.isVisible) {
|
|
149
|
+
this.toggle();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
hide() {
|
|
154
|
+
if (this.isVisible) {
|
|
155
|
+
this.toggle();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
cleanup() {
|
|
160
|
+
if (this.updateInterval) {
|
|
161
|
+
clearInterval(this.updateInterval);
|
|
162
|
+
this.updateInterval = null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.panelContainer && this.panelContainer.parentNode) {
|
|
166
|
+
this.panelContainer.parentNode.removeChild(this.panelContainer);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (this.map) {
|
|
170
|
+
this.map.showTileBoundaries = false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
@@ -4,13 +4,15 @@ html
|
|
|
4
4
|
meta[charset="utf-8"]
|
|
5
5
|
title Map Preview
|
|
6
6
|
meta[name="viewport" content="width=device-width, initial-scale=1.0"]
|
|
7
|
-
|
|
7
|
+
- base_path = request.script_name
|
|
8
|
+
link[rel="icon" type="image/png" href="#{base_path}/icons/favicon.png"]
|
|
8
9
|
link[href="https://unpkg.com/maplibre-gl@#{MapLibrePreview::MAPLIBRE_VERSION}/dist/maplibre-gl.css" rel='stylesheet']
|
|
9
10
|
script[src="https://unpkg.com/maplibre-gl@#{MapLibrePreview::MAPLIBRE_VERSION}/dist/maplibre-gl.js"]
|
|
10
11
|
script[src="https://unpkg.com/maplibre-contour@#{MapLibrePreview::CONTOUR_VERSION}/dist/index.min.js"]
|
|
11
12
|
script[src="https://d3js.org/d3.v#{MapLibrePreview::D3_VERSION}.min.js"]
|
|
12
|
-
script[src=
|
|
13
|
-
script[src=
|
|
13
|
+
script[src="#{base_path}/js/filters.js"]
|
|
14
|
+
script[src="#{base_path}/js/contour.js"]
|
|
15
|
+
script[src="#{base_path}/js/tilegrid.js"]
|
|
14
16
|
style
|
|
15
17
|
| * { margin: 0; padding: 0; box-sizing: border-box; }
|
|
16
18
|
| html { height: 100%; }
|
|
@@ -385,6 +387,62 @@ html
|
|
|
385
387
|
| .version-info-version:hover {
|
|
386
388
|
| color: #a9b7c6; text-decoration: none;
|
|
387
389
|
| }
|
|
390
|
+
| /* TileGrid Panel Styles */
|
|
391
|
+
| .tilegrid-panel {
|
|
392
|
+
| position: fixed; top: 50%; right: 10px; transform: translateY(-50%); z-index: 1000;
|
|
393
|
+
| background: rgba(60, 63, 65, 0.95); border: 1px solid #555555;
|
|
394
|
+
| border-radius: 4px; padding: 12px; width: 200px;
|
|
395
|
+
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
396
|
+
| }
|
|
397
|
+
| .tilegrid-header {
|
|
398
|
+
| display: flex; justify-content: space-between; align-items: center;
|
|
399
|
+
| margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #555555;
|
|
400
|
+
| }
|
|
401
|
+
| .tilegrid-title {
|
|
402
|
+
| color: #ffc66d; font-weight: bold; font-size: 12px;
|
|
403
|
+
| }
|
|
404
|
+
| .tilegrid-close {
|
|
405
|
+
| background: none; border: none; color: #808080; cursor: pointer;
|
|
406
|
+
| font-size: 14px; padding: 0; width: 16px; height: 16px;
|
|
407
|
+
| display: flex; align-items: center; justify-content: center;
|
|
408
|
+
| border-radius: 2px; transition: all 0.2s ease;
|
|
409
|
+
| }
|
|
410
|
+
| .tilegrid-close:hover {
|
|
411
|
+
| background: #4b4d4f; color: #a9b7c6;
|
|
412
|
+
| }
|
|
413
|
+
| .tilegrid-stats {
|
|
414
|
+
| margin-bottom: 12px; padding: 10px; background: #313335; border-radius: 3px;
|
|
415
|
+
| border: 1px solid #464749;
|
|
416
|
+
| }
|
|
417
|
+
| .tilegrid-stat-label {
|
|
418
|
+
| color: #808080; font-size: 10px; font-weight: 500; letter-spacing: 0.5px;
|
|
419
|
+
| text-transform: uppercase; margin-bottom: 4px;
|
|
420
|
+
| }
|
|
421
|
+
| .tilegrid-stat-value {
|
|
422
|
+
| color: #6897bb; font-size: 28px; font-weight: bold; line-height: 1;
|
|
423
|
+
| font-family: 'Courier New', monospace;
|
|
424
|
+
| }
|
|
425
|
+
| .tilegrid-controls {
|
|
426
|
+
| padding-top: 8px; border-top: 1px solid #555555;
|
|
427
|
+
| }
|
|
428
|
+
| .tilegrid-checkbox-label {
|
|
429
|
+
| display: flex; align-items: center; justify-content: space-between;
|
|
430
|
+
| cursor: pointer; user-select: none; color: #a9b7c6; font-size: 11px;
|
|
431
|
+
| }
|
|
432
|
+
| .tilegrid-checkbox-label input[type="checkbox"] {
|
|
433
|
+
| display: none;
|
|
434
|
+
| }
|
|
435
|
+
| .tilegrid-checkbox-custom {
|
|
436
|
+
| width: 16px; height: 16px; border: 1px solid #555555; border-radius: 2px;
|
|
437
|
+
| display: flex; align-items: center; justify-content: center;
|
|
438
|
+
| transition: all 0.2s ease; background: #3c3f41;
|
|
439
|
+
| }
|
|
440
|
+
| .tilegrid-checkbox-label input[type="checkbox"]:checked + .tilegrid-checkbox-custom {
|
|
441
|
+
| background: #6a9955; border-color: #7bb366;
|
|
442
|
+
| }
|
|
443
|
+
| .tilegrid-checkbox-label input[type="checkbox"]:checked + .tilegrid-checkbox-custom::after {
|
|
444
|
+
| content: '✓'; color: white; font-size: 10px; font-weight: bold;
|
|
445
|
+
| }
|
|
388
446
|
body
|
|
389
447
|
.container
|
|
390
448
|
== yield
|
|
@@ -18,6 +18,7 @@ ruby:
|
|
|
18
18
|
button.control-button onclick="toggleHoverMode()" id="hover-mode-btn" Hover Mode
|
|
19
19
|
button.control-button onclick="toggleProfileMode()" id="profile-mode-btn" style="display: none;" Elevation Profile
|
|
20
20
|
button.control-button onclick="toggleAntialias()" id="antialias-btn" Antialias
|
|
21
|
+
button.control-button onclick="toggleTileGrid()" id="tilegrid-mode-btn" Tile Grid
|
|
21
22
|
|
|
22
23
|
#filters-panel.control-panel.active
|
|
23
24
|
.control-panel-header
|
|
@@ -91,6 +92,7 @@ javascript:
|
|
|
91
92
|
let profileLine = null;
|
|
92
93
|
let currentProfile = null;
|
|
93
94
|
let contourManager = null;
|
|
95
|
+
let tileGridManager = null;
|
|
94
96
|
|
|
95
97
|
const toDomId = (prefix, id) => `${prefix}-${String(id).replace(/[^a-zA-Z0-9_-]/g, '_')}`;
|
|
96
98
|
|
|
@@ -305,6 +307,16 @@ javascript:
|
|
|
305
307
|
contourManager.init();
|
|
306
308
|
};
|
|
307
309
|
|
|
310
|
+
const initializeTileGridManager = () => {
|
|
311
|
+
tileGridManager = new TileGridManager({map});
|
|
312
|
+
tileGridManager.init();
|
|
313
|
+
window.tileGridManager = tileGridManager;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const toggleTileGrid = () => {
|
|
317
|
+
tileGridManager?.toggle();
|
|
318
|
+
};
|
|
319
|
+
|
|
308
320
|
const onStyleReady = () => {
|
|
309
321
|
const hasTerrain = currentStyle?.terrain;
|
|
310
322
|
const projectionType = hasTerrain ? 'mercator' : 'globe';
|
|
@@ -330,6 +342,7 @@ javascript:
|
|
|
330
342
|
initializeContourManager();
|
|
331
343
|
}
|
|
332
344
|
|
|
345
|
+
initializeTileGridManager();
|
|
333
346
|
updateTerrainIndicator();
|
|
334
347
|
};
|
|
335
348
|
|
|
@@ -880,5 +893,7 @@ javascript:
|
|
|
880
893
|
window.toggleAntialias = toggleAntialias;
|
|
881
894
|
window.toggleLayerControls = toggleLayerControls;
|
|
882
895
|
window.hideProfile = hideProfile;
|
|
896
|
+
window.toggleTileGrid = toggleTileGrid;
|
|
897
|
+
window.tileGridManager = null;
|
|
883
898
|
|
|
884
899
|
initializeMap();
|
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.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Ludov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-12-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -183,6 +183,7 @@ files:
|
|
|
183
183
|
- lib/maplibre-preview.rb
|
|
184
184
|
- lib/maplibre-preview/public/js/contour.js
|
|
185
185
|
- lib/maplibre-preview/public/js/filters.js
|
|
186
|
+
- lib/maplibre-preview/public/js/tilegrid.js
|
|
186
187
|
- lib/maplibre-preview/version.rb
|
|
187
188
|
- lib/maplibre-preview/views/maplibre_layout.slim
|
|
188
189
|
- lib/maplibre-preview/views/maplibre_map.slim
|