@smartnet360/svelte-components 0.0.74 → 0.0.76
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.
- package/dist/map-v2/core/providers/MapboxProvider.svelte +29 -0
- package/dist/map-v2/core/providers/MapboxProvider.svelte.d.ts +2 -0
- package/dist/map-v2/features/cells/layers/CellsLayer.svelte +127 -106
- package/dist/map-v2/features/cells/stores/cellStoreContext.svelte.js +4 -16
- package/dist/map-v2/features/cells/utils/cellGeoJSON.js +2 -0
- package/dist/map-v2/features/repeaters/constants/techBandZOrder.d.ts +4 -17
- package/dist/map-v2/features/repeaters/constants/techBandZOrder.js +17 -34
- package/dist/map-v2/features/repeaters/index.d.ts +1 -1
- package/dist/map-v2/features/repeaters/index.js +1 -1
- package/dist/map-v2/features/repeaters/layers/RepeatersLayer.svelte +114 -98
- package/dist/map-v2/features/repeaters/stores/repeaterStoreContext.svelte.js +4 -17
- package/dist/map-v2/features/repeaters/utils/repeaterGeoJSON.js +24 -10
- package/dist/map-v2/index.d.ts +1 -1
- package/dist/map-v2/index.js +1 -1
- package/dist/map-v2/shared/controls/FeatureSettingsControl.svelte +55 -142
- package/dist/map-v2/shared/controls/MapControl.svelte +16 -6
- package/dist/map-v2/shared/controls/MapControl.svelte.d.ts +4 -0
- package/dist/map-v2/shared/controls/panels/CellSettingsPanel.svelte +240 -306
- package/dist/map-v2/shared/controls/panels/RepeaterSettingsPanel.svelte +180 -232
- package/dist/map-v2/shared/controls/panels/SiteSettingsPanel.svelte +119 -178
- package/package.json +7 -7
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
navigationPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
41
41
|
/** Position for scale control */
|
|
42
42
|
scalePosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
43
|
+
/** List of anchor layer IDs to create (invisible background layers for positioning) */
|
|
44
|
+
anchorLayerIds?: string[];
|
|
43
45
|
/** Custom CSS class for container */
|
|
44
46
|
class?: string;
|
|
45
47
|
/** Optional child content (layers, controls, etc.) */
|
|
@@ -59,6 +61,7 @@
|
|
|
59
61
|
controls = [],
|
|
60
62
|
navigationPosition = 'top-right',
|
|
61
63
|
scalePosition = 'bottom-left',
|
|
64
|
+
anchorLayerIds = [],
|
|
62
65
|
class: className = '',
|
|
63
66
|
children
|
|
64
67
|
}: Props = $props();
|
|
@@ -71,6 +74,25 @@
|
|
|
71
74
|
let map: MapboxMap | null = null;
|
|
72
75
|
let isExternalMap = false;
|
|
73
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Creates invisible anchor layers for predictable layer ordering
|
|
79
|
+
* These are background layers with visibility: none that serve as positioning markers
|
|
80
|
+
*/
|
|
81
|
+
function createAnchorLayers(mapInstance: MapboxMap, layerIds: string[]) {
|
|
82
|
+
layerIds.forEach((layerId) => {
|
|
83
|
+
// Check if layer already exists (e.g., when style reloads)
|
|
84
|
+
if (!mapInstance.getLayer(layerId)) {
|
|
85
|
+
mapInstance.addLayer({
|
|
86
|
+
id: layerId,
|
|
87
|
+
type: 'background',
|
|
88
|
+
layout: {
|
|
89
|
+
visibility: 'none'
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
74
96
|
onMount(() => {
|
|
75
97
|
// If external map is provided, use it instead of creating a new one
|
|
76
98
|
if (externalMap) {
|
|
@@ -129,10 +151,17 @@
|
|
|
129
151
|
|
|
130
152
|
// Wait for style to load before distributing map
|
|
131
153
|
const onStyleLoad = () => {
|
|
154
|
+
// Create anchor layers if specified
|
|
155
|
+
if (anchorLayerIds.length > 0 && map) {
|
|
156
|
+
createAnchorLayers(map, anchorLayerIds);
|
|
157
|
+
}
|
|
132
158
|
mapStore.set(map);
|
|
133
159
|
};
|
|
134
160
|
|
|
135
161
|
if (map.isStyleLoaded()) {
|
|
162
|
+
if (anchorLayerIds.length > 0) {
|
|
163
|
+
createAnchorLayers(map, anchorLayerIds);
|
|
164
|
+
}
|
|
136
165
|
mapStore.set(map);
|
|
137
166
|
} else {
|
|
138
167
|
map.once('style.load', onStyleLoad);
|
|
@@ -25,6 +25,8 @@ interface Props {
|
|
|
25
25
|
navigationPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
26
26
|
/** Position for scale control */
|
|
27
27
|
scalePosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
28
|
+
/** List of anchor layer IDs to create (invisible background layers for positioning) */
|
|
29
|
+
anchorLayerIds?: string[];
|
|
28
30
|
/** Custom CSS class for container */
|
|
29
31
|
class?: string;
|
|
30
32
|
/** Optional child content (layers, controls, etc.) */
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import type { CellStoreContext } from '../stores/cellStoreContext.svelte';
|
|
15
15
|
import { useMapbox } from '../../../core/hooks/useMapbox';
|
|
16
16
|
import { cellsToGeoJSON } from '../utils/cellGeoJSON';
|
|
17
|
-
import { CELL_FILL_Z_INDEX, CELL_LINE_Z_INDEX } from '../constants/zIndex';
|
|
17
|
+
import { CELL_FILL_Z_INDEX, CELL_LINE_Z_INDEX, Z_INDEX_BY_BAND } from '../constants/zIndex';
|
|
18
18
|
|
|
19
19
|
interface Props {
|
|
20
20
|
/** Cell store context */
|
|
@@ -24,28 +24,34 @@
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
let { store, namespace }: Props = $props();
|
|
27
|
-
|
|
28
|
-
const FILL_LAYER_ID = `${namespace}-cells-fill`;
|
|
29
|
-
const LINE_LAYER_ID = `${namespace}-cells-line`;
|
|
27
|
+
|
|
30
28
|
const SOURCE_ID = `${namespace}-cells`;
|
|
31
|
-
|
|
29
|
+
|
|
32
30
|
// Get map from mapbox hook
|
|
33
31
|
const mapStore = useMapbox();
|
|
34
|
-
|
|
32
|
+
|
|
35
33
|
let map = $state<MapboxMap | null>(null);
|
|
36
34
|
let mounted = $state(false);
|
|
37
35
|
let viewportUpdateTimer: ReturnType<typeof setTimeout> | null = null;
|
|
38
36
|
let viewportVersion = $state(0); // Increment this to force $effect re-run on viewport changes
|
|
39
|
-
|
|
37
|
+
|
|
38
|
+
// Get all unique z-index values and sort them
|
|
39
|
+
const sortedZIndexes = [...new Set(Object.values(Z_INDEX_BY_BAND))].sort((a, b) => a - b);
|
|
40
|
+
|
|
41
|
+
// Create layer IDs for each z-index
|
|
42
|
+
const fillLayerIds = sortedZIndexes.map((zIndex) => `${namespace}-cells-fill-${zIndex}`);
|
|
43
|
+
const lineLayerIds = sortedZIndexes.map((zIndex) => `${namespace}-cells-line-${zIndex}`);
|
|
44
|
+
const allLayerIds = [...fillLayerIds, ...lineLayerIds];
|
|
45
|
+
|
|
40
46
|
// Viewport change handler (pan/zoom/move) with debouncing
|
|
41
47
|
function handleMoveEnd() {
|
|
42
48
|
if (!map) return;
|
|
43
|
-
|
|
49
|
+
|
|
44
50
|
// Clear any existing timer
|
|
45
51
|
if (viewportUpdateTimer) {
|
|
46
52
|
clearTimeout(viewportUpdateTimer);
|
|
47
53
|
}
|
|
48
|
-
|
|
54
|
+
|
|
49
55
|
// Debounce: wait 200ms after last move event before re-rendering
|
|
50
56
|
viewportUpdateTimer = setTimeout(() => {
|
|
51
57
|
if (!map) return;
|
|
@@ -57,86 +63,85 @@
|
|
|
57
63
|
viewportVersion++;
|
|
58
64
|
}, 200);
|
|
59
65
|
}
|
|
60
|
-
|
|
66
|
+
|
|
61
67
|
/**
|
|
62
68
|
* Initialize or reinitialize the cell layers
|
|
63
69
|
* Called on mount and when map style changes
|
|
64
70
|
*/
|
|
65
71
|
function initializeLayer() {
|
|
66
72
|
if (!map) return;
|
|
67
|
-
|
|
73
|
+
|
|
68
74
|
console.log('CellsLayer: initializeLayer called');
|
|
69
|
-
|
|
75
|
+
|
|
70
76
|
// Set initial zoom
|
|
71
77
|
store.setCurrentZoom(map.getZoom());
|
|
72
|
-
|
|
78
|
+
|
|
73
79
|
// Add moveend listener if not already added
|
|
74
80
|
// Note: We need to be careful not to duplicate listeners
|
|
75
81
|
map.off('moveend', handleMoveEnd); // Remove if exists
|
|
76
|
-
map.on('moveend', handleMoveEnd);
|
|
77
|
-
|
|
82
|
+
map.on('moveend', handleMoveEnd); // Add it back
|
|
83
|
+
|
|
78
84
|
// Mark as mounted to trigger $effect
|
|
79
85
|
mounted = true;
|
|
80
|
-
|
|
86
|
+
|
|
81
87
|
// Force $effect to re-run by incrementing viewportVersion
|
|
82
88
|
// This is critical after style.load when layers need to be recreated
|
|
83
89
|
// but mounted/map haven't changed (they're already true/set)
|
|
84
90
|
viewportVersion++;
|
|
85
91
|
}
|
|
86
|
-
|
|
92
|
+
|
|
87
93
|
onMount(() => {
|
|
88
94
|
console.log('CellsLayer: onMount, waiting for map...');
|
|
89
|
-
|
|
95
|
+
|
|
90
96
|
// Subscribe to map store
|
|
91
97
|
const unsubscribe = mapStore.subscribe((mapInstance) => {
|
|
92
98
|
if (mapInstance && !map) {
|
|
93
99
|
console.log('CellsLayer: Map available, initializing...');
|
|
94
100
|
map = mapInstance;
|
|
95
|
-
|
|
101
|
+
|
|
96
102
|
// Initial layer setup
|
|
97
103
|
initializeLayer();
|
|
98
|
-
|
|
104
|
+
|
|
99
105
|
// Re-initialize layer when map style changes
|
|
100
106
|
// Mapbox removes all custom layers/sources when setStyle() is called
|
|
101
107
|
map.on('style.load', initializeLayer);
|
|
102
108
|
}
|
|
103
109
|
});
|
|
104
|
-
|
|
110
|
+
|
|
105
111
|
return () => {
|
|
106
112
|
unsubscribe();
|
|
107
113
|
};
|
|
108
114
|
});
|
|
109
|
-
|
|
115
|
+
|
|
110
116
|
onDestroy(() => {
|
|
111
117
|
if (!map) return;
|
|
112
|
-
|
|
118
|
+
|
|
113
119
|
// Clean up timer
|
|
114
120
|
if (viewportUpdateTimer) {
|
|
115
121
|
clearTimeout(viewportUpdateTimer);
|
|
116
122
|
}
|
|
117
|
-
|
|
123
|
+
|
|
118
124
|
// Remove style.load listener
|
|
119
125
|
map.off('style.load', initializeLayer);
|
|
120
|
-
|
|
126
|
+
|
|
121
127
|
map.off('moveend', handleMoveEnd);
|
|
122
|
-
|
|
128
|
+
|
|
123
129
|
// Clean up layers and source
|
|
124
|
-
|
|
125
|
-
map.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
+
allLayerIds.forEach((id) => {
|
|
131
|
+
if (map.getLayer(id)) {
|
|
132
|
+
map.removeLayer(id);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
130
135
|
if (map.getSource(SOURCE_ID)) {
|
|
131
136
|
map.removeSource(SOURCE_ID);
|
|
132
137
|
}
|
|
133
138
|
});
|
|
134
|
-
|
|
139
|
+
|
|
135
140
|
// Reactive: Update GeoJSON when cells/zoom/filters/settings change
|
|
136
141
|
$effect(() => {
|
|
137
142
|
// Track viewportVersion to force re-run on pan (even without zoom change)
|
|
138
143
|
viewportVersion;
|
|
139
|
-
|
|
144
|
+
|
|
140
145
|
console.log('CellsLayer $effect triggered:', {
|
|
141
146
|
mounted,
|
|
142
147
|
hasMap: !!map,
|
|
@@ -146,35 +151,35 @@
|
|
|
146
151
|
baseRadius: store.baseRadius,
|
|
147
152
|
viewportVersion
|
|
148
153
|
});
|
|
149
|
-
|
|
150
|
-
if (!mounted || !map
|
|
154
|
+
|
|
155
|
+
if (!mounted || !map) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!store.showCells) {
|
|
151
160
|
// Remove layers if showCells is false
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
map.removeLayer(LINE_LAYER_ID);
|
|
156
|
-
}
|
|
157
|
-
if (map.getLayer(FILL_LAYER_ID)) {
|
|
158
|
-
map.removeLayer(FILL_LAYER_ID);
|
|
159
|
-
}
|
|
160
|
-
if (map.getSource(SOURCE_ID)) {
|
|
161
|
-
map.removeSource(SOURCE_ID);
|
|
161
|
+
allLayerIds.forEach((id) => {
|
|
162
|
+
if (map.getLayer(id)) {
|
|
163
|
+
map.removeLayer(id);
|
|
162
164
|
}
|
|
165
|
+
});
|
|
166
|
+
if (map.getSource(SOURCE_ID)) {
|
|
167
|
+
map.removeSource(SOURCE_ID);
|
|
163
168
|
}
|
|
164
169
|
return;
|
|
165
170
|
}
|
|
166
|
-
|
|
171
|
+
|
|
167
172
|
// Filter cells by viewport bounds (only render visible cells)
|
|
168
173
|
const bounds = map.getBounds();
|
|
169
174
|
if (!bounds) {
|
|
170
175
|
console.warn('CellsLayer: Cannot get map bounds, skipping viewport filter');
|
|
171
176
|
return;
|
|
172
177
|
}
|
|
173
|
-
|
|
174
|
-
const visibleCells = store.filteredCells.filter(cell =>
|
|
178
|
+
|
|
179
|
+
const visibleCells = store.filteredCells.filter((cell) =>
|
|
175
180
|
bounds.contains([cell.longitude, cell.latitude])
|
|
176
181
|
);
|
|
177
|
-
|
|
182
|
+
|
|
178
183
|
console.log('CellsLayer: Viewport filtering:', {
|
|
179
184
|
totalFiltered: store.filteredCells.length,
|
|
180
185
|
visibleInViewport: visibleCells.length,
|
|
@@ -185,7 +190,7 @@
|
|
|
185
190
|
west: bounds.getWest()
|
|
186
191
|
}
|
|
187
192
|
});
|
|
188
|
-
|
|
193
|
+
|
|
189
194
|
// Generate GeoJSON from visible cells only
|
|
190
195
|
const geoJSON = cellsToGeoJSON(
|
|
191
196
|
visibleCells,
|
|
@@ -194,12 +199,12 @@
|
|
|
194
199
|
store.groupColorMap,
|
|
195
200
|
store.cellGroupMap // Pass lookup map for O(1) color lookup
|
|
196
201
|
);
|
|
197
|
-
|
|
202
|
+
|
|
198
203
|
console.log('CellsLayer: Generated GeoJSON:', {
|
|
199
204
|
featureCount: geoJSON.features.length,
|
|
200
205
|
firstFeature: geoJSON.features[0]
|
|
201
206
|
});
|
|
202
|
-
|
|
207
|
+
|
|
203
208
|
// Update or create source
|
|
204
209
|
const source = map.getSource(SOURCE_ID);
|
|
205
210
|
if (source && source.type === 'geojson') {
|
|
@@ -212,60 +217,76 @@
|
|
|
212
217
|
data: geoJSON
|
|
213
218
|
});
|
|
214
219
|
}
|
|
215
|
-
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
paint: {
|
|
224
|
-
'fill-color': [
|
|
225
|
-
'coalesce',
|
|
226
|
-
['get', 'groupColor'],
|
|
227
|
-
['get', 'techBandColor'],
|
|
228
|
-
'#888888' // Fallback
|
|
229
|
-
],
|
|
230
|
-
'fill-opacity': store.fillOpacity
|
|
231
|
-
},
|
|
232
|
-
metadata: {
|
|
233
|
-
zIndex: CELL_FILL_Z_INDEX
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
} else {
|
|
237
|
-
// Update fill opacity
|
|
238
|
-
console.log('CellsLayer: Updating fill opacity:', store.fillOpacity);
|
|
239
|
-
map.setPaintProperty(FILL_LAYER_ID, 'fill-opacity', store.fillOpacity);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Add line layer if not exists
|
|
243
|
-
if (!map.getLayer(LINE_LAYER_ID)) {
|
|
244
|
-
console.log('CellsLayer: Creating line layer');
|
|
245
|
-
map.addLayer({
|
|
246
|
-
id: LINE_LAYER_ID,
|
|
247
|
-
type: 'line',
|
|
248
|
-
source: SOURCE_ID,
|
|
249
|
-
paint: {
|
|
250
|
-
'line-color': ['get', 'lineColor'],
|
|
251
|
-
'line-width': store.lineWidth,
|
|
252
|
-
'line-opacity': ['get', 'lineOpacity'],
|
|
253
|
-
'line-dasharray': [
|
|
254
|
-
'case',
|
|
255
|
-
['has', 'dashArray'],
|
|
256
|
-
['get', 'dashArray'],
|
|
257
|
-
['literal', [1, 0]] // Solid line default
|
|
258
|
-
]
|
|
259
|
-
},
|
|
260
|
-
metadata: {
|
|
261
|
-
zIndex: CELL_LINE_Z_INDEX
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
} else {
|
|
265
|
-
// Update line width
|
|
266
|
-
console.log('CellsLayer: Updating line width:', store.lineWidth);
|
|
267
|
-
map.setPaintProperty(LINE_LAYER_ID, 'line-width', store.lineWidth);
|
|
220
|
+
|
|
221
|
+
// Get all unique bands for the current z-index
|
|
222
|
+
const bandsByZIndex = new Map<number, string[]>();
|
|
223
|
+
for (const [band, zIndex] of Object.entries(Z_INDEX_BY_BAND)) {
|
|
224
|
+
if (!bandsByZIndex.has(zIndex)) {
|
|
225
|
+
bandsByZIndex.set(zIndex, []);
|
|
226
|
+
}
|
|
227
|
+
bandsByZIndex.get(zIndex)?.push(band);
|
|
268
228
|
}
|
|
229
|
+
|
|
230
|
+
// Add/update layers for each z-index level
|
|
231
|
+
sortedZIndexes.forEach((zIndex) => {
|
|
232
|
+
const fillLayerId = `${namespace}-cells-fill-${zIndex}`;
|
|
233
|
+
const lineLayerId = `${namespace}-cells-line-${zIndex}`;
|
|
234
|
+
const bands = bandsByZIndex.get(zIndex) || [];
|
|
235
|
+
|
|
236
|
+
// Add fill layer if not exists
|
|
237
|
+
if (!map.getLayer(fillLayerId)) {
|
|
238
|
+
console.log(`CellsLayer: Creating fill layer for z-index ${zIndex}`);
|
|
239
|
+
map.addLayer({
|
|
240
|
+
id: fillLayerId,
|
|
241
|
+
type: 'fill',
|
|
242
|
+
source: SOURCE_ID,
|
|
243
|
+
filter: ['in', ['get', 'techBandKey'], ['literal', bands]],
|
|
244
|
+
paint: {
|
|
245
|
+
'fill-color': [
|
|
246
|
+
'coalesce',
|
|
247
|
+
['get', 'groupColor'],
|
|
248
|
+
['get', 'techBandColor'],
|
|
249
|
+
'#888888' // Fallback
|
|
250
|
+
],
|
|
251
|
+
'fill-opacity': store.fillOpacity
|
|
252
|
+
},
|
|
253
|
+
metadata: {
|
|
254
|
+
zIndex: CELL_FILL_Z_INDEX + zIndex
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
} else {
|
|
258
|
+
// Update fill opacity
|
|
259
|
+
map.setPaintProperty(fillLayerId, 'fill-opacity', store.fillOpacity);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Add line layer if not exists
|
|
263
|
+
if (!map.getLayer(lineLayerId)) {
|
|
264
|
+
console.log(`CellsLayer: Creating line layer for z-index ${zIndex}`);
|
|
265
|
+
map.addLayer({
|
|
266
|
+
id: lineLayerId,
|
|
267
|
+
type: 'line',
|
|
268
|
+
source: SOURCE_ID,
|
|
269
|
+
filter: ['in', ['get', 'techBandKey'], ['literal', bands]],
|
|
270
|
+
paint: {
|
|
271
|
+
'line-color': ['get', 'lineColor'],
|
|
272
|
+
'line-width': store.lineWidth,
|
|
273
|
+
'line-opacity': ['get', 'lineOpacity'],
|
|
274
|
+
'line-dasharray': [
|
|
275
|
+
'case',
|
|
276
|
+
['has', 'dashArray'],
|
|
277
|
+
['get', 'dashArray'],
|
|
278
|
+
['literal', [1, 0]] // Solid line default
|
|
279
|
+
]
|
|
280
|
+
},
|
|
281
|
+
metadata: {
|
|
282
|
+
zIndex: CELL_LINE_Z_INDEX + zIndex
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
} else {
|
|
286
|
+
// Update line width
|
|
287
|
+
map.setPaintProperty(lineLayerId, 'line-width', store.lineWidth);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
269
290
|
});
|
|
270
291
|
</script>
|
|
271
292
|
|
|
@@ -74,22 +74,10 @@ export function createCellStoreContext(cells) {
|
|
|
74
74
|
});
|
|
75
75
|
// Derived: Filter cells by status based on includePlannedCells flag
|
|
76
76
|
// IMPORTANT: This is a pure $derived - it only READS from state, never writes
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
let cellsFilteredByStatus = $derived.by(() => {
|
|
82
|
-
// Only recompute if cells reference or flag changed
|
|
83
|
-
if (state.cells === lastCellsRef && state.includePlannedCells === lastIncludePlanned) {
|
|
84
|
-
return cachedFilteredCells;
|
|
85
|
-
}
|
|
86
|
-
lastCellsRef = state.cells;
|
|
87
|
-
lastIncludePlanned = state.includePlannedCells;
|
|
88
|
-
cachedFilteredCells = state.includePlannedCells
|
|
89
|
-
? state.cells // Include all cells
|
|
90
|
-
: state.cells.filter(cell => cell.status.startsWith('On_Air')); // Only On Air cells
|
|
91
|
-
return cachedFilteredCells;
|
|
92
|
-
});
|
|
77
|
+
let cellsFilteredByStatus = $derived(state.includePlannedCells
|
|
78
|
+
? state.cells // Include all cells
|
|
79
|
+
: state.cells.filter(cell => cell.status.startsWith('On_Air')) // Only On Air cells
|
|
80
|
+
);
|
|
93
81
|
// Auto-save settings when they change
|
|
94
82
|
$effect(() => {
|
|
95
83
|
// Convert Map to plain object for serialization
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { TECHNOLOGY_BAND_COLORS } from '../constants/colors';
|
|
7
7
|
import { DEFAULT_STATUS_STYLES } from '../constants/statusStyles';
|
|
8
|
+
import { Z_INDEX_BY_BAND } from '../constants/zIndex';
|
|
8
9
|
import { calculateRadius } from './zoomScaling';
|
|
9
10
|
import { createArcPolygon } from './arcGeometry';
|
|
10
11
|
/**
|
|
@@ -61,6 +62,7 @@ export function cellsToGeoJSON(cells, currentZoom, baseRadius = 500, groupColorM
|
|
|
61
62
|
techBandKey,
|
|
62
63
|
techBandColor: defaultColor, // Default color from constants
|
|
63
64
|
groupColor: groupColor, // Custom color (if set)
|
|
65
|
+
zIndex: Z_INDEX_BY_BAND[techBandKey] || 0, // Z-index for layer ordering
|
|
64
66
|
// Line (border) styling from status
|
|
65
67
|
lineColor: statusStyle.lineColor,
|
|
66
68
|
lineWidth: statusStyle.lineWidth,
|
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Z-Index ordering for repeater tech-band combinations
|
|
3
3
|
*
|
|
4
|
+
* This uses the same Z_INDEX_BY_BAND constant as cells for consistency.
|
|
4
5
|
* Higher frequency bands are rendered on top when repeaters overlap.
|
|
5
|
-
* This follows the same pattern as cells.
|
|
6
6
|
*/
|
|
7
|
+
import type { TechnologyBandKey } from '../../cells/types';
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* Shared z-index ordering by technology-band key
|
|
9
10
|
* Higher number = rendered on top
|
|
10
|
-
*
|
|
11
|
-
* Organized by frequency (low to high):
|
|
12
|
-
* - Lower frequencies (700-900) at bottom
|
|
13
|
-
* - Mid frequencies (1800-2100) in middle
|
|
14
|
-
* - Higher frequencies (2600-3500) on top
|
|
15
|
-
*/
|
|
16
|
-
export declare const REPEATER_TECH_BAND_Z_ORDER: Record<string, number>;
|
|
17
|
-
/**
|
|
18
|
-
* Get z-order index for a tech:fband combination
|
|
19
|
-
* Used to set the sort key for layering overlapping repeaters
|
|
20
|
-
*
|
|
21
|
-
* @param tech - Technology (2G, 4G, 5G)
|
|
22
|
-
* @param fband - Frequency band (e.g., "LTE1800", "GSM900")
|
|
23
|
-
* @returns Z-order index (defaults to 0 if not found)
|
|
24
11
|
*/
|
|
25
|
-
export declare
|
|
12
|
+
export declare const Z_INDEX_BY_BAND: Record<TechnologyBandKey, number>;
|
|
@@ -1,43 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Z-Index ordering for repeater tech-band combinations
|
|
3
3
|
*
|
|
4
|
+
* This uses the same Z_INDEX_BY_BAND constant as cells for consistency.
|
|
4
5
|
* Higher frequency bands are rendered on top when repeaters overlap.
|
|
5
|
-
* This follows the same pattern as cells.
|
|
6
6
|
*/
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Shared z-index ordering by technology-band key
|
|
9
9
|
* Higher number = rendered on top
|
|
10
|
-
*
|
|
11
|
-
* Organized by frequency (low to high):
|
|
12
|
-
* - Lower frequencies (700-900) at bottom
|
|
13
|
-
* - Mid frequencies (1800-2100) in middle
|
|
14
|
-
* - Higher frequencies (2600-3500) on top
|
|
15
10
|
*/
|
|
16
|
-
export const
|
|
17
|
-
// 2G bands
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
// 4G bands
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
// 5G bands
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
11
|
+
export const Z_INDEX_BY_BAND = {
|
|
12
|
+
// 2G bands
|
|
13
|
+
'2G_900': 5,
|
|
14
|
+
'2G_1800': 4,
|
|
15
|
+
// 4G bands
|
|
16
|
+
'4G_700': 2,
|
|
17
|
+
'4G_800': 3,
|
|
18
|
+
'4G_900': 4,
|
|
19
|
+
'4G_1800': 8,
|
|
20
|
+
'4G_2100': 9,
|
|
21
|
+
'4G_2600': 11,
|
|
22
|
+
// 5G bands
|
|
23
|
+
'5G_700': 9,
|
|
24
|
+
'5G_2100': 10,
|
|
25
|
+
'5G_3500': 12
|
|
31
26
|
};
|
|
32
|
-
/**
|
|
33
|
-
* Get z-order index for a tech:fband combination
|
|
34
|
-
* Used to set the sort key for layering overlapping repeaters
|
|
35
|
-
*
|
|
36
|
-
* @param tech - Technology (2G, 4G, 5G)
|
|
37
|
-
* @param fband - Frequency band (e.g., "LTE1800", "GSM900")
|
|
38
|
-
* @returns Z-order index (defaults to 0 if not found)
|
|
39
|
-
*/
|
|
40
|
-
export function getRepeaterZOrder(tech, fband) {
|
|
41
|
-
const key = `${tech}:${fband}`;
|
|
42
|
-
return REPEATER_TECH_BAND_Z_ORDER[key] ?? 0;
|
|
43
|
-
}
|
|
@@ -13,4 +13,4 @@ export { repeatersToGeoJSON } from './utils/repeaterGeoJSON';
|
|
|
13
13
|
export { buildRepeaterTree, getFilteredRepeaters } from './utils/repeaterTree';
|
|
14
14
|
export { REPEATER_FILL_Z_INDEX, REPEATER_LINE_Z_INDEX, REPEATER_LABEL_Z_INDEX } from './constants/zIndex';
|
|
15
15
|
export { REPEATER_RADIUS_MULTIPLIER, getRepeaterRadiusMultiplier } from './constants/radiusMultipliers';
|
|
16
|
-
export {
|
|
16
|
+
export { Z_INDEX_BY_BAND } from './constants/techBandZOrder';
|
|
@@ -16,4 +16,4 @@ export { buildRepeaterTree, getFilteredRepeaters } from './utils/repeaterTree';
|
|
|
16
16
|
// Constants
|
|
17
17
|
export { REPEATER_FILL_Z_INDEX, REPEATER_LINE_Z_INDEX, REPEATER_LABEL_Z_INDEX } from './constants/zIndex';
|
|
18
18
|
export { REPEATER_RADIUS_MULTIPLIER, getRepeaterRadiusMultiplier } from './constants/radiusMultipliers';
|
|
19
|
-
export {
|
|
19
|
+
export { Z_INDEX_BY_BAND } from './constants/techBandZOrder';
|