@smartnet360/svelte-components 0.0.102 → 0.0.103

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.
Files changed (65) hide show
  1. package/dist/apps/antenna-pattern/index.d.ts +1 -0
  2. package/dist/apps/antenna-pattern/index.js +1 -0
  3. package/dist/apps/antenna-pattern/utils/load-static-antennas.d.ts +17 -0
  4. package/dist/apps/antenna-pattern/utils/load-static-antennas.js +83 -0
  5. package/dist/apps/site-check/SiteCheck.svelte +4 -6
  6. package/dist/core/Charts/ChartCard.svelte +122 -12
  7. package/dist/core/Charts/ChartCard.svelte.d.ts +2 -0
  8. package/dist/core/Charts/ChartComponent.svelte +8 -6
  9. package/dist/core/CoverageMap/ai/AITools.d.ts +117 -0
  10. package/dist/core/CoverageMap/ai/AITools.js +380 -0
  11. package/dist/core/CoverageMap/core/CoverageCalculator.d.ts +138 -0
  12. package/dist/core/CoverageMap/core/CoverageCalculator.js +375 -0
  13. package/dist/core/CoverageMap/core/GridCalculator.d.ts +115 -0
  14. package/dist/core/CoverageMap/core/GridCalculator.js +484 -0
  15. package/dist/core/CoverageMap/core/PathLossModels.d.ts +253 -0
  16. package/dist/core/CoverageMap/core/PathLossModels.js +380 -0
  17. package/dist/core/CoverageMap/core/SignalProcessor.d.ts +288 -0
  18. package/dist/core/CoverageMap/core/SignalProcessor.js +424 -0
  19. package/dist/core/CoverageMap/data/AntennaStore.d.ts +165 -0
  20. package/dist/core/CoverageMap/data/AntennaStore.js +327 -0
  21. package/dist/core/CoverageMap/data/SiteStore.d.ts +155 -0
  22. package/dist/core/CoverageMap/data/SiteStore.js +355 -0
  23. package/dist/core/CoverageMap/index.d.ts +74 -0
  24. package/dist/core/CoverageMap/index.js +103 -0
  25. package/dist/core/CoverageMap/types.d.ts +252 -0
  26. package/dist/core/CoverageMap/types.js +7 -0
  27. package/dist/core/CoverageMap/utils/geoUtils.d.ts +223 -0
  28. package/dist/core/CoverageMap/utils/geoUtils.js +374 -0
  29. package/dist/core/CoverageMap/utils/rfUtils.d.ts +329 -0
  30. package/dist/core/CoverageMap/utils/rfUtils.js +434 -0
  31. package/dist/core/CoverageMap/visualization/ColorSchemes.d.ts +149 -0
  32. package/dist/core/CoverageMap/visualization/ColorSchemes.js +377 -0
  33. package/dist/core/TreeView/index.d.ts +4 -4
  34. package/dist/core/TreeView/index.js +5 -5
  35. package/dist/core/TreeView/tree-utils.d.ts +12 -0
  36. package/dist/core/TreeView/tree-utils.js +115 -6
  37. package/dist/core/TreeView/tree.store.svelte.d.ts +94 -0
  38. package/dist/core/TreeView/tree.store.svelte.js +274 -0
  39. package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +16 -27
  40. package/dist/map-v2/features/repeaters/controls/RepeaterFilterControl.svelte +33 -42
  41. package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +12 -19
  42. package/dist/map-v3/core/components/Map.svelte +4 -0
  43. package/dist/map-v3/core/stores/map.store.svelte.js +2 -0
  44. package/dist/map-v3/features/cells/components/CellFilterControl.svelte +24 -30
  45. package/dist/map-v3/features/coverage/index.d.ts +12 -0
  46. package/dist/map-v3/features/coverage/index.js +16 -0
  47. package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte +198 -0
  48. package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte.d.ts +10 -0
  49. package/dist/map-v3/features/coverage/logic/coloring.d.ts +28 -0
  50. package/dist/map-v3/features/coverage/logic/coloring.js +77 -0
  51. package/dist/map-v3/features/coverage/logic/geometry.d.ts +33 -0
  52. package/dist/map-v3/features/coverage/logic/geometry.js +112 -0
  53. package/dist/map-v3/features/coverage/stores/coverage.data.svelte.d.ts +46 -0
  54. package/dist/map-v3/features/coverage/stores/coverage.data.svelte.js +95 -0
  55. package/dist/map-v3/features/coverage/stores/coverage.display.svelte.d.ts +33 -0
  56. package/dist/map-v3/features/coverage/stores/coverage.display.svelte.js +90 -0
  57. package/dist/map-v3/features/coverage/types.d.ts +52 -0
  58. package/dist/map-v3/features/coverage/types.js +7 -0
  59. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +14 -20
  60. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +23 -33
  61. package/dist/map-v3/index.d.ts +4 -0
  62. package/dist/map-v3/index.js +5 -0
  63. package/package.json +2 -2
  64. package/dist/core/TreeView/tree.store.d.ts +0 -10
  65. package/dist/core/TreeView/tree.store.js +0 -320
