@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,229 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
displayStore: CellDisplayStore;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let { displayStore }: Props = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div class="card border-0 shadow-sm rounded-2">
|
|
12
|
+
<div class="card-body bg-light p-3">
|
|
13
|
+
<!-- Target Pixel Size (Radius) -->
|
|
14
|
+
<div class="row align-items-center g-2 mb-3">
|
|
15
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Pixel Size</div>
|
|
16
|
+
<div class="col-3 text-end">
|
|
17
|
+
<span class="badge bg-white text-muted border">{displayStore.targetPixelSize}px</span>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="col-5">
|
|
20
|
+
<input
|
|
21
|
+
id="cell-radius-slider"
|
|
22
|
+
type="range"
|
|
23
|
+
class="form-range w-100"
|
|
24
|
+
min="10"
|
|
25
|
+
max="200"
|
|
26
|
+
step="5"
|
|
27
|
+
bind:value={displayStore.targetPixelSize}
|
|
28
|
+
/>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<!-- Line Width -->
|
|
33
|
+
<div class="row align-items-center g-2 mb-3">
|
|
34
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Line Width</div>
|
|
35
|
+
<div class="col-3 text-end">
|
|
36
|
+
<span class="badge bg-white text-muted border">{displayStore.lineWidth}px</span>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="col-5">
|
|
39
|
+
<input
|
|
40
|
+
id="cell-line-width-slider"
|
|
41
|
+
type="range"
|
|
42
|
+
class="form-range w-100"
|
|
43
|
+
min="0.5"
|
|
44
|
+
max="5"
|
|
45
|
+
step="0.5"
|
|
46
|
+
bind:value={displayStore.lineWidth}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- Fill Opacity -->
|
|
52
|
+
<div class="row align-items-center g-2 mb-3">
|
|
53
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Fill Opacity</div>
|
|
54
|
+
<div class="col-3 text-end">
|
|
55
|
+
<span class="badge bg-white text-muted border">{Math.round(displayStore.fillOpacity * 100)}%</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="col-5">
|
|
58
|
+
<input
|
|
59
|
+
id="cell-opacity-slider"
|
|
60
|
+
type="range"
|
|
61
|
+
class="form-range w-100"
|
|
62
|
+
min="0"
|
|
63
|
+
max="1"
|
|
64
|
+
step="0.1"
|
|
65
|
+
bind:value={displayStore.fillOpacity}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="border-top my-3"></div>
|
|
71
|
+
|
|
72
|
+
<!-- Show Labels -->
|
|
73
|
+
<div class="row align-items-center g-2 mb-3">
|
|
74
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Show Labels</div>
|
|
75
|
+
<div class="col-3"></div>
|
|
76
|
+
<div class="col-5">
|
|
77
|
+
<div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
|
|
78
|
+
<input
|
|
79
|
+
id="cell-labels-toggle"
|
|
80
|
+
type="checkbox"
|
|
81
|
+
class="form-check-input"
|
|
82
|
+
role="switch"
|
|
83
|
+
bind:checked={displayStore.showLabels}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{#if displayStore.showLabels}
|
|
90
|
+
<div class="ps-3 border-start border-2 mb-3">
|
|
91
|
+
<!-- 2G Labels -->
|
|
92
|
+
<div class="mb-2">
|
|
93
|
+
<div class="text-secondary fw-semibold small text-uppercase mb-1">2G Labels</div>
|
|
94
|
+
<div class="row g-1">
|
|
95
|
+
<div class="col-6">
|
|
96
|
+
<select class="form-select form-select-sm" bind:value={displayStore.labels2G.primary}>
|
|
97
|
+
<option value="cellName">Name</option>
|
|
98
|
+
<option value="cellID">Cell ID</option>
|
|
99
|
+
<option value="bcch">BCCH</option>
|
|
100
|
+
<option value="siteId">Site ID</option>
|
|
101
|
+
</select>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="col-6">
|
|
104
|
+
<select class="form-select form-select-sm" bind:value={displayStore.labels2G.secondary}>
|
|
105
|
+
<option value="none">None</option>
|
|
106
|
+
<option value="bcch">BCCH</option>
|
|
107
|
+
<option value="cellID">Cell ID</option>
|
|
108
|
+
<option value="siteId">Site ID</option>
|
|
109
|
+
</select>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- 4G/5G Labels -->
|
|
115
|
+
<div class="mb-2">
|
|
116
|
+
<div class="text-secondary fw-semibold small text-uppercase mb-1">4G/5G Labels</div>
|
|
117
|
+
<div class="row g-1">
|
|
118
|
+
<div class="col-6">
|
|
119
|
+
<select class="form-select form-select-sm" bind:value={displayStore.labels4G.primary}>
|
|
120
|
+
<option value="cellName">Name</option>
|
|
121
|
+
<option value="pci1">PCI</option>
|
|
122
|
+
<option value="dlEarfn">EARFCN</option>
|
|
123
|
+
<option value="cellID">Cell ID</option>
|
|
124
|
+
</select>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="col-6">
|
|
127
|
+
<select class="form-select form-select-sm" bind:value={displayStore.labels4G.secondary}>
|
|
128
|
+
<option value="none">None</option>
|
|
129
|
+
<option value="pci1">PCI</option>
|
|
130
|
+
<option value="dlEarfn">EARFCN</option>
|
|
131
|
+
<option value="cellID">Cell ID</option>
|
|
132
|
+
</select>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
<!-- Label Distance -->
|
|
137
|
+
<div class="row align-items-center g-2 mb-2">
|
|
138
|
+
<div class="col-4 text-secondary small">Distance</div>
|
|
139
|
+
<div class="col-3 text-end">
|
|
140
|
+
<span class="badge bg-white text-muted border">{displayStore.labelPixelDistance}px</span>
|
|
141
|
+
</div>
|
|
142
|
+
<div class="col-5">
|
|
143
|
+
<input
|
|
144
|
+
type="range"
|
|
145
|
+
class="form-range w-100"
|
|
146
|
+
min="10"
|
|
147
|
+
max="100"
|
|
148
|
+
step="5"
|
|
149
|
+
bind:value={displayStore.labelPixelDistance}
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<!-- Font Size -->
|
|
155
|
+
<div class="row align-items-center g-2 mb-3">
|
|
156
|
+
<div class="col-4 text-secondary small">Font Size</div>
|
|
157
|
+
<div class="col-3 text-end">
|
|
158
|
+
<span class="badge bg-white text-muted border">{displayStore.labelFontSize}px</span>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="col-5">
|
|
161
|
+
<input
|
|
162
|
+
type="range"
|
|
163
|
+
class="form-range w-100"
|
|
164
|
+
min="8"
|
|
165
|
+
max="24"
|
|
166
|
+
step="1"
|
|
167
|
+
bind:value={displayStore.labelFontSize}
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<!-- Label Colors -->
|
|
173
|
+
<div class="row align-items-center g-2 mb-2">
|
|
174
|
+
<div class="col-4 text-secondary small">Color</div>
|
|
175
|
+
<div class="col-8">
|
|
176
|
+
<div class="input-group input-group-sm">
|
|
177
|
+
<input
|
|
178
|
+
type="color"
|
|
179
|
+
class="form-control form-control-color"
|
|
180
|
+
bind:value={displayStore.labelColor}
|
|
181
|
+
title="Label Color"
|
|
182
|
+
/>
|
|
183
|
+
<span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
|
|
184
|
+
{displayStore.labelColor}
|
|
185
|
+
</span>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<!-- Halo Settings -->
|
|
191
|
+
<div class="row align-items-center g-2 mb-2">
|
|
192
|
+
<div class="col-4 text-secondary small">Halo</div>
|
|
193
|
+
<div class="col-8">
|
|
194
|
+
<div class="input-group input-group-sm">
|
|
195
|
+
<input
|
|
196
|
+
type="color"
|
|
197
|
+
class="form-control form-control-color"
|
|
198
|
+
bind:value={displayStore.labelHaloColor}
|
|
199
|
+
title="Halo Color"
|
|
200
|
+
/>
|
|
201
|
+
<span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
|
|
202
|
+
{displayStore.labelHaloColor}
|
|
203
|
+
</span>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div class="row align-items-center g-2 mb-3">
|
|
209
|
+
<div class="col-4 text-secondary small">Halo Width</div>
|
|
210
|
+
<div class="col-3 text-end">
|
|
211
|
+
<span class="badge bg-white text-muted border">{displayStore.labelHaloWidth}px</span>
|
|
212
|
+
</div>
|
|
213
|
+
<div class="col-5">
|
|
214
|
+
<input
|
|
215
|
+
type="range"
|
|
216
|
+
class="form-range w-100"
|
|
217
|
+
min="0"
|
|
218
|
+
max="5"
|
|
219
|
+
step="0.5"
|
|
220
|
+
bind:value={displayStore.labelHaloWidth}
|
|
221
|
+
/>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
</div>
|
|
227
|
+
{/if}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
displayStore: CellDisplayStore;
|
|
4
|
+
}
|
|
5
|
+
declare const CellSettingsPanel: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type CellSettingsPanel = ReturnType<typeof CellSettingsPanel>;
|
|
7
|
+
export default CellSettingsPanel;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Z-Index Layer Ordering by Technology-Band
|
|
3
|
+
*
|
|
4
|
+
* Controls which sectors appear on top when overlapping
|
|
5
|
+
* Higher frequency bands typically rendered on top
|
|
6
|
+
*/
|
|
7
|
+
import type { TechnologyBandKey } from './types';
|
|
8
|
+
export declare const Z_INDEX_BY_BAND: Record<TechnologyBandKey, number>;
|
|
9
|
+
/**
|
|
10
|
+
* Base Z-Index for Mapbox layer ordering
|
|
11
|
+
* Cells should render below sites but above base map features
|
|
12
|
+
*/
|
|
13
|
+
export declare const CELL_FILL_Z_INDEX = 100;
|
|
14
|
+
export declare const CELL_LINE_Z_INDEX = 101;
|
|
15
|
+
/**
|
|
16
|
+
* Default colors for cyclical palette
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_PALETTE: string[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Z-Index Layer Ordering by Technology-Band
|
|
3
|
+
*
|
|
4
|
+
* Controls which sectors appear on top when overlapping
|
|
5
|
+
* Higher frequency bands typically rendered on top
|
|
6
|
+
*/
|
|
7
|
+
export const Z_INDEX_BY_BAND = {
|
|
8
|
+
// 2G bands
|
|
9
|
+
'2G_900': 3,
|
|
10
|
+
'2G_1800': 7,
|
|
11
|
+
// 4G bands
|
|
12
|
+
'4G_700': 0,
|
|
13
|
+
'4G_800': 2,
|
|
14
|
+
'4G_900': 4,
|
|
15
|
+
'4G_1800': 8,
|
|
16
|
+
'4G_2100': 9,
|
|
17
|
+
'4G_2600': 11,
|
|
18
|
+
// 5G bands
|
|
19
|
+
'5G_700': 1,
|
|
20
|
+
'5G_2100': 10,
|
|
21
|
+
'5G_3500': 14
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Base Z-Index for Mapbox layer ordering
|
|
25
|
+
* Cells should render below sites but above base map features
|
|
26
|
+
*/
|
|
27
|
+
export const CELL_FILL_Z_INDEX = 100;
|
|
28
|
+
export const CELL_LINE_Z_INDEX = 101;
|
|
29
|
+
/**
|
|
30
|
+
* Default colors for cyclical palette
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_PALETTE = [
|
|
33
|
+
'#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231',
|
|
34
|
+
'#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe',
|
|
35
|
+
'#008080', '#e6beff', '#9a6324', '#fffac8', '#800000',
|
|
36
|
+
'#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080'
|
|
37
|
+
];
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import type { MapStore } from '../../../core/stores/map.store.svelte';
|
|
4
|
+
import type { CellDataStore } from '../stores/cell.data.svelte';
|
|
5
|
+
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
6
|
+
import type { CellRegistry } from '../stores/cell.registry.svelte';
|
|
7
|
+
import { groupCells, getColorForGroup } from '../logic/grouping';
|
|
8
|
+
import type { Cell } from '../types';
|
|
9
|
+
import type mapboxgl from 'mapbox-gl';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
dataStore: CellDataStore;
|
|
13
|
+
displayStore: CellDisplayStore;
|
|
14
|
+
registry: CellRegistry;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let { dataStore, displayStore, registry }: Props = $props();
|
|
18
|
+
|
|
19
|
+
const mapStore = getContext<MapStore>('MAP_CONTEXT');
|
|
20
|
+
let sourceId = 'cell-labels-source';
|
|
21
|
+
let layerId = 'cell-labels-layer';
|
|
22
|
+
let updateTimeout: any;
|
|
23
|
+
|
|
24
|
+
// Helper to get label text
|
|
25
|
+
function getLabelText(cell: Cell, config: { primary: string, secondary: string }): string {
|
|
26
|
+
const pVal = cell[config.primary as keyof Cell];
|
|
27
|
+
const sVal = config.secondary !== 'none' ? cell[config.secondary as keyof Cell] : null;
|
|
28
|
+
|
|
29
|
+
let text = pVal != null ? String(pVal) : '';
|
|
30
|
+
if (sVal != null) {
|
|
31
|
+
text += ` | ${sVal}`;
|
|
32
|
+
}
|
|
33
|
+
return text;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Helper to round azimuth
|
|
37
|
+
function roundAzimuth(azimuth: number, tolerance: number): number {
|
|
38
|
+
return Math.round(azimuth / tolerance) * tolerance;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// React to changes
|
|
42
|
+
$effect(() => {
|
|
43
|
+
// Read dependencies
|
|
44
|
+
const _cells = dataStore.filteredCells;
|
|
45
|
+
const _show = displayStore.showLabels;
|
|
46
|
+
const _dist = displayStore.labelPixelDistance;
|
|
47
|
+
const _size = displayStore.labelFontSize;
|
|
48
|
+
const _tol = displayStore.labelAzimuthTolerance;
|
|
49
|
+
const _l2g = displayStore.labels2G;
|
|
50
|
+
const _l4g = displayStore.labels4G;
|
|
51
|
+
const _color = displayStore.labelColor;
|
|
52
|
+
const _haloColor = displayStore.labelHaloColor;
|
|
53
|
+
const _haloWidth = displayStore.labelHaloWidth;
|
|
54
|
+
const _registryVersion = registry.version;
|
|
55
|
+
|
|
56
|
+
const map = mapStore.map;
|
|
57
|
+
if (map && map.getLayer(layerId)) {
|
|
58
|
+
map.setPaintProperty(layerId, 'text-color', displayStore.labelColor);
|
|
59
|
+
map.setPaintProperty(layerId, 'text-halo-color', displayStore.labelHaloColor);
|
|
60
|
+
map.setPaintProperty(layerId, 'text-halo-width', displayStore.labelHaloWidth);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
updateLayer();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function updateLayer() {
|
|
67
|
+
const map = mapStore.map;
|
|
68
|
+
if (!map) return;
|
|
69
|
+
|
|
70
|
+
clearTimeout(updateTimeout);
|
|
71
|
+
updateTimeout = setTimeout(() => {
|
|
72
|
+
renderLabels(map);
|
|
73
|
+
}, 150);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function renderLabels(map: mapboxgl.Map) {
|
|
77
|
+
if (!displayStore.showLabels) {
|
|
78
|
+
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'none');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'visible');
|
|
83
|
+
|
|
84
|
+
// Update text size if changed
|
|
85
|
+
if (map.getLayer(layerId)) {
|
|
86
|
+
map.setLayoutProperty(layerId, 'text-size', displayStore.labelFontSize);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const bounds = map.getBounds();
|
|
90
|
+
if (!bounds) return;
|
|
91
|
+
|
|
92
|
+
// 1. Filter visible cells (Bounds + Registry Visibility)
|
|
93
|
+
const visibleCells: Cell[] = [];
|
|
94
|
+
|
|
95
|
+
// Group cells using the same logic as CellsLayer to determine visibility
|
|
96
|
+
const cellGroups = groupCells(dataStore.filteredCells, displayStore.level1, displayStore.level2);
|
|
97
|
+
let groupIndex = 0;
|
|
98
|
+
|
|
99
|
+
for (const [groupId, cells] of cellGroups) {
|
|
100
|
+
const defaultColor = getColorForGroup(groupIndex++);
|
|
101
|
+
const style = registry.getStyle(groupId, defaultColor);
|
|
102
|
+
|
|
103
|
+
if (style.visible) {
|
|
104
|
+
for (const cell of cells) {
|
|
105
|
+
if (bounds.contains([cell.longitude, cell.latitude])) {
|
|
106
|
+
visibleCells.push(cell);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 2. Group by Site + Azimuth
|
|
113
|
+
const groups = new Map<string, Cell[]>();
|
|
114
|
+
for (const cell of visibleCells) {
|
|
115
|
+
const az = roundAzimuth(cell.azimuth, displayStore.labelAzimuthTolerance);
|
|
116
|
+
const key = `${cell.siteId}_${az}`;
|
|
117
|
+
if (!groups.has(key)) groups.set(key, []);
|
|
118
|
+
groups.get(key)?.push(cell);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const features: GeoJSON.Feature[] = [];
|
|
122
|
+
const distEm = displayStore.labelPixelDistance / displayStore.labelFontSize;
|
|
123
|
+
|
|
124
|
+
// 3. Generate Features
|
|
125
|
+
for (const [key, cells] of groups) {
|
|
126
|
+
// Sort cells (e.g. by band/tech) to have consistent order
|
|
127
|
+
cells.sort((a, b) => {
|
|
128
|
+
const ta = a.tech + a.fband;
|
|
129
|
+
const tb = b.tech + b.fband;
|
|
130
|
+
return ta.localeCompare(tb);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Calculate base offset for the group
|
|
134
|
+
const az = roundAzimuth(cells[0].azimuth, displayStore.labelAzimuthTolerance);
|
|
135
|
+
const azRad = (az * Math.PI) / 180;
|
|
136
|
+
|
|
137
|
+
// Mapbox text-offset: [x, y] in ems.
|
|
138
|
+
// x is right, y is down.
|
|
139
|
+
// 0 deg (N) -> x=0, y=-1
|
|
140
|
+
const baseX = Math.sin(azRad) * distEm;
|
|
141
|
+
const baseY = -Math.cos(azRad) * distEm;
|
|
142
|
+
|
|
143
|
+
// Center the stack vertically around the base point
|
|
144
|
+
const totalHeight = cells.length * 1.2; // 1.2 em line height
|
|
145
|
+
const startY = baseY - (totalHeight / 2) + 0.6; // +0.6 to center first line
|
|
146
|
+
|
|
147
|
+
cells.forEach((cell, index) => {
|
|
148
|
+
const is2G = cell.tech.includes('2G');
|
|
149
|
+
const config = is2G ? displayStore.labels2G : displayStore.labels4G;
|
|
150
|
+
const text = getLabelText(cell, config);
|
|
151
|
+
|
|
152
|
+
if (!text) return;
|
|
153
|
+
|
|
154
|
+
const stackY = startY + (index * 1.2);
|
|
155
|
+
|
|
156
|
+
features.push({
|
|
157
|
+
type: 'Feature',
|
|
158
|
+
geometry: {
|
|
159
|
+
type: 'Point',
|
|
160
|
+
coordinates: [cell.longitude, cell.latitude]
|
|
161
|
+
},
|
|
162
|
+
properties: {
|
|
163
|
+
text,
|
|
164
|
+
offset: [baseX, stackY], // Array property
|
|
165
|
+
color: '#333333'
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 4. Update Source
|
|
172
|
+
const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
|
|
173
|
+
if (source) {
|
|
174
|
+
source.setData({
|
|
175
|
+
type: 'FeatureCollection',
|
|
176
|
+
features: features as any
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Initial Setup
|
|
182
|
+
$effect(() => {
|
|
183
|
+
const map = mapStore.map;
|
|
184
|
+
if (!map) return;
|
|
185
|
+
|
|
186
|
+
const addLayers = () => {
|
|
187
|
+
if (!map.getSource(sourceId)) {
|
|
188
|
+
map.addSource(sourceId, {
|
|
189
|
+
type: 'geojson',
|
|
190
|
+
data: { type: 'FeatureCollection', features: [] }
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!map.getLayer(layerId)) {
|
|
195
|
+
map.addLayer({
|
|
196
|
+
id: layerId,
|
|
197
|
+
type: 'symbol',
|
|
198
|
+
source: sourceId,
|
|
199
|
+
layout: {
|
|
200
|
+
'text-field': ['get', 'text'],
|
|
201
|
+
'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
|
|
202
|
+
'text-size': displayStore.labelFontSize,
|
|
203
|
+
'text-offset': ['get', 'offset'],
|
|
204
|
+
'text-allow-overlap': true,
|
|
205
|
+
'text-ignore-placement': true,
|
|
206
|
+
'text-anchor': 'center'
|
|
207
|
+
},
|
|
208
|
+
paint: {
|
|
209
|
+
'text-color': displayStore.labelColor,
|
|
210
|
+
'text-halo-color': displayStore.labelHaloColor,
|
|
211
|
+
'text-halo-width': displayStore.labelHaloWidth
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
addLayers();
|
|
218
|
+
map.on('style.load', addLayers);
|
|
219
|
+
map.on('moveend', updateLayer);
|
|
220
|
+
map.on('zoomend', updateLayer);
|
|
221
|
+
|
|
222
|
+
return () => {
|
|
223
|
+
map.off('style.load', addLayers);
|
|
224
|
+
map.off('moveend', updateLayer);
|
|
225
|
+
map.off('zoomend', updateLayer);
|
|
226
|
+
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
|
227
|
+
if (map.getSource(sourceId)) map.removeSource(sourceId);
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CellDataStore } from '../stores/cell.data.svelte';
|
|
2
|
+
import type { CellDisplayStore } from '../stores/cell.display.svelte';
|
|
3
|
+
import type { CellRegistry } from '../stores/cell.registry.svelte';
|
|
4
|
+
interface Props {
|
|
5
|
+
dataStore: CellDataStore;
|
|
6
|
+
displayStore: CellDisplayStore;
|
|
7
|
+
registry: CellRegistry;
|
|
8
|
+
}
|
|
9
|
+
declare const CellLabelsLayer: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type CellLabelsLayer = ReturnType<typeof CellLabelsLayer>;
|
|
11
|
+
export default CellLabelsLayer;
|