@smartnet360/svelte-components 0.0.91 → 0.0.93
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/demo/DemoMap.svelte +1 -7
- package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte +38 -0
- package/dist/map-v3/features/cells/layers/CellsLayer.svelte +35 -21
- package/dist/map-v3/features/cells/layers/CellsLayer.svelte.d.ts +0 -2
- package/dist/map-v3/features/cells/logic/site-distance.d.ts +4 -2
- package/dist/map-v3/features/cells/logic/site-distance.js +13 -5
- package/dist/map-v3/features/cells/stores/cell.data.svelte.d.ts +10 -2
- package/dist/map-v3/features/cells/stores/cell.data.svelte.js +19 -3
- package/dist/map-v3/features/cells/stores/cell.display.svelte.d.ts +2 -0
- package/dist/map-v3/features/cells/stores/cell.display.svelte.js +6 -0
- package/dist/map-v3/features/cells/stores/site.distance.svelte.d.ts +3 -1
- package/dist/map-v3/features/cells/stores/site.distance.svelte.js +8 -12
- package/package.json +1 -1
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import { createCellDataStore } from '../features/cells/stores/cell.data.svelte';
|
|
6
6
|
import { createCellRegistry } from '../features/cells/stores/cell.registry.svelte';
|
|
7
7
|
import { CellDisplayStore } from '../features/cells/stores/cell.display.svelte';
|
|
8
|
-
import { SiteDistanceStore } from '../features/cells/stores/site.distance.svelte';
|
|
9
8
|
import FeatureSettingsControl from '../core/controls/FeatureSettingsControl.svelte';
|
|
10
9
|
import CellFilterControl from '../features/cells/components/CellFilterControl.svelte';
|
|
11
10
|
import { createRepeaterDataStore } from '../features/repeaters/stores/repeater.data.svelte';
|
|
@@ -31,10 +30,9 @@
|
|
|
31
30
|
let { accessToken }: Props = $props();
|
|
32
31
|
|
|
33
32
|
// Initialize stores
|
|
34
|
-
const cellData = createCellDataStore();
|
|
35
33
|
const cellRegistry = createCellRegistry('demo-map');
|
|
36
34
|
const cellDisplay = new CellDisplayStore();
|
|
37
|
-
const
|
|
35
|
+
const cellData = createCellDataStore(cellDisplay);
|
|
38
36
|
|
|
39
37
|
const siteData = createSiteDataStore(cellData);
|
|
40
38
|
const siteRegistry = createSiteRegistry('demo-map');
|
|
@@ -49,9 +47,6 @@
|
|
|
49
47
|
// Need to cast or map if types slightly differ, but they should match
|
|
50
48
|
cellData.setCells(demoCells as any);
|
|
51
49
|
repeaterData.setRepeaters(demoRepeaters);
|
|
52
|
-
|
|
53
|
-
// Compute site distances
|
|
54
|
-
siteDistanceStore.updateDistances(demoCells as any);
|
|
55
50
|
});
|
|
56
51
|
</script>
|
|
57
52
|
|
|
@@ -99,7 +94,6 @@
|
|
|
99
94
|
dataStore={cellData}
|
|
100
95
|
registry={cellRegistry}
|
|
101
96
|
displayStore={cellDisplay}
|
|
102
|
-
siteDistanceStore={siteDistanceStore}
|
|
103
97
|
/>
|
|
104
98
|
<CellLabelsLayer dataStore={cellData} registry={cellRegistry} displayStore={cellDisplay} />
|
|
105
99
|
<RepeatersLayer dataStore={repeaterData} registry={repeaterRegistry} displayStore={repeaterDisplay} />
|
|
@@ -115,6 +115,44 @@
|
|
|
115
115
|
</select>
|
|
116
116
|
</div>
|
|
117
117
|
</div>
|
|
118
|
+
|
|
119
|
+
<!-- Neighbor Count -->
|
|
120
|
+
<div class="row align-items-center g-2 mb-3 ps-3">
|
|
121
|
+
<div class="col-4 text-secondary small">Neighbors</div>
|
|
122
|
+
<div class="col-3 text-end">
|
|
123
|
+
<span class="badge bg-white text-muted border">{displayStore.autoSizeNeighborCount}</span>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="col-5">
|
|
126
|
+
<input
|
|
127
|
+
type="range"
|
|
128
|
+
class="form-range w-100"
|
|
129
|
+
min="1"
|
|
130
|
+
max="10"
|
|
131
|
+
step="1"
|
|
132
|
+
bind:value={displayStore.autoSizeNeighborCount}
|
|
133
|
+
title="Number of nearest neighbors to average for density calculation"
|
|
134
|
+
/>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<!-- Size Multiplier -->
|
|
139
|
+
<div class="row align-items-center g-2 mb-3 ps-3">
|
|
140
|
+
<div class="col-4 text-secondary small">Size Scale</div>
|
|
141
|
+
<div class="col-3 text-end">
|
|
142
|
+
<span class="badge bg-white text-muted border">{displayStore.autoSizeMultiplier.toFixed(1)}x</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="col-5">
|
|
145
|
+
<input
|
|
146
|
+
type="range"
|
|
147
|
+
class="form-range w-100"
|
|
148
|
+
min="0.3"
|
|
149
|
+
max="2.0"
|
|
150
|
+
step="0.1"
|
|
151
|
+
bind:value={displayStore.autoSizeMultiplier}
|
|
152
|
+
title="Scale all auto-sized cells up or down"
|
|
153
|
+
/>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
118
156
|
{/if}
|
|
119
157
|
|
|
120
158
|
<div class="border-top my-3"></div>
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import type { CellDataStore } from '../stores/cell.data.svelte';
|
|
5
5
|
import type { CellRegistry } from '../stores/cell.registry.svelte';
|
|
6
6
|
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
7
|
-
import type { SiteDistanceStore } from '../stores/site.distance.svelte';
|
|
8
7
|
import { groupCells, getColorForGroup } from '../logic/grouping';
|
|
9
8
|
import { generateCellArc, calculateRadiusInMeters, calculateAutoRadius } from '../logic/geometry';
|
|
10
9
|
import type { TechnologyBandKey } from '../types';
|
|
@@ -14,10 +13,9 @@
|
|
|
14
13
|
dataStore: CellDataStore;
|
|
15
14
|
registry: CellRegistry;
|
|
16
15
|
displayStore: CellDisplayStore;
|
|
17
|
-
siteDistanceStore: SiteDistanceStore;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
let { dataStore, registry, displayStore
|
|
18
|
+
let { dataStore, registry, displayStore }: Props = $props();
|
|
21
19
|
|
|
22
20
|
const mapStore = getContext<MapStore>('MAP_CONTEXT');
|
|
23
21
|
let sourceId = 'cells-source';
|
|
@@ -104,8 +102,13 @@
|
|
|
104
102
|
|
|
105
103
|
// Events for updating
|
|
106
104
|
map.on('style.load', addLayers);
|
|
107
|
-
|
|
108
|
-
map
|
|
105
|
+
|
|
106
|
+
// Only listen to map events when NOT using auto-size
|
|
107
|
+
// Auto-size uses fixed meter-based sizes that don't change with zoom
|
|
108
|
+
if (!displayStore.useAutoSize) {
|
|
109
|
+
map.on('moveend', updateLayer);
|
|
110
|
+
map.on('zoomend', updateLayer);
|
|
111
|
+
}
|
|
109
112
|
|
|
110
113
|
// Cleanup
|
|
111
114
|
return () => {
|
|
@@ -131,6 +134,8 @@
|
|
|
131
134
|
const _layerGrouping = displayStore.layerGrouping;
|
|
132
135
|
const _useAutoSize = displayStore.useAutoSize;
|
|
133
136
|
const _autoSizeMode = displayStore.autoSizeMode;
|
|
137
|
+
const _autoSizeNeighborCount = displayStore.autoSizeNeighborCount;
|
|
138
|
+
const _autoSizeMultiplier = displayStore.autoSizeMultiplier;
|
|
134
139
|
|
|
135
140
|
updateLayer();
|
|
136
141
|
});
|
|
@@ -146,20 +151,22 @@
|
|
|
146
151
|
}
|
|
147
152
|
|
|
148
153
|
function renderCells(map: mapboxgl.Map) {
|
|
149
|
-
const
|
|
150
|
-
if (!bounds) return;
|
|
151
|
-
|
|
152
|
-
const zoom = map.getZoom();
|
|
153
|
-
const centerLat = map.getCenter().lat;
|
|
154
|
+
const useAutoSize = displayStore.useAutoSize;
|
|
154
155
|
|
|
155
|
-
|
|
156
|
+
// Only need bounds checking and zoom calculations for manual mode
|
|
157
|
+
const bounds = useAutoSize ? null : map.getBounds();
|
|
158
|
+
const zoom = useAutoSize ? 0 : map.getZoom();
|
|
159
|
+
const centerLat = useAutoSize ? 0 : map.getCenter().lat;
|
|
160
|
+
|
|
161
|
+
console.log(`[CellsLayer] Rendering ${dataStore.filteredCells.length} cells (auto-size: ${useAutoSize})`);
|
|
156
162
|
|
|
157
|
-
// 1. Calculate base radius
|
|
158
|
-
const baseRadiusMeters = calculateRadiusInMeters(centerLat, zoom, displayStore.targetPixelSize);
|
|
159
|
-
|
|
163
|
+
// 1. Calculate base radius (only for manual mode)
|
|
164
|
+
const baseRadiusMeters = useAutoSize ? 0 : calculateRadiusInMeters(centerLat, zoom, displayStore.targetPixelSize);
|
|
165
|
+
if (!useAutoSize) {
|
|
166
|
+
console.log(`[CellsLayer] Base radius: ${baseRadiusMeters.toFixed(2)}m for target ${displayStore.targetPixelSize}px at zoom ${zoom.toFixed(2)}`);
|
|
167
|
+
}
|
|
160
168
|
|
|
161
|
-
// 2. Group cells
|
|
162
|
-
// In real app, this comes from a store
|
|
169
|
+
// 2. Group cells
|
|
163
170
|
const groups = groupCells(dataStore.filteredCells, displayStore.level1, displayStore.level2);
|
|
164
171
|
console.log(`[CellsLayer] Groups: ${groups.size}`);
|
|
165
172
|
|
|
@@ -175,25 +182,32 @@
|
|
|
175
182
|
if (!style.visible) continue;
|
|
176
183
|
|
|
177
184
|
for (const cell of cells) {
|
|
178
|
-
// 4. BBox Filter (
|
|
179
|
-
|
|
185
|
+
// 4. BBox Filter (skip for auto-size - Mapbox handles culling efficiently)
|
|
186
|
+
const inView = useAutoSize ? true : (bounds?.contains([cell.longitude, cell.latitude]) ?? false);
|
|
187
|
+
|
|
188
|
+
if (inView) {
|
|
180
189
|
// 5. Z-Index Lookup
|
|
181
190
|
const zIndexKey = `${cell.tech}_${cell.frq}` as TechnologyBandKey;
|
|
182
191
|
const zIndex = displayStore.currentZIndex[zIndexKey] ?? 10;
|
|
183
192
|
|
|
184
|
-
// 6. Calculate radius
|
|
193
|
+
// 6. Calculate radius
|
|
194
|
+
// Auto-size: Fixed meter-based size from site density
|
|
195
|
+
// Manual: Pixel-based size that scales with zoom
|
|
185
196
|
const MAX_Z = 35;
|
|
186
197
|
let radiusMeters: number;
|
|
187
198
|
|
|
188
199
|
if (displayStore.useAutoSize) {
|
|
189
200
|
// Auto-size mode: get target radius for this site
|
|
190
|
-
const siteDistance = siteDistanceStore.getDistance(cell.siteId, 500);
|
|
201
|
+
const siteDistance = dataStore.siteDistanceStore.getDistance(cell.siteId, 500);
|
|
191
202
|
const autoRadius = calculateAutoRadius(siteDistance, displayStore.autoSizeMode);
|
|
192
203
|
|
|
204
|
+
// Apply user's multiplier to scale the result
|
|
205
|
+
const adjustedAutoRadius = autoRadius * displayStore.autoSizeMultiplier;
|
|
206
|
+
|
|
193
207
|
// Scale based on z-index for stacking visibility
|
|
194
208
|
// Lower z-index (background) = larger, higher z-index (foreground) = smaller
|
|
195
209
|
const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08; // 8% per layer
|
|
196
|
-
radiusMeters =
|
|
210
|
+
radiusMeters = adjustedAutoRadius * scaleFactor;
|
|
197
211
|
} else {
|
|
198
212
|
// Manual mode: base from pixel size, then scale by z-index
|
|
199
213
|
const scaleFactor = 1 + Math.max(0, MAX_Z - zIndex) * 0.08;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import type { CellDataStore } from '../stores/cell.data.svelte';
|
|
2
2
|
import type { CellRegistry } from '../stores/cell.registry.svelte';
|
|
3
3
|
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
4
|
-
import type { SiteDistanceStore } from '../stores/site.distance.svelte';
|
|
5
4
|
interface Props {
|
|
6
5
|
dataStore: CellDataStore;
|
|
7
6
|
registry: CellRegistry;
|
|
8
7
|
displayStore: CellDisplayStore;
|
|
9
|
-
siteDistanceStore: SiteDistanceStore;
|
|
10
8
|
}
|
|
11
9
|
declare const CellsLayer: import("svelte").Component<Props, {}, "">;
|
|
12
10
|
type CellsLayer = ReturnType<typeof CellsLayer>;
|
|
@@ -26,9 +26,11 @@ export declare function groupBySite(cells: Cell[]): Map<string, Cell[]>;
|
|
|
26
26
|
export declare function extractSiteLocations(cells: Cell[]): Map<string, [number, number]>;
|
|
27
27
|
/**
|
|
28
28
|
* Compute nearest neighbor distance for a single site
|
|
29
|
+
* Can average multiple neighbors for better density estimation
|
|
29
30
|
* @param siteId Site to compute distance for
|
|
30
31
|
* @param siteLocation Location of the site
|
|
31
32
|
* @param allSiteLocations Map of all site locations
|
|
32
|
-
* @
|
|
33
|
+
* @param neighborCount Number of closest neighbors to average (default: 5)
|
|
34
|
+
* @returns Average distance to N nearest neighbors in meters, or Infinity if no neighbors
|
|
33
35
|
*/
|
|
34
|
-
export declare function computeNearestNeighbor(siteId: string, siteLocation: [number, number], allSiteLocations: Map<string, [number, number]
|
|
36
|
+
export declare function computeNearestNeighbor(siteId: string, siteLocation: [number, number], allSiteLocations: Map<string, [number, number]>, neighborCount?: number): number;
|
|
@@ -54,18 +54,26 @@ export function extractSiteLocations(cells) {
|
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
56
|
* Compute nearest neighbor distance for a single site
|
|
57
|
+
* Can average multiple neighbors for better density estimation
|
|
57
58
|
* @param siteId Site to compute distance for
|
|
58
59
|
* @param siteLocation Location of the site
|
|
59
60
|
* @param allSiteLocations Map of all site locations
|
|
60
|
-
* @
|
|
61
|
+
* @param neighborCount Number of closest neighbors to average (default: 5)
|
|
62
|
+
* @returns Average distance to N nearest neighbors in meters, or Infinity if no neighbors
|
|
61
63
|
*/
|
|
62
|
-
export function computeNearestNeighbor(siteId, siteLocation, allSiteLocations) {
|
|
63
|
-
|
|
64
|
+
export function computeNearestNeighbor(siteId, siteLocation, allSiteLocations, neighborCount = 5) {
|
|
65
|
+
const distances = [];
|
|
64
66
|
for (const [otherId, otherLoc] of allSiteLocations) {
|
|
65
67
|
if (siteId === otherId)
|
|
66
68
|
continue;
|
|
67
69
|
const dist = haversineDistance(siteLocation, otherLoc);
|
|
68
|
-
|
|
70
|
+
distances.push(dist);
|
|
69
71
|
}
|
|
70
|
-
|
|
72
|
+
if (distances.length === 0)
|
|
73
|
+
return Infinity;
|
|
74
|
+
// Sort distances and take N closest
|
|
75
|
+
distances.sort((a, b) => a - b);
|
|
76
|
+
const closestN = distances.slice(0, Math.min(neighborCount, distances.length));
|
|
77
|
+
// Return average of closest neighbors
|
|
78
|
+
return closestN.reduce((sum, d) => sum + d, 0) / closestN.length;
|
|
71
79
|
}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { Cell } from '../types';
|
|
2
|
+
import { SiteDistanceStore } from './site.distance.svelte';
|
|
3
|
+
import type { CellDisplayStore } from './cell.display.svelte';
|
|
2
4
|
export declare class CellDataStore {
|
|
3
5
|
rawCells: Cell[];
|
|
4
6
|
filterOnAir: boolean;
|
|
5
|
-
|
|
7
|
+
siteDistanceStore: SiteDistanceStore;
|
|
8
|
+
displayStore?: CellDisplayStore;
|
|
9
|
+
constructor(displayStore?: CellDisplayStore);
|
|
6
10
|
setCells(cells: Cell[]): void;
|
|
7
11
|
get filteredCells(): Cell[];
|
|
8
12
|
}
|
|
9
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Factory function to create a new CellDataStore
|
|
15
|
+
* @param displayStore Optional display store for auto-size settings
|
|
16
|
+
*/
|
|
17
|
+
export declare function createCellDataStore(displayStore?: CellDisplayStore): CellDataStore;
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
import { SiteDistanceStore } from './site.distance.svelte';
|
|
1
2
|
export class CellDataStore {
|
|
2
3
|
rawCells = $state([]);
|
|
3
4
|
filterOnAir = $state(false);
|
|
4
|
-
|
|
5
|
+
// Internal site distance store for auto-sizing
|
|
6
|
+
siteDistanceStore;
|
|
7
|
+
// Reference to display store for auto-size settings
|
|
8
|
+
displayStore;
|
|
9
|
+
constructor(displayStore) {
|
|
10
|
+
this.siteDistanceStore = new SiteDistanceStore();
|
|
11
|
+
this.displayStore = displayStore;
|
|
12
|
+
}
|
|
5
13
|
setCells(cells) {
|
|
6
14
|
this.rawCells = cells;
|
|
15
|
+
// Automatically update site distances when cells are loaded
|
|
16
|
+
// Use neighborCount from displayStore if available
|
|
17
|
+
const neighborCount = this.displayStore?.autoSizeNeighborCount ?? 5;
|
|
18
|
+
this.siteDistanceStore.updateDistances(cells, neighborCount);
|
|
7
19
|
}
|
|
8
20
|
get filteredCells() {
|
|
9
21
|
if (!this.filterOnAir)
|
|
@@ -11,6 +23,10 @@ export class CellDataStore {
|
|
|
11
23
|
return this.rawCells.filter(c => c.status === 'On_Air');
|
|
12
24
|
}
|
|
13
25
|
}
|
|
14
|
-
|
|
15
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Factory function to create a new CellDataStore
|
|
28
|
+
* @param displayStore Optional display store for auto-size settings
|
|
29
|
+
*/
|
|
30
|
+
export function createCellDataStore(displayStore) {
|
|
31
|
+
return new CellDataStore(displayStore);
|
|
16
32
|
}
|
|
@@ -9,6 +9,8 @@ export declare class CellDisplayStore {
|
|
|
9
9
|
layerGrouping: LayerGroupingPreset;
|
|
10
10
|
useAutoSize: boolean;
|
|
11
11
|
autoSizeMode: AutoSizeMode;
|
|
12
|
+
autoSizeNeighborCount: number;
|
|
13
|
+
autoSizeMultiplier: number;
|
|
12
14
|
level1: CellGroupingField;
|
|
13
15
|
level2: CellGroupingField;
|
|
14
16
|
currentZIndex: Record<string, number>;
|
|
@@ -11,6 +11,8 @@ export class CellDisplayStore {
|
|
|
11
11
|
// Auto-size settings
|
|
12
12
|
useAutoSize = $state(false);
|
|
13
13
|
autoSizeMode = $state('logarithmic');
|
|
14
|
+
autoSizeNeighborCount = $state(5); // Number of neighbors to average for density
|
|
15
|
+
autoSizeMultiplier = $state(1.0); // Scale factor for auto-size results (0.3 - 2.0)
|
|
14
16
|
// Grouping
|
|
15
17
|
level1 = $state('tech');
|
|
16
18
|
level2 = $state('fband');
|
|
@@ -40,6 +42,8 @@ export class CellDisplayStore {
|
|
|
40
42
|
this.layerGrouping = parsed.layerGrouping ?? 'frequency';
|
|
41
43
|
this.useAutoSize = parsed.useAutoSize ?? false;
|
|
42
44
|
this.autoSizeMode = parsed.autoSizeMode ?? 'logarithmic';
|
|
45
|
+
this.autoSizeNeighborCount = parsed.autoSizeNeighborCount ?? 5;
|
|
46
|
+
this.autoSizeMultiplier = parsed.autoSizeMultiplier ?? 1.0;
|
|
43
47
|
this.level1 = parsed.level1 ?? 'tech';
|
|
44
48
|
this.level2 = parsed.level2 ?? 'fband';
|
|
45
49
|
this.labelPixelDistance = parsed.labelPixelDistance ?? 60;
|
|
@@ -64,6 +68,8 @@ export class CellDisplayStore {
|
|
|
64
68
|
layerGrouping: this.layerGrouping,
|
|
65
69
|
useAutoSize: this.useAutoSize,
|
|
66
70
|
autoSizeMode: this.autoSizeMode,
|
|
71
|
+
autoSizeNeighborCount: this.autoSizeNeighborCount,
|
|
72
|
+
autoSizeMultiplier: this.autoSizeMultiplier,
|
|
67
73
|
level1: this.level1,
|
|
68
74
|
level2: this.level2,
|
|
69
75
|
labelPixelDistance: this.labelPixelDistance,
|
|
@@ -15,8 +15,10 @@ export declare class SiteDistanceStore {
|
|
|
15
15
|
/**
|
|
16
16
|
* Incrementally update distances for new sites
|
|
17
17
|
* Only computes distances for sites not in cache
|
|
18
|
+
* @param cells Array of cells to compute distances for
|
|
19
|
+
* @param neighborCount Number of neighbors to average for density calculation
|
|
18
20
|
*/
|
|
19
|
-
updateDistances(cells: Cell[]): void;
|
|
21
|
+
updateDistances(cells: Cell[], neighborCount?: number): void;
|
|
20
22
|
/**
|
|
21
23
|
* Update existing sites if needed (when no new sites but data might have changed)
|
|
22
24
|
*/
|
|
@@ -23,8 +23,10 @@ export class SiteDistanceStore {
|
|
|
23
23
|
/**
|
|
24
24
|
* Incrementally update distances for new sites
|
|
25
25
|
* Only computes distances for sites not in cache
|
|
26
|
+
* @param cells Array of cells to compute distances for
|
|
27
|
+
* @param neighborCount Number of neighbors to average for density calculation
|
|
26
28
|
*/
|
|
27
|
-
updateDistances(cells) {
|
|
29
|
+
updateDistances(cells, neighborCount = 5) {
|
|
28
30
|
if (!cells || cells.length === 0)
|
|
29
31
|
return;
|
|
30
32
|
const startTime = performance.now();
|
|
@@ -37,11 +39,11 @@ export class SiteDistanceStore {
|
|
|
37
39
|
this.updateExistingSitesIfNeeded(siteLocations);
|
|
38
40
|
return;
|
|
39
41
|
}
|
|
40
|
-
console.log(`[SiteDistance] Computing distances for ${newSites.length} new sites`);
|
|
42
|
+
console.log(`[SiteDistance] Computing distances for ${newSites.length} new sites (${neighborCount} neighbors)`);
|
|
41
43
|
// Compute distances for NEW sites
|
|
42
44
|
for (const siteId of newSites) {
|
|
43
45
|
const location = siteLocations.get(siteId);
|
|
44
|
-
const nearestDist = computeNearestNeighbor(siteId, location, siteLocations);
|
|
46
|
+
const nearestDist = computeNearestNeighbor(siteId, location, siteLocations, neighborCount);
|
|
45
47
|
this.distances.set(siteId, nearestDist);
|
|
46
48
|
this.computedSites.add(siteId);
|
|
47
49
|
}
|
|
@@ -49,15 +51,9 @@ export class SiteDistanceStore {
|
|
|
49
51
|
for (const [existingId, existingLoc] of siteLocations) {
|
|
50
52
|
if (newSites.includes(existingId))
|
|
51
53
|
continue; // Skip new sites
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const dist = haversineDistance(existingLoc, newLoc);
|
|
56
|
-
currentMin = Math.min(currentMin, dist);
|
|
57
|
-
}
|
|
58
|
-
if (currentMin < (this.distances.get(existingId) || Infinity)) {
|
|
59
|
-
this.distances.set(existingId, currentMin);
|
|
60
|
-
}
|
|
54
|
+
// Recalculate with new sites present
|
|
55
|
+
const updatedDist = computeNearestNeighbor(existingId, existingLoc, siteLocations, neighborCount);
|
|
56
|
+
this.distances.set(existingId, updatedDist);
|
|
61
57
|
}
|
|
62
58
|
const endTime = performance.now();
|
|
63
59
|
this.lastComputeTime = endTime - startTime;
|