@smartnet360/svelte-components 0.0.51 → 0.0.54
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/apps/site-check/transforms.js +2 -2
- package/dist/core/Charts/ChartCard.svelte +6 -1
- package/dist/core/Charts/ChartComponent.svelte +8 -3
- package/dist/core/Charts/GlobalControls.svelte +116 -14
- package/dist/core/Charts/adapt.js +1 -1
- package/dist/core/Charts/charts.model.d.ts +3 -0
- package/dist/core/FeatureRegistry/index.js +1 -1
- package/dist/core/TreeView/TreeNode.svelte +40 -45
- package/dist/core/TreeView/TreeNode.svelte.d.ts +10 -0
- package/dist/core/TreeView/TreeView.svelte +14 -2
- package/dist/core/TreeView/TreeView.svelte.d.ts +10 -0
- package/dist/core/TreeView/tree-utils.d.ts +3 -0
- package/dist/core/TreeView/tree-utils.js +33 -9
- package/dist/core/TreeView/tree.store.js +49 -24
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.js +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/map/controls/MapControl.svelte +204 -0
- package/dist/map/controls/MapControl.svelte.d.ts +17 -0
- package/dist/map/controls/SiteFilterControl.svelte +126 -0
- package/dist/map/controls/SiteFilterControl.svelte.d.ts +16 -0
- package/dist/map/demo/DemoMap.svelte +98 -0
- package/dist/map/demo/DemoMap.svelte.d.ts +12 -0
- package/dist/map/demo/demo-data.d.ts +12 -0
- package/dist/map/demo/demo-data.js +220 -0
- package/dist/map/hooks/useCellData.d.ts +14 -0
- package/dist/map/hooks/useCellData.js +29 -0
- package/dist/map/hooks/useMapbox.d.ts +14 -0
- package/dist/map/hooks/useMapbox.js +29 -0
- package/dist/map/index.d.ts +27 -0
- package/dist/map/index.js +47 -0
- package/dist/map/layers/CellsLayer.svelte +242 -0
- package/dist/map/layers/CellsLayer.svelte.d.ts +21 -0
- package/dist/map/layers/CoverageLayer.svelte +37 -0
- package/dist/map/layers/CoverageLayer.svelte.d.ts +9 -0
- package/dist/map/layers/LayerBase.d.ts +42 -0
- package/dist/map/layers/LayerBase.js +58 -0
- package/dist/map/layers/SitesLayer.svelte +282 -0
- package/dist/map/layers/SitesLayer.svelte.d.ts +19 -0
- package/dist/map/providers/CellDataProvider.svelte +43 -0
- package/dist/map/providers/CellDataProvider.svelte.d.ts +12 -0
- package/dist/map/providers/MapboxProvider.svelte +38 -0
- package/dist/map/providers/MapboxProvider.svelte.d.ts +9 -0
- package/dist/map/providers/providerHelpers.d.ts +17 -0
- package/dist/map/providers/providerHelpers.js +26 -0
- package/dist/map/stores/cellDataStore.d.ts +21 -0
- package/dist/map/stores/cellDataStore.js +53 -0
- package/dist/map/stores/interactions.d.ts +20 -0
- package/dist/map/stores/interactions.js +33 -0
- package/dist/map/stores/mapStore.d.ts +8 -0
- package/dist/map/stores/mapStore.js +10 -0
- package/dist/map/types.d.ts +115 -0
- package/dist/map/types.js +10 -0
- package/dist/map/utils/geojson.d.ts +20 -0
- package/dist/map/utils/geojson.js +78 -0
- package/dist/map/utils/mapboxHelpers.d.ts +51 -0
- package/dist/map/utils/mapboxHelpers.js +98 -0
- package/dist/map/utils/math.d.ts +40 -0
- package/dist/map/utils/math.js +95 -0
- package/dist/map/utils/siteTreeUtils.d.ts +27 -0
- package/dist/map/utils/siteTreeUtils.js +164 -0
- package/package.json +1 -1
- package/dist/core/Map/Map.svelte +0 -312
- package/dist/core/Map/Map.svelte.d.ts +0 -230
- package/dist/core/Map/index.d.ts +0 -9
- package/dist/core/Map/index.js +0 -9
- package/dist/core/Map/mapSettings.d.ts +0 -147
- package/dist/core/Map/mapSettings.js +0 -226
- package/dist/core/Map/mapStore.d.ts +0 -73
- package/dist/core/Map/mapStore.js +0 -136
- package/dist/core/Map/types.d.ts +0 -72
- package/dist/core/Map/types.js +0 -32
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo cellular data for testing and examples
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Sample cellular sites (San Francisco area)
|
|
6
|
+
*/
|
|
7
|
+
export const demoSites = [
|
|
8
|
+
{
|
|
9
|
+
id: 'site-001',
|
|
10
|
+
name: 'Downtown Tower',
|
|
11
|
+
longitude: -122.4194,
|
|
12
|
+
latitude: 37.7749,
|
|
13
|
+
fbands: ['2100', '1800'],
|
|
14
|
+
technology: '5G',
|
|
15
|
+
properties: {
|
|
16
|
+
height: 45,
|
|
17
|
+
type: 'macro'
|
|
18
|
+
},
|
|
19
|
+
cellNames: ['cell-001-1', 'cell-001-2', 'cell-001-3'],
|
|
20
|
+
provider: 'Verizon',
|
|
21
|
+
featureGroup: 'Urban Macro'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'site-002',
|
|
25
|
+
name: 'Financial District',
|
|
26
|
+
longitude: -122.4008,
|
|
27
|
+
latitude: 37.7946,
|
|
28
|
+
fbands: ['1800', '850'],
|
|
29
|
+
technology: 'LTE',
|
|
30
|
+
properties: {
|
|
31
|
+
height: 60,
|
|
32
|
+
type: 'macro'
|
|
33
|
+
},
|
|
34
|
+
cellNames: ['cell-002-1', 'cell-002-2', 'cell-002-3'],
|
|
35
|
+
provider: 'AT&T',
|
|
36
|
+
featureGroup: 'Urban Macro'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'site-003',
|
|
40
|
+
name: 'Mission Bay',
|
|
41
|
+
longitude: -122.3912,
|
|
42
|
+
latitude: 37.7699,
|
|
43
|
+
fbands: ['2600'],
|
|
44
|
+
technology: '5G',
|
|
45
|
+
properties: {
|
|
46
|
+
height: 30,
|
|
47
|
+
type: 'micro'
|
|
48
|
+
},
|
|
49
|
+
cellNames: ['cell-003-1', 'cell-003-2'],
|
|
50
|
+
provider: 'T-Mobile',
|
|
51
|
+
featureGroup: 'Small Cell'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'site-004',
|
|
55
|
+
name: 'Golden Gate Park',
|
|
56
|
+
longitude: -122.4862,
|
|
57
|
+
latitude: 37.7694,
|
|
58
|
+
fbands: ['850', '700'],
|
|
59
|
+
technology: 'LTE',
|
|
60
|
+
properties: {
|
|
61
|
+
height: 40,
|
|
62
|
+
type: 'macro'
|
|
63
|
+
},
|
|
64
|
+
cellNames: ['cell-004-1', 'cell-004-2', 'cell-004-3'],
|
|
65
|
+
provider: 'Verizon',
|
|
66
|
+
featureGroup: 'Rural Macro'
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
/**
|
|
70
|
+
* Sample cellular cells/sectors
|
|
71
|
+
*/
|
|
72
|
+
export const demoCells = [
|
|
73
|
+
// Site 001 - Downtown Tower (3 sectors)
|
|
74
|
+
{
|
|
75
|
+
id: 'cell-001-1',
|
|
76
|
+
siteId: 'site-001',
|
|
77
|
+
sector: 1,
|
|
78
|
+
azimuth: 0,
|
|
79
|
+
beamwidth: 65,
|
|
80
|
+
radius: 800,
|
|
81
|
+
properties: {
|
|
82
|
+
band: '2100MHz',
|
|
83
|
+
technology: '5G',
|
|
84
|
+
power: 40
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'cell-001-2',
|
|
89
|
+
siteId: 'site-001',
|
|
90
|
+
sector: 2,
|
|
91
|
+
azimuth: 120,
|
|
92
|
+
beamwidth: 65,
|
|
93
|
+
radius: 800,
|
|
94
|
+
properties: {
|
|
95
|
+
band: '2100MHz',
|
|
96
|
+
technology: '5G',
|
|
97
|
+
power: 40
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'cell-001-3',
|
|
102
|
+
siteId: 'site-001',
|
|
103
|
+
sector: 3,
|
|
104
|
+
azimuth: 240,
|
|
105
|
+
beamwidth: 65,
|
|
106
|
+
radius: 800,
|
|
107
|
+
properties: {
|
|
108
|
+
band: '2100MHz',
|
|
109
|
+
technology: '5G',
|
|
110
|
+
power: 40
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
// Site 002 - Financial District (3 sectors)
|
|
114
|
+
{
|
|
115
|
+
id: 'cell-002-1',
|
|
116
|
+
siteId: 'site-002',
|
|
117
|
+
sector: 1,
|
|
118
|
+
azimuth: 30,
|
|
119
|
+
beamwidth: 65,
|
|
120
|
+
radius: 700,
|
|
121
|
+
properties: {
|
|
122
|
+
band: '1800MHz',
|
|
123
|
+
technology: 'LTE',
|
|
124
|
+
power: 38
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: 'cell-002-2',
|
|
129
|
+
siteId: 'site-002',
|
|
130
|
+
sector: 2,
|
|
131
|
+
azimuth: 150,
|
|
132
|
+
beamwidth: 65,
|
|
133
|
+
radius: 700,
|
|
134
|
+
properties: {
|
|
135
|
+
band: '1800MHz',
|
|
136
|
+
technology: 'LTE',
|
|
137
|
+
power: 38
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'cell-002-3',
|
|
142
|
+
siteId: 'site-002',
|
|
143
|
+
sector: 3,
|
|
144
|
+
azimuth: 270,
|
|
145
|
+
beamwidth: 65,
|
|
146
|
+
radius: 700,
|
|
147
|
+
properties: {
|
|
148
|
+
band: '1800MHz',
|
|
149
|
+
technology: 'LTE',
|
|
150
|
+
power: 38
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
// Site 003 - Mission Bay (2 sectors, smaller coverage)
|
|
154
|
+
{
|
|
155
|
+
id: 'cell-003-1',
|
|
156
|
+
siteId: 'site-003',
|
|
157
|
+
sector: 1,
|
|
158
|
+
azimuth: 90,
|
|
159
|
+
beamwidth: 90,
|
|
160
|
+
radius: 400,
|
|
161
|
+
properties: {
|
|
162
|
+
band: '2600MHz',
|
|
163
|
+
technology: '5G',
|
|
164
|
+
power: 30
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'cell-003-2',
|
|
169
|
+
siteId: 'site-003',
|
|
170
|
+
sector: 2,
|
|
171
|
+
azimuth: 270,
|
|
172
|
+
beamwidth: 90,
|
|
173
|
+
radius: 400,
|
|
174
|
+
properties: {
|
|
175
|
+
band: '2600MHz',
|
|
176
|
+
technology: '5G',
|
|
177
|
+
power: 30
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
// Site 004 - Golden Gate Park (3 sectors)
|
|
181
|
+
{
|
|
182
|
+
id: 'cell-004-1',
|
|
183
|
+
siteId: 'site-004',
|
|
184
|
+
sector: 1,
|
|
185
|
+
azimuth: 0,
|
|
186
|
+
beamwidth: 120,
|
|
187
|
+
radius: 900,
|
|
188
|
+
properties: {
|
|
189
|
+
band: '850MHz',
|
|
190
|
+
technology: 'LTE',
|
|
191
|
+
power: 43
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: 'cell-004-2',
|
|
196
|
+
siteId: 'site-004',
|
|
197
|
+
sector: 2,
|
|
198
|
+
azimuth: 120,
|
|
199
|
+
beamwidth: 120,
|
|
200
|
+
radius: 900,
|
|
201
|
+
properties: {
|
|
202
|
+
band: '850MHz',
|
|
203
|
+
technology: 'LTE',
|
|
204
|
+
power: 43
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: 'cell-004-3',
|
|
209
|
+
siteId: 'site-004',
|
|
210
|
+
sector: 3,
|
|
211
|
+
azimuth: 240,
|
|
212
|
+
beamwidth: 120,
|
|
213
|
+
radius: 900,
|
|
214
|
+
properties: {
|
|
215
|
+
band: '850MHz',
|
|
216
|
+
technology: 'LTE',
|
|
217
|
+
power: 43
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook to safely access cell data stores from context
|
|
3
|
+
*/
|
|
4
|
+
import type { CellDataContext } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* Retrieves the cell data context stores
|
|
7
|
+
* @throws Error if called outside of CellDataProvider
|
|
8
|
+
*/
|
|
9
|
+
export declare function useCellData(): CellDataContext;
|
|
10
|
+
/**
|
|
11
|
+
* Tries to retrieve the cell data context stores
|
|
12
|
+
* Returns null if not available (safe version)
|
|
13
|
+
*/
|
|
14
|
+
export declare function tryUseCellData(): CellDataContext | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook to safely access cell data stores from context
|
|
3
|
+
*/
|
|
4
|
+
import { getContext } from 'svelte';
|
|
5
|
+
import { CELL_DATA_CONTEXT_KEY } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves the cell data context stores
|
|
8
|
+
* @throws Error if called outside of CellDataProvider
|
|
9
|
+
*/
|
|
10
|
+
export function useCellData() {
|
|
11
|
+
const cellDataContext = getContext(CELL_DATA_CONTEXT_KEY);
|
|
12
|
+
if (!cellDataContext) {
|
|
13
|
+
throw new Error('useCellData() must be called within a <CellDataProvider> component. ' +
|
|
14
|
+
'Make sure your component is wrapped in <CellDataProvider>.');
|
|
15
|
+
}
|
|
16
|
+
return cellDataContext;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tries to retrieve the cell data context stores
|
|
20
|
+
* Returns null if not available (safe version)
|
|
21
|
+
*/
|
|
22
|
+
export function tryUseCellData() {
|
|
23
|
+
try {
|
|
24
|
+
return getContext(CELL_DATA_CONTEXT_KEY);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook to safely access the Mapbox instance from context
|
|
3
|
+
*/
|
|
4
|
+
import type { MapStore } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* Retrieves the Mapbox instance store from context
|
|
7
|
+
* @throws Error if called outside of MapboxProvider
|
|
8
|
+
*/
|
|
9
|
+
export declare function useMapbox(): MapStore;
|
|
10
|
+
/**
|
|
11
|
+
* Tries to retrieve the Mapbox instance store from context
|
|
12
|
+
* Returns null if not available (safe version)
|
|
13
|
+
*/
|
|
14
|
+
export declare function tryUseMapbox(): MapStore | null;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook to safely access the Mapbox instance from context
|
|
3
|
+
*/
|
|
4
|
+
import { getContext } from 'svelte';
|
|
5
|
+
import { MAP_CONTEXT_KEY } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves the Mapbox instance store from context
|
|
8
|
+
* @throws Error if called outside of MapboxProvider
|
|
9
|
+
*/
|
|
10
|
+
export function useMapbox() {
|
|
11
|
+
const mapStore = getContext(MAP_CONTEXT_KEY);
|
|
12
|
+
if (!mapStore) {
|
|
13
|
+
throw new Error('useMapbox() must be called within a <MapboxProvider> component. ' +
|
|
14
|
+
'Make sure your component is wrapped in <MapboxProvider>.');
|
|
15
|
+
}
|
|
16
|
+
return mapStore;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Tries to retrieve the Mapbox instance store from context
|
|
20
|
+
* Returns null if not available (safe version)
|
|
21
|
+
*/
|
|
22
|
+
export function tryUseMapbox() {
|
|
23
|
+
try {
|
|
24
|
+
return getContext(MAP_CONTEXT_KEY);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map module - Mapbox-based cellular visualization library
|
|
3
|
+
*
|
|
4
|
+
* @module map
|
|
5
|
+
*/
|
|
6
|
+
export type { Site, Cell, InteractionState, MapStore, CellDataContext, SiteFeature, CellFeature, FeatureCollection } from './types';
|
|
7
|
+
export { MAP_CONTEXT_KEY, CELL_DATA_CONTEXT_KEY } from './types';
|
|
8
|
+
export { default as MapboxProvider } from './providers/MapboxProvider.svelte';
|
|
9
|
+
export { default as CellDataProvider } from './providers/CellDataProvider.svelte';
|
|
10
|
+
export * from './providers/providerHelpers';
|
|
11
|
+
export { default as SitesLayer } from './layers/SitesLayer.svelte';
|
|
12
|
+
export { default as CellsLayer } from './layers/CellsLayer.svelte';
|
|
13
|
+
export { default as CoverageLayer } from './layers/CoverageLayer.svelte';
|
|
14
|
+
export { LayerBase } from './layers/LayerBase';
|
|
15
|
+
export { default as MapControl } from './controls/MapControl.svelte';
|
|
16
|
+
export { default as SiteFilterControl } from './controls/SiteFilterControl.svelte';
|
|
17
|
+
export { useMapbox, tryUseMapbox } from './hooks/useMapbox';
|
|
18
|
+
export { useCellData, tryUseCellData } from './hooks/useCellData';
|
|
19
|
+
export { createMapStore } from './stores/mapStore';
|
|
20
|
+
export { createCellDataStores, createCellsBySiteStore, createSelectedSiteStore, createSelectedCellStore } from './stores/cellDataStore';
|
|
21
|
+
export * from './stores/interactions';
|
|
22
|
+
export { sitesToGeoJSON, cellsToGeoJSON, siteToFeature, cellToFeature } from './utils/geojson';
|
|
23
|
+
export { createSectorPolygon, createCirclePolygon, destinationPoint, normalizeAzimuth } from './utils/math';
|
|
24
|
+
export { addSourceIfMissing, removeSourceIfExists, addLayerIfMissing, removeLayerIfExists, updateGeoJSONSource, removeLayerAndSource, isStyleLoaded, waitForStyleLoad, setFeatureState, removeFeatureState, generateLayerId, generateSourceId } from './utils/mapboxHelpers';
|
|
25
|
+
export { buildSiteTree, getFilteredSites, saveTreeState, loadTreeState, clearTreeState } from './utils/siteTreeUtils';
|
|
26
|
+
export { demoSites, demoCells } from './demo/demo-data';
|
|
27
|
+
export { default as DemoMap } from './demo/DemoMap.svelte';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map module - Mapbox-based cellular visualization library
|
|
3
|
+
*
|
|
4
|
+
* @module map
|
|
5
|
+
*/
|
|
6
|
+
export { MAP_CONTEXT_KEY, CELL_DATA_CONTEXT_KEY } from './types';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Providers
|
|
9
|
+
// ============================================================================
|
|
10
|
+
export { default as MapboxProvider } from './providers/MapboxProvider.svelte';
|
|
11
|
+
export { default as CellDataProvider } from './providers/CellDataProvider.svelte';
|
|
12
|
+
export * from './providers/providerHelpers';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Layers
|
|
15
|
+
// ============================================================================
|
|
16
|
+
export { default as SitesLayer } from './layers/SitesLayer.svelte';
|
|
17
|
+
export { default as CellsLayer } from './layers/CellsLayer.svelte';
|
|
18
|
+
export { default as CoverageLayer } from './layers/CoverageLayer.svelte';
|
|
19
|
+
export { LayerBase } from './layers/LayerBase';
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Controls
|
|
22
|
+
// ============================================================================
|
|
23
|
+
export { default as MapControl } from './controls/MapControl.svelte';
|
|
24
|
+
export { default as SiteFilterControl } from './controls/SiteFilterControl.svelte';
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Hooks
|
|
27
|
+
// ============================================================================
|
|
28
|
+
export { useMapbox, tryUseMapbox } from './hooks/useMapbox';
|
|
29
|
+
export { useCellData, tryUseCellData } from './hooks/useCellData';
|
|
30
|
+
// ============================================================================
|
|
31
|
+
// Stores
|
|
32
|
+
// ============================================================================
|
|
33
|
+
export { createMapStore } from './stores/mapStore';
|
|
34
|
+
export { createCellDataStores, createCellsBySiteStore, createSelectedSiteStore, createSelectedCellStore } from './stores/cellDataStore';
|
|
35
|
+
export * from './stores/interactions';
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Utilities
|
|
38
|
+
// ============================================================================
|
|
39
|
+
export { sitesToGeoJSON, cellsToGeoJSON, siteToFeature, cellToFeature } from './utils/geojson';
|
|
40
|
+
export { createSectorPolygon, createCirclePolygon, destinationPoint, normalizeAzimuth } from './utils/math';
|
|
41
|
+
export { addSourceIfMissing, removeSourceIfExists, addLayerIfMissing, removeLayerIfExists, updateGeoJSONSource, removeLayerAndSource, isStyleLoaded, waitForStyleLoad, setFeatureState, removeFeatureState, generateLayerId, generateSourceId } from './utils/mapboxHelpers';
|
|
42
|
+
export { buildSiteTree, getFilteredSites, saveTreeState, loadTreeState, clearTreeState } from './utils/siteTreeUtils';
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Demo
|
|
45
|
+
// ============================================================================
|
|
46
|
+
export { demoSites, demoCells } from './demo/demo-data';
|
|
47
|
+
export { default as DemoMap } from './demo/DemoMap.svelte';
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* CellsLayer - Visualizes cellular sectors/cells as polygons on the map
|
|
4
|
+
*
|
|
5
|
+
* Consumes:
|
|
6
|
+
* - Map instance from MapboxProvider
|
|
7
|
+
* - Cells and sites data from CellDataProvider
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - Displays cell coverage as sector polygons
|
|
11
|
+
* - Supports selection and hover states
|
|
12
|
+
* - Click to select a cell
|
|
13
|
+
*/
|
|
14
|
+
import { onMount, onDestroy } from 'svelte';
|
|
15
|
+
import type mapboxgl from 'mapbox-gl';
|
|
16
|
+
import { useMapbox } from '../hooks/useMapbox';
|
|
17
|
+
import { useCellData } from '../hooks/useCellData';
|
|
18
|
+
import { cellsToGeoJSON } from '../utils/geojson';
|
|
19
|
+
import { LayerBase } from './LayerBase';
|
|
20
|
+
import type { Cell, Site } from '../types';
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
/** Namespace for layer IDs (default: 'cellular') */
|
|
24
|
+
namespace?: string;
|
|
25
|
+
/** Enable click to select cells (default: true) */
|
|
26
|
+
clickable?: boolean;
|
|
27
|
+
/** Default cell radius in meters (default: 500) */
|
|
28
|
+
defaultRadius?: number;
|
|
29
|
+
/** Cell fill color (default: '#3b82f6') */
|
|
30
|
+
fillColor?: string;
|
|
31
|
+
/** Cell fill opacity (default: 0.3) */
|
|
32
|
+
fillOpacity?: number;
|
|
33
|
+
/** Selected cell fill color (default: '#ef4444') */
|
|
34
|
+
selectedFillColor?: string;
|
|
35
|
+
/** Cell outline color (default: '#1e40af') */
|
|
36
|
+
outlineColor?: string;
|
|
37
|
+
/** Cell outline width (default: 2) */
|
|
38
|
+
outlineWidth?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let {
|
|
42
|
+
namespace = 'cellular',
|
|
43
|
+
clickable = true,
|
|
44
|
+
defaultRadius = 500,
|
|
45
|
+
fillColor = '#3b82f6',
|
|
46
|
+
fillOpacity = 0.3,
|
|
47
|
+
selectedFillColor = '#ef4444',
|
|
48
|
+
outlineColor = '#1e40af',
|
|
49
|
+
outlineWidth = 2
|
|
50
|
+
}: Props = $props();
|
|
51
|
+
|
|
52
|
+
const mapStore = useMapbox();
|
|
53
|
+
const cellDataContext = useCellData();
|
|
54
|
+
|
|
55
|
+
let map: mapboxgl.Map | null = null;
|
|
56
|
+
let layerManager: LayerBase | null = null;
|
|
57
|
+
let unsubscribers: (() => void)[] = [];
|
|
58
|
+
|
|
59
|
+
onMount(() => {
|
|
60
|
+
// Subscribe to map store
|
|
61
|
+
const mapUnsub = mapStore.subscribe((m) => {
|
|
62
|
+
if (!m) return;
|
|
63
|
+
map = m;
|
|
64
|
+
initializeLayer();
|
|
65
|
+
});
|
|
66
|
+
unsubscribers.push(mapUnsub);
|
|
67
|
+
|
|
68
|
+
// Subscribe to cells data
|
|
69
|
+
const cellsUnsub = cellDataContext.cells.subscribe(() => {
|
|
70
|
+
if (map && layerManager) {
|
|
71
|
+
updateLayerData();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
unsubscribers.push(cellsUnsub);
|
|
75
|
+
|
|
76
|
+
// Subscribe to sites data (cells need site locations)
|
|
77
|
+
const sitesUnsub = cellDataContext.sites.subscribe(() => {
|
|
78
|
+
if (map && layerManager) {
|
|
79
|
+
updateLayerData();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
unsubscribers.push(sitesUnsub);
|
|
83
|
+
|
|
84
|
+
// Subscribe to selection state
|
|
85
|
+
const selectionUnsub = cellDataContext.selectedCellId.subscribe(() => {
|
|
86
|
+
if (map) {
|
|
87
|
+
updateLayerStyle();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
unsubscribers.push(selectionUnsub);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
onDestroy(() => {
|
|
94
|
+
cleanup();
|
|
95
|
+
unsubscribers.forEach((unsub) => unsub());
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
function initializeLayer(): void {
|
|
99
|
+
if (!map) return;
|
|
100
|
+
|
|
101
|
+
layerManager = new LayerBase(map, {
|
|
102
|
+
namespace,
|
|
103
|
+
layerName: 'cells',
|
|
104
|
+
sourceName: 'cells'
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Add empty GeoJSON source
|
|
108
|
+
layerManager['addGeoJSONSource']({
|
|
109
|
+
type: 'FeatureCollection',
|
|
110
|
+
features: []
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Add fill layer
|
|
114
|
+
layerManager['addLayer']({
|
|
115
|
+
id: layerManager.getLayerId(),
|
|
116
|
+
type: 'fill',
|
|
117
|
+
source: layerManager.getSourceId(),
|
|
118
|
+
paint: {
|
|
119
|
+
'fill-color': [
|
|
120
|
+
'case',
|
|
121
|
+
['boolean', ['feature-state', 'selected'], false],
|
|
122
|
+
selectedFillColor,
|
|
123
|
+
fillColor
|
|
124
|
+
],
|
|
125
|
+
'fill-opacity': fillOpacity
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Add outline layer
|
|
130
|
+
layerManager['addLayer']({
|
|
131
|
+
id: `${layerManager.getLayerId()}-outline`,
|
|
132
|
+
type: 'line',
|
|
133
|
+
source: layerManager.getSourceId(),
|
|
134
|
+
paint: {
|
|
135
|
+
'line-color': outlineColor,
|
|
136
|
+
'line-width': outlineWidth
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Add click handler
|
|
141
|
+
if (clickable) {
|
|
142
|
+
map.on('click', layerManager.getLayerId(), handleClick);
|
|
143
|
+
map.on('mouseenter', layerManager.getLayerId(), handleMouseEnter);
|
|
144
|
+
map.on('mouseleave', layerManager.getLayerId(), handleMouseLeave);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Initial data load
|
|
148
|
+
updateLayerData();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function updateLayerData(): void {
|
|
152
|
+
if (!layerManager) return;
|
|
153
|
+
|
|
154
|
+
let currentCells: Cell[] = [];
|
|
155
|
+
let currentSites: Site[] = [];
|
|
156
|
+
|
|
157
|
+
cellDataContext.cells.subscribe((c) => {
|
|
158
|
+
currentCells = c;
|
|
159
|
+
})();
|
|
160
|
+
cellDataContext.sites.subscribe((s) => {
|
|
161
|
+
currentSites = s;
|
|
162
|
+
})();
|
|
163
|
+
|
|
164
|
+
const geojson = cellsToGeoJSON(currentCells, currentSites, defaultRadius);
|
|
165
|
+
layerManager['updateSource'](geojson);
|
|
166
|
+
updateLayerStyle();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function updateLayerStyle(): void {
|
|
170
|
+
if (!map || !layerManager) return;
|
|
171
|
+
|
|
172
|
+
let currentSelectedId: string | null = null;
|
|
173
|
+
cellDataContext.selectedCellId.subscribe((id) => {
|
|
174
|
+
currentSelectedId = id;
|
|
175
|
+
})();
|
|
176
|
+
|
|
177
|
+
// Update feature states
|
|
178
|
+
let currentCells: Cell[] = [];
|
|
179
|
+
cellDataContext.cells.subscribe((c) => {
|
|
180
|
+
currentCells = c;
|
|
181
|
+
})();
|
|
182
|
+
|
|
183
|
+
currentCells.forEach((cell) => {
|
|
184
|
+
if (map) {
|
|
185
|
+
map.setFeatureState(
|
|
186
|
+
{
|
|
187
|
+
source: layerManager!.getSourceId(),
|
|
188
|
+
id: cell.id
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
selected: cell.id === currentSelectedId
|
|
192
|
+
}
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function handleClick(e: mapboxgl.MapLayerMouseEvent): void {
|
|
199
|
+
if (!e.features || e.features.length === 0) return;
|
|
200
|
+
const feature = e.features[0];
|
|
201
|
+
const cellId = feature.properties?.id;
|
|
202
|
+
const siteId = feature.properties?.siteId;
|
|
203
|
+
if (cellId) {
|
|
204
|
+
cellDataContext.selectedCellId.set(cellId);
|
|
205
|
+
if (siteId) {
|
|
206
|
+
cellDataContext.selectedSiteId.set(siteId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function handleMouseEnter(e: mapboxgl.MapLayerMouseEvent): void {
|
|
212
|
+
if (!map) return;
|
|
213
|
+
map.getCanvas().style.cursor = 'pointer';
|
|
214
|
+
if (!e.features || e.features.length === 0) return;
|
|
215
|
+
const feature = e.features[0];
|
|
216
|
+
const cellId = feature.properties?.id;
|
|
217
|
+
if (cellId) {
|
|
218
|
+
cellDataContext.hoveredCellId.set(cellId);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function handleMouseLeave(): void {
|
|
223
|
+
if (!map) return;
|
|
224
|
+
map.getCanvas().style.cursor = '';
|
|
225
|
+
cellDataContext.hoveredCellId.set(null);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function cleanup(): void {
|
|
229
|
+
if (map && layerManager) {
|
|
230
|
+
if (clickable) {
|
|
231
|
+
map.off('click', layerManager.getLayerId(), handleClick);
|
|
232
|
+
map.off('mouseenter', layerManager.getLayerId(), handleMouseEnter);
|
|
233
|
+
map.off('mouseleave', layerManager.getLayerId(), handleMouseLeave);
|
|
234
|
+
}
|
|
235
|
+
// Remove outline layer as well
|
|
236
|
+
if (map.getLayer(`${layerManager.getLayerId()}-outline`)) {
|
|
237
|
+
map.removeLayer(`${layerManager.getLayerId()}-outline`);
|
|
238
|
+
}
|
|
239
|
+
layerManager.cleanup();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
</script>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Namespace for layer IDs (default: 'cellular') */
|
|
3
|
+
namespace?: string;
|
|
4
|
+
/** Enable click to select cells (default: true) */
|
|
5
|
+
clickable?: boolean;
|
|
6
|
+
/** Default cell radius in meters (default: 500) */
|
|
7
|
+
defaultRadius?: number;
|
|
8
|
+
/** Cell fill color (default: '#3b82f6') */
|
|
9
|
+
fillColor?: string;
|
|
10
|
+
/** Cell fill opacity (default: 0.3) */
|
|
11
|
+
fillOpacity?: number;
|
|
12
|
+
/** Selected cell fill color (default: '#ef4444') */
|
|
13
|
+
selectedFillColor?: string;
|
|
14
|
+
/** Cell outline color (default: '#1e40af') */
|
|
15
|
+
outlineColor?: string;
|
|
16
|
+
/** Cell outline width (default: 2) */
|
|
17
|
+
outlineWidth?: number;
|
|
18
|
+
}
|
|
19
|
+
declare const CellsLayer: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type CellsLayer = ReturnType<typeof CellsLayer>;
|
|
21
|
+
export default CellsLayer;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* CoverageLayer - Optional aggregated coverage visualization
|
|
4
|
+
*
|
|
5
|
+
* This is a placeholder for future coverage/heatmap features
|
|
6
|
+
* Can be extended to show:
|
|
7
|
+
* - Signal strength heatmaps
|
|
8
|
+
* - Coverage overlays
|
|
9
|
+
* - Interference zones
|
|
10
|
+
*/
|
|
11
|
+
import { onMount, onDestroy } from 'svelte';
|
|
12
|
+
import { useMapbox } from '../hooks/useMapbox';
|
|
13
|
+
import { useCellData } from '../hooks/useCellData';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
/** Namespace for layer IDs (default: 'cellular') */
|
|
17
|
+
namespace?: string;
|
|
18
|
+
/** Coverage visualization type */
|
|
19
|
+
type?: 'heatmap' | 'overlay' | 'none';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let { namespace = 'cellular', type = 'none' }: Props = $props();
|
|
23
|
+
|
|
24
|
+
const mapStore = useMapbox();
|
|
25
|
+
const cellDataContext = useCellData();
|
|
26
|
+
|
|
27
|
+
onMount(() => {
|
|
28
|
+
// Placeholder for future implementation
|
|
29
|
+
console.log('CoverageLayer mounted with type:', type);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
onDestroy(() => {
|
|
33
|
+
// Cleanup logic here
|
|
34
|
+
});
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<!-- Placeholder component - no visual output yet -->
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Namespace for layer IDs (default: 'cellular') */
|
|
3
|
+
namespace?: string;
|
|
4
|
+
/** Coverage visualization type */
|
|
5
|
+
type?: 'heatmap' | 'overlay' | 'none';
|
|
6
|
+
}
|
|
7
|
+
declare const CoverageLayer: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type CoverageLayer = ReturnType<typeof CoverageLayer>;
|
|
9
|
+
export default CoverageLayer;
|