@smartnet360/svelte-components 0.0.56 → 0.0.58
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/map-v2/core/components/ViewportSync.svelte +79 -0
- package/dist/map-v2/core/components/ViewportSync.svelte.d.ts +8 -0
- package/dist/map-v2/core/index.d.ts +2 -0
- package/dist/map-v2/core/index.js +3 -0
- package/dist/map-v2/core/stores/viewportStore.svelte.d.ts +51 -0
- package/dist/map-v2/core/stores/viewportStore.svelte.js +120 -0
- package/dist/map-v2/demo/DemoMap.svelte +30 -2
- package/dist/map-v2/demo/demo-cells.d.ts +12 -0
- package/dist/map-v2/demo/demo-cells.js +114 -0
- package/dist/map-v2/demo/index.d.ts +1 -0
- package/dist/map-v2/demo/index.js +1 -0
- package/dist/map-v2/features/cells/constants/colors.d.ts +7 -0
- package/dist/map-v2/features/cells/constants/colors.js +21 -0
- package/dist/map-v2/features/cells/constants/radiusMultipliers.d.ts +8 -0
- package/dist/map-v2/features/cells/constants/radiusMultipliers.js +22 -0
- package/dist/map-v2/features/cells/constants/statusStyles.d.ts +7 -0
- package/dist/map-v2/features/cells/constants/statusStyles.js +49 -0
- package/dist/map-v2/features/cells/constants/zIndex.d.ts +14 -0
- package/dist/map-v2/features/cells/constants/zIndex.js +28 -0
- package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +242 -0
- package/dist/map-v2/features/cells/controls/CellFilterControl.svelte.d.ts +14 -0
- package/dist/map-v2/features/cells/controls/CellStyleControl.svelte +139 -0
- package/dist/map-v2/features/cells/controls/CellStyleControl.svelte.d.ts +14 -0
- package/dist/map-v2/features/cells/index.d.ts +20 -0
- package/dist/map-v2/features/cells/index.js +24 -0
- package/dist/map-v2/features/cells/layers/CellsLayer.svelte +195 -0
- package/dist/map-v2/features/cells/layers/CellsLayer.svelte.d.ts +10 -0
- package/dist/map-v2/features/cells/stores/cellStoreContext.svelte.d.ts +46 -0
- package/dist/map-v2/features/cells/stores/cellStoreContext.svelte.js +137 -0
- package/dist/map-v2/features/cells/types.d.ts +99 -0
- package/dist/map-v2/features/cells/types.js +12 -0
- package/dist/map-v2/features/cells/utils/arcGeometry.d.ts +36 -0
- package/dist/map-v2/features/cells/utils/arcGeometry.js +55 -0
- package/dist/map-v2/features/cells/utils/cellGeoJSON.d.ts +22 -0
- package/dist/map-v2/features/cells/utils/cellGeoJSON.js +81 -0
- package/dist/map-v2/features/cells/utils/cellTree.d.ts +25 -0
- package/dist/map-v2/features/cells/utils/cellTree.js +226 -0
- package/dist/map-v2/features/cells/utils/techBandParser.d.ts +11 -0
- package/dist/map-v2/features/cells/utils/techBandParser.js +17 -0
- package/dist/map-v2/features/cells/utils/zoomScaling.d.ts +42 -0
- package/dist/map-v2/features/cells/utils/zoomScaling.js +53 -0
- package/dist/map-v2/index.d.ts +4 -3
- package/dist/map-v2/index.js +7 -3
- package/package.json +1 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* ViewportSync - Synchronizes map viewport changes with ViewportStore
|
|
4
|
+
*
|
|
5
|
+
* This component:
|
|
6
|
+
* - Gets map instance from MapboxProvider context
|
|
7
|
+
* - Listens to moveend/zoomend events
|
|
8
|
+
* - Updates viewport store when map moves/zooms
|
|
9
|
+
* - Handles cleanup on unmount
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```svelte
|
|
13
|
+
* <MapboxProvider center={viewport.center} zoom={viewport.zoom}>
|
|
14
|
+
* <ViewportSync store={viewport} />
|
|
15
|
+
* <!-- other layers and controls -->
|
|
16
|
+
* </MapboxProvider>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
import { getContext, onDestroy } from 'svelte';
|
|
20
|
+
import type { MapStore } from '../types';
|
|
21
|
+
import type { ViewportStore } from '../stores/viewportStore.svelte';
|
|
22
|
+
import { MAP_CONTEXT_KEY } from '../types';
|
|
23
|
+
|
|
24
|
+
interface Props {
|
|
25
|
+
/** Viewport store to sync with */
|
|
26
|
+
store: ViewportStore;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let { store }: Props = $props();
|
|
30
|
+
|
|
31
|
+
const mapStore = getContext<MapStore>(MAP_CONTEXT_KEY);
|
|
32
|
+
let map = $derived($mapStore);
|
|
33
|
+
|
|
34
|
+
// Track event listener cleanup functions
|
|
35
|
+
let cleanupFunctions: Array<() => void> = [];
|
|
36
|
+
|
|
37
|
+
// Sync map viewport to store on move/zoom end
|
|
38
|
+
$effect(() => {
|
|
39
|
+
if (!map) return;
|
|
40
|
+
|
|
41
|
+
// Handler to update store with current map viewport
|
|
42
|
+
const handleViewportChange = () => {
|
|
43
|
+
const center = map.getCenter();
|
|
44
|
+
const zoom = map.getZoom();
|
|
45
|
+
const bearing = map.getBearing();
|
|
46
|
+
const pitch = map.getPitch();
|
|
47
|
+
|
|
48
|
+
// Bulk update for efficiency
|
|
49
|
+
store.updateViewport({
|
|
50
|
+
center: [center.lng, center.lat],
|
|
51
|
+
zoom,
|
|
52
|
+
bearing,
|
|
53
|
+
pitch
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Listen to moveend (fires after pan, zoom, rotate, pitch)
|
|
58
|
+
map.on('moveend', handleViewportChange);
|
|
59
|
+
|
|
60
|
+
// Store cleanup function
|
|
61
|
+
cleanupFunctions.push(() => {
|
|
62
|
+
map?.off('moveend', handleViewportChange);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Cleanup when effect re-runs or component unmounts
|
|
66
|
+
return () => {
|
|
67
|
+
cleanupFunctions.forEach(cleanup => cleanup());
|
|
68
|
+
cleanupFunctions = [];
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
onDestroy(() => {
|
|
73
|
+
// Final cleanup on unmount
|
|
74
|
+
cleanupFunctions.forEach(cleanup => cleanup());
|
|
75
|
+
cleanupFunctions = [];
|
|
76
|
+
});
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<!-- This component doesn't render anything, it's just for side effects -->
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ViewportStore } from '../stores/viewportStore.svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Viewport store to sync with */
|
|
4
|
+
store: ViewportStore;
|
|
5
|
+
}
|
|
6
|
+
declare const ViewportSync: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type ViewportSync = ReturnType<typeof ViewportSync>;
|
|
8
|
+
export default ViewportSync;
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
export type { MapStore } from './types';
|
|
7
7
|
export { MAP_CONTEXT_KEY } from './types';
|
|
8
8
|
export { default as MapboxProvider } from './providers/MapboxProvider.svelte';
|
|
9
|
+
export { default as ViewportSync } from './components/ViewportSync.svelte';
|
|
9
10
|
export { default as MapStyleControl } from './controls/MapStyleControl.svelte';
|
|
10
11
|
export { createMapStore } from './stores/mapStore';
|
|
12
|
+
export { createViewportStore, type ViewportStore, type ViewportState } from './stores/viewportStore.svelte';
|
|
11
13
|
export { useMapbox, tryUseMapbox } from './hooks/useMapbox';
|
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
export { MAP_CONTEXT_KEY } from './types';
|
|
7
7
|
// Providers
|
|
8
8
|
export { default as MapboxProvider } from './providers/MapboxProvider.svelte';
|
|
9
|
+
// Components
|
|
10
|
+
export { default as ViewportSync } from './components/ViewportSync.svelte';
|
|
9
11
|
// Controls
|
|
10
12
|
export { default as MapStyleControl } from './controls/MapStyleControl.svelte';
|
|
11
13
|
// Stores
|
|
12
14
|
export { createMapStore } from './stores/mapStore';
|
|
15
|
+
export { createViewportStore } from './stores/viewportStore.svelte';
|
|
13
16
|
// Hooks
|
|
14
17
|
export { useMapbox, tryUseMapbox } from './hooks/useMapbox';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Viewport Store - Manages and persists map viewport state (center, zoom, pitch, bearing)
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Reactive state using Svelte 5 runes
|
|
6
|
+
* - Auto-persists to localStorage via $effect
|
|
7
|
+
* - Namespace isolation for multiple map instances
|
|
8
|
+
* - Provides getters/setters for direct binding
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* const viewport = createViewportStore('my-map', {
|
|
13
|
+
* center: [-122.4194, 37.7749],
|
|
14
|
+
* zoom: 12
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* <MapboxProvider center={viewport.center} zoom={viewport.zoom}>
|
|
18
|
+
* <ViewportSync store={viewport} />
|
|
19
|
+
* </MapboxProvider>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface ViewportState {
|
|
23
|
+
/** Map center [lng, lat] */
|
|
24
|
+
center: [number, number];
|
|
25
|
+
/** Zoom level */
|
|
26
|
+
zoom: number;
|
|
27
|
+
/** Map bearing (rotation) in degrees */
|
|
28
|
+
bearing: number;
|
|
29
|
+
/** Map pitch (tilt) in degrees */
|
|
30
|
+
pitch: number;
|
|
31
|
+
}
|
|
32
|
+
export interface ViewportStore {
|
|
33
|
+
readonly center: [number, number];
|
|
34
|
+
readonly zoom: number;
|
|
35
|
+
readonly bearing: number;
|
|
36
|
+
readonly pitch: number;
|
|
37
|
+
setCenter(center: [number, number]): void;
|
|
38
|
+
setZoom(zoom: number): void;
|
|
39
|
+
setBearing(bearing: number): void;
|
|
40
|
+
setPitch(pitch: number): void;
|
|
41
|
+
updateViewport(state: Partial<ViewportState>): void;
|
|
42
|
+
reset(): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a viewport store with persistence
|
|
46
|
+
*
|
|
47
|
+
* @param namespace - Unique identifier for localStorage key
|
|
48
|
+
* @param defaults - Default viewport values (used if no saved state exists)
|
|
49
|
+
* @returns ViewportStore instance
|
|
50
|
+
*/
|
|
51
|
+
export declare function createViewportStore(namespace: string, defaults?: Partial<ViewportState>): ViewportStore;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Viewport Store - Manages and persists map viewport state (center, zoom, pitch, bearing)
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Reactive state using Svelte 5 runes
|
|
6
|
+
* - Auto-persists to localStorage via $effect
|
|
7
|
+
* - Namespace isolation for multiple map instances
|
|
8
|
+
* - Provides getters/setters for direct binding
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```svelte
|
|
12
|
+
* const viewport = createViewportStore('my-map', {
|
|
13
|
+
* center: [-122.4194, 37.7749],
|
|
14
|
+
* zoom: 12
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* <MapboxProvider center={viewport.center} zoom={viewport.zoom}>
|
|
18
|
+
* <ViewportSync store={viewport} />
|
|
19
|
+
* </MapboxProvider>
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_STATE = {
|
|
23
|
+
center: [0, 0],
|
|
24
|
+
zoom: 2,
|
|
25
|
+
bearing: 0,
|
|
26
|
+
pitch: 0
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Load viewport state from localStorage
|
|
30
|
+
*/
|
|
31
|
+
function loadViewportState(namespace, defaults) {
|
|
32
|
+
if (typeof window === 'undefined') {
|
|
33
|
+
return { ...DEFAULT_STATE, ...defaults };
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const key = `${namespace}:viewport`;
|
|
37
|
+
const stored = localStorage.getItem(key);
|
|
38
|
+
if (stored) {
|
|
39
|
+
const parsed = JSON.parse(stored);
|
|
40
|
+
return {
|
|
41
|
+
center: parsed.center || defaults.center || DEFAULT_STATE.center,
|
|
42
|
+
zoom: parsed.zoom ?? defaults.zoom ?? DEFAULT_STATE.zoom,
|
|
43
|
+
bearing: parsed.bearing ?? defaults.bearing ?? DEFAULT_STATE.bearing,
|
|
44
|
+
pitch: parsed.pitch ?? defaults.pitch ?? DEFAULT_STATE.pitch
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.warn('Failed to load viewport state:', error);
|
|
50
|
+
}
|
|
51
|
+
return { ...DEFAULT_STATE, ...defaults };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Save viewport state to localStorage
|
|
55
|
+
*/
|
|
56
|
+
function saveViewportState(namespace, state) {
|
|
57
|
+
if (typeof window === 'undefined')
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
const key = `${namespace}:viewport`;
|
|
61
|
+
localStorage.setItem(key, JSON.stringify(state));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.warn('Failed to save viewport state:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Create a viewport store with persistence
|
|
69
|
+
*
|
|
70
|
+
* @param namespace - Unique identifier for localStorage key
|
|
71
|
+
* @param defaults - Default viewport values (used if no saved state exists)
|
|
72
|
+
* @returns ViewportStore instance
|
|
73
|
+
*/
|
|
74
|
+
export function createViewportStore(namespace, defaults = {}) {
|
|
75
|
+
// Load initial state
|
|
76
|
+
let state = $state(loadViewportState(namespace, defaults));
|
|
77
|
+
// Auto-save to localStorage on any change
|
|
78
|
+
$effect(() => {
|
|
79
|
+
saveViewportState(namespace, state);
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
// Getters
|
|
83
|
+
get center() { return state.center; },
|
|
84
|
+
get zoom() { return state.zoom; },
|
|
85
|
+
get bearing() { return state.bearing; },
|
|
86
|
+
get pitch() { return state.pitch; },
|
|
87
|
+
// Setters
|
|
88
|
+
setCenter(center) {
|
|
89
|
+
state.center = center;
|
|
90
|
+
},
|
|
91
|
+
setZoom(zoom) {
|
|
92
|
+
state.zoom = zoom;
|
|
93
|
+
},
|
|
94
|
+
setBearing(bearing) {
|
|
95
|
+
state.bearing = bearing;
|
|
96
|
+
},
|
|
97
|
+
setPitch(pitch) {
|
|
98
|
+
state.pitch = pitch;
|
|
99
|
+
},
|
|
100
|
+
// Bulk update (more efficient than individual setters)
|
|
101
|
+
updateViewport(updates) {
|
|
102
|
+
if (updates.center !== undefined)
|
|
103
|
+
state.center = updates.center;
|
|
104
|
+
if (updates.zoom !== undefined)
|
|
105
|
+
state.zoom = updates.zoom;
|
|
106
|
+
if (updates.bearing !== undefined)
|
|
107
|
+
state.bearing = updates.bearing;
|
|
108
|
+
if (updates.pitch !== undefined)
|
|
109
|
+
state.pitch = updates.pitch;
|
|
110
|
+
},
|
|
111
|
+
// Reset to defaults
|
|
112
|
+
reset() {
|
|
113
|
+
const resetState = { ...DEFAULT_STATE, ...defaults };
|
|
114
|
+
state.center = resetState.center;
|
|
115
|
+
state.zoom = resetState.zoom;
|
|
116
|
+
state.bearing = resetState.bearing;
|
|
117
|
+
state.pitch = resetState.pitch;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* DemoMap - Complete demo of map-v2 site
|
|
3
|
+
* DemoMap - Complete demo of map-v2 site and cell features
|
|
4
4
|
*
|
|
5
5
|
* Demonstrates:
|
|
6
6
|
* - Simplified MapboxProvider with automatic initialization
|
|
@@ -8,14 +8,24 @@
|
|
|
8
8
|
* - SitesLayer rendering sites from store
|
|
9
9
|
* - SiteFilterControl for hierarchical filtering
|
|
10
10
|
* - SiteSizeSlider for visual property adjustment
|
|
11
|
+
* - CellsLayer rendering cells with arc geometries
|
|
12
|
+
* - CellFilterControl with dynamic hierarchical grouping
|
|
13
|
+
* - CellStyleControl for cell visual settings
|
|
11
14
|
*/
|
|
12
15
|
import MapboxProvider from '../core/providers/MapboxProvider.svelte';
|
|
13
16
|
import MapStyleControl from '../core/controls/MapStyleControl.svelte';
|
|
17
|
+
import ViewportSync from '../core/components/ViewportSync.svelte';
|
|
14
18
|
import SitesLayer from '../features/sites/layers/SitesLayer.svelte';
|
|
15
19
|
import SiteFilterControl from '../features/sites/controls/SiteFilterControl.svelte';
|
|
16
20
|
import SiteSizeSlider from '../features/sites/controls/SiteSizeSlider.svelte';
|
|
21
|
+
import CellsLayer from '../features/cells/layers/CellsLayer.svelte';
|
|
22
|
+
import CellFilterControl from '../features/cells/controls/CellFilterControl.svelte';
|
|
23
|
+
import CellStyleControl from '../features/cells/controls/CellStyleControl.svelte';
|
|
17
24
|
import { createSiteStoreContext } from '../features/sites/stores/siteStoreContext.svelte';
|
|
25
|
+
import { createCellStoreContext } from '../features/cells/stores/cellStoreContext.svelte';
|
|
26
|
+
import { createViewportStore } from '../core/stores/viewportStore.svelte';
|
|
18
27
|
import { demoSites } from './demo-data';
|
|
28
|
+
import { demoCells } from './demo-cells';
|
|
19
29
|
|
|
20
30
|
interface Props {
|
|
21
31
|
/** Mapbox access token */
|
|
@@ -32,25 +42,43 @@
|
|
|
32
42
|
zoom = 12
|
|
33
43
|
}: Props = $props();
|
|
34
44
|
|
|
45
|
+
// Create viewport store with persistence (uses defaults as fallback)
|
|
46
|
+
const viewport = createViewportStore('demo-map', { center, zoom });
|
|
47
|
+
|
|
35
48
|
// Create site store with demo data
|
|
36
49
|
const siteStore = createSiteStoreContext(demoSites);
|
|
37
50
|
|
|
51
|
+
// Create cell store with demo data
|
|
52
|
+
const cellStore = createCellStoreContext(demoCells);
|
|
53
|
+
|
|
38
54
|
</script>
|
|
39
55
|
|
|
40
56
|
<!-- // controls={['navigation', 'scale']} -->
|
|
41
57
|
<div class="demo-map-container">
|
|
42
|
-
<MapboxProvider {accessToken} {center} {zoom}>
|
|
58
|
+
<MapboxProvider {accessToken} center={viewport.center} zoom={viewport.zoom} bearing={viewport.bearing} pitch={viewport.pitch}>
|
|
59
|
+
<!-- Sync map viewport changes to store (with localStorage persistence) -->
|
|
60
|
+
<ViewportSync store={viewport} />
|
|
61
|
+
|
|
43
62
|
<!-- Site layer - renders circles from store -->
|
|
44
63
|
<SitesLayer store={siteStore} namespace="demo-sites" />
|
|
45
64
|
|
|
65
|
+
<!-- Cell layer - renders arc sectors from store -->
|
|
66
|
+
<CellsLayer store={cellStore} namespace="demo-cells" />
|
|
67
|
+
|
|
46
68
|
<!-- Map style control - switch between map styles -->
|
|
47
69
|
<MapStyleControl position="top-right" initiallyCollapsed={true} namespace="demo-map" />
|
|
48
70
|
|
|
49
71
|
<!-- Site filter control - updates store.filteredSites -->
|
|
50
72
|
<SiteFilterControl store={siteStore} position="top-left" title="Site Filter" />
|
|
51
73
|
|
|
74
|
+
<!-- Cell filter control - dynamic hierarchical filtering -->
|
|
75
|
+
<CellFilterControl store={cellStore} position="top-left" title="Cell Filter" initiallyCollapsed={true} />
|
|
76
|
+
|
|
52
77
|
<!-- Site size control - updates store visual properties -->
|
|
53
78
|
<SiteSizeSlider store={siteStore} position="bottom-right" title="Site Display" />
|
|
79
|
+
|
|
80
|
+
<!-- Cell style control - visual settings for cells -->
|
|
81
|
+
<CellStyleControl store={cellStore} position="bottom-left" title="Cell Display" initiallyCollapsed={true} />
|
|
54
82
|
</MapboxProvider>
|
|
55
83
|
</div>
|
|
56
84
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Cell Data
|
|
3
|
+
*
|
|
4
|
+
* 1 site with 3 sectors (azimuths: 0°, 120°, 240°)
|
|
5
|
+
* Each sector has 12 cells (all tech-band combinations)
|
|
6
|
+
* Total: 36 cells
|
|
7
|
+
*/
|
|
8
|
+
import type { Cell } from '../features/cells/types';
|
|
9
|
+
/**
|
|
10
|
+
* Generate demo cells: 3 sectors × 12 tech-bands = 36 cells
|
|
11
|
+
*/
|
|
12
|
+
export declare const demoCells: Cell[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Cell Data
|
|
3
|
+
*
|
|
4
|
+
* 1 site with 3 sectors (azimuths: 0°, 120°, 240°)
|
|
5
|
+
* Each sector has 12 cells (all tech-band combinations)
|
|
6
|
+
* Total: 36 cells
|
|
7
|
+
*/
|
|
8
|
+
// Site location: San Francisco
|
|
9
|
+
const SITE_LAT = 37.7749;
|
|
10
|
+
const SITE_LNG = -122.4194;
|
|
11
|
+
const SITE_ID = 'DEMO-SITE-001';
|
|
12
|
+
// Standard beamwidth for sectors
|
|
13
|
+
const BEAMWIDTH = 65;
|
|
14
|
+
// Cell tech-band definitions with proper fband format
|
|
15
|
+
const TECH_BANDS = [
|
|
16
|
+
// 2G bands
|
|
17
|
+
{ tech: '2G', band: '900', fband: 'GSM900' },
|
|
18
|
+
{ tech: '2G', band: '1800', fband: 'GSM1800' },
|
|
19
|
+
// 4G bands
|
|
20
|
+
{ tech: '4G', band: '700', fband: 'LTE700' },
|
|
21
|
+
{ tech: '4G', band: '800', fband: 'LTE800' },
|
|
22
|
+
{ tech: '4G', band: '900', fband: 'LTE900' },
|
|
23
|
+
{ tech: '4G', band: '1800', fband: 'LTE1800' },
|
|
24
|
+
{ tech: '4G', band: '2100', fband: 'LTE2100' },
|
|
25
|
+
{ tech: '4G', band: '2600', fband: 'LTE2600' },
|
|
26
|
+
// 5G bands
|
|
27
|
+
{ tech: '5G', band: '700', fband: '5G-700' },
|
|
28
|
+
{ tech: '5G', band: '2100', fband: '5G-2100' },
|
|
29
|
+
{ tech: '5G', band: '3500', fband: '5G-3500' }
|
|
30
|
+
];
|
|
31
|
+
// Three sector azimuths
|
|
32
|
+
const AZIMUTHS = [0, 120, 240];
|
|
33
|
+
// Status rotation for variety
|
|
34
|
+
const STATUSES = [
|
|
35
|
+
'On_Air',
|
|
36
|
+
'On_Air',
|
|
37
|
+
'On_Air',
|
|
38
|
+
'On_Air',
|
|
39
|
+
'On_Air_UNDER_CONSTRUCTION',
|
|
40
|
+
'On_Air_Locked',
|
|
41
|
+
'RF_Plan_Ready',
|
|
42
|
+
'RF_Plan_Ready',
|
|
43
|
+
'Re-Planned_RF_Plan_Ready',
|
|
44
|
+
'Tavlati_RF_Plan_Ready',
|
|
45
|
+
'On_Air',
|
|
46
|
+
'On_Air'
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Generate demo cells: 3 sectors × 12 tech-bands = 36 cells
|
|
50
|
+
*/
|
|
51
|
+
export const demoCells = [];
|
|
52
|
+
let cellCounter = 1;
|
|
53
|
+
AZIMUTHS.forEach((azimuth, sectorIndex) => {
|
|
54
|
+
TECH_BANDS.forEach((techBand, techIndex) => {
|
|
55
|
+
const cellId = `CELL-${String(cellCounter).padStart(3, '0')}`;
|
|
56
|
+
const status = STATUSES[techIndex];
|
|
57
|
+
demoCells.push({
|
|
58
|
+
// Core properties
|
|
59
|
+
id: cellId,
|
|
60
|
+
txId: `TX-${String(cellCounter).padStart(3, '0')}`,
|
|
61
|
+
cellID: cellId,
|
|
62
|
+
cellID2G: techBand.tech === '2G' ? cellId : '',
|
|
63
|
+
cellName: `Demo Cell ${techBand.tech} ${techBand.band} - Sector ${sectorIndex + 1}`,
|
|
64
|
+
siteId: SITE_ID,
|
|
65
|
+
tech: techBand.tech,
|
|
66
|
+
fband: techBand.fband,
|
|
67
|
+
frq: techBand.band,
|
|
68
|
+
type: 'MACRO',
|
|
69
|
+
status: status,
|
|
70
|
+
onAirDate: '2024-01-15',
|
|
71
|
+
// 2G specific
|
|
72
|
+
bcch: techBand.tech === '2G' ? 100 + techIndex : 0,
|
|
73
|
+
ctrlid: techBand.tech === '2G' ? `CTRL-${cellId}` : '',
|
|
74
|
+
// 4G specific
|
|
75
|
+
dlEarfn: techBand.tech === '4G' ? 6200 + techIndex * 100 : 0,
|
|
76
|
+
// Physical properties
|
|
77
|
+
antenna: 'DEMO-ANTENNA-MODEL',
|
|
78
|
+
azimuth: azimuth,
|
|
79
|
+
height: 30, // 30 meters antenna height
|
|
80
|
+
electricalTilt: '3',
|
|
81
|
+
beamwidth: BEAMWIDTH,
|
|
82
|
+
latitude: SITE_LAT,
|
|
83
|
+
longitude: SITE_LNG,
|
|
84
|
+
dx: 0,
|
|
85
|
+
dy: 0,
|
|
86
|
+
siteLatitude: SITE_LAT,
|
|
87
|
+
siteLongitude: SITE_LNG,
|
|
88
|
+
// Planning
|
|
89
|
+
comment: `Demo ${techBand.tech} ${techBand.band} cell at azimuth ${azimuth}°`,
|
|
90
|
+
planner: 'Demo User',
|
|
91
|
+
// Atoll properties
|
|
92
|
+
atollETP: 43.0,
|
|
93
|
+
atollPW: 20.0,
|
|
94
|
+
atollRS: 500.0 + (techBand.band === '700' ? 200 : 0), // Lower freq = longer range
|
|
95
|
+
atollBW: parseFloat(techBand.band) / 100, // Simplified bandwidth
|
|
96
|
+
// Network properties
|
|
97
|
+
cellId3: `${cellId}-3G`,
|
|
98
|
+
nwtP1: 20,
|
|
99
|
+
nwtP2: 40,
|
|
100
|
+
pci1: (cellCounter % 504), // Physical Cell ID for LTE
|
|
101
|
+
nwtRS: 450.0,
|
|
102
|
+
nwtBW: 10.0,
|
|
103
|
+
// Other
|
|
104
|
+
other: {
|
|
105
|
+
demoCell: true,
|
|
106
|
+
sector: sectorIndex + 1,
|
|
107
|
+
techBandKey: `${techBand.tech}_${techBand.band}`
|
|
108
|
+
},
|
|
109
|
+
customSubgroup: `Sector-${sectorIndex + 1}`
|
|
110
|
+
});
|
|
111
|
+
cellCounter++;
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
console.log(`Generated ${demoCells.length} demo cells across ${AZIMUTHS.length} sectors`);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Technology-Band Color Mappings
|
|
3
|
+
*
|
|
4
|
+
* Defines fill colors for each technology-band combination
|
|
5
|
+
*/
|
|
6
|
+
export const TECHNOLOGY_BAND_COLORS = {
|
|
7
|
+
// 2G bands
|
|
8
|
+
'2G_900': '#ffcc99',
|
|
9
|
+
'2G_1800': '#ff9a2e',
|
|
10
|
+
// 4G bands
|
|
11
|
+
'4G_700': '#ffcfff',
|
|
12
|
+
'4G_800': '#ff90ff',
|
|
13
|
+
'4G_900': '#ff66cc',
|
|
14
|
+
'4G_1800': '#ff33cc',
|
|
15
|
+
'4G_2100': '#ccae09',
|
|
16
|
+
'4G_2600': '#903399',
|
|
17
|
+
// 5G bands
|
|
18
|
+
'5G_700': '#00ff00',
|
|
19
|
+
'5G_2100': '#00cc66',
|
|
20
|
+
'5G_3500': '#009933'
|
|
21
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Radius Multipliers by Technology-Band
|
|
3
|
+
*
|
|
4
|
+
* Lower frequency = larger coverage area
|
|
5
|
+
* Higher frequency = smaller coverage area
|
|
6
|
+
*/
|
|
7
|
+
import type { TechnologyBandKey } from '../types';
|
|
8
|
+
export declare const RADIUS_MULTIPLIER: Record<TechnologyBandKey, number>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Radius Multipliers by Technology-Band
|
|
3
|
+
*
|
|
4
|
+
* Lower frequency = larger coverage area
|
|
5
|
+
* Higher frequency = smaller coverage area
|
|
6
|
+
*/
|
|
7
|
+
export const RADIUS_MULTIPLIER = {
|
|
8
|
+
// 2G bands
|
|
9
|
+
'2G_900': 1.2,
|
|
10
|
+
'2G_1800': 1.0,
|
|
11
|
+
// 4G bands
|
|
12
|
+
'4G_700': 1.5,
|
|
13
|
+
'4G_800': 1.4,
|
|
14
|
+
'4G_900': 1.1,
|
|
15
|
+
'4G_1800': 0.9,
|
|
16
|
+
'4G_2100': 0.8,
|
|
17
|
+
'4G_2600': 0.6,
|
|
18
|
+
// 5G bands
|
|
19
|
+
'5G_700': 1.4,
|
|
20
|
+
'5G_2100': 0.7,
|
|
21
|
+
'5G_3500': 0.5
|
|
22
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Status Styles
|
|
3
|
+
*
|
|
4
|
+
* Visual styling for cell sector borders based on status
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_STATUS_STYLES = {
|
|
7
|
+
On_Air: {
|
|
8
|
+
lineType: 'solid',
|
|
9
|
+
lineWidth: 2,
|
|
10
|
+
lineColor: '#000000',
|
|
11
|
+
opacity: 1.0,
|
|
12
|
+
dashArray: []
|
|
13
|
+
},
|
|
14
|
+
On_Air_UNDER_CONSTRUCTION: {
|
|
15
|
+
lineType: 'solid',
|
|
16
|
+
lineWidth: 2,
|
|
17
|
+
lineColor: '#FFA500',
|
|
18
|
+
opacity: 0.8,
|
|
19
|
+
dashArray: []
|
|
20
|
+
},
|
|
21
|
+
On_Air_Locked: {
|
|
22
|
+
lineType: 'solid',
|
|
23
|
+
lineWidth: 2,
|
|
24
|
+
lineColor: '#FF0000',
|
|
25
|
+
opacity: 0.9,
|
|
26
|
+
dashArray: []
|
|
27
|
+
},
|
|
28
|
+
RF_Plan_Ready: {
|
|
29
|
+
lineType: 'dashed',
|
|
30
|
+
lineWidth: 2,
|
|
31
|
+
lineColor: '#666666',
|
|
32
|
+
opacity: 0.6,
|
|
33
|
+
dashArray: [5, 5]
|
|
34
|
+
},
|
|
35
|
+
'Re-Planned_RF_Plan_Ready': {
|
|
36
|
+
lineType: 'dashed',
|
|
37
|
+
lineWidth: 2,
|
|
38
|
+
lineColor: '#888888',
|
|
39
|
+
opacity: 0.5,
|
|
40
|
+
dashArray: [5, 5]
|
|
41
|
+
},
|
|
42
|
+
Tavlati_RF_Plan_Ready: {
|
|
43
|
+
lineType: 'dotted',
|
|
44
|
+
lineWidth: 1,
|
|
45
|
+
lineColor: '#AAAAAA',
|
|
46
|
+
opacity: 0.4,
|
|
47
|
+
dashArray: [2, 4]
|
|
48
|
+
}
|
|
49
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
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;
|
|
@@ -0,0 +1,28 @@
|
|
|
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': 5,
|
|
10
|
+
'2G_1800': 4,
|
|
11
|
+
// 4G bands
|
|
12
|
+
'4G_700': 2,
|
|
13
|
+
'4G_800': 3,
|
|
14
|
+
'4G_900': 4,
|
|
15
|
+
'4G_1800': 8,
|
|
16
|
+
'4G_2100': 9,
|
|
17
|
+
'4G_2600': 11,
|
|
18
|
+
// 5G bands
|
|
19
|
+
'5G_700': 9,
|
|
20
|
+
'5G_2100': 10,
|
|
21
|
+
'5G_3500': 12
|
|
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;
|