@smartnet360/svelte-components 0.0.68 → 0.0.69
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.
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import { waitForStyleLoad, generateLayerId, generateSourceId } from '../../../shared/utils/mapboxHelpers';
|
|
16
16
|
import type { CellStoreContext } from '../stores/cellStoreContext.svelte';
|
|
17
17
|
import type { Cell } from '../types';
|
|
18
|
+
import { parseTechBand } from '../utils/techBandParser';
|
|
19
|
+
import { calculateRadius } from '../utils/zoomScaling';
|
|
18
20
|
import * as turf from '@turf/turf';
|
|
19
21
|
|
|
20
22
|
interface Props {
|
|
@@ -75,10 +77,13 @@
|
|
|
75
77
|
function buildLabelFeatures(): GeoJSON.FeatureCollection {
|
|
76
78
|
const features: GeoJSON.Feature[] = [];
|
|
77
79
|
|
|
78
|
-
if (!store.showLabels || store.filteredCells.length === 0) {
|
|
80
|
+
if (!store.showLabels || store.filteredCells.length === 0 || !map) {
|
|
79
81
|
return { type: 'FeatureCollection', features: [] };
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
// Get current zoom level
|
|
85
|
+
const currentZoom = map.getZoom();
|
|
86
|
+
|
|
82
87
|
// Group cells by site + azimuth
|
|
83
88
|
const cellGroups = groupCellsByAzimuth(store.filteredCells, store.azimuthTolerance);
|
|
84
89
|
|
|
@@ -86,6 +91,11 @@
|
|
|
86
91
|
// Sort cells within group for consistent stacking
|
|
87
92
|
cells.sort((a, b) => a.id.localeCompare(b.id));
|
|
88
93
|
|
|
94
|
+
// Calculate total height of all labels in this group
|
|
95
|
+
const totalGroupHeight = cells.length * store.labelSize * 1.5;
|
|
96
|
+
// Center offset: shift entire group up by half its height
|
|
97
|
+
const groupCenterOffset = -totalGroupHeight / 2;
|
|
98
|
+
|
|
89
99
|
cells.forEach((cell, index) => {
|
|
90
100
|
// Determine which fields to use based on tech
|
|
91
101
|
const is2G = cell.tech === '2G';
|
|
@@ -104,17 +114,27 @@
|
|
|
104
114
|
|
|
105
115
|
if (!labelText.trim()) return; // Skip if no text
|
|
106
116
|
|
|
117
|
+
// Calculate zoom-aware radius for this cell
|
|
118
|
+
const techBandParsed = parseTechBand(cell.fband);
|
|
119
|
+
const techBandKey = techBandParsed?.key || '4G_1800'; // Fallback to default
|
|
120
|
+
const cellRadius = calculateRadius(store.baseRadius, techBandKey, currentZoom);
|
|
121
|
+
|
|
122
|
+
// Position label at a percentage of the arc radius (configurable via labelOffset)
|
|
123
|
+
// labelOffset is treated as a percentage (e.g., 300 = 300% = beyond arc, 70 = 70% = inside arc)
|
|
124
|
+
const labelDistance = (cellRadius * store.labelOffset) / 100;
|
|
125
|
+
|
|
107
126
|
// Project label position along azimuth
|
|
108
127
|
const origin = turf.point([cell.siteLongitude, cell.siteLatitude]);
|
|
109
128
|
const labelPosition = turf.destination(
|
|
110
129
|
origin,
|
|
111
|
-
|
|
130
|
+
labelDistance / 1000, // Convert meters to kilometers
|
|
112
131
|
cell.azimuth,
|
|
113
132
|
{ units: 'kilometers' }
|
|
114
133
|
);
|
|
115
134
|
|
|
116
135
|
// Calculate vertical offset for stacking (pixels)
|
|
117
|
-
|
|
136
|
+
// Position within the centered group
|
|
137
|
+
const stackOffset = groupCenterOffset + (index * store.labelSize * 1.5) + (store.labelSize * 0.75);
|
|
118
138
|
|
|
119
139
|
features.push({
|
|
120
140
|
type: 'Feature',
|
|
@@ -140,6 +160,15 @@
|
|
|
140
160
|
const geojson = buildLabelFeatures();
|
|
141
161
|
const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
|
|
142
162
|
source.setData(geojson);
|
|
163
|
+
|
|
164
|
+
// Also update paint/layout properties when labels are refreshed
|
|
165
|
+
if (map.getLayer(layerId)) {
|
|
166
|
+
map.setLayoutProperty(layerId, 'text-size', store.labelSize);
|
|
167
|
+
map.setPaintProperty(layerId, 'text-color', store.labelColor);
|
|
168
|
+
map.setPaintProperty(layerId, 'text-halo-color', store.labelHaloColor);
|
|
169
|
+
map.setPaintProperty(layerId, 'text-halo-width', store.labelHaloWidth);
|
|
170
|
+
map.setLayerZoomRange(layerId, store.minLabelZoom, 24);
|
|
171
|
+
}
|
|
143
172
|
}
|
|
144
173
|
|
|
145
174
|
onMount(async () => {
|
|
@@ -173,15 +202,15 @@
|
|
|
173
202
|
'text-field': ['get', 'text'],
|
|
174
203
|
'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
|
|
175
204
|
'text-size': store.labelSize,
|
|
176
|
-
'text-anchor': 'center',
|
|
205
|
+
'text-anchor': 'center', // Center horizontally only
|
|
177
206
|
'text-offset': [
|
|
178
207
|
0,
|
|
179
|
-
['/', ['get', 'stackOffset'], store.labelSize] //
|
|
208
|
+
['/', ['get', 'stackOffset'], store.labelSize] // Offset includes centering adjustment
|
|
180
209
|
],
|
|
181
210
|
'text-allow-overlap': true,
|
|
182
211
|
'text-ignore-placement': false,
|
|
183
212
|
'text-max-width': 999, // Essentially disable wrapping
|
|
184
|
-
'text-justify': 'center' // Center
|
|
213
|
+
'text-justify': 'center' // Center horizontally
|
|
185
214
|
},
|
|
186
215
|
paint: {
|
|
187
216
|
'text-color': store.labelColor,
|
|
@@ -193,6 +222,9 @@
|
|
|
193
222
|
|
|
194
223
|
// Initial render
|
|
195
224
|
updateLabels();
|
|
225
|
+
|
|
226
|
+
// Listen for zoom changes to update label positions
|
|
227
|
+
map.on('zoom', updateLabels);
|
|
196
228
|
});
|
|
197
229
|
});
|
|
198
230
|
|
|
@@ -207,25 +239,26 @@
|
|
|
207
239
|
store.secondaryLabelField2G;
|
|
208
240
|
store.labelOffset;
|
|
209
241
|
store.azimuthTolerance;
|
|
242
|
+
store.baseRadius; // Zoom-based radius calculation depends on baseRadius
|
|
210
243
|
|
|
211
|
-
updateLabels()
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
244
|
+
// Also watch style properties - when they change, updateLabels() will
|
|
245
|
+
// rebuild the GeoJSON which forces Mapbox to re-read paint properties from store
|
|
246
|
+
store.labelSize;
|
|
247
|
+
store.labelColor;
|
|
248
|
+
store.labelHaloColor;
|
|
249
|
+
store.labelHaloWidth;
|
|
250
|
+
store.minLabelZoom;
|
|
217
251
|
|
|
218
|
-
|
|
219
|
-
map.setPaintProperty(layerId, 'text-color', store.labelColor);
|
|
220
|
-
map.setPaintProperty(layerId, 'text-halo-color', store.labelHaloColor);
|
|
221
|
-
map.setPaintProperty(layerId, 'text-halo-width', store.labelHaloWidth);
|
|
222
|
-
map.setLayerZoomRange(layerId, store.minLabelZoom, 24);
|
|
252
|
+
updateLabels();
|
|
223
253
|
});
|
|
224
254
|
|
|
225
255
|
onDestroy(() => {
|
|
226
256
|
unsubscribe?.();
|
|
227
257
|
|
|
228
258
|
if (map) {
|
|
259
|
+
// Remove zoom listener
|
|
260
|
+
map.off('zoom', updateLabels);
|
|
261
|
+
|
|
229
262
|
if (map.getLayer(layerId)) {
|
|
230
263
|
map.removeLayer(layerId);
|
|
231
264
|
}
|
|
@@ -70,7 +70,7 @@ export function createCellStoreContext(cells) {
|
|
|
70
70
|
labelHaloColor: persistedSettings.labelHaloColor ?? '#ffffff',
|
|
71
71
|
labelHaloWidth: persistedSettings.labelHaloWidth ?? 1,
|
|
72
72
|
minLabelZoom: persistedSettings.minLabelZoom ?? 14,
|
|
73
|
-
azimuthTolerance: persistedSettings.azimuthTolerance ??
|
|
73
|
+
azimuthTolerance: persistedSettings.azimuthTolerance ?? 25
|
|
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
|