@@ -1,8 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { onDestroy, untrack } from 'svelte';
3
3
  import { MapControl } from '../../../shared';
4
- import { createTreeStore } from '../../../../core/TreeView/tree.store';
5
- import TreeView from '../../../../core/TreeView/TreeView.svelte';
4
+ import { createTreeStore, TreeView } from '../../../../core/TreeView';
6
5
  import type { CellDataStore } from '../stores/cell.data.svelte';
7
6
  import type { CellRegistry } from '../stores/cell.registry.svelte';
8
7
  import type { CellDisplayStore } from '../stores/cell.display.svelte';
@@ -64,36 +63,31 @@
64
63
 
65
64
  // Sync Tree Selection -> Cell Registry Visibility
66
65
  $effect(() => {
67
- const unsubscribe = treeStore.subscribe((val) => {
68
- // Iterate all leaf nodes to sync visibility
69
- // This is a bit heavy but ensures consistency.
70
- // Optimization: Only update changed nodes if we had a diff.
66
+ const val = treeStore;
67
+ // Iterate all leaf nodes to sync visibility
68
+ // This is a bit heavy but ensures consistency.
69
+ // Optimization: Only update changed nodes if we had a diff.
70
+
71
+ // We only care about leaf nodes (bands)
72
+ let changes = 0;
73
+ val.state.nodes.forEach((nodeState) => {
74
+ if (nodeState.node.children && nodeState.node.children.length > 0) return; // Skip folders
75
+
76
+ const groupId = nodeState.node.id;
77
+ // IMPORTANT: Read from checkedPaths set, NOT nodeState.checked (which is static/stale)
78
+ const isVisible = val.state.checkedPaths.has(nodeState.path);
71
79
 
72
- // We only care about leaf nodes (bands)
73
- let changes = 0;
74
- val.state.nodes.forEach((nodeState) => {
75
- if (nodeState.node.children && nodeState.node.children.length > 0) return; // Skip folders
76
-
77
- const groupId = nodeState.node.id;
78
- // IMPORTANT: Read from checkedPaths set, NOT nodeState.checked (which is static/stale)
79
- const isVisible = val.state.checkedPaths.has(nodeState.path);
80
-
81
- // Update registry if different
82
- const currentStyle = registry.getStyle(groupId, '#000'); // Color doesn't matter here
83
- if (currentStyle.visible !== isVisible) {
84
- // console.log(`[CellFilterControl] Syncing ${groupId}: ${currentStyle.visible} -> ${isVisible}`);
85
- registry.toggleVisibility(groupId);
86
- changes++;
87
- }
88
- });
89
- if (changes > 0) {
90
- console.log(`[CellFilterControl] Synced ${changes} visibility changes to registry`);
80
+ // Update registry if different
81
+ const currentStyle = registry.getStyle(groupId, '#000'); // Color doesn't matter here
82
+ if (currentStyle.visible !== isVisible) {
83
+ // console.log(`[CellFilterControl] Syncing ${groupId}: ${currentStyle.visible} -> ${isVisible}`);
84
+ registry.toggleVisibility(groupId);
85
+ changes++;
91
86
  }
92
87
  });
93
-
94
- return () => {
95
- unsubscribe();
96
- };
88
+ if (changes > 0) {
89
+ console.log(`[CellFilterControl] Synced ${changes} visibility changes to registry`);
90
+ }
97
91
  });
98
92
 
99
93
  function handleColorChange(groupId: string, event: Event) {
@@ -184,7 +178,7 @@
184
178
  No cells loaded.
185
179
  </div>
186
180
  {:else}
187
- <TreeView showControls={false} store={$treeStore} height="300px">
181
+ <TreeView showControls={false} store={treeStore} height="300px">
188
182
  {#snippet children({ node, state })}
189
183
  <!-- Color Picker (Only for leaves) -->
190
184
  {#if !node.children || node.children.length === 0}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Coverage Feature - Public Exports
3
+ *
4
+ * Self-contained RF coverage prediction and visualization.
5
+ * Zero dependencies on cells/sites features.
6
+ */
7
+ export * from './types';
8
+ export { CoverageDataStore, createCoverageDataStore } from './stores/coverage.data.svelte';
9
+ export { CoverageDisplayStore, createCoverageDisplayStore } from './stores/coverage.display.svelte';
10
+ export { default as CoverageLayer } from './layers/CoverageLayer.svelte';
11
+ export { gridToGeoJSON, getGridBounds, getSignalRange } from './logic/geometry';
12
+ export { getColorForSignal, getLegendItems } from './logic/coloring';
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Coverage Feature - Public Exports
3
+ *
4
+ * Self-contained RF coverage prediction and visualization.
5
+ * Zero dependencies on cells/sites features.
6
+ */
7
+ // Types
8
+ export * from './types';
9
+ // Stores
10
+ export { CoverageDataStore, createCoverageDataStore } from './stores/coverage.data.svelte';
11
+ export { CoverageDisplayStore, createCoverageDisplayStore } from './stores/coverage.display.svelte';
12
+ // Layer
13
+ export { default as CoverageLayer } from './layers/CoverageLayer.svelte';
14
+ // Utilities (for advanced usage)
15
+ export { gridToGeoJSON, getGridBounds, getSignalRange } from './logic/geometry';
16
+ export { getColorForSignal, getLegendItems } from './logic/coloring';
@@ -0,0 +1,198 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Coverage Layer
4
+ *
5
+ * Renders coverage grid as Mapbox fill layer.
6
+ * Self-contained - only needs map instance from context.
7
+ */
8
+
9
+ import { getContext, onMount } from 'svelte';
10
+ import type { MapStore } from '../../../core/stores/map.store.svelte';
11
+ import type { CoverageDataStore } from '../stores/coverage.data.svelte';
12
+ import type { CoverageDisplayStore } from '../stores/coverage.display.svelte';
13
+ import { gridToGeoJSON, getGridBounds } from '../logic/geometry';
14
+ import type mapboxgl from 'mapbox-gl';
15
+
16
+ interface Props {
17
+ dataStore: CoverageDataStore;
18
+ displayStore: CoverageDisplayStore;
19
+ autoFitBounds?: boolean; // Automatically fit map to coverage bounds
20
+ }
21
+
22
+ let { dataStore, displayStore, autoFitBounds = true }: Props = $props();
23
+
24
+ const mapStore = getContext<MapStore>('MAP_CONTEXT');
25
+ const sourceId = 'coverage-source';
26
+ const layerId = 'coverage-layer';
27
+ const componentId = Math.random().toString(36).substring(7);
28
+
29
+ let layerInitialized = $state(false);
30
+
31
+ console.log(`[CoverageLayer-${componentId}] Component created, mapStore:`, mapStore);
32
+
33
+ // Main effect: manage layer lifecycle - runs once (avoid reactive dependencies!)
34
+ $effect(() => {
35
+ const map = mapStore.map;
36
+ if (!map) return;
37
+
38
+ // Initialize layers when style is loaded
39
+ const initializeLayers = () => {
40
+ // Always check if layer already exists (style might have reloaded)
41
+ if (map.getLayer(layerId)) {
42
+ console.log(`[CoverageLayer-${componentId}] Layer already exists, skipping initialization`);
43
+ layerInitialized = true;
44
+ return;
45
+ }
46
+
47
+ console.log(`[CoverageLayer-${componentId}] Initializing layer...`);
48
+
49
+ // Add source if it doesn't exist
50
+ if (!map.getSource(sourceId)) {
51
+ map.addSource(sourceId, {
52
+ type: 'geojson',
53
+ data: { type: 'FeatureCollection', features: [] }
54
+ });
55
+ console.log(`[CoverageLayer-${componentId}] Source added:`, sourceId);
56
+ }
57
+
58
+ // Add layer - use static defaults
59
+ map.addLayer({
60
+ id: layerId,
61
+ type: 'fill',
62
+ source: sourceId,
63
+ paint: {
64
+ 'fill-color': ['get', 'color'],
65
+ 'fill-opacity': 0.7 // Will be updated by separate effect
66
+ },
67
+ layout: {
68
+ 'visibility': 'visible' // Will be updated by separate effect
69
+ }
70
+ });
71
+ console.log(`[CoverageLayer-${componentId}] Layer added:`, layerId);
72
+
73
+ layerInitialized = true;
74
+ };
75
+
76
+ // Listen to style.load event - fires when style finishes loading (and on style changes)
77
+ const handleStyleLoad = () => {
78
+ console.log(`[CoverageLayer-${componentId}] Style loaded, reinitializing layers...`);
79
+ layerInitialized = false; // Reset flag so layers are recreated
80
+ initializeLayers();
81
+ };
82
+
83
+ // Initialize immediately if style is already loaded
84
+ if (map.isStyleLoaded()) {
85
+ initializeLayers();
86
+ } else {
87
+ map.once('style.load', initializeLayers);
88
+ }
89
+
90
+ // Re-initialize layers when style changes/reloads
91
+ map.on('style.load', handleStyleLoad);
92
+
93
+ // Cleanup on unmount only
94
+ return () => {
95
+ console.log(`[CoverageLayer-${componentId}] Component unmounting, cleaning up...`);
96
+ map.off('style.load', handleStyleLoad);
97
+ if (map.getLayer(layerId)) map.removeLayer(layerId);
98
+ if (map.getSource(sourceId)) map.removeSource(sourceId);
99
+ layerInitialized = false;
100
+ };
101
+ });
102
+
103
+ // Update layer visibility and opacity dynamically
104
+ $effect(() => {
105
+ const map = mapStore.map;
106
+ if (!map || !layerInitialized) return;
107
+
108
+ const opacity = displayStore.opacity;
109
+ const visible = displayStore.visible;
110
+
111
+ if (map.getLayer(layerId)) {
112
+ map.setPaintProperty(layerId, 'fill-opacity', opacity);
113
+ map.setLayoutProperty(layerId, 'visibility', visible ? 'visible' : 'none');
114
+ console.log(`[CoverageLayer-${componentId}] Updated opacity:`, opacity, 'visible:', visible);
115
+ }
116
+ });
117
+
118
+ // React to data changes - separate effect
119
+ $effect(() => {
120
+ const map = mapStore.map;
121
+ if (!map || !layerInitialized) {
122
+ return;
123
+ }
124
+
125
+ // Explicitly read dependencies for reactivity
126
+ const hasResults = dataStore.hasResults;
127
+ const colorScheme = displayStore.colorScheme;
128
+ const minSignal = displayStore.minSignalThreshold;
129
+ const maxSignal = displayStore.maxSignalThreshold;
130
+
131
+ console.log(`[CoverageLayer-${componentId}] Data/settings changed, hasResults:`, hasResults);
132
+
133
+ if (hasResults) {
134
+ updateLayer(map);
135
+ }
136
+ });
137
+
138
+ function updateLayer(map: mapboxgl.Map) {
139
+ if (!dataStore.result) {
140
+ console.log(`[CoverageLayer-${componentId}] No coverage data to render`);
141
+ return;
142
+ }
143
+
144
+ const grid = dataStore.result.grid;
145
+ const thresholds = dataStore.result.config.signalThresholds;
146
+
147
+ console.log(`[CoverageLayer-${componentId}] Grid structure:`, {
148
+ rows: grid.cells.length,
149
+ cols: grid.cells[0]?.length,
150
+ cellSizeMeters: grid.cellSizeMeters,
151
+ bounds: grid.bounds,
152
+ firstCell: grid.cells[0]?.[0]
153
+ });
154
+
155
+ // Convert grid to GeoJSON
156
+ const geojson = gridToGeoJSON(
157
+ grid,
158
+ displayStore.colorScheme,
159
+ displayStore.minSignalThreshold,
160
+ displayStore.maxSignalThreshold,
161
+ thresholds
162
+ );
163
+
164
+ console.log(`[CoverageLayer-${componentId}] GeoJSON created:`, {
165
+ features: geojson.features.length,
166
+ firstFeature: geojson.features[0],
167
+ firstFeatureCoords: geojson.features[0]?.geometry.coordinates,
168
+ firstFeatureColor: geojson.features[0]?.properties?.color
169
+ });
170
+
171
+ // Check if layer exists
172
+ console.log(`[CoverageLayer-${componentId}] Layer exists?`, map.getLayer(layerId) !== undefined);
173
+ console.log(`[CoverageLayer-${componentId}] Source exists?`, map.getSource(sourceId) !== undefined);
174
+
175
+ // Update source
176
+ const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
177
+ if (source) {
178
+ source.setData(geojson);
179
+ console.log(`[CoverageLayer-${componentId}] Source data updated successfully`);
180
+
181
+ // Force a repaint
182
+ map.triggerRepaint();
183
+ } else {
184
+ console.error('[CoverageLayer] Source not found:', sourceId);
185
+ }
186
+
187
+ // Auto-fit bounds on first render
188
+ if (autoFitBounds && geojson.features.length > 0) {
189
+ const bounds = getGridBounds(grid);
190
+ console.log(`[CoverageLayer-${componentId}] Fitting bounds:`, bounds);
191
+ map.fitBounds(bounds, {
192
+ padding: 50,
193
+ maxZoom: 14,
194
+ duration: 1000
195
+ });
196
+ }
197
+ }
198
+ </script>
@@ -0,0 +1,10 @@
1
+ import type { CoverageDataStore } from '../stores/coverage.data.svelte';
2
+ import type { CoverageDisplayStore } from '../stores/coverage.display.svelte';
3
+ interface Props {
4
+ dataStore: CoverageDataStore;
5
+ displayStore: CoverageDisplayStore;
6
+ autoFitBounds?: boolean;
7
+ }
8
+ declare const CoverageLayer: import("svelte").Component<Props, {}, "">;
9
+ type CoverageLayer = ReturnType<typeof CoverageLayer>;
10
+ export default CoverageLayer;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Color Scheme Utilities
3
+ *
4
+ * Wraps CoverageMap color scheme functions for coverage layer use.
5
+ */
6
+ import type { SignalThresholds } from '../../../../core/CoverageMap';
7
+ import type { CoverageColorScheme } from '../types';
8
+ /**
9
+ * Get color for signal strength using specified color scheme
10
+ *
11
+ * @param signalDbm - Signal strength in dBm
12
+ * @param colorScheme - Color scheme name
13
+ * @param thresholds - Optional signal thresholds
14
+ * @returns Hex color string
15
+ */
16
+ export declare function getColorForSignal(signalDbm: number, colorScheme: CoverageColorScheme, thresholds?: SignalThresholds): string;
17
+ /**
18
+ * Get legend items for a color scheme
19
+ *
20
+ * @param colorScheme - Color scheme name
21
+ * @param thresholds - Signal thresholds
22
+ * @returns Array of legend items with color and label
23
+ */
24
+ export declare function getLegendItems(colorScheme: CoverageColorScheme, thresholds?: SignalThresholds): Array<{
25
+ color: string;
26
+ label: string;
27
+ range: string;
28
+ }>;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Color Scheme Utilities
3
+ *
4
+ * Wraps CoverageMap color scheme functions for coverage layer use.
5
+ */
6
+ import { getColorForSignal as getCoverageColor, HEATMAP_RED_BLUE, VIRIDIS, PLASMA, CATEGORICAL } from '../../../../core/CoverageMap';
7
+ /**
8
+ * Default signal thresholds for coloring
9
+ */
10
+ const DEFAULT_THRESHOLDS = {
11
+ excellent: -70,
12
+ good: -85,
13
+ fair: -95,
14
+ edge: -105
15
+ };
16
+ /**
17
+ * Map coverage color scheme names to CoverageMap ColorScheme objects
18
+ */
19
+ const SCHEME_MAP = {
20
+ 'heatmap': HEATMAP_RED_BLUE,
21
+ 'quality': CATEGORICAL,
22
+ 'viridis': VIRIDIS,
23
+ 'plasma': PLASMA,
24
+ 'categorical': CATEGORICAL
25
+ };
26
+ /**
27
+ * Get color for signal strength using specified color scheme
28
+ *
29
+ * @param signalDbm - Signal strength in dBm
30
+ * @param colorScheme - Color scheme name
31
+ * @param thresholds - Optional signal thresholds
32
+ * @returns Hex color string
33
+ */
34
+ export function getColorForSignal(signalDbm, colorScheme, thresholds = DEFAULT_THRESHOLDS) {
35
+ const scheme = SCHEME_MAP[colorScheme];
36
+ if (!scheme) {
37
+ console.warn(`[ColorSchemes] Unknown scheme: ${colorScheme}, using heatmap`);
38
+ return getCoverageColor(signalDbm, thresholds, HEATMAP_RED_BLUE);
39
+ }
40
+ return getCoverageColor(signalDbm, thresholds, scheme);
41
+ }
42
+ /**
43
+ * Get legend items for a color scheme
44
+ *
45
+ * @param colorScheme - Color scheme name
46
+ * @param thresholds - Signal thresholds
47
+ * @returns Array of legend items with color and label
48
+ */
49
+ export function getLegendItems(colorScheme, thresholds = DEFAULT_THRESHOLDS) {
50
+ return [
51
+ {
52
+ color: getColorForSignal(thresholds.excellent, colorScheme, thresholds),
53
+ label: 'Excellent',
54
+ range: `> ${thresholds.excellent} dBm`
55
+ },
56
+ {
57
+ color: getColorForSignal((thresholds.excellent + thresholds.good) / 2, colorScheme, thresholds),
58
+ label: 'Good',
59
+ range: `${thresholds.good} to ${thresholds.excellent} dBm`
60
+ },
61
+ {
62
+ color: getColorForSignal((thresholds.good + thresholds.fair) / 2, colorScheme, thresholds),
63
+ label: 'Fair',
64
+ range: `${thresholds.fair} to ${thresholds.good} dBm`
65
+ },
66
+ {
67
+ color: getColorForSignal((thresholds.fair + thresholds.edge) / 2, colorScheme, thresholds),
68
+ label: 'Poor',
69
+ range: `${thresholds.edge} to ${thresholds.fair} dBm`
70
+ },
71
+ {
72
+ color: getColorForSignal(thresholds.edge - 5, colorScheme, thresholds),
73
+ label: 'Edge',
74
+ range: `< ${thresholds.edge} dBm`
75
+ }
76
+ ];
77
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Grid Geometry Utilities
3
+ *
4
+ * Convert coverage grid cells to GeoJSON for Mapbox rendering.
5
+ * Applies color schemes and filtering.
6
+ */
7
+ import type { CoverageGrid, SignalThresholds } from '../types';
8
+ import type { CoverageColorScheme } from '../types';
9
+ import type { FeatureCollection, Polygon } from 'geojson';
10
+ /**
11
+ * Convert coverage grid to GeoJSON FeatureCollection
12
+ *
13
+ * @param grid - Coverage grid from calculation
14
+ * @param colorScheme - Color scheme to apply
15
+ * @param minSignal - Minimum signal threshold (dBm)
16
+ * @param maxSignal - Maximum signal threshold (dBm)
17
+ * @returns GeoJSON FeatureCollection ready for Mapbox
18
+ */
19
+ export declare function gridToGeoJSON(grid: CoverageGrid, colorScheme: CoverageColorScheme, minSignal?: number, maxSignal?: number, thresholds?: SignalThresholds): FeatureCollection<Polygon>;
20
+ /**
21
+ * Calculate grid bounds for initial map view
22
+ *
23
+ * @param grid - Coverage grid
24
+ * @returns Mapbox-compatible bounds [[west, south], [east, north]]
25
+ */
26
+ export declare function getGridBounds(grid: CoverageGrid): [[number, number], [number, number]];
27
+ /**
28
+ * Get signal strength range from grid
29
+ *
30
+ * @param grid - Coverage grid
31
+ * @returns [min, max] signal strength in dBm
32
+ */
33
+ export declare function getSignalRange(grid: CoverageGrid): [number, number];
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Grid Geometry Utilities
3
+ *
4
+ * Convert coverage grid cells to GeoJSON for Mapbox rendering.
5
+ * Applies color schemes and filtering.
6
+ */
7
+ import { getColorForSignal } from './coloring';
8
+ /**
9
+ * Convert coverage grid to GeoJSON FeatureCollection
10
+ *
11
+ * @param grid - Coverage grid from calculation
12
+ * @param colorScheme - Color scheme to apply
13
+ * @param minSignal - Minimum signal threshold (dBm)
14
+ * @param maxSignal - Maximum signal threshold (dBm)
15
+ * @returns GeoJSON FeatureCollection ready for Mapbox
16
+ */
17
+ export function gridToGeoJSON(grid, colorScheme, minSignal = -115, maxSignal = -40, thresholds) {
18
+ const features = [];
19
+ const cellSizeMeters = grid.cellSizeMeters;
20
+ // Flatten 2D grid to 1D for processing
21
+ for (const row of grid.cells) {
22
+ for (const cell of row) {
23
+ // Filter by signal strength
24
+ if (cell.signalStrength < minSignal || cell.signalStrength > maxSignal) {
25
+ continue;
26
+ }
27
+ // Skip no-signal cells
28
+ if (cell.quality === 'no-signal') {
29
+ continue;
30
+ }
31
+ // Get color for this cell
32
+ const color = getColorForSignal(cell.signalStrength, colorScheme, thresholds);
33
+ // Create polygon feature
34
+ const feature = createCellPolygon(cell, cellSizeMeters, color);
35
+ features.push(feature);
36
+ }
37
+ }
38
+ return {
39
+ type: 'FeatureCollection',
40
+ features
41
+ };
42
+ }
43
+ /**
44
+ * Create a GeoJSON polygon for a single grid cell
45
+ *
46
+ * @param cell - Grid cell data
47
+ * @param cellSizeMeters - Size of cell in meters
48
+ * @param color - Hex color for this cell
49
+ * @returns GeoJSON Feature
50
+ */
51
+ function createCellPolygon(cell, cellSizeMeters, color) {
52
+ // Calculate cell corners (approximate, good enough for visualization)
53
+ const latOffset = (cellSizeMeters / 111320); // ~111,320 meters per degree lat
54
+ const lngOffset = (cellSizeMeters / (111320 * Math.cos(cell.position.lat * Math.PI / 180)));
55
+ const lat = cell.position.lat;
56
+ const lng = cell.position.lng;
57
+ // Cell corners (clockwise from bottom-left)
58
+ const coordinates = [[
59
+ [lng - lngOffset / 2, lat - latOffset / 2], // SW
60
+ [lng + lngOffset / 2, lat - latOffset / 2], // SE
61
+ [lng + lngOffset / 2, lat + latOffset / 2], // NE
62
+ [lng - lngOffset / 2, lat + latOffset / 2], // NW
63
+ [lng - lngOffset / 2, lat - latOffset / 2] // Close polygon
64
+ ]];
65
+ return {
66
+ type: 'Feature',
67
+ geometry: {
68
+ type: 'Polygon',
69
+ coordinates
70
+ },
71
+ properties: {
72
+ signal: cell.signalStrength,
73
+ quality: cell.quality,
74
+ color: color,
75
+ lat: cell.position.lat,
76
+ lon: cell.position.lng,
77
+ dominantSector: cell.dominantSector
78
+ }
79
+ };
80
+ }
81
+ /**
82
+ * Calculate grid bounds for initial map view
83
+ *
84
+ * @param grid - Coverage grid
85
+ * @returns Mapbox-compatible bounds [[west, south], [east, north]]
86
+ */
87
+ export function getGridBounds(grid) {
88
+ const bounds = grid.bounds;
89
+ return [
90
+ [bounds.west, bounds.south],
91
+ [bounds.east, bounds.north]
92
+ ];
93
+ }
94
+ /**
95
+ * Get signal strength range from grid
96
+ *
97
+ * @param grid - Coverage grid
98
+ * @returns [min, max] signal strength in dBm
99
+ */
100
+ export function getSignalRange(grid) {
101
+ let min = 0;
102
+ let max = -120;
103
+ for (const row of grid.cells) {
104
+ for (const cell of row) {
105
+ if (cell.quality !== 'no-signal') {
106
+ min = Math.min(min, cell.signalStrength);
107
+ max = Math.max(max, cell.signalStrength);
108
+ }
109
+ }
110
+ }
111
+ return [min, max];
112
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Coverage Data Store
3
+ *
4
+ * Self-contained store for coverage calculation data and state.
5
+ * Zero dependencies on cells/sites features - fully encapsulated.
6
+ */
7
+ import type { CoverageResult, CoverageConfig } from '../types';
8
+ export declare class CoverageDataStore {
9
+ result: CoverageResult | null;
10
+ calculating: boolean;
11
+ progress: number;
12
+ progressStage: string;
13
+ error: string | null;
14
+ private calculator;
15
+ constructor();
16
+ /**
17
+ * Calculate coverage prediction
18
+ * @param config - Complete RF configuration
19
+ */
20
+ calculateCoverage(config: CoverageConfig): Promise<void>;
21
+ /**
22
+ * Clear current coverage data
23
+ */
24
+ clearCoverage(): void;
25
+ /**
26
+ * Get grid cells for rendering
27
+ * Returns null if no result available
28
+ */
29
+ get gridCells(): import("../../../../core/CoverageMap").GridCell[][] | null;
30
+ /**
31
+ * Get coverage statistics
32
+ */
33
+ get stats(): import("../../../../core/CoverageMap").CoverageStats | null;
34
+ /**
35
+ * Get coverage configuration used
36
+ */
37
+ get config(): CoverageConfig | null;
38
+ /**
39
+ * Check if results are available
40
+ */
41
+ get hasResults(): boolean;
42
+ }
43
+ /**
44
+ * Factory function to create coverage data store
45
+ */
46
+ export declare function createCoverageDataStore(): CoverageDataStore;