@smartnet360/svelte-components 0.0.95 → 0.0.97

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.
@@ -111,12 +111,102 @@
111
111
  <option value="logarithmic">Logarithmic (smooth)</option>
112
112
  <option value="percentage">Proportional (40%)</option>
113
113
  <option value="tiered">Tiered (4 levels)</option>
114
+ <option value="hybrid">Hybrid (stepped proportional)</option>
114
115
  </select>
115
116
  </div>
116
117
  </div>
117
- {/if}
118
118
 
119
- <div class="border-top my-3"></div>
119
+ <!-- Auto Size Base Multiplier -->
120
+ <div class="row align-items-center g-2 mb-3 ps-3">
121
+ <div class="col-4 text-secondary small">Base Size</div>
122
+ <div class="col-3 text-end">
123
+ <span class="badge bg-white text-muted border">{displayStore.autoSizeBase.toFixed(1)}x</span>
124
+ </div>
125
+ <div class="col-5">
126
+ <input
127
+ id="cell-autosize-base-slider"
128
+ type="range"
129
+ class="form-range w-100"
130
+ min="0.5"
131
+ max="3.0"
132
+ step="0.1"
133
+ bind:value={displayStore.autoSizeBase}
134
+ />
135
+ </div>
136
+ </div>
137
+ {:else}
138
+ <!-- Density-Based Caps (Manual Mode Only) -->
139
+ <div class="ps-3 border-start border-2 mb-3">
140
+ <!-- Min Cap -->
141
+ <div class="row align-items-center g-2 mb-2">
142
+ <div class="col-7 text-secondary small">Min Size Cap (Density)</div>
143
+ <div class="col-5">
144
+ <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
145
+ <input
146
+ id="cell-mincap-toggle"
147
+ type="checkbox"
148
+ class="form-check-input"
149
+ role="switch"
150
+ bind:checked={displayStore.useMinCap}
151
+ />
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ <!-- Max Cap -->
157
+ <div class="row align-items-center g-2 mb-2">
158
+ <div class="col-7 text-secondary small">Max Size Cap (Density)</div>
159
+ <div class="col-5">
160
+ <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
161
+ <input
162
+ id="cell-maxcap-toggle"
163
+ type="checkbox"
164
+ class="form-check-input"
165
+ role="switch"
166
+ bind:checked={displayStore.useMaxCap}
167
+ />
168
+ </div>
169
+ </div>
170
+ </div>
171
+
172
+ {#if displayStore.useMinCap || displayStore.useMaxCap}
173
+ <!-- Cap Mode (shares auto-size settings) -->
174
+ <div class="row align-items-center g-2 mb-2 mt-2">
175
+ <div class="col-4 text-secondary small">Cap Mode</div>
176
+ <div class="col-8">
177
+ <select
178
+ class="form-select form-select-sm"
179
+ bind:value={displayStore.autoSizeMode}
180
+ >
181
+ <option value="logarithmic">Logarithmic</option>
182
+ <option value="percentage">Proportional</option>
183
+ <option value="tiered">Tiered</option>
184
+ <option value="hybrid">Hybrid</option>
185
+ </select>
186
+ </div>
187
+ </div>
188
+
189
+ <!-- Cap Base -->
190
+ <div class="row align-items-center g-2 mb-2">
191
+ <div class="col-4 text-secondary small">Cap Base</div>
192
+ <div class="col-3 text-end">
193
+ <span class="badge bg-white text-muted border">{displayStore.autoSizeBase.toFixed(1)}x</span>
194
+ </div>
195
+ <div class="col-5">
196
+ <input
197
+ id="cell-cap-base-slider"
198
+ type="range"
199
+ class="form-range w-100"
200
+ min="0.5"
201
+ max="3.0"
202
+ step="0.1"
203
+ bind:value={displayStore.autoSizeBase}
204
+ />
205
+ </div>
206
+ </div>
207
+ {/if}
208
+ </div>
209
+ {/if} <div class="border-top my-3"></div>
120
210
 
121
211
  <!-- Show Labels -->
122
212
  <div class="row align-items-center g-2 mb-3">
@@ -100,10 +100,14 @@
100
100
  // Initial setup
101
101
  addLayers();
102
102
 
103
- // Events for updating
103
+ // Events for updating - conditional based on auto-size
104
104
  map.on('style.load', addLayers);
105
- map.on('moveend', updateLayer);
106
- map.on('zoomend', updateLayer);
105
+
106
+ // Only listen to zoom/move events if NOT using auto-size
107
+ if (!displayStore.useAutoSize) {
108
+ map.on('moveend', updateLayer);
109
+ map.on('zoomend', updateLayer);
110
+ }
107
111
 
108
112
  // Cleanup
109
113
  return () => {
@@ -129,6 +133,9 @@
129
133
  const _layerGrouping = displayStore.layerGrouping;
130
134
  const _useAutoSize = displayStore.useAutoSize;
131
135
  const _autoSizeMode = displayStore.autoSizeMode;
136
+ const _autoSizeBase = displayStore.autoSizeBase;
137
+ const _useMinCap = displayStore.useMinCap;
138
+ const _useMaxCap = displayStore.useMaxCap;
132
139
 
133
140
  updateLayer();
134
141
  });
