@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.
- package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte +92 -2
- package/dist/map-v3/features/cells/layers/CellsLayer.svelte +69 -35
- package/dist/map-v3/features/cells/logic/geometry.js +23 -0
- package/dist/map-v3/features/cells/stores/cell.display.svelte.d.ts +3 -0
- package/dist/map-v3/features/cells/stores/cell.display.svelte.js +10 -0
- package/dist/map-v3/features/cells/types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
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
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
187
|
-
|
|
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
|
-
//
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
radiusMeters
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
236
|
+
// 7. Apply beamwidth boost from displayStore preset
|
|
237
|
+
const beamwidthBoost = displayStore.currentBeamwidthBoost[zIndexKey] || 0;
|
|
238
|
+
const adjustedBeamwidth = cell.beamwidth + beamwidthBoost;
|
|
204
239
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
*/
|