@smartnet360/svelte-components 0.0.85 → 0.0.86
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/antenna-pattern/components/AntennaControls.svelte +1 -106
- package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +0 -36
- package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte +0 -2
- package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte +0 -22
- package/dist/apps/antenna-pattern/components/chart-engines/PolarAreaChart.svelte +0 -2
- package/dist/apps/antenna-pattern/components/chart-engines/PolarBarChart.svelte +0 -2
- package/dist/apps/antenna-pattern/components/chart-engines/PolarLineChart.svelte +0 -2
- package/dist/apps/site-check/data-loader.js +0 -8
- package/dist/core/Charts/GlobalControls.svelte +0 -4
- package/dist/core/Desktop/Grid/ResizeHandle.svelte +0 -7
- package/dist/core/Desktop/Grid/resizeStore.js +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/map-v2/demo/DemoMap.svelte +0 -2
- package/dist/map-v2/demo/demo-cells.js +0 -1
- package/dist/map-v2/features/cells/layers/CellsLayer.svelte +7 -26
- package/dist/map-v2/features/cells/utils/cellTree.js +0 -29
- package/dist/map-v2/features/repeaters/layers/RepeaterLabelsLayer.svelte +3 -27
- package/dist/map-v2/features/repeaters/layers/RepeatersLayer.svelte +8 -25
- package/dist/map-v2/features/repeaters/utils/repeaterTree.js +0 -6
- package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +0 -8
- package/dist/map-v2/features/sites/utils/siteTreeUtils.js +0 -6
- package/dist/map-v3/core/components/Map.svelte +89 -0
- package/dist/map-v3/core/components/Map.svelte.d.ts +13 -0
- package/dist/map-v3/core/controls/FeatureSettingsControl.svelte +103 -0
- package/dist/map-v3/core/controls/FeatureSettingsControl.svelte.d.ts +15 -0
- package/dist/map-v3/core/controls/MapStyleControl.svelte +271 -0
- package/dist/map-v3/core/controls/MapStyleControl.svelte.d.ts +28 -0
- package/dist/map-v3/core/index.d.ts +3 -0
- package/dist/map-v3/core/index.js +3 -0
- package/dist/map-v3/core/stores/map.store.svelte.d.ts +8 -0
- package/dist/map-v3/core/stores/map.store.svelte.js +29 -0
- package/dist/map-v3/core/stores/viewport.store.svelte.d.ts +38 -0
- package/dist/map-v3/core/stores/viewport.store.svelte.js +107 -0
- package/dist/map-v3/demo/DemoMap.svelte +104 -0
- package/dist/map-v3/demo/DemoMap.svelte.d.ts +6 -0
- package/dist/map-v3/demo/demo-cells.d.ts +13 -0
- package/dist/map-v3/demo/demo-cells.js +130 -0
- package/dist/map-v3/demo/demo-data.d.ts +8 -0
- package/dist/map-v3/demo/demo-data.js +104 -0
- package/dist/map-v3/demo/demo-repeaters.d.ts +13 -0
- package/dist/map-v3/demo/demo-repeaters.js +73 -0
- package/dist/map-v3/features/cells/components/CellFilterControl.svelte +208 -0
- package/dist/map-v3/features/cells/components/CellFilterControl.svelte.d.ts +12 -0
- package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte +229 -0
- package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte.d.ts +7 -0
- package/dist/map-v3/features/cells/constants.d.ts +18 -0
- package/dist/map-v3/features/cells/constants.js +37 -0
- package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte +230 -0
- package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte.d.ts +11 -0
- package/dist/map-v3/features/cells/layers/CellsLayer.svelte +194 -0
- package/dist/map-v3/features/cells/layers/CellsLayer.svelte.d.ts +11 -0
- package/dist/map-v3/features/cells/layers/index.d.ts +2 -0
- package/dist/map-v3/features/cells/layers/index.js +2 -0
- package/dist/map-v3/features/cells/logic/geometry.d.ts +12 -0
- package/dist/map-v3/features/cells/logic/geometry.js +35 -0
- package/dist/map-v3/features/cells/logic/grouping.d.ts +18 -0
- package/dist/map-v3/features/cells/logic/grouping.js +30 -0
- package/dist/map-v3/features/cells/logic/tree-adapter.d.ts +11 -0
- package/dist/map-v3/features/cells/logic/tree-adapter.js +53 -0
- package/dist/map-v3/features/cells/stores/cell.data.svelte.d.ts +9 -0
- package/dist/map-v3/features/cells/stores/cell.data.svelte.js +16 -0
- package/dist/map-v3/features/cells/stores/cell.display.svelte.d.ts +25 -0
- package/dist/map-v3/features/cells/stores/cell.display.svelte.js +67 -0
- package/dist/map-v3/features/cells/stores/cell.registry.svelte.d.ts +23 -0
- package/dist/map-v3/features/cells/stores/cell.registry.svelte.js +68 -0
- package/dist/map-v3/features/cells/types.d.ts +62 -0
- package/dist/map-v3/features/cells/types.js +6 -0
- package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +148 -0
- package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte.d.ts +12 -0
- package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte +209 -0
- package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte.d.ts +7 -0
- package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte +177 -0
- package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte.d.ts +11 -0
- package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte +163 -0
- package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte.d.ts +11 -0
- package/dist/map-v3/features/repeaters/logic/geometry.d.ts +3 -0
- package/dist/map-v3/features/repeaters/logic/geometry.js +23 -0
- package/dist/map-v3/features/repeaters/logic/grouping.d.ts +8 -0
- package/dist/map-v3/features/repeaters/logic/grouping.js +20 -0
- package/dist/map-v3/features/repeaters/logic/tree-adapter.d.ts +8 -0
- package/dist/map-v3/features/repeaters/logic/tree-adapter.js +43 -0
- package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.d.ts +8 -0
- package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.js +13 -0
- package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.d.ts +21 -0
- package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.js +64 -0
- package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.d.ts +23 -0
- package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.js +68 -0
- package/dist/map-v3/features/repeaters/types.d.ts +18 -0
- package/dist/map-v3/features/repeaters/types.js +1 -0
- package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +119 -0
- package/dist/map-v3/features/sites/components/SiteFilterControl.svelte.d.ts +12 -0
- package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte +241 -0
- package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte.d.ts +7 -0
- package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte +152 -0
- package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte.d.ts +11 -0
- package/dist/map-v3/features/sites/layers/SitesLayer.svelte +132 -0
- package/dist/map-v3/features/sites/layers/SitesLayer.svelte.d.ts +11 -0
- package/dist/map-v3/features/sites/logic/tree-adapter.d.ts +9 -0
- package/dist/map-v3/features/sites/logic/tree-adapter.js +75 -0
- package/dist/map-v3/features/sites/stores/site.data.svelte.d.ts +8 -0
- package/dist/map-v3/features/sites/stores/site.data.svelte.js +40 -0
- package/dist/map-v3/features/sites/stores/site.display.svelte.d.ts +20 -0
- package/dist/map-v3/features/sites/stores/site.display.svelte.js +63 -0
- package/dist/map-v3/features/sites/stores/site.registry.svelte.d.ts +13 -0
- package/dist/map-v3/features/sites/stores/site.registry.svelte.js +83 -0
- package/dist/map-v3/features/sites/types.d.ts +12 -0
- package/dist/map-v3/features/sites/types.js +1 -0
- package/dist/map-v3/index.d.ts +26 -0
- package/dist/map-v3/index.js +31 -0
- package/dist/map-v3/shared/controls/MapControl.svelte +242 -0
- package/dist/map-v3/shared/controls/MapControl.svelte.d.ts +27 -0
- package/dist/map-v3/shared/index.d.ts +1 -0
- package/dist/map-v3/shared/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Cell Data
|
|
3
|
+
*
|
|
4
|
+
* 100 sites across San Francisco Bay Area
|
|
5
|
+
* Each site has 3 sectors (azimuths: 0°, 120°, 240°)
|
|
6
|
+
* Each sector has 12 cells (all tech-band combinations)
|
|
7
|
+
* Total: 100 sites × 3 sectors × 12 tech-bands = 3,600 cells
|
|
8
|
+
*/
|
|
9
|
+
const BASE_LAT = 47.4979;
|
|
10
|
+
const BASE_LNG = 19.0402;
|
|
11
|
+
// Grid parameters for distributing sites
|
|
12
|
+
const NUM_SITES = 1700;
|
|
13
|
+
const GRID_SIZE = 10; // 10×10 grid
|
|
14
|
+
const LAT_SPACING = 0.01; // ~1.1 km spacing
|
|
15
|
+
const LNG_SPACING = 0.015; // ~1.1 km spacing (adjusted for longitude)
|
|
16
|
+
// Standard beamwidth for sectors
|
|
17
|
+
const BEAMWIDTH = 65;
|
|
18
|
+
// Cell tech-band definitions with proper fband format
|
|
19
|
+
const TECH_BANDS = [
|
|
20
|
+
// 2G bands
|
|
21
|
+
{ tech: '2G', band: '900', fband: 'GSM900' },
|
|
22
|
+
{ tech: '2G', band: '1800', fband: 'GSM1800' },
|
|
23
|
+
// 4G bands
|
|
24
|
+
{ tech: '4G', band: '700', fband: 'LTE700' },
|
|
25
|
+
{ tech: '4G', band: '800', fband: 'LTE800' },
|
|
26
|
+
{ tech: '4G', band: '900', fband: 'LTE900' },
|
|
27
|
+
{ tech: '4G', band: '1800', fband: 'LTE1800' },
|
|
28
|
+
{ tech: '4G', band: '2100', fband: 'LTE2100' },
|
|
29
|
+
{ tech: '4G', band: '2600', fband: 'LTE2600' },
|
|
30
|
+
// 5G bands
|
|
31
|
+
{ tech: '5G', band: '700', fband: '5G-700' },
|
|
32
|
+
{ tech: '5G', band: '2100', fband: '5G-2100' },
|
|
33
|
+
{ tech: '5G', band: '3500', fband: '5G-3500' }
|
|
34
|
+
];
|
|
35
|
+
// Three sector azimuths
|
|
36
|
+
const AZIMUTHS = [0, 120, 240];
|
|
37
|
+
// Status rotation for variety
|
|
38
|
+
const STATUSES = [
|
|
39
|
+
'On_Air',
|
|
40
|
+
'On_Air',
|
|
41
|
+
'On_Air',
|
|
42
|
+
'On_Air',
|
|
43
|
+
'On_Air_UNDER_CONSTRUCTION',
|
|
44
|
+
'On_Air_Locked',
|
|
45
|
+
'RF_Plan_Ready',
|
|
46
|
+
'RF_Plan_Ready',
|
|
47
|
+
'Re-Planned_RF_Plan_Ready',
|
|
48
|
+
'Tavlati_RF_Plan_Ready',
|
|
49
|
+
'On_Air',
|
|
50
|
+
'On_Air'
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Generate demo cells: 100 sites × 3 sectors × 12 tech-bands = 3,600 cells
|
|
54
|
+
*/
|
|
55
|
+
export const demoCells = [];
|
|
56
|
+
let cellCounter = 1;
|
|
57
|
+
// Generate sites in a grid pattern
|
|
58
|
+
for (let siteIndex = 0; siteIndex < NUM_SITES; siteIndex++) {
|
|
59
|
+
const row = Math.floor(siteIndex / GRID_SIZE);
|
|
60
|
+
const col = siteIndex % GRID_SIZE;
|
|
61
|
+
// Calculate site position (centered grid around base location)
|
|
62
|
+
const siteLat = BASE_LAT + (row - GRID_SIZE / 2) * LAT_SPACING;
|
|
63
|
+
const siteLng = BASE_LNG + (col - GRID_SIZE / 2) * LNG_SPACING;
|
|
64
|
+
const siteId = `DEMO-SITE-${String(siteIndex + 1).padStart(3, '0')}`;
|
|
65
|
+
// Generate 3 sectors per site
|
|
66
|
+
AZIMUTHS.forEach((azimuth, sectorIndex) => {
|
|
67
|
+
// Generate 12 tech-bands per sector
|
|
68
|
+
TECH_BANDS.forEach((techBand, techIndex) => {
|
|
69
|
+
const cellId = `CELL-${String(cellCounter).padStart(4, '0')}`;
|
|
70
|
+
const status = STATUSES[techIndex];
|
|
71
|
+
demoCells.push({
|
|
72
|
+
// Core properties
|
|
73
|
+
id: cellId,
|
|
74
|
+
txId: `TX-${String(cellCounter).padStart(4, '0')}`,
|
|
75
|
+
cellID: cellId,
|
|
76
|
+
cellID2G: techBand.tech === '2G' ? cellId : '',
|
|
77
|
+
cellName: `${siteId} ${techBand.tech}${techBand.band} S${sectorIndex + 1}`,
|
|
78
|
+
siteId: siteId,
|
|
79
|
+
tech: techBand.tech,
|
|
80
|
+
fband: techBand.fband,
|
|
81
|
+
frq: techBand.band,
|
|
82
|
+
type: 'MACRO',
|
|
83
|
+
status: status,
|
|
84
|
+
onAirDate: '2024-01-15',
|
|
85
|
+
// 2G specific
|
|
86
|
+
bcch: techBand.tech === '2G' ? 100 + techIndex : 0,
|
|
87
|
+
ctrlid: techBand.tech === '2G' ? `CTRL-${cellId}` : '',
|
|
88
|
+
// 4G specific
|
|
89
|
+
dlEarfn: techBand.tech === '4G' ? 6200 + techIndex * 100 : 0,
|
|
90
|
+
// Physical properties
|
|
91
|
+
antenna: 'DEMO-ANTENNA-MODEL',
|
|
92
|
+
azimuth: azimuth,
|
|
93
|
+
height: 30, // 30 meters antenna height
|
|
94
|
+
electricalTilt: '3',
|
|
95
|
+
beamwidth: BEAMWIDTH,
|
|
96
|
+
latitude: siteLat,
|
|
97
|
+
longitude: siteLng,
|
|
98
|
+
dx: 0,
|
|
99
|
+
dy: 0,
|
|
100
|
+
siteLatitude: siteLat,
|
|
101
|
+
siteLongitude: siteLng,
|
|
102
|
+
// Planning
|
|
103
|
+
comment: `Demo ${techBand.tech} ${techBand.band} cell at azimuth ${azimuth}°`,
|
|
104
|
+
planner: 'Demo User',
|
|
105
|
+
// Atoll properties
|
|
106
|
+
atollETP: 43.0,
|
|
107
|
+
atollPW: 20.0,
|
|
108
|
+
atollRS: 500.0 + (techBand.band === '700' ? 200 : 0), // Lower freq = longer range
|
|
109
|
+
atollBW: parseFloat(techBand.band) / 100, // Simplified bandwidth
|
|
110
|
+
// Network properties
|
|
111
|
+
cellId3: `${cellId}-3G`,
|
|
112
|
+
nwtP1: 20,
|
|
113
|
+
nwtP2: 40,
|
|
114
|
+
pci1: (cellCounter % 504), // Physical Cell ID for LTE
|
|
115
|
+
nwtRS: 450.0,
|
|
116
|
+
nwtBW: 10.0,
|
|
117
|
+
// Other
|
|
118
|
+
other: {
|
|
119
|
+
demoCell: true,
|
|
120
|
+
siteNumber: siteIndex + 1,
|
|
121
|
+
sector: sectorIndex + 1,
|
|
122
|
+
techBandKey: `${techBand.tech}_${techBand.band}`,
|
|
123
|
+
gridPosition: { row, col }
|
|
124
|
+
},
|
|
125
|
+
customSubgroup: `Sector-${sectorIndex + 1}`
|
|
126
|
+
});
|
|
127
|
+
cellCounter++;
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo cellular data for testing map-v2 site feature
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Sample cellular sites (San Francisco area)
|
|
6
|
+
*/
|
|
7
|
+
export const demoSites = [
|
|
8
|
+
{
|
|
9
|
+
siteId: 'site-001',
|
|
10
|
+
siteName: 'Downtown Tower',
|
|
11
|
+
longitude: -122.4194,
|
|
12
|
+
latitude: 37.7749,
|
|
13
|
+
fbands: ['2100', '1800'],
|
|
14
|
+
techs: ['5G'],
|
|
15
|
+
provider: 'Verizon',
|
|
16
|
+
level1: 'Urban Macro',
|
|
17
|
+
level2: 'San Francisco',
|
|
18
|
+
cellCount: 3
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
siteId: 'site-002',
|
|
22
|
+
siteName: 'Financial District',
|
|
23
|
+
longitude: -122.4008,
|
|
24
|
+
latitude: 37.7946,
|
|
25
|
+
fbands: ['1800', '850'],
|
|
26
|
+
techs: ['LTE'],
|
|
27
|
+
provider: 'AT&T',
|
|
28
|
+
level1: 'Urban Macro',
|
|
29
|
+
level2: 'San Francisco',
|
|
30
|
+
cellCount: 3
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
siteId: 'site-003',
|
|
34
|
+
siteName: 'Mission Bay',
|
|
35
|
+
longitude: -122.3912,
|
|
36
|
+
latitude: 37.7699,
|
|
37
|
+
fbands: ['2600'],
|
|
38
|
+
techs: ['5G'],
|
|
39
|
+
provider: 'T-Mobile',
|
|
40
|
+
level1: 'Small Cell',
|
|
41
|
+
level2: 'San Francisco',
|
|
42
|
+
cellCount: 2
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
siteId: 'site-004',
|
|
46
|
+
siteName: 'Golden Gate Park',
|
|
47
|
+
longitude: -122.4862,
|
|
48
|
+
latitude: 37.7694,
|
|
49
|
+
fbands: ['850', '700'],
|
|
50
|
+
techs: ['LTE'],
|
|
51
|
+
provider: 'Verizon',
|
|
52
|
+
level1: 'Rural Macro',
|
|
53
|
+
level2: 'San Francisco',
|
|
54
|
+
cellCount: 3
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
siteId: 'site-005',
|
|
58
|
+
siteName: 'Presidio Heights',
|
|
59
|
+
longitude: -122.4545,
|
|
60
|
+
latitude: 37.7881,
|
|
61
|
+
fbands: ['2100', '850'],
|
|
62
|
+
techs: ['5G'],
|
|
63
|
+
provider: 'AT&T',
|
|
64
|
+
level1: 'Suburban Macro',
|
|
65
|
+
level2: 'San Francisco',
|
|
66
|
+
cellCount: 3
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
siteId: 'site-006',
|
|
70
|
+
siteName: 'Castro District',
|
|
71
|
+
longitude: -122.4352,
|
|
72
|
+
latitude: 37.7609,
|
|
73
|
+
fbands: ['1800'],
|
|
74
|
+
techs: ['LTE'],
|
|
75
|
+
provider: 'T-Mobile',
|
|
76
|
+
level1: 'Small Cell',
|
|
77
|
+
level2: 'San Francisco',
|
|
78
|
+
cellCount: 2
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
siteId: 'site-007',
|
|
82
|
+
siteName: 'Embarcadero',
|
|
83
|
+
longitude: -122.3959,
|
|
84
|
+
latitude: 37.7956,
|
|
85
|
+
fbands: ['2600', '2100'],
|
|
86
|
+
techs: ['5G'],
|
|
87
|
+
provider: 'Verizon',
|
|
88
|
+
level1: 'Urban Macro',
|
|
89
|
+
level2: 'San Francisco',
|
|
90
|
+
cellCount: 3
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
siteId: 'site-008',
|
|
94
|
+
siteName: 'Sunset District',
|
|
95
|
+
longitude: -122.4944,
|
|
96
|
+
latitude: 37.7599,
|
|
97
|
+
fbands: ['850', '700'],
|
|
98
|
+
techs: ['LTE'],
|
|
99
|
+
provider: 'AT&T',
|
|
100
|
+
level1: 'Suburban Macro',
|
|
101
|
+
level2: 'San Francisco',
|
|
102
|
+
cellCount: 3
|
|
103
|
+
}
|
|
104
|
+
];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Repeater Data
|
|
3
|
+
*
|
|
4
|
+
* 15 repeaters strategically placed near demo sites
|
|
5
|
+
* Mix of 2G, 4G, and 5G technologies across various frequency bands
|
|
6
|
+
* Fixed 30° beamwidth
|
|
7
|
+
*/
|
|
8
|
+
import type { Repeater } from '../features/repeaters/types';
|
|
9
|
+
/**
|
|
10
|
+
* Generate demo repeater data
|
|
11
|
+
* Creates 15 repeaters with various tech:fband combinations
|
|
12
|
+
*/
|
|
13
|
+
export declare const demoRepeaters: Repeater[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Repeater Data
|
|
3
|
+
*
|
|
4
|
+
* 15 repeaters strategically placed near demo sites
|
|
5
|
+
* Mix of 2G, 4G, and 5G technologies across various frequency bands
|
|
6
|
+
* Fixed 30° beamwidth
|
|
7
|
+
*/
|
|
8
|
+
const BASE_LAT = 47.4979;
|
|
9
|
+
const BASE_LNG = 19.0402;
|
|
10
|
+
// Repeater positions (distributed across demo area)
|
|
11
|
+
const REPEATER_POSITIONS = [
|
|
12
|
+
{ lat: BASE_LAT + 0.015, lng: BASE_LNG - 0.020 },
|
|
13
|
+
{ lat: BASE_LAT + 0.025, lng: BASE_LNG + 0.010 },
|
|
14
|
+
{ lat: BASE_LAT - 0.010, lng: BASE_LNG + 0.025 },
|
|
15
|
+
{ lat: BASE_LAT - 0.020, lng: BASE_LNG - 0.015 },
|
|
16
|
+
{ lat: BASE_LAT + 0.005, lng: BASE_LNG + 0.030 },
|
|
17
|
+
{ lat: BASE_LAT + 0.030, lng: BASE_LNG - 0.005 },
|
|
18
|
+
{ lat: BASE_LAT - 0.025, lng: BASE_LNG + 0.015 },
|
|
19
|
+
{ lat: BASE_LAT + 0.010, lng: BASE_LNG - 0.030 },
|
|
20
|
+
{ lat: BASE_LAT - 0.015, lng: BASE_LNG - 0.025 },
|
|
21
|
+
{ lat: BASE_LAT + 0.020, lng: BASE_LNG + 0.020 },
|
|
22
|
+
{ lat: BASE_LAT - 0.005, lng: BASE_LNG - 0.010 },
|
|
23
|
+
{ lat: BASE_LAT + 0.035, lng: BASE_LNG + 0.005 },
|
|
24
|
+
{ lat: BASE_LAT - 0.030, lng: BASE_LNG + 0.020 },
|
|
25
|
+
{ lat: BASE_LAT + 0.012, lng: BASE_LNG - 0.018 },
|
|
26
|
+
{ lat: BASE_LAT - 0.018, lng: BASE_LNG + 0.012 }
|
|
27
|
+
];
|
|
28
|
+
// Repeater tech-band combinations
|
|
29
|
+
const REPEATER_CONFIGS = [
|
|
30
|
+
// 2G repeaters
|
|
31
|
+
{ tech: '2G', fband: 'GSM900', azimuth: 45 },
|
|
32
|
+
{ tech: '2G', fband: 'GSM1800', azimuth: 180 },
|
|
33
|
+
{ tech: '2G', fband: 'GSM900', azimuth: 270 },
|
|
34
|
+
// 4G repeaters
|
|
35
|
+
{ tech: '4G', fband: 'LTE700', azimuth: 90 },
|
|
36
|
+
{ tech: '4G', fband: 'LTE800', azimuth: 225 },
|
|
37
|
+
{ tech: '4G', fband: 'LTE1800', azimuth: 135 },
|
|
38
|
+
{ tech: '4G', fband: 'LTE2100', azimuth: 315 },
|
|
39
|
+
{ tech: '4G', fband: 'LTE2600', azimuth: 60 },
|
|
40
|
+
// 5G repeaters
|
|
41
|
+
{ tech: '5G', fband: '5G-700', azimuth: 150 },
|
|
42
|
+
{ tech: '5G', fband: '5G-2100', azimuth: 210 },
|
|
43
|
+
{ tech: '5G', fband: '5G-3500', azimuth: 330 },
|
|
44
|
+
{ tech: '5G', fband: '5G-3500', azimuth: 0 },
|
|
45
|
+
{ tech: '5G', fband: '5G-2100', azimuth: 120 },
|
|
46
|
+
{ tech: '5G', fband: '5G-700', azimuth: 240 },
|
|
47
|
+
{ tech: '5G', fband: '5G-3500', azimuth: 180 }
|
|
48
|
+
];
|
|
49
|
+
/**
|
|
50
|
+
* Generate demo repeater data
|
|
51
|
+
* Creates 15 repeaters with various tech:fband combinations
|
|
52
|
+
*/
|
|
53
|
+
export const demoRepeaters = REPEATER_POSITIONS.map((pos, idx) => {
|
|
54
|
+
const config = REPEATER_CONFIGS[idx];
|
|
55
|
+
const repeaterId = `REP-${String(idx + 1).padStart(3, '0')}`;
|
|
56
|
+
const donorCellId = `CELL-${String((idx * 7) % 100).padStart(3, '0')}`;
|
|
57
|
+
return {
|
|
58
|
+
repeaterId,
|
|
59
|
+
donorCellId,
|
|
60
|
+
donorCellName: `Cell-${config.tech}-${config.fband}-${idx + 1}`,
|
|
61
|
+
latitude: pos.lat,
|
|
62
|
+
longitude: pos.lng,
|
|
63
|
+
azimuth: config.azimuth,
|
|
64
|
+
beamwidth: 30, // Fixed 30° beamwidth
|
|
65
|
+
tech: config.tech,
|
|
66
|
+
fband: config.fband,
|
|
67
|
+
type: 'REPEATER',
|
|
68
|
+
height: 15 + (idx % 5) * 5, // Heights between 15-35m
|
|
69
|
+
factory_nbr: `FN-${String(1000 + idx).padStart(6, '0')}`,
|
|
70
|
+
provider: idx % 2 === 0 ? 'Operator-A' : 'Operator-B',
|
|
71
|
+
featureGroup: config.tech === '5G' ? 'High-Priority' : config.tech === '4G' ? 'Standard' : 'Legacy'
|
|
72
|
+
};
|
|
73
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy, untrack } from 'svelte';
|
|
3
|
+
import { MapControl } from '../../../shared';
|
|
4
|
+
import { createTreeStore } from '../../../../core/TreeView/tree.store';
|
|
5
|
+
import TreeView from '../../../../core/TreeView/TreeView.svelte';
|
|
6
|
+
import type { CellDataStore } from '../stores/cell.data.svelte';
|
|
7
|
+
import type { CellRegistry } from '../stores/cell.registry.svelte';
|
|
8
|
+
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
9
|
+
import type { CellGroupingField } from '../types';
|
|
10
|
+
import { buildCellTree } from '../logic/tree-adapter';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
dataStore: CellDataStore;
|
|
14
|
+
registry: CellRegistry;
|
|
15
|
+
displayStore: CellDisplayStore;
|
|
16
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let { dataStore, registry, displayStore, position = 'top-left' }: Props = $props();
|
|
20
|
+
|
|
21
|
+
// Local State for Grouping
|
|
22
|
+
// let level1 = $state<CellGroupingField>('tech');
|
|
23
|
+
// let level2 = $state<CellGroupingField>('fband');
|
|
24
|
+
let includePlanned = $state(false);
|
|
25
|
+
|
|
26
|
+
const level1Options: CellGroupingField[] = ['tech', 'status', 'siteId'];
|
|
27
|
+
const level2Options: CellGroupingField[] = ['fband', 'frq', 'status', 'none'];
|
|
28
|
+
|
|
29
|
+
// Sync includePlanned with dataStore
|
|
30
|
+
$effect(() => {
|
|
31
|
+
// If includePlanned is true, we show ALL (filterOnAir = false)
|
|
32
|
+
// If includePlanned is false, we show ONLY On Air (filterOnAir = true)
|
|
33
|
+
dataStore.filterOnAir = !includePlanned;
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Create Tree Store
|
|
37
|
+
// We recreate it whenever the cell data changes significantly (e.g. new file loaded)
|
|
38
|
+
// or when grouping configuration changes.
|
|
39
|
+
// We use untrack() to avoid recreating the tree when the registry changes (visibility toggles),
|
|
40
|
+
// as the tree itself drives those changes.
|
|
41
|
+
let treeStore = $derived.by(() => {
|
|
42
|
+
const _cells = dataStore.filteredCells;
|
|
43
|
+
const _l1 = displayStore.level1;
|
|
44
|
+
const _l2 = displayStore.level2;
|
|
45
|
+
|
|
46
|
+
return untrack(() => {
|
|
47
|
+
const nodes = buildCellTree(_cells, registry, _l1, _l2);
|
|
48
|
+
return createTreeStore({
|
|
49
|
+
nodes,
|
|
50
|
+
namespace: `${registry.namespace}:tree:${_l1}:${_l2}`,
|
|
51
|
+
persistState: true,
|
|
52
|
+
defaultExpandAll: true
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Sync Tree Selection -> Cell Registry Visibility
|
|
58
|
+
$effect(() => {
|
|
59
|
+
const unsubscribe = treeStore.subscribe((val) => {
|
|
60
|
+
// Iterate all leaf nodes to sync visibility
|
|
61
|
+
// This is a bit heavy but ensures consistency.
|
|
62
|
+
// Optimization: Only update changed nodes if we had a diff.
|
|
63
|
+
|
|
64
|
+
// We only care about leaf nodes (bands)
|
|
65
|
+
let changes = 0;
|
|
66
|
+
val.state.nodes.forEach((nodeState) => {
|
|
67
|
+
if (nodeState.node.children && nodeState.node.children.length > 0) return; // Skip folders
|
|
68
|
+
|
|
69
|
+
const groupId = nodeState.node.id;
|
|
70
|
+
// IMPORTANT: Read from checkedPaths set, NOT nodeState.checked (which is static/stale)
|
|
71
|
+
const isVisible = val.state.checkedPaths.has(nodeState.path);
|
|
72
|
+
|
|
73
|
+
// Update registry if different
|
|
74
|
+
const currentStyle = registry.getStyle(groupId, '#000'); // Color doesn't matter here
|
|
75
|
+
if (currentStyle.visible !== isVisible) {
|
|
76
|
+
// console.log(`[CellFilterControl] Syncing ${groupId}: ${currentStyle.visible} -> ${isVisible}`);
|
|
77
|
+
registry.toggleVisibility(groupId);
|
|
78
|
+
changes++;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
if (changes > 0) {
|
|
82
|
+
console.log(`[CellFilterControl] Synced ${changes} visibility changes to registry`);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
unsubscribe();
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
function handleColorChange(groupId: string, event: Event) {
|
|
92
|
+
const input = event.target as HTMLInputElement;
|
|
93
|
+
registry.setColor(groupId, input.value);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getFieldLabel(field: string): string {
|
|
97
|
+
const labels: Record<string, string> = {
|
|
98
|
+
tech: 'Technology',
|
|
99
|
+
fband: 'Tech + Band',
|
|
100
|
+
frq: 'Frequency',
|
|
101
|
+
status: 'Status',
|
|
102
|
+
siteId: 'Site ID',
|
|
103
|
+
none: 'None'
|
|
104
|
+
};
|
|
105
|
+
return labels[field] || field;
|
|
106
|
+
}
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<MapControl {position} title="Layers" icon="radioactive" controlWidth="320px">
|
|
110
|
+
<div class="cell-filter-control">
|
|
111
|
+
<!-- Status Filter Checkbox -->
|
|
112
|
+
<div class="mb-3 px-1">
|
|
113
|
+
<div class="form-check">
|
|
114
|
+
<input
|
|
115
|
+
type="checkbox"
|
|
116
|
+
class="form-check-input"
|
|
117
|
+
id="includePlanned"
|
|
118
|
+
bind:checked={includePlanned}
|
|
119
|
+
/>
|
|
120
|
+
<label class="form-check-label small" for="includePlanned">
|
|
121
|
+
Include Planned Cells
|
|
122
|
+
<span class="text-muted ms-1">
|
|
123
|
+
({includePlanned ? 'All cells' : 'On Air only'})
|
|
124
|
+
</span>
|
|
125
|
+
</label>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
<!-- Grouping Configuration -->
|
|
130
|
+
<div class="grouping-config mb-3 pb-2 border-bottom">
|
|
131
|
+
<div class="row g-2">
|
|
132
|
+
<!-- Level 1 Grouping -->
|
|
133
|
+
<div class="col-6">
|
|
134
|
+
<label for="level1-select" class="form-label small mb-1 text-muted">Level 1</label>
|
|
135
|
+
<select
|
|
136
|
+
id="level1-select"
|
|
137
|
+
class="form-select form-select-sm"
|
|
138
|
+
bind:value={displayStore.level1}
|
|
139
|
+
>
|
|
140
|
+
{#each level1Options as option}
|
|
141
|
+
<option value={option}>{getFieldLabel(option)}</option>
|
|
142
|
+
{/each}
|
|
143
|
+
</select>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<!-- Level 2 Grouping -->
|
|
147
|
+
<div class="col-6">
|
|
148
|
+
<label for="level2-select" class="form-label small mb-1 text-muted">Level 2</label>
|
|
149
|
+
<select
|
|
150
|
+
id="level2-select"
|
|
151
|
+
class="form-select form-select-sm"
|
|
152
|
+
bind:value={displayStore.level2}
|
|
153
|
+
>
|
|
154
|
+
{#each level2Options as option}
|
|
155
|
+
<option value={option}>{getFieldLabel(option)}</option>
|
|
156
|
+
{/each}
|
|
157
|
+
</select>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<!-- Tree View -->
|
|
163
|
+
<div class="cell-filter-tree">
|
|
164
|
+
{#if dataStore.filteredCells.length === 0}
|
|
165
|
+
<div class="text-muted p-3 text-center small">
|
|
166
|
+
No cells loaded.
|
|
167
|
+
</div>
|
|
168
|
+
{:else}
|
|
169
|
+
<TreeView showControls={false} store={$treeStore} height="300px">
|
|
170
|
+
{#snippet children({ node, state })}
|
|
171
|
+
<!-- Color Picker (Only for leaves) -->
|
|
172
|
+
{#if !node.children || node.children.length === 0}
|
|
173
|
+
<div
|
|
174
|
+
class="d-flex align-items-center"
|
|
175
|
+
role="group"
|
|
176
|
+
onclick={(e) => e.stopPropagation()}
|
|
177
|
+
onkeydown={(e) => e.stopPropagation()}
|
|
178
|
+
>
|
|
179
|
+
<input
|
|
180
|
+
type="color"
|
|
181
|
+
class="form-control form-control-color form-control-sm border-0 p-0"
|
|
182
|
+
style="width: 16px; height: 16px; min-height: 0;"
|
|
183
|
+
value={node.metadata?.color}
|
|
184
|
+
oninput={(e) => handleColorChange(node.id, e)}
|
|
185
|
+
title="Change color"
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
{/if}
|
|
189
|
+
{/snippet}
|
|
190
|
+
</TreeView>
|
|
191
|
+
{/if}
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<!-- Stats Footer -->
|
|
195
|
+
<!-- <div class="cell-filter-stats text-center pt-2 mt-2 border-top">
|
|
196
|
+
<small class="text-muted">
|
|
197
|
+
Showing {dataStore.filteredCells.length} of {dataStore.rawCells.length} cells
|
|
198
|
+
</small>
|
|
199
|
+
</div> -->
|
|
200
|
+
</div>
|
|
201
|
+
</MapControl>
|
|
202
|
+
|
|
203
|
+
<style>
|
|
204
|
+
.cell-filter-control {
|
|
205
|
+
background: #fff;
|
|
206
|
+
overflow: hidden;
|
|
207
|
+
}
|
|
208
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CellDataStore } from '../stores/cell.data.svelte';
|
|
2
|
+
import type { CellRegistry } from '../stores/cell.registry.svelte';
|
|
3
|
+
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
4
|
+
interface Props {
|
|
5
|
+
dataStore: CellDataStore;
|
|
6
|
+
registry: CellRegistry;
|
|
7
|
+
displayStore: CellDisplayStore;
|
|
8
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
9
|
+
}
|
|
10
|
+
declare const CellFilterControl: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type CellFilterControl = ReturnType<typeof CellFilterControl>;
|
|
12
|
+
export default CellFilterControl;
|