@@ -144,17 +151,17 @@
144
151
  }
145
152
 
146
153
  function renderCells(map: mapboxgl.Map) {
147
- const bounds = map.getBounds();
148
- if (!bounds) return;
149
-
150
154
  const zoom = map.getZoom();
151
155
  const centerLat = map.getCenter().lat;
152
156
 
153
157
  console.log(`[CellsLayer] Rendering.. Zoom: ${zoom.toFixed(2)}, Cells: ${dataStore.filteredCells.length}`);
154
158
 
155
- // 1. Calculate base radius
159
+ // 1. Calculate base radius (only used in manual mode)
156
160
  const baseRadiusMeters = calculateRadiusInMeters(centerLat, zoom, displayStore.targetPixelSize);
157
- console.log(`[CellsLayer] Base radius: ${baseRadiusMeters.toFixed(2)}m for target ${displayStore.targetPixelSize}px`);
161
+
162
+ if (!displayStore.useAutoSize) {
163
+ console.log(`[CellsLayer] Base radius: ${baseRadiusMeters.toFixed(2)}m for target ${displayStore.targetPixelSize}px`);
164
+ }
158
165
 
159
166
  // 2. Group cells (Level 1=Tech, Level 2=Band for now hardcoded)
160
167
  // In real app, this comes from a store
@@ -173,43 +180,70 @@
173
180
  if (!style.visible) continue;
174
181
 
175
182
  for (const cell of cells) {
176
- // 4. BBox Filter (Simple point check)
177
- if (bounds.contains([cell.longitude, cell.latitude])) {
178
- // 5. Z-Index Lookup
179
- const zIndexKey = `${cell.tech}_${cell.frq}` as TechnologyBandKey;
180
- const zIndex = displayStore.currentZIndex[zIndexKey] ?? 10;
181
-
182
- // 6. Calculate radius with z-index scaling
183
- const MAX_Z = 35;
184
- let radiusMeters: number;
183
+ // 4. BBox Filter - SKIP if auto-size is enabled
184
+ if (!displayStore.useAutoSize) {
185
+ const bounds = map.getBounds();
186
+ if (!bounds || !bounds.contains([cell.longitude, cell.latitude])) {
187
+ continue;
188
+ }
189
+ }
190
+
191
+ // 5. Z-Index Lookup
192
+ const zIndexKey = `${cell.tech}_${cell.frq}` as TechnologyBandKey;
193
+ const zIndex = displayStore.currentZIndex[zIndexKey] ?? 10;
194
+
195
+ // 6. Calculate radius with z-index scaling
196
+ const MAX_Z = 35;
197
+ let radiusMeters: number;
198
+
199
+ if (displayStore.useAutoSize) {
200
+ // Auto-size mode: get target radius for this site
201
+ const siteDistance = dataStore.siteDistanceStore.getDistance(cell.siteId, 500);
202
+ const autoRadius = calculateAutoRadius(siteDistance, displayStore.autoSizeMode);
203
+
204
+ // Apply base size multiplier
205
+ const baseAdjusted = autoRadius * displayStore.autoSizeBase;
206
+
207
+ // Scale based on z-index for stacking visibility
208
+ // Lower z-index (background) = larger, higher z-index (foreground) = smaller
209
+ const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08; // 8% per layer
210
+ radiusMeters = baseAdjusted * scaleFactor;
211
+ } else {
212
+ // Manual mode: base from pixel size, then scale by z-index
213
+ const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08;
214
+ radiusMeters = baseRadiusMeters * scaleFactor;
185
215
 
186
- if (displayStore.useAutoSize) {
187
- // Auto-size mode: get target radius for this site
216
+ // Apply density-based caps if enabled
217
+ if (displayStore.useMinCap || displayStore.useMaxCap) {
188
218
  const siteDistance = dataStore.siteDistanceStore.getDistance(cell.siteId, 500);
189
219
  const autoRadius = calculateAutoRadius(siteDistance, displayStore.autoSizeMode);
220
+ const baseAdjusted = autoRadius * displayStore.autoSizeBase;
221
+ const scaledAuto = baseAdjusted * scaleFactor;
190
222
 
191
- // Scale based on z-index for stacking visibility
192
- // Lower z-index (background) = larger, higher z-index (foreground) = smaller
193
- const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08; // 8% per layer
194
- radiusMeters = autoRadius * scaleFactor;
195
- } else {
196
- // Manual mode: base from pixel size, then scale by z-index
197
- const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08;
198
- radiusMeters = baseRadiusMeters * scaleFactor;
223
+ // Apply caps: min = 60% of auto-size, max = 140% of auto-size
224
+ const minCap = scaledAuto * 0.6;
225
+ const maxCap = scaledAuto * 1.4;
226
+
227
+ if (displayStore.useMinCap && radiusMeters < minCap) {
228
+ radiusMeters = minCap;
229
+ }
230
+ if (displayStore.useMaxCap && radiusMeters > maxCap) {
231
+ radiusMeters = maxCap;
232
+ }
199
233
  }
234
+ }
200
235
 
201
- // 7. Apply beamwidth boost from displayStore preset
202
- const beamwidthBoost = displayStore.currentBeamwidthBoost[zIndexKey] || 0;
203
- const adjustedBeamwidth = cell.beamwidth + beamwidthBoost;
236
+ // 7. Apply beamwidth boost from displayStore preset
237
+ const beamwidthBoost = displayStore.currentBeamwidthBoost[zIndexKey] || 0;
238
+ const adjustedBeamwidth = cell.beamwidth + beamwidthBoost;
204
239
 
205
- // 8. Generate Arc
206
- const feature = generateCellArc(cell, radiusMeters, zIndex, style.color, adjustedBeamwidth);
207
- features.push(feature);
208
- }
240
+ // 8. Generate Arc
241
+ const feature = generateCellArc(cell, radiusMeters, zIndex, style.color, adjustedBeamwidth);
242
+ features.push(feature);
209
243
  }
210
244
  }
211
245
 
212
- console.log(`[CellsLayer] Generated ${features.length} features in view`);
246
+ console.log(`[CellsLayer] Generated ${features.length} features ${displayStore.useAutoSize ? '(all cells)' : 'in view'}`);
213
247
 
214
248
  // 8. Update Source
215
249
  const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
@@ -38,6 +38,29 @@ export function calculateAutoRadius(nearestSiteDistance, mode = 'logarithmic') {
38
38
  return 120;
39
39
  return 180;
40
40
  }
41
+ case 'hybrid': {
42
+ // Stepped proportional - tiers with proportional scaling within each tier
43
+ if (nearestSiteDistance < 300) {
44
+ // Tier 1: 0-300m → 30-50m radius
45
+ const ratio = nearestSiteDistance / 300;
46
+ return 30 + (ratio * 20);
47
+ }
48
+ else if (nearestSiteDistance < 600) {
49
+ // Tier 2: 300-600m → 50-80m radius
50
+ const ratio = (nearestSiteDistance - 300) / 300;
51
+ return 50 + (ratio * 30);
52
+ }
53
+ else if (nearestSiteDistance < 1200) {
54
+ // Tier 3: 600-1200m → 80-120m radius
55
+ const ratio = (nearestSiteDistance - 600) / 600;
56
+ return 80 + (ratio * 40);
57
+ }
58
+ else {
59
+ // Tier 4: 1200m+ → 120-180m radius
60
+ const ratio = Math.min((nearestSiteDistance - 1200) / 1200, 1);
61
+ return 120 + (ratio * 60);
62
+ }
63
+ }
41
64
  default:
42
65
  return 80;
43
66
  }
