@smartnet360/svelte-components 0.0.85 → 0.0.87
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 +6 -0
- package/dist/map-v3/core/index.js +5 -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 +30 -0
- package/dist/map-v3/index.js +36 -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,241 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SiteDisplayStore } from '../stores/site.display.svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
displayStore: SiteDisplayStore;
|
|
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
|
+
<!-- Radius -->
|
|
14
|
+
<div class="row align-items-center g-2 mb-3">
|
|
15
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Radius</div>
|
|
16
|
+
<div class="col-3 text-end">
|
|
17
|
+
<span class="badge bg-white text-muted border">{displayStore.radius}px</span>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="col-5">
|
|
20
|
+
<input
|
|
21
|
+
type="range"
|
|
22
|
+
class="form-range w-100"
|
|
23
|
+
min="2"
|
|
24
|
+
max="20"
|
|
25
|
+
step="1"
|
|
26
|
+
bind:value={displayStore.radius}
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<!-- Fill Color -->
|
|
32
|
+
<div class="row align-items-center g-2 mb-2">
|
|
33
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Fill Color</div>
|
|
34
|
+
<div class="col-8">
|
|
35
|
+
<div class="input-group input-group-sm">
|
|
36
|
+
<input
|
|
37
|
+
type="color"
|
|
38
|
+
class="form-control form-control-color"
|
|
39
|
+
bind:value={displayStore.color}
|
|
40
|
+
title="Fill Color"
|
|
41
|
+
/>
|
|
42
|
+
<span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
|
|
43
|
+
{displayStore.color}
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<!-- Fill Opacity -->
|
|
50
|
+
<div class="row align-items-center g-2 mb-3">
|
|
51
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Opacity</div>
|
|
52
|
+
<div class="col-3 text-end">
|
|
53
|
+
<span class="badge bg-white text-muted border">{Math.round(displayStore.opacity * 100)}%</span>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="col-5">
|
|
56
|
+
<input
|
|
57
|
+
type="range"
|
|
58
|
+
class="form-range w-100"
|
|
59
|
+
min="0"
|
|
60
|
+
max="1"
|
|
61
|
+
step="0.1"
|
|
62
|
+
bind:value={displayStore.opacity}
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<!-- Stroke Color -->
|
|
68
|
+
<div class="row align-items-center g-2 mb-2">
|
|
69
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Stroke</div>
|
|
70
|
+
<div class="col-8">
|
|
71
|
+
<div class="input-group input-group-sm">
|
|
72
|
+
<input
|
|
73
|
+
type="color"
|
|
74
|
+
class="form-control form-control-color"
|
|
75
|
+
bind:value={displayStore.strokeColor}
|
|
76
|
+
title="Stroke Color"
|
|
77
|
+
/>
|
|
78
|
+
<span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
|
|
79
|
+
{displayStore.strokeColor}
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<!-- Stroke Width -->
|
|
86
|
+
<div class="row align-items-center g-2 mb-3">
|
|
87
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Stroke Width</div>
|
|
88
|
+
<div class="col-3 text-end">
|
|
89
|
+
<span class="badge bg-white text-muted border">{displayStore.strokeWidth}px</span>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="col-5">
|
|
92
|
+
<input
|
|
93
|
+
type="range"
|
|
94
|
+
class="form-range w-100"
|
|
95
|
+
min="0"
|
|
96
|
+
max="5"
|
|
97
|
+
step="0.5"
|
|
98
|
+
bind:value={displayStore.strokeWidth}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="border-top my-3"></div>
|
|
104
|
+
|
|
105
|
+
<!-- Show Labels -->
|
|
106
|
+
<div class="row align-items-center g-2 mb-3">
|
|
107
|
+
<div class="col-4 text-secondary fw-semibold small text-uppercase">Show Labels</div>
|
|
108
|
+
<div class="col-3"></div>
|
|
109
|
+
<div class="col-5">
|
|
110
|
+
<div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
|
|
111
|
+
<input
|
|
112
|
+
type="checkbox"
|
|
113
|
+
class="form-check-input"
|
|
114
|
+
role="switch"
|
|
115
|
+
bind:checked={displayStore.showLabels}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{#if displayStore.showLabels}
|
|
122
|
+
<div class="ps-3 border-start border-2 mb-3">
|
|
123
|
+
<!-- Label Fields -->
|
|
124
|
+
<div class="mb-2">
|
|
125
|
+
<div class="text-secondary fw-semibold small text-uppercase mb-1">Label Fields</div>
|
|
126
|
+
<div class="row g-1">
|
|
127
|
+
<div class="col-6">
|
|
128
|
+
<select class="form-select form-select-sm" bind:value={displayStore.labels.primary}>
|
|
129
|
+
<option value="siteId">Site ID</option>
|
|
130
|
+
<option value="siteName">Name</option>
|
|
131
|
+
<option value="provider">Provider</option>
|
|
132
|
+
<option value="cellCount">Cell Count</option>
|
|
133
|
+
</select>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="col-6">
|
|
136
|
+
<select class="form-select form-select-sm" bind:value={displayStore.labels.secondary}>
|
|
137
|
+
<option value="none">None</option>
|
|
138
|
+
<option value="siteId">Site ID</option>
|
|
139
|
+
<option value="siteName">Name</option>
|
|
140
|
+
<option value="provider">Provider</option>
|
|
141
|
+
<option value="cellCount">Cell Count</option>
|
|
142
|
+
</select>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- Label Settings -->
|
|
148
|
+
<div class="mt-3">
|
|
149
|
+
<!-- Label Distance -->
|
|
150
|
+
<div class="row align-items-center g-2 mb-2">
|
|
151
|
+
<div class="col-4 text-secondary small">Distance</div>
|
|
152
|
+
<div class="col-3 text-end">
|
|
153
|
+
<span class="badge bg-white text-muted border">{displayStore.labelPixelDistance}px</span>
|
|
154
|
+
</div>
|
|
155
|
+
<div class="col-5">
|
|
156
|
+
<input
|
|
157
|
+
type="range"
|
|
158
|
+
class="form-range w-100"
|
|
159
|
+
min="5"
|
|
160
|
+
max="100"
|
|
161
|
+
step="5"
|
|
162
|
+
bind:value={displayStore.labelPixelDistance}
|
|
163
|
+
/>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<!-- Font Size -->
|
|
168
|
+
<div class="row align-items-center g-2 mb-3">
|
|
169
|
+
<div class="col-4 text-secondary small">Font Size</div>
|
|
170
|
+
<div class="col-3 text-end">
|
|
171
|
+
<span class="badge bg-white text-muted border">{displayStore.labelFontSize}px</span>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="col-5">
|
|
174
|
+
<input
|
|
175
|
+
type="range"
|
|
176
|
+
class="form-range w-100"
|
|
177
|
+
min="8"
|
|
178
|
+
max="24"
|
|
179
|
+
step="1"
|
|
180
|
+
bind:value={displayStore.labelFontSize}
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<!-- Label Colors -->
|
|
186
|
+
<div class="row align-items-center g-2 mb-2">
|
|
187
|
+
<div class="col-4 text-secondary small">Color</div>
|
|
188
|
+
<div class="col-8">
|
|
189
|
+
<div class="input-group input-group-sm">
|
|
190
|
+
<input
|
|
191
|
+
type="color"
|
|
192
|
+
class="form-control form-control-color"
|
|
193
|
+
bind:value={displayStore.labelColor}
|
|
194
|
+
title="Label Color"
|
|
195
|
+
/>
|
|
196
|
+
<span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
|
|
197
|
+
{displayStore.labelColor}
|
|
198
|
+
</span>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<!-- Halo Settings -->
|
|
204
|
+
<div class="row align-items-center g-2 mb-2">
|
|
205
|
+
<div class="col-4 text-secondary small">Halo</div>
|
|
206
|
+
<div class="col-8">
|
|
207
|
+
<div class="input-group input-group-sm">
|
|
208
|
+
<input
|
|
209
|
+
type="color"
|
|
210
|
+
class="form-control form-control-color"
|
|
211
|
+
bind:value={displayStore.labelHaloColor}
|
|
212
|
+
title="Halo Color"
|
|
213
|
+
/>
|
|
214
|
+
<span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
|
|
215
|
+
{displayStore.labelHaloColor}
|
|
216
|
+
</span>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div class="row align-items-center g-2 mb-3">
|
|
222
|
+
<div class="col-4 text-secondary small">Halo Width</div>
|
|
223
|
+
<div class="col-3 text-end">
|
|
224
|
+
<span class="badge bg-white text-muted border">{displayStore.labelHaloWidth}px</span>
|
|
225
|
+
</div>
|
|
226
|
+
<div class="col-5">
|
|
227
|
+
<input
|
|
228
|
+
type="range"
|
|
229
|
+
class="form-range w-100"
|
|
230
|
+
min="0"
|
|
231
|
+
max="5"
|
|
232
|
+
step="0.5"
|
|
233
|
+
bind:value={displayStore.labelHaloWidth}
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
{/if}
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SiteDisplayStore } from '../stores/site.display.svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
displayStore: SiteDisplayStore;
|
|
4
|
+
}
|
|
5
|
+
declare const SiteSettingsPanel: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type SiteSettingsPanel = ReturnType<typeof SiteSettingsPanel>;
|
|
7
|
+
export default SiteSettingsPanel;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import type { MapStore } from '../../../core/stores/map.store.svelte';
|
|
4
|
+
import type { SiteDataStore } from '../stores/site.data.svelte';
|
|
5
|
+
import type { SiteDisplayStore } from '../stores/site.display.svelte';
|
|
6
|
+
import type { SiteRegistry } from '../stores/site.registry.svelte';
|
|
7
|
+
import type { Site } from '../types';
|
|
8
|
+
import type mapboxgl from 'mapbox-gl';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
dataStore: SiteDataStore;
|
|
12
|
+
displayStore: SiteDisplayStore;
|
|
13
|
+
registry: SiteRegistry;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { dataStore, displayStore, registry }: Props = $props();
|
|
17
|
+
|
|
18
|
+
const mapStore = getContext<MapStore>('MAP_CONTEXT');
|
|
19
|
+
let sourceId = 'site-labels-source';
|
|
20
|
+
let layerId = 'site-labels-layer';
|
|
21
|
+
let updateTimeout: any;
|
|
22
|
+
|
|
23
|
+
// Helper to get label text
|
|
24
|
+
function getLabelText(site: Site, config: { primary: string, secondary: string }): string {
|
|
25
|
+
const pVal = site[config.primary as keyof Site];
|
|
26
|
+
const sVal = config.secondary !== 'none' ? site[config.secondary as keyof Site] : null;
|
|
27
|
+
|
|
28
|
+
let text = pVal != null ? String(pVal) : '';
|
|
29
|
+
if (sVal != null) {
|
|
30
|
+
text += ` | ${sVal}`;
|
|
31
|
+
}
|
|
32
|
+
return text;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// React to changes
|
|
36
|
+
$effect(() => {
|
|
37
|
+
// Read dependencies
|
|
38
|
+
const _sites = dataStore.sites;
|
|
39
|
+
const _show = displayStore.showLabels;
|
|
40
|
+
const _dist = displayStore.labelPixelDistance;
|
|
41
|
+
const _size = displayStore.labelFontSize;
|
|
42
|
+
const _lPrim = displayStore.labels.primary;
|
|
43
|
+
const _lSec = displayStore.labels.secondary;
|
|
44
|
+
const _color = displayStore.labelColor;
|
|
45
|
+
const _haloColor = displayStore.labelHaloColor;
|
|
46
|
+
const _haloWidth = displayStore.labelHaloWidth;
|
|
47
|
+
const _version = registry.version;
|
|
48
|
+
|
|
49
|
+
const map = mapStore.map;
|
|
50
|
+
if (map && map.getLayer(layerId)) {
|
|
51
|
+
map.setPaintProperty(layerId, 'text-color', displayStore.labelColor);
|
|
52
|
+
map.setPaintProperty(layerId, 'text-halo-color', displayStore.labelHaloColor);
|
|
53
|
+
map.setPaintProperty(layerId, 'text-halo-width', displayStore.labelHaloWidth);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateLayer();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function updateLayer() {
|
|
60
|
+
const map = mapStore.map;
|
|
61
|
+
if (!map) return;
|
|
62
|
+
|
|
63
|
+
clearTimeout(updateTimeout);
|
|
64
|
+
updateTimeout = setTimeout(() => {
|
|
65
|
+
renderLabels(map);
|
|
66
|
+
}, 150);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function renderLabels(map: mapboxgl.Map) {
|
|
70
|
+
if (!displayStore.showLabels) {
|
|
71
|
+
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'none');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'visible');
|
|
76
|
+
|
|
77
|
+
// Update text size if changed
|
|
78
|
+
if (map.getLayer(layerId)) {
|
|
79
|
+
map.setLayoutProperty(layerId, 'text-size', displayStore.labelFontSize);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const bounds = map.getBounds();
|
|
83
|
+
if (!bounds) return;
|
|
84
|
+
|
|
85
|
+
const sites = dataStore.sites;
|
|
86
|
+
const features: GeoJSON.Feature[] = [];
|
|
87
|
+
|
|
88
|
+
// Convert pixel distance to ems (approx 1em = 16px or labelFontSize)
|
|
89
|
+
const distEm = displayStore.labelPixelDistance / displayStore.labelFontSize;
|
|
90
|
+
|
|
91
|
+
for (const site of sites) {
|
|
92
|
+
if (!registry.isVisible(site.siteId)) continue;
|
|
93
|
+
if (!bounds.contains([site.longitude, site.latitude])) continue;
|
|
94
|
+
|
|
95
|
+
const labelText = getLabelText(site, displayStore.labels);
|
|
96
|
+
if (!labelText) continue;
|
|
97
|
+
|
|
98
|
+
// For sites (circles), we usually place label on top or bottom
|
|
99
|
+
// Let's place it on top (offset Y negative)
|
|
100
|
+
const offsetX = 0;
|
|
101
|
+
const offsetY = -distEm;
|
|
102
|
+
|
|
103
|
+
features.push({
|
|
104
|
+
type: 'Feature',
|
|
105
|
+
geometry: {
|
|
106
|
+
type: 'Point',
|
|
107
|
+
coordinates: [site.longitude, site.latitude]
|
|
108
|
+
},
|
|
109
|
+
properties: {
|
|
110
|
+
label: labelText,
|
|
111
|
+
offset: [offsetX, offsetY]
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const geojson: GeoJSON.FeatureCollection = {
|
|
117
|
+
type: 'FeatureCollection',
|
|
118
|
+
features
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
|
|
122
|
+
if (source) {
|
|
123
|
+
source.setData(geojson);
|
|
124
|
+
} else {
|
|
125
|
+
map.addSource(sourceId, {
|
|
126
|
+
type: 'geojson',
|
|
127
|
+
data: geojson
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
map.addLayer({
|
|
131
|
+
id: layerId,
|
|
132
|
+
type: 'symbol',
|
|
133
|
+
source: sourceId,
|
|
134
|
+
layout: {
|
|
135
|
+
'text-field': ['get', 'label'],
|
|
136
|
+
'text-font': ['Open Sans Semibold', 'Arial Unicode MS Bold'],
|
|
137
|
+
'text-size': displayStore.labelFontSize,
|
|
138
|
+
'text-offset': ['get', 'offset'],
|
|
139
|
+
'text-anchor': 'center',
|
|
140
|
+
'text-justify': 'auto',
|
|
141
|
+
'text-allow-overlap': false,
|
|
142
|
+
'text-ignore-placement': false
|
|
143
|
+
},
|
|
144
|
+
paint: {
|
|
145
|
+
'text-color': displayStore.labelColor,
|
|
146
|
+
'text-halo-color': displayStore.labelHaloColor,
|
|
147
|
+
'text-halo-width': displayStore.labelHaloWidth
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SiteDataStore } from '../stores/site.data.svelte';
|
|
2
|
+
import type { SiteDisplayStore } from '../stores/site.display.svelte';
|
|
3
|
+
import type { SiteRegistry } from '../stores/site.registry.svelte';
|
|
4
|
+
interface Props {
|
|
5
|
+
dataStore: SiteDataStore;
|
|
6
|
+
displayStore: SiteDisplayStore;
|
|
7
|
+
registry: SiteRegistry;
|
|
8
|
+
}
|
|
9
|
+
declare const SiteLabelsLayer: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type SiteLabelsLayer = ReturnType<typeof SiteLabelsLayer>;
|
|
11
|
+
export default SiteLabelsLayer;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import type { MapStore } from '../../../core/stores/map.store.svelte';
|
|
4
|
+
import type { SiteDataStore } from '../stores/site.data.svelte';
|
|
5
|
+
import type { SiteDisplayStore } from '../stores/site.display.svelte';
|
|
6
|
+
import type { SiteRegistry } from '../stores/site.registry.svelte';
|
|
7
|
+
import type mapboxgl from 'mapbox-gl';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
dataStore: SiteDataStore;
|
|
11
|
+
displayStore: SiteDisplayStore;
|
|
12
|
+
registry: SiteRegistry;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { dataStore, displayStore, registry }: Props = $props();
|
|
16
|
+
|
|
17
|
+
const mapStore = getContext<MapStore>('MAP_CONTEXT');
|
|
18
|
+
let sourceId = 'sites-source';
|
|
19
|
+
let layerId = 'sites-layer';
|
|
20
|
+
let updateTimeout: any;
|
|
21
|
+
|
|
22
|
+
$effect(() => {
|
|
23
|
+
const map = mapStore.map;
|
|
24
|
+
if (!map) return;
|
|
25
|
+
|
|
26
|
+
const addLayers = () => {
|
|
27
|
+
if (!map.getSource(sourceId)) {
|
|
28
|
+
map.addSource(sourceId, {
|
|
29
|
+
type: 'geojson',
|
|
30
|
+
data: { type: 'FeatureCollection', features: [] }
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!map.getLayer(layerId)) {
|
|
35
|
+
map.addLayer({
|
|
36
|
+
id: layerId,
|
|
37
|
+
type: 'circle',
|
|
38
|
+
source: sourceId,
|
|
39
|
+
paint: {
|
|
40
|
+
'circle-radius': displayStore.radius,
|
|
41
|
+
'circle-color': ['get', 'color'],
|
|
42
|
+
'circle-opacity': displayStore.opacity,
|
|
43
|
+
'circle-stroke-color': displayStore.strokeColor,
|
|
44
|
+
'circle-stroke-width': displayStore.strokeWidth
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
updateLayer();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
addLayers();
|
|
53
|
+
map.on('style.load', addLayers);
|
|
54
|
+
map.on('moveend', updateLayer);
|
|
55
|
+
map.on('zoomend', updateLayer);
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
map.off('style.load', addLayers);
|
|
59
|
+
map.off('moveend', updateLayer);
|
|
60
|
+
map.off('zoomend', updateLayer);
|
|
61
|
+
|
|
62
|
+
if (map.getLayer(layerId)) map.removeLayer(layerId);
|
|
63
|
+
if (map.getSource(sourceId)) map.removeSource(sourceId);
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// React to display changes
|
|
68
|
+
$effect(() => {
|
|
69
|
+
const map = mapStore.map;
|
|
70
|
+
if (!map || !map.getLayer(layerId)) return;
|
|
71
|
+
|
|
72
|
+
map.setPaintProperty(layerId, 'circle-radius', displayStore.radius);
|
|
73
|
+
// Color is now data-driven
|
|
74
|
+
// map.setPaintProperty(layerId, 'circle-color', displayStore.color);
|
|
75
|
+
map.setPaintProperty(layerId, 'circle-opacity', displayStore.opacity);
|
|
76
|
+
map.setPaintProperty(layerId, 'circle-stroke-color', displayStore.strokeColor);
|
|
77
|
+
map.setPaintProperty(layerId, 'circle-stroke-width', displayStore.strokeWidth);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// React to data changes
|
|
81
|
+
$effect(() => {
|
|
82
|
+
const _sites = dataStore.sites;
|
|
83
|
+
const _version = registry.version;
|
|
84
|
+
updateLayer();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
function updateLayer() {
|
|
88
|
+
const map = mapStore.map;
|
|
89
|
+
if (!map) return;
|
|
90
|
+
|
|
91
|
+
clearTimeout(updateTimeout);
|
|
92
|
+
updateTimeout = setTimeout(() => {
|
|
93
|
+
renderSites(map);
|
|
94
|
+
}, 100);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function renderSites(map: mapboxgl.Map) {
|
|
98
|
+
const bounds = map.getBounds();
|
|
99
|
+
if (!bounds) return;
|
|
100
|
+
|
|
101
|
+
const sites = dataStore.sites;
|
|
102
|
+
const features: GeoJSON.Feature[] = [];
|
|
103
|
+
|
|
104
|
+
for (const site of sites) {
|
|
105
|
+
if (!registry.isVisible(site.siteId)) continue;
|
|
106
|
+
if (!bounds.contains([site.longitude, site.latitude])) continue;
|
|
107
|
+
|
|
108
|
+
const color = registry.getColor(site.level1, site.level2, displayStore.color);
|
|
109
|
+
|
|
110
|
+
features.push({
|
|
111
|
+
type: 'Feature',
|
|
112
|
+
geometry: {
|
|
113
|
+
type: 'Point',
|
|
114
|
+
coordinates: [site.longitude, site.latitude]
|
|
115
|
+
},
|
|
116
|
+
properties: {
|
|
117
|
+
siteId: site.siteId,
|
|
118
|
+
name: site.siteName,
|
|
119
|
+
color: color
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
|
|
125
|
+
if (source) {
|
|
126
|
+
source.setData({
|
|
127
|
+
type: 'FeatureCollection',
|
|
128
|
+
features
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
</script>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SiteDataStore } from '../stores/site.data.svelte';
|
|
2
|
+
import type { SiteDisplayStore } from '../stores/site.display.svelte';
|
|
3
|
+
import type { SiteRegistry } from '../stores/site.registry.svelte';
|
|
4
|
+
interface Props {
|
|
5
|
+
dataStore: SiteDataStore;
|
|
6
|
+
displayStore: SiteDisplayStore;
|
|
7
|
+
registry: SiteRegistry;
|
|
8
|
+
}
|
|
9
|
+
declare const SitesLayer: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type SitesLayer = ReturnType<typeof SitesLayer>;
|
|
11
|
+
export default SitesLayer;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TreeNode } from '../../../../core/TreeView/tree.model';
|
|
2
|
+
import type { Site } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Converts a list of sites into a Tree structure for the TreeView component
|
|
5
|
+
* Structure: Root (Sites) -> Level 1 -> Level 2 -> Site
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildSiteTree(sites: Site[]): TreeNode<{
|
|
8
|
+
count: number;
|
|
9
|
+
}>[];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a list of sites into a Tree structure for the TreeView component
|
|
3
|
+
* Structure: Root (Sites) -> Level 1 -> Level 2 -> Site
|
|
4
|
+
*/
|
|
5
|
+
export function buildSiteTree(sites) {
|
|
6
|
+
// Root node
|
|
7
|
+
const rootNode = {
|
|
8
|
+
id: 'root-sites',
|
|
9
|
+
label: 'Sites',
|
|
10
|
+
children: [],
|
|
11
|
+
defaultExpanded: true
|
|
12
|
+
};
|
|
13
|
+
const level1Map = new Map();
|
|
14
|
+
for (const site of sites) {
|
|
15
|
+
const l1 = site.level1 || 'Unknown';
|
|
16
|
+
const l2 = site.level2 || 'Unknown';
|
|
17
|
+
// Level 1
|
|
18
|
+
if (!level1Map.has(l1)) {
|
|
19
|
+
const node = {
|
|
20
|
+
id: `l1-${l1}`,
|
|
21
|
+
label: l1,
|
|
22
|
+
children: [],
|
|
23
|
+
defaultExpanded: true
|
|
24
|
+
};
|
|
25
|
+
level1Map.set(l1, node);
|
|
26
|
+
rootNode.children.push(node);
|
|
27
|
+
}
|
|
28
|
+
const l1Node = level1Map.get(l1);
|
|
29
|
+
// Level 2
|
|
30
|
+
// We use a composite ID to ensure uniqueness if same L2 name exists in different L1s
|
|
31
|
+
// But we need to find the node object in the children array
|
|
32
|
+
let l2Node = l1Node.children.find(c => c.label === l2);
|
|
33
|
+
if (!l2Node) {
|
|
34
|
+
l2Node = {
|
|
35
|
+
id: `l2-${l1}-${l2}`,
|
|
36
|
+
label: l2,
|
|
37
|
+
children: [],
|
|
38
|
+
defaultExpanded: false,
|
|
39
|
+
metadata: {
|
|
40
|
+
level1: l1,
|
|
41
|
+
level2: l2,
|
|
42
|
+
siteIds: [],
|
|
43
|
+
count: 0,
|
|
44
|
+
color: '#3388ff' // Default color, will be overridden by registry in UI
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
l1Node.children.push(l2Node);
|
|
48
|
+
}
|
|
49
|
+
// Add site to L2 metadata
|
|
50
|
+
l2Node.metadata.siteIds.push(site.siteId);
|
|
51
|
+
l2Node.metadata.count++;
|
|
52
|
+
}
|
|
53
|
+
// Sort Level 1
|
|
54
|
+
rootNode.children.sort((a, b) => a.label.localeCompare(b.label));
|
|
55
|
+
// Sort Level 2 and Calculate Counts
|
|
56
|
+
let totalCount = 0;
|
|
57
|
+
for (const l1Node of rootNode.children) {
|
|
58
|
+
let l1Count = 0;
|
|
59
|
+
if (l1Node.children) {
|
|
60
|
+
l1Node.children.sort((a, b) => a.label.localeCompare(b.label));
|
|
61
|
+
for (const l2Node of l1Node.children) {
|
|
62
|
+
const count = l2Node.metadata?.count || 0;
|
|
63
|
+
l1Count += count;
|
|
64
|
+
// Update L2 label with count
|
|
65
|
+
l2Node.label = `${l2Node.label} (${count})`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
totalCount += l1Count;
|
|
69
|
+
// Update L1 label with count
|
|
70
|
+
l1Node.label = `${l1Node.label} (${l1Count})`;
|
|
71
|
+
}
|
|
72
|
+
// Update Root label with count
|
|
73
|
+
rootNode.label = `${rootNode.label} (${totalCount})`;
|
|
74
|
+
return [rootNode];
|
|
75
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CellDataStore } from '../../cells/stores/cell.data.svelte';
|
|
2
|
+
import type { Site } from '../types';
|
|
3
|
+
export declare class SiteDataStore {
|
|
4
|
+
cellDataStore: CellDataStore;
|
|
5
|
+
constructor(cellDataStore: CellDataStore);
|
|
6
|
+
get sites(): Site[];
|
|
7
|
+
}
|
|
8
|
+
export declare function createSiteDataStore(cellDataStore: CellDataStore): SiteDataStore;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class SiteDataStore {
|
|
2
|
+
cellDataStore;
|
|
3
|
+
constructor(cellDataStore) {
|
|
4
|
+
this.cellDataStore = cellDataStore;
|
|
5
|
+
}
|
|
6
|
+
get sites() {
|
|
7
|
+
const cells = this.cellDataStore.filteredCells;
|
|
8
|
+
const siteMap = new Map();
|
|
9
|
+
for (const cell of cells) {
|
|
10
|
+
if (!cell.siteId)
|
|
11
|
+
continue;
|
|
12
|
+
if (!siteMap.has(cell.siteId)) {
|
|
13
|
+
siteMap.set(cell.siteId, {
|
|
14
|
+
siteId: cell.siteId,
|
|
15
|
+
siteName: cell.cellName, // Fallback to cell name if site name not available
|
|
16
|
+
latitude: cell.siteLatitude || cell.latitude,
|
|
17
|
+
longitude: cell.siteLongitude || cell.longitude,
|
|
18
|
+
techs: [cell.tech],
|
|
19
|
+
fbands: [cell.fband],
|
|
20
|
+
provider: 'Cetin',
|
|
21
|
+
level1: 'Cetin', // Need to check where provider comes from in V3,
|
|
22
|
+
level2: 'Unknown',
|
|
23
|
+
cellCount: 1
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const site = siteMap.get(cell.siteId);
|
|
28
|
+
if (!site.techs.includes(cell.tech))
|
|
29
|
+
site.techs.push(cell.tech);
|
|
30
|
+
if (!site.fbands.includes(cell.fband))
|
|
31
|
+
site.fbands.push(cell.fband);
|
|
32
|
+
site.cellCount++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return Array.from(siteMap.values());
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function createSiteDataStore(cellDataStore) {
|
|
39
|
+
return new SiteDataStore(cellDataStore);
|
|
40
|
+
}
|