@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.
@@ -317,7 +317,7 @@
317
317
  type="range"
318
318
  class="form-range"
319
319
  min="1"
320
- max="15"
320
+ max="45"
321
321
  step="1"
322
322
  value={store.azimuthTolerance}
323
323
  oninput={(e) => store.setAzimuthTolerance(Number(e.currentTarget.value))}
@@ -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
- store.labelOffset / 1000, // Convert meters to kilometers
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
- const stackOffset = index * (store.labelSize + 4);
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] // Dynamic vertical offset
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 multi-line text
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
- // Watch for style changes and update layer paint/layout properties
215
- $effect(() => {
216
- if (!map || !map.getLayer(layerId)) return;
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
- map.setLayoutProperty(layerId, 'text-size', store.labelSize);
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 ?? 5
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.68",
3
+ "version": "0.0.69",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",