@@ -9,6 +9,9 @@ export declare class CellDisplayStore {
9
9
  layerGrouping: LayerGroupingPreset;
10
10
  useAutoSize: boolean;
11
11
  autoSizeMode: AutoSizeMode;
12
+ autoSizeBase: number;
13
+ useMinCap: boolean;
14
+ useMaxCap: boolean;
12
15
  level1: CellGroupingField;
13
16
  level2: CellGroupingField;
14
17
  currentZIndex: Record<string, number>;
@@ -11,6 +11,10 @@ export class CellDisplayStore {
11
11
  // Auto-size settings
12
12
  useAutoSize = $state(false);
13
13
  autoSizeMode = $state('logarithmic');
14
+ autoSizeBase = $state(1.0);
15
+ // Density-based caps (for manual mode)
16
+ useMinCap = $state(false);
17
+ useMaxCap = $state(false);
14
18
  // Grouping
15
19
  level1 = $state('tech');
16
20
  level2 = $state('fband');
@@ -40,6 +44,9 @@ export class CellDisplayStore {
40
44
  this.layerGrouping = parsed.layerGrouping ?? 'frequency';
41
45
  this.useAutoSize = parsed.useAutoSize ?? false;
42
46
  this.autoSizeMode = parsed.autoSizeMode ?? 'logarithmic';
47
+ this.autoSizeBase = parsed.autoSizeBase ?? 1.0;
48
+ this.useMinCap = parsed.useMinCap ?? false;
49
+ this.useMaxCap = parsed.useMaxCap ?? false;
43
50
  this.level1 = parsed.level1 ?? 'tech';
44
51
  this.level2 = parsed.level2 ?? 'fband';
45
52
  this.labelPixelDistance = parsed.labelPixelDistance ?? 60;
@@ -64,6 +71,9 @@ export class CellDisplayStore {
64
71
  layerGrouping: this.layerGrouping,
65
72
  useAutoSize: this.useAutoSize,
66
73
  autoSizeMode: this.autoSizeMode,
74
+ autoSizeBase: this.autoSizeBase,
75
+ useMinCap: this.useMinCap,
76
+ useMaxCap: this.useMaxCap,
67
77
  level1: this.level1,
68
78
  level2: this.level2,
69
79
  labelPixelDistance: this.labelPixelDistance,
@@ -63,7 +63,7 @@ export type CellGroupingField = 'tech' | 'fband' | 'frq' | 'status' | 'siteId' |
63
63
  /**
64
64
  * Auto-size calculation modes
65
65
  */
66
- export type AutoSizeMode = 'logarithmic' | 'percentage' | 'tiered';
66
+ export type AutoSizeMode = 'logarithmic' | 'percentage' | 'tiered' | 'hybrid';
67
67
  /**
68
68
  * Site distance data for auto-sizing
69
69
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.95",
3
+ "version": "0.0.97",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",