@smartnet360/svelte-components 0.0.57 → 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.
Files changed (44) hide show
  1. package/dist/map-v2/core/components/ViewportSync.svelte +79 -0
  2. package/dist/map-v2/core/components/ViewportSync.svelte.d.ts +8 -0
  3. package/dist/map-v2/core/index.d.ts +2 -0
  4. package/dist/map-v2/core/index.js +3 -0
  5. package/dist/map-v2/core/stores/viewportStore.svelte.d.ts +51 -0
  6. package/dist/map-v2/core/stores/viewportStore.svelte.js +120 -0
  7. package/dist/map-v2/demo/DemoMap.svelte +30 -3
  8. package/dist/map-v2/demo/demo-cells.d.ts +12 -0
  9. package/dist/map-v2/demo/demo-cells.js +114 -0
  10. package/dist/map-v2/demo/index.d.ts +1 -0
  11. package/dist/map-v2/demo/index.js +1 -0
  12. package/dist/map-v2/features/cells/constants/colors.d.ts +7 -0
  13. package/dist/map-v2/features/cells/constants/colors.js +21 -0
  14. package/dist/map-v2/features/cells/constants/radiusMultipliers.d.ts +8 -0
  15. package/dist/map-v2/features/cells/constants/radiusMultipliers.js +22 -0
  16. package/dist/map-v2/features/cells/constants/statusStyles.d.ts +7 -0
  17. package/dist/map-v2/features/cells/constants/statusStyles.js +49 -0
  18. package/dist/map-v2/features/cells/constants/zIndex.d.ts +14 -0
  19. package/dist/map-v2/features/cells/constants/zIndex.js +28 -0
  20. package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +242 -0
  21. package/dist/map-v2/features/cells/controls/CellFilterControl.svelte.d.ts +14 -0
  22. package/dist/map-v2/features/cells/controls/CellStyleControl.svelte +139 -0
  23. package/dist/map-v2/features/cells/controls/CellStyleControl.svelte.d.ts +14 -0
  24. package/dist/map-v2/features/cells/index.d.ts +20 -0
  25. package/dist/map-v2/features/cells/index.js +24 -0
  26. package/dist/map-v2/features/cells/layers/CellsLayer.svelte +195 -0
  27. package/dist/map-v2/features/cells/layers/CellsLayer.svelte.d.ts +10 -0
  28. package/dist/map-v2/features/cells/stores/cellStoreContext.svelte.d.ts +46 -0
  29. package/dist/map-v2/features/cells/stores/cellStoreContext.svelte.js +137 -0
  30. package/dist/map-v2/features/cells/types.d.ts +99 -0
  31. package/dist/map-v2/features/cells/types.js +12 -0
  32. package/dist/map-v2/features/cells/utils/arcGeometry.d.ts +36 -0
  33. package/dist/map-v2/features/cells/utils/arcGeometry.js +55 -0
  34. package/dist/map-v2/features/cells/utils/cellGeoJSON.d.ts +22 -0
  35. package/dist/map-v2/features/cells/utils/cellGeoJSON.js +81 -0
  36. package/dist/map-v2/features/cells/utils/cellTree.d.ts +25 -0
  37. package/dist/map-v2/features/cells/utils/cellTree.js +226 -0
  38. package/dist/map-v2/features/cells/utils/techBandParser.d.ts +11 -0
  39. package/dist/map-v2/features/cells/utils/techBandParser.js +17 -0
  40. package/dist/map-v2/features/cells/utils/zoomScaling.d.ts +42 -0
  41. package/dist/map-v2/features/cells/utils/zoomScaling.js +53 -0
  42. package/dist/map-v2/index.d.ts +3 -2
  43. package/dist/map-v2/index.js +6 -2
  44. package/package.json +1 -1
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Cell Tree Builder
3
+ *
4
+ * Build hierarchical tree structure for cell filtering
5
+ * Structure: Tech → Band → Status → Individual Cells (configurable)
6
+ */
7
+ /**
8
+ * Get the value of a grouping field from a cell
9
+ */
10
+ function getGroupingValue(cell, field) {
11
+ switch (field) {
12
+ case 'tech':
13
+ return cell.tech;
14
+ case 'band':
15
+ return cell.frq; // Use numeric band from 'frq' field
16
+ case 'status':
17
+ return cell.status;
18
+ case 'siteId':
19
+ return cell.siteId;
20
+ case 'customSubgroup':
21
+ return cell.customSubgroup || 'Ungrouped';
22
+ case 'type':
23
+ return cell.type;
24
+ case 'planner':
25
+ return cell.planner || 'Unassigned';
26
+ case 'none':
27
+ return '';
28
+ default:
29
+ return 'Unknown';
30
+ }
31
+ }
32
+ /**
33
+ * Get a human-readable label for a grouping field value
34
+ */
35
+ function getGroupLabel(field, value, count) {
36
+ switch (field) {
37
+ case 'tech':
38
+ return `${value} (${count})`;
39
+ case 'band':
40
+ return `${value} MHz (${count})`;
41
+ case 'status':
42
+ return `${value} (${count})`;
43
+ case 'siteId':
44
+ return `${value} (${count})`;
45
+ case 'customSubgroup':
46
+ return `${value} (${count})`;
47
+ case 'type':
48
+ return `${value} (${count})`;
49
+ case 'planner':
50
+ return `${value} (${count})`;
51
+ default:
52
+ return `${value} (${count})`;
53
+ }
54
+ }
55
+ /**
56
+ * Generate a unique node ID based on grouping path
57
+ */
58
+ function generateNodeId(field, value, parentId) {
59
+ if (parentId) {
60
+ return `${parentId}:${value}`;
61
+ }
62
+ return value;
63
+ }
64
+ /**
65
+ * Build hierarchical tree from flat cell array with dynamic grouping
66
+ *
67
+ * @param cells - Array of cells to build tree from
68
+ * @param config - Grouping configuration (level1, level2)
69
+ * @param colorMap - Optional map of leaf group IDs to custom colors
70
+ * @returns Root tree node
71
+ */
72
+ export function buildCellTree(cells, config, colorMap) {
73
+ const { level1, level2 } = config;
74
+ // If level2 is 'none', create flat 2-level tree
75
+ if (level2 === 'none') {
76
+ return buildFlatTree(cells, level1, colorMap);
77
+ }
78
+ // Otherwise, create nested 3-level tree
79
+ return buildNestedTree(cells, level1, level2, colorMap);
80
+ }
81
+ /**
82
+ * Build flat 2-level tree (Root → Level1 groups)
83
+ */
84
+ function buildFlatTree(cells, level1, colorMap) {
85
+ // Group cells by level1 field
86
+ const groups = new Map();
87
+ cells.forEach((cell) => {
88
+ const value = getGroupingValue(cell, level1);
89
+ if (!groups.has(value)) {
90
+ groups.set(value, []);
91
+ }
92
+ groups.get(value).push(cell);
93
+ });
94
+ // Sort groups alphabetically
95
+ const sortedGroups = Array.from(groups.entries()).sort(([a], [b]) => a.localeCompare(b));
96
+ // Build tree nodes
97
+ const children = sortedGroups.map(([value, groupCells]) => {
98
+ const nodeId = generateNodeId(level1, value);
99
+ const color = colorMap?.get(nodeId);
100
+ return {
101
+ id: nodeId,
102
+ label: getGroupLabel(level1, value, groupCells.length),
103
+ defaultChecked: true,
104
+ children: [],
105
+ metadata: {
106
+ type: 'leafGroup',
107
+ groupingField: level1,
108
+ groupingValue: value,
109
+ cellIds: groupCells.map((c) => c.id),
110
+ color
111
+ }
112
+ };
113
+ });
114
+ return {
115
+ id: 'root',
116
+ label: `Cells (${cells.length})`,
117
+ defaultChecked: true,
118
+ children,
119
+ metadata: {
120
+ type: 'root'
121
+ }
122
+ };
123
+ }
124
+ /**
125
+ * Build nested 3-level tree (Root → Level1 → Level2 groups)
126
+ */
127
+ function buildNestedTree(cells, level1, level2, colorMap) {
128
+ // Group cells by level1, then by level2
129
+ const level1Groups = new Map();
130
+ cells.forEach((cell) => {
131
+ const value1 = getGroupingValue(cell, level1);
132
+ const value2 = getGroupingValue(cell, level2);
133
+ if (!level1Groups.has(value1)) {
134
+ level1Groups.set(value1, new Map());
135
+ }
136
+ const level2Groups = level1Groups.get(value1);
137
+ if (!level2Groups.has(value2)) {
138
+ level2Groups.set(value2, []);
139
+ }
140
+ level2Groups.get(value2).push(cell);
141
+ });
142
+ // Sort level1 groups
143
+ const sortedLevel1 = Array.from(level1Groups.entries()).sort(([a], [b]) => a.localeCompare(b));
144
+ // Build tree nodes
145
+ const children = sortedLevel1.map(([value1, level2Groups]) => {
146
+ const parentId = generateNodeId(level1, value1);
147
+ // Sort level2 groups
148
+ const sortedLevel2 = Array.from(level2Groups.entries()).sort(([a], [b]) => a.localeCompare(b));
149
+ const level2Children = sortedLevel2.map(([value2, groupCells]) => {
150
+ const nodeId = generateNodeId(level2, value2, parentId);
151
+ const color = colorMap?.get(nodeId);
152
+ return {
153
+ id: nodeId,
154
+ label: getGroupLabel(level2, value2, groupCells.length),
155
+ defaultChecked: true,
156
+ children: [],
157
+ metadata: {
158
+ type: 'leafGroup',
159
+ groupingField: level2,
160
+ groupingValue: value2,
161
+ parentGroupingField: level1,
162
+ parentGroupingValue: value1,
163
+ cellIds: groupCells.map((c) => c.id),
164
+ color
165
+ }
166
+ };
167
+ });
168
+ // Calculate total cells in this level1 group
169
+ const totalCells = Array.from(level2Groups.values()).flat().length;
170
+ return {
171
+ id: parentId,
172
+ label: getGroupLabel(level1, value1, totalCells),
173
+ defaultChecked: true,
174
+ children: level2Children,
175
+ metadata: {
176
+ type: 'parentGroup',
177
+ groupingField: level1,
178
+ groupingValue: value1
179
+ }
180
+ };
181
+ });
182
+ return {
183
+ id: 'root',
184
+ label: `Cells (${cells.length})`,
185
+ defaultChecked: true,
186
+ children,
187
+ metadata: {
188
+ type: 'root'
189
+ }
190
+ };
191
+ }
192
+ /**
193
+ * Get filtered cells based on checked tree paths
194
+ *
195
+ * @param checkedPaths - Set of checked node IDs from TreeView
196
+ * @param allCells - All available cells
197
+ * @returns Filtered array of cells
198
+ */
199
+ export function getFilteredCells(checkedPaths, allCells) {
200
+ // If root is checked (or no specific filters), return all cells
201
+ if (checkedPaths.has('root') && checkedPaths.size === 1) {
202
+ return allCells;
203
+ }
204
+ // Filter cells that match any of the checked paths
205
+ const filtered = allCells.filter((cell) => {
206
+ // Check if this cell belongs to any checked path
207
+ return Array.from(checkedPaths).some((path) => {
208
+ // Match cell properties against path segments
209
+ if (path === 'root')
210
+ return true;
211
+ // Parse path and check if cell matches
212
+ const segments = path.split(':');
213
+ // Simple string matching against cell properties
214
+ return segments.every((segment) => {
215
+ return (cell.tech === segment ||
216
+ cell.frq === segment ||
217
+ cell.status === segment ||
218
+ cell.siteId === segment ||
219
+ cell.customSubgroup === segment ||
220
+ cell.type === segment ||
221
+ cell.planner === segment);
222
+ });
223
+ });
224
+ });
225
+ return filtered;
226
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tech-Band Parser
3
+ *
4
+ * Parse mixed format fband strings to standardized tech-band keys
5
+ */
6
+ import type { ParsedTechBand } from '../types';
7
+ /**
8
+ * Parse fband string to tech-band components
9
+ * @example parseTechBand('LTE2600') → { tech: '4G', band: '2600', key: '4G_2600' }
10
+ */
11
+ export declare function parseTechBand(fband: string): ParsedTechBand | null;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tech-Band Parser
3
+ *
4
+ * Parse mixed format fband strings to standardized tech-band keys
5
+ */
6
+ // TODO: Implement parseTechBand
7
+ // - Handle formats: 'LTE700', 'GSM900', '5G-3500'
8
+ // - Return { tech, band, key }
9
+ // - Export helper functions
10
+ /**
11
+ * Parse fband string to tech-band components
12
+ * @example parseTechBand('LTE2600') → { tech: '4G', band: '2600', key: '4G_2600' }
13
+ */
14
+ export function parseTechBand(fband) {
15
+ // TODO: Implementation
16
+ return null;
17
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Zoom-Based Scaling
3
+ *
4
+ * Calculate radius adjustments based on zoom level
5
+ * Maintains visual size consistency across zoom levels
6
+ */
7
+ import type { TechnologyBandKey } from '../types';
8
+ /**
9
+ * Get zoom factor for visual size scaling
10
+ * Lower zoom = larger radius (to compensate for zoomed out view)
11
+ * Higher zoom = smaller radius (to compensate for zoomed in view)
12
+ *
13
+ * Formula: 2^((referenceZoom - currentZoom) / 2)
14
+ * - At zoom 12 (reference): factor = 1.0
15
+ * - At zoom 10: factor = 2.0 (double the radius)
16
+ * - At zoom 14: factor = 0.5 (half the radius)
17
+ *
18
+ * @param currentZoom - Current map zoom level
19
+ * @returns Scaling factor for radius
20
+ */
21
+ export declare function getZoomFactor(currentZoom: number): number;
22
+ /**
23
+ * Calculate final radius for a cell based on base radius, tech-band, and zoom
24
+ *
25
+ * Formula: baseRadius × techBandMultiplier × zoomFactor
26
+ *
27
+ * @param baseRadius - Base radius in meters (e.g., 500)
28
+ * @param techBand - Technology-band key (e.g., '4G_2600')
29
+ * @param currentZoom - Current map zoom level
30
+ * @returns Final radius in meters
31
+ *
32
+ * @example
33
+ * // 4G-2600 at zoom 12 with 500m base
34
+ * calculateRadius(500, '4G_2600', 12)
35
+ * // Returns: 500 × 0.6 × 1.0 = 300 meters
36
+ *
37
+ * @example
38
+ * // Same cell at zoom 10 (zoomed out)
39
+ * calculateRadius(500, '4G_2600', 10)
40
+ * // Returns: 500 × 0.6 × 2.0 = 600 meters (larger to stay visible)
41
+ */
42
+ export declare function calculateRadius(baseRadius: number, techBand: TechnologyBandKey, currentZoom: number): number;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Zoom-Based Scaling
3
+ *
4
+ * Calculate radius adjustments based on zoom level
5
+ * Maintains visual size consistency across zoom levels
6
+ */
7
+ import { RADIUS_MULTIPLIER } from '../constants/radiusMultipliers';
8
+ /**
9
+ * Reference zoom level where base radius is used as-is
10
+ * At zoom 12, radius = baseRadius * techBandMultiplier
11
+ */
12
+ const REFERENCE_ZOOM = 12;
13
+ /**
14
+ * Get zoom factor for visual size scaling
15
+ * Lower zoom = larger radius (to compensate for zoomed out view)
16
+ * Higher zoom = smaller radius (to compensate for zoomed in view)
17
+ *
18
+ * Formula: 2^((referenceZoom - currentZoom) / 2)
19
+ * - At zoom 12 (reference): factor = 1.0
20
+ * - At zoom 10: factor = 2.0 (double the radius)
21
+ * - At zoom 14: factor = 0.5 (half the radius)
22
+ *
23
+ * @param currentZoom - Current map zoom level
24
+ * @returns Scaling factor for radius
25
+ */
26
+ export function getZoomFactor(currentZoom) {
27
+ return Math.pow(2, (REFERENCE_ZOOM - currentZoom) / 2);
28
+ }
29
+ /**
30
+ * Calculate final radius for a cell based on base radius, tech-band, and zoom
31
+ *
32
+ * Formula: baseRadius × techBandMultiplier × zoomFactor
33
+ *
34
+ * @param baseRadius - Base radius in meters (e.g., 500)
35
+ * @param techBand - Technology-band key (e.g., '4G_2600')
36
+ * @param currentZoom - Current map zoom level
37
+ * @returns Final radius in meters
38
+ *
39
+ * @example
40
+ * // 4G-2600 at zoom 12 with 500m base
41
+ * calculateRadius(500, '4G_2600', 12)
42
+ * // Returns: 500 × 0.6 × 1.0 = 300 meters
43
+ *
44
+ * @example
45
+ * // Same cell at zoom 10 (zoomed out)
46
+ * calculateRadius(500, '4G_2600', 10)
47
+ * // Returns: 500 × 0.6 × 2.0 = 600 meters (larger to stay visible)
48
+ */
49
+ export function calculateRadius(baseRadius, techBand, currentZoom) {
50
+ const techBandMultiplier = RADIUS_MULTIPLIER[techBand] || 1.0;
51
+ const zoomFactor = getZoomFactor(currentZoom);
52
+ return baseRadius * techBandMultiplier * zoomFactor;
53
+ }
@@ -4,7 +4,8 @@
4
4
  * A decoupled, feature-based architecture for Mapbox cellular network visualization.
5
5
  * Each feature (sites, cells) is completely independent with its own store, layers, and controls.
6
6
  */
7
- export { type MapStore, MAP_CONTEXT_KEY, MapboxProvider, MapStyleControl, createMapStore, useMapbox, tryUseMapbox } from './core';
7
+ export { type MapStore, MAP_CONTEXT_KEY, MapboxProvider, ViewportSync, MapStyleControl, createMapStore, createViewportStore, type ViewportStore, type ViewportState, useMapbox, tryUseMapbox } from './core';
8
8
  export { MapControl, addSourceIfMissing, removeSourceIfExists, addLayerIfMissing, removeLayerIfExists, updateGeoJSONSource, removeLayerAndSource, isStyleLoaded, waitForStyleLoad, setFeatureState, removeFeatureState, generateLayerId, generateSourceId } from './shared';
9
9
  export { type Site, type SiteStoreValue, type SiteStoreContext, createSiteStore, createSiteStoreContext, SitesLayer, SiteFilterControl, SiteSizeSlider, sitesToGeoJSON, siteToFeature, buildSiteTree, getFilteredSites } from './features/sites';
10
- export { DemoMap, demoSites } from './demo';
10
+ export { type Cell, type CellStatus, type CellStatusStyle, type CellGroupingField, type CellTreeConfig, type TechnologyBandKey, type CellStoreValue, type CellStoreContext, createCellStoreContext, CellsLayer, CellFilterControl, CellStyleControl, cellsToGeoJSON, buildCellTree, getFilteredCells, calculateRadius, getZoomFactor, createArcPolygon, DEFAULT_CELL_TREE_CONFIG, TECHNOLOGY_BAND_COLORS, DEFAULT_STATUS_STYLES, RADIUS_MULTIPLIER } from './features/cells';
11
+ export { DemoMap, demoSites, demoCells } from './demo';
@@ -7,7 +7,7 @@
7
7
  // ============================================================================
8
8
  // CORE INFRASTRUCTURE
9
9
  // ============================================================================
10
- export { MAP_CONTEXT_KEY, MapboxProvider, MapStyleControl, createMapStore, useMapbox, tryUseMapbox } from './core';
10
+ export { MAP_CONTEXT_KEY, MapboxProvider, ViewportSync, MapStyleControl, createMapStore, createViewportStore, useMapbox, tryUseMapbox } from './core';
11
11
  // ============================================================================
12
12
  // SHARED UTILITIES
13
13
  // ============================================================================
@@ -17,6 +17,10 @@ export { MapControl, addSourceIfMissing, removeSourceIfExists, addLayerIfMissing
17
17
  // ============================================================================
18
18
  export { createSiteStore, createSiteStoreContext, SitesLayer, SiteFilterControl, SiteSizeSlider, sitesToGeoJSON, siteToFeature, buildSiteTree, getFilteredSites } from './features/sites';
19
19
  // ============================================================================
20
+ // CELL FEATURE
21
+ // ============================================================================
22
+ export { createCellStoreContext, CellsLayer, CellFilterControl, CellStyleControl, cellsToGeoJSON, buildCellTree, getFilteredCells, calculateRadius, getZoomFactor, createArcPolygon, DEFAULT_CELL_TREE_CONFIG, TECHNOLOGY_BAND_COLORS, DEFAULT_STATUS_STYLES, RADIUS_MULTIPLIER } from './features/cells';
23
+ // ============================================================================
20
24
  // DEMO
21
25
  // ============================================================================
22
- export { DemoMap, demoSites } from './demo';
26
+ export { DemoMap, demoSites, demoCells } from './demo';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.57",
3
+ "version": "0.0.58",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",