@sjvair/monitor-map 4.2.0 → 5.0.0-beta.0

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 (265) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -126
  3. package/dist/lib/MonitorDetailPanel.svelte +23 -20
  4. package/dist/lib/MonitorMapLayout.svelte +147 -77
  5. package/dist/lib/abort.d.ts +16 -0
  6. package/dist/lib/abort.js +51 -0
  7. package/dist/lib/components/DataBox.svelte +4 -1
  8. package/dist/lib/components/ui/range-calendar/range-calendar.svelte.d.ts +1 -1
  9. package/dist/lib/config.svelte.d.ts +7 -0
  10. package/dist/lib/config.svelte.js +10 -0
  11. package/dist/lib/core.d.ts +2 -2
  12. package/dist/lib/core.js +2 -1
  13. package/dist/lib/data-chart/DataChart.svelte +54 -24
  14. package/dist/lib/data-chart/DateRange.d.ts +13 -4
  15. package/dist/lib/data-chart/DateRange.js +50 -14
  16. package/dist/lib/data-chart/chart-utils.js +5 -7
  17. package/dist/lib/data-chart/level-colors.d.ts +3 -4
  18. package/dist/lib/data-chart/level-colors.js +7 -4
  19. package/dist/lib/data-chart/pacific-time.d.ts +9 -0
  20. package/dist/lib/data-chart/pacific-time.js +25 -0
  21. package/dist/lib/data-chart/requests.d.ts +2 -1
  22. package/dist/lib/data-chart/requests.js +5 -2
  23. package/dist/lib/data-chart/tooltip.js +2 -2
  24. package/{src/lib/ev-stations/components/EvStationTooltip.svelte → dist/lib/ev-stations/EvPopup.svelte} +6 -5
  25. package/dist/lib/ev-stations/EvPopup.svelte.d.ts +7 -0
  26. package/dist/lib/ev-stations/ev-plugin.svelte.d.ts +14 -0
  27. package/dist/lib/ev-stations/ev-plugin.svelte.js +71 -0
  28. package/dist/lib/ev-stations/ev-station-icons.d.ts +1 -4
  29. package/dist/lib/ev-stations/ev-station-icons.js +1 -13
  30. package/dist/lib/ev-stations/ev-stations.d.ts +7 -0
  31. package/dist/lib/ev-stations/ev-stations.js +53 -0
  32. package/dist/lib/ev-stations/ev-store.svelte.d.ts +21 -0
  33. package/dist/lib/ev-stations/ev-store.svelte.js +83 -0
  34. package/dist/lib/ev-stations/station-popup.svelte.d.ts +14 -0
  35. package/dist/lib/ev-stations/station-popup.svelte.js +54 -0
  36. package/dist/lib/ev-stations/types.d.ts +0 -29
  37. package/dist/lib/ev-stations/zip-codes.d.ts +11 -0
  38. package/dist/lib/ev-stations/zip-codes.js +28 -16
  39. package/dist/lib/index.d.ts +8 -56
  40. package/dist/lib/index.js +7 -46
  41. package/dist/lib/legend/Legend.svelte +18 -19
  42. package/dist/lib/legend/Legend.svelte.d.ts +2 -2
  43. package/dist/lib/levels.d.ts +7 -0
  44. package/dist/lib/levels.js +19 -0
  45. package/dist/lib/map/MapShell.svelte +17 -24
  46. package/dist/lib/map/MapShell.svelte.d.ts +4 -4
  47. package/dist/lib/map/basemaps.d.ts +25 -0
  48. package/dist/lib/map/basemaps.js +66 -0
  49. package/dist/lib/map/live-map.svelte.d.ts +3 -0
  50. package/dist/lib/map/live-map.svelte.js +22 -0
  51. package/dist/lib/map/sync-plugins.svelte.d.ts +19 -0
  52. package/dist/lib/map/sync-plugins.svelte.js +52 -0
  53. package/dist/lib/monitors/components/MonitorSubscriptionManager.svelte +82 -82
  54. package/dist/lib/monitors/filtered-source.svelte.d.ts +9 -0
  55. package/dist/lib/monitors/filtered-source.svelte.js +28 -0
  56. package/dist/lib/monitors/monitor-details.svelte.js +11 -3
  57. package/dist/lib/monitors/monitors.svelte.d.ts +29 -26
  58. package/dist/lib/monitors/monitors.svelte.js +35 -126
  59. package/dist/lib/monitors/retry-load.d.ts +8 -0
  60. package/dist/lib/monitors/retry-load.js +11 -0
  61. package/dist/lib/monitors/subscriptions.d.ts +3 -0
  62. package/dist/lib/monitors/subscriptions.js +9 -0
  63. package/dist/lib/monitors/types.d.ts +1 -41
  64. package/dist/lib/options/map-option-defaults.d.ts +25 -36
  65. package/dist/lib/options/map-option-defaults.js +12 -30
  66. package/dist/lib/options/map-options.svelte.d.ts +13 -0
  67. package/dist/lib/options/map-options.svelte.js +26 -0
  68. package/dist/lib/options/menus/LayersMenu.svelte +8 -20
  69. package/dist/lib/options/menus/LayersMenu.svelte.d.ts +17 -2
  70. package/dist/lib/options/menus/MonitorsMenu.svelte +34 -18
  71. package/dist/lib/options/menus/OverlaysMenu.svelte +2 -2
  72. package/dist/lib/options/menus/PollutantMenu.svelte +4 -9
  73. package/dist/lib/options/menus/SettingsMenu.svelte +32 -25
  74. package/dist/lib/options/menus/SettingsMenu.svelte.d.ts +5 -16
  75. package/dist/lib/options/plugin-order.d.ts +8 -0
  76. package/dist/lib/options/plugin-order.js +36 -0
  77. package/dist/lib/options/pollutant-param.d.ts +7 -5
  78. package/dist/lib/options/pollutant-param.js +9 -5
  79. package/dist/lib/options/row-icons.d.ts +8 -0
  80. package/dist/lib/options/row-icons.js +15 -0
  81. package/dist/lib/options/rows/MarkerIcon.svelte +5 -5
  82. package/dist/lib/options/rows/MarkerIcon.svelte.d.ts +1 -2
  83. package/dist/lib/search/Search.svelte +20 -33
  84. package/dist/lib/search/Search.svelte.d.ts +5 -1
  85. package/dist/lib/search/search-pin.svelte.d.ts +11 -0
  86. package/dist/lib/search/search-pin.svelte.js +28 -0
  87. package/dist/lib/search/service.d.ts +7 -2
  88. package/dist/lib/search/service.js +17 -10
  89. package/dist/lib/stores.svelte.d.ts +8 -0
  90. package/dist/lib/stores.svelte.js +9 -0
  91. package/dist/lib/wind/wind-plugin.d.ts +11 -0
  92. package/dist/lib/wind/wind-plugin.js +25 -0
  93. package/package.json +12 -10
  94. package/src/lib/MonitorDetailPanel.svelte +23 -20
  95. package/src/lib/MonitorMapLayout.svelte +147 -77
  96. package/src/lib/abort.ts +59 -0
  97. package/src/lib/components/DataBox.svelte +4 -1
  98. package/src/lib/config.svelte.ts +16 -0
  99. package/src/lib/core.ts +2 -2
  100. package/src/lib/data-chart/DataChart.svelte +54 -24
  101. package/src/lib/data-chart/DateRange.ts +58 -15
  102. package/src/lib/data-chart/chart-utils.ts +6 -7
  103. package/src/lib/data-chart/level-colors.ts +8 -9
  104. package/src/lib/data-chart/pacific-time.ts +31 -0
  105. package/src/lib/data-chart/requests.ts +14 -7
  106. package/src/lib/data-chart/tooltip.ts +2 -2
  107. package/{dist/lib/ev-stations/components/EvStationTooltip.svelte → src/lib/ev-stations/EvPopup.svelte} +6 -5
  108. package/src/lib/ev-stations/ev-plugin.svelte.ts +86 -0
  109. package/src/lib/ev-stations/ev-station-icons.ts +1 -17
  110. package/src/lib/ev-stations/ev-stations.ts +72 -0
  111. package/src/lib/ev-stations/ev-store.svelte.ts +117 -0
  112. package/src/lib/ev-stations/station-popup.svelte.ts +64 -0
  113. package/src/lib/ev-stations/types.ts +0 -34
  114. package/src/lib/ev-stations/zip-codes.ts +29 -16
  115. package/src/lib/index.ts +8 -99
  116. package/src/lib/legend/Legend.svelte +18 -19
  117. package/src/lib/levels.ts +24 -0
  118. package/src/lib/map/MapShell.svelte +17 -24
  119. package/src/lib/map/basemaps.ts +76 -0
  120. package/src/lib/map/live-map.svelte.ts +24 -0
  121. package/src/lib/map/sync-plugins.svelte.ts +69 -0
  122. package/src/lib/monitors/components/MonitorSubscriptionManager.svelte +82 -82
  123. package/src/lib/monitors/filtered-source.svelte.ts +38 -0
  124. package/src/lib/monitors/monitor-details.svelte.ts +12 -3
  125. package/src/lib/monitors/monitors.svelte.ts +62 -153
  126. package/src/lib/monitors/retry-load.ts +14 -0
  127. package/src/lib/monitors/subscriptions.ts +17 -0
  128. package/src/lib/monitors/types.ts +1 -47
  129. package/src/lib/options/map-option-defaults.ts +30 -53
  130. package/src/lib/options/map-options.svelte.ts +30 -0
  131. package/src/lib/options/menus/LayersMenu.svelte +8 -20
  132. package/src/lib/options/menus/MonitorsMenu.svelte +34 -18
  133. package/src/lib/options/menus/OverlaysMenu.svelte +2 -2
  134. package/src/lib/options/menus/PollutantMenu.svelte +4 -9
  135. package/src/lib/options/menus/SettingsMenu.svelte +32 -25
  136. package/src/lib/options/plugin-order.ts +38 -0
  137. package/src/lib/options/pollutant-param.ts +14 -9
  138. package/src/lib/options/row-icons.ts +17 -0
  139. package/src/lib/options/rows/MarkerIcon.svelte +5 -5
  140. package/src/lib/search/Search.svelte +20 -33
  141. package/src/lib/search/search-pin.svelte.ts +35 -0
  142. package/src/lib/search/service.ts +34 -15
  143. package/src/lib/stores.svelte.ts +15 -0
  144. package/src/lib/wind/wind-plugin.ts +33 -0
  145. package/dist/lib/collocation-sites/CollocationTooltip.svelte +0 -98
  146. package/dist/lib/collocation-sites/CollocationTooltip.svelte.d.ts +0 -7
  147. package/dist/lib/collocation-sites/collocations-icon-manager.svelte.d.ts +0 -7
  148. package/dist/lib/collocation-sites/collocations-icon-manager.svelte.js +0 -18
  149. package/dist/lib/collocation-sites/collocations-map-integration.svelte.d.ts +0 -21
  150. package/dist/lib/collocation-sites/collocations-map-integration.svelte.js +0 -113
  151. package/dist/lib/collocation-sites/collocations.svelte.d.ts +0 -9
  152. package/dist/lib/collocation-sites/collocations.svelte.js +0 -17
  153. package/dist/lib/collocation-sites/types.d.ts +0 -8
  154. package/dist/lib/collocation-sites/types.js +0 -1
  155. package/dist/lib/ev-stations/components/EvStationClusterTooltip.svelte +0 -16
  156. package/dist/lib/ev-stations/components/EvStationClusterTooltip.svelte.d.ts +0 -7
  157. package/dist/lib/ev-stations/components/EvStationTooltip.svelte.d.ts +0 -7
  158. package/dist/lib/ev-stations/ev-stations-cluster-renderer.d.ts +0 -19
  159. package/dist/lib/ev-stations/ev-stations-cluster-renderer.js +0 -131
  160. package/dist/lib/ev-stations/ev-stations-map-integration.svelte.d.ts +0 -31
  161. package/dist/lib/ev-stations/ev-stations-map-integration.svelte.js +0 -226
  162. package/dist/lib/ev-stations/ev-stations.svelte.d.ts +0 -9
  163. package/dist/lib/ev-stations/ev-stations.svelte.js +0 -55
  164. package/dist/lib/hms/hms-fire-icon-manager.d.ts +0 -13
  165. package/dist/lib/hms/hms-fire-icon-manager.js +0 -39
  166. package/dist/lib/hms/hms-fire-map-integration.svelte.d.ts +0 -23
  167. package/dist/lib/hms/hms-fire-map-integration.svelte.js +0 -94
  168. package/dist/lib/hms/hms-smoke-map-integration.svelte.d.ts +0 -39
  169. package/dist/lib/hms/hms-smoke-map-integration.svelte.js +0 -87
  170. package/dist/lib/hms/hms.svelte.d.ts +0 -19
  171. package/dist/lib/hms/hms.svelte.js +0 -227
  172. package/dist/lib/legend/LegendBar.svelte +0 -45
  173. package/dist/lib/legend/LegendBar.svelte.d.ts +0 -7
  174. package/dist/lib/legend/legend-data.d.ts +0 -40
  175. package/dist/lib/legend/legend-data.js +0 -65
  176. package/dist/lib/map/Map.svelte +0 -13
  177. package/dist/lib/map/Map.svelte.d.ts +0 -8
  178. package/dist/lib/map/basemap.d.ts +0 -14
  179. package/dist/lib/map/basemap.js +0 -10
  180. package/dist/lib/map/icons.d.ts +0 -7
  181. package/dist/lib/map/icons.js +0 -33
  182. package/dist/lib/map/integrations/base-layer-seperator.d.ts +0 -9
  183. package/dist/lib/map/integrations/base-layer-seperator.js +0 -11
  184. package/dist/lib/map/integrations/click-manager.d.ts +0 -14
  185. package/dist/lib/map/integrations/click-manager.js +0 -52
  186. package/dist/lib/map/integrations/integrations-manager.d.ts +0 -8
  187. package/dist/lib/map/integrations/integrations-manager.js +0 -20
  188. package/dist/lib/map/integrations/map-display-option.svelte.d.ts +0 -7
  189. package/dist/lib/map/integrations/map-display-option.svelte.js +0 -10
  190. package/dist/lib/map/integrations/map-geojson-integration.svelte.d.ts +0 -32
  191. package/dist/lib/map/integrations/map-geojson-integration.svelte.js +0 -60
  192. package/dist/lib/map/integrations/map-icon-manager.d.ts +0 -15
  193. package/dist/lib/map/integrations/map-icon-manager.js +0 -52
  194. package/dist/lib/map/integrations/map-integration.svelte.d.ts +0 -6
  195. package/dist/lib/map/integrations/map-integration.svelte.js +0 -2
  196. package/dist/lib/map/integrations/map-layer-integration.svelte.d.ts +0 -11
  197. package/dist/lib/map/integrations/map-layer-integration.svelte.js +0 -40
  198. package/dist/lib/map/integrations/tooltip.svelte.d.ts +0 -36
  199. package/dist/lib/map/integrations/tooltip.svelte.js +0 -131
  200. package/dist/lib/map/integrations/types.d.ts +0 -14
  201. package/dist/lib/map/integrations/types.js +0 -1
  202. package/dist/lib/map/map-manager.svelte.d.ts +0 -20
  203. package/dist/lib/map/map-manager.svelte.js +0 -63
  204. package/dist/lib/map/map-style-state.svelte.d.ts +0 -23
  205. package/dist/lib/map/map-style-state.svelte.js +0 -86
  206. package/dist/lib/map/utils.d.ts +0 -7
  207. package/dist/lib/map/utils.js +0 -36
  208. package/dist/lib/monitors/components/MonitorClusterTooltip.svelte +0 -112
  209. package/dist/lib/monitors/components/MonitorClusterTooltip.svelte.d.ts +0 -7
  210. package/dist/lib/monitors/components/MonitorTooltip.svelte +0 -61
  211. package/dist/lib/monitors/components/MonitorTooltip.svelte.d.ts +0 -7
  212. package/dist/lib/monitors/monitor-utils.d.ts +0 -4
  213. package/dist/lib/monitors/monitor-utils.js +0 -41
  214. package/dist/lib/monitors/monitors-cluster-renderer.d.ts +0 -33
  215. package/dist/lib/monitors/monitors-cluster-renderer.js +0 -221
  216. package/dist/lib/monitors/monitors-icon-manager.svelte.d.ts +0 -15
  217. package/dist/lib/monitors/monitors-icon-manager.svelte.js +0 -78
  218. package/dist/lib/monitors/monitors-map-integration.svelte.d.ts +0 -43
  219. package/dist/lib/monitors/monitors-map-integration.svelte.js +0 -301
  220. package/dist/lib/options/reset-map-options.d.ts +0 -2
  221. package/dist/lib/options/reset-map-options.js +0 -24
  222. package/dist/lib/reactivity.svelte.d.ts +0 -0
  223. package/dist/lib/reactivity.svelte.js +0 -83
  224. package/dist/lib/wind/wind.svelte.d.ts +0 -10
  225. package/dist/lib/wind/wind.svelte.js +0 -13
  226. package/src/lib/collocation-sites/CollocationTooltip.svelte +0 -98
  227. package/src/lib/collocation-sites/collocations-icon-manager.svelte.ts +0 -21
  228. package/src/lib/collocation-sites/collocations-map-integration.svelte.ts +0 -136
  229. package/src/lib/collocation-sites/collocations.svelte.ts +0 -24
  230. package/src/lib/collocation-sites/types.ts +0 -10
  231. package/src/lib/ev-stations/components/EvStationClusterTooltip.svelte +0 -16
  232. package/src/lib/ev-stations/ev-stations-cluster-renderer.ts +0 -162
  233. package/src/lib/ev-stations/ev-stations-map-integration.svelte.ts +0 -248
  234. package/src/lib/ev-stations/ev-stations.svelte.ts +0 -79
  235. package/src/lib/hms/hms-fire-icon-manager.ts +0 -48
  236. package/src/lib/hms/hms-fire-map-integration.svelte.ts +0 -109
  237. package/src/lib/hms/hms-smoke-map-integration.svelte.ts +0 -105
  238. package/src/lib/hms/hms.svelte.ts +0 -264
  239. package/src/lib/legend/LegendBar.svelte +0 -45
  240. package/src/lib/legend/legend-data.ts +0 -101
  241. package/src/lib/map/Map.svelte +0 -13
  242. package/src/lib/map/basemap.ts +0 -24
  243. package/src/lib/map/icons.ts +0 -42
  244. package/src/lib/map/integrations/base-layer-seperator.ts +0 -17
  245. package/src/lib/map/integrations/click-manager.ts +0 -64
  246. package/src/lib/map/integrations/integrations-manager.ts +0 -25
  247. package/src/lib/map/integrations/map-display-option.svelte.ts +0 -13
  248. package/src/lib/map/integrations/map-geojson-integration.svelte.ts +0 -77
  249. package/src/lib/map/integrations/map-icon-manager.ts +0 -67
  250. package/src/lib/map/integrations/map-integration.svelte.ts +0 -8
  251. package/src/lib/map/integrations/map-layer-integration.svelte.ts +0 -49
  252. package/src/lib/map/integrations/tooltip.svelte.ts +0 -153
  253. package/src/lib/map/integrations/types.ts +0 -24
  254. package/src/lib/map/map-manager.svelte.ts +0 -83
  255. package/src/lib/map/map-style-state.svelte.ts +0 -94
  256. package/src/lib/map/utils.ts +0 -53
  257. package/src/lib/monitors/components/MonitorClusterTooltip.svelte +0 -112
  258. package/src/lib/monitors/components/MonitorTooltip.svelte +0 -61
  259. package/src/lib/monitors/monitor-utils.ts +0 -57
  260. package/src/lib/monitors/monitors-cluster-renderer.ts +0 -285
  261. package/src/lib/monitors/monitors-icon-manager.svelte.ts +0 -98
  262. package/src/lib/monitors/monitors-map-integration.svelte.ts +0 -318
  263. package/src/lib/options/reset-map-options.ts +0 -28
  264. package/src/lib/reactivity.svelte.ts +0 -82
  265. package/src/lib/wind/wind.svelte.ts +0 -20
@@ -0,0 +1,117 @@
1
+ import type { FeatureCollection, Point } from "geojson";
2
+ import {
3
+ encodeRef,
4
+ type DataSource,
5
+ type DataStatus,
6
+ type MapFeatureProperties
7
+ } from "@sjvair/map-sdk/data";
8
+ import { fetchEvStations } from "./ev-stations";
9
+ import type { EvStation } from "./types";
10
+
11
+ export type EvLevel = "lvl2" | "lvl3";
12
+
13
+ /** A charger on the map: `name` is the station's name; chargers have no value. */
14
+ export interface EvFeatureProperties extends MapFeatureProperties {
15
+ level: EvLevel;
16
+ }
17
+
18
+ export type FetchEvStations = (level: EvLevel, signal?: AbortSignal) => Promise<EvStation[]>;
19
+
20
+ export interface EvStore {
21
+ /** One level's chargers for its plugin; `loading` until the first `load(level)`. */
22
+ dataSource(level: EvLevel): DataSource<EvFeatureProperties>;
23
+ /**
24
+ * Fetches a level once; overlapping calls share the run. A failure sets the status to `error`
25
+ * (its `retry()` fetches again) and rejects.
26
+ */
27
+ load(level: EvLevel): Promise<void>;
28
+ /** A loaded station by id, from either level. */
29
+ station(id: string): EvStation | undefined;
30
+ }
31
+
32
+ /** NREL has this station in the wrong place, so it is left out (as in 4.x). */
33
+ const DROPPED_STATION = 226154;
34
+
35
+ function toFeatures(
36
+ stations: EvStation[],
37
+ level: EvLevel
38
+ ): FeatureCollection<Point, EvFeatureProperties> {
39
+ return {
40
+ type: "FeatureCollection",
41
+ features: stations.map((station) => ({
42
+ type: "Feature",
43
+ properties: {
44
+ refKey: encodeRef({ kind: "feature", id: String(station.id) }),
45
+ name: station.station_name ?? "",
46
+ value: null,
47
+ level
48
+ },
49
+ geometry: { type: "Point", coordinates: [station.longitude, station.latitude] }
50
+ }))
51
+ };
52
+ }
53
+
54
+ class EvLevelSource implements DataSource<EvFeatureProperties> {
55
+ status: DataStatus = $state.raw({ state: "loading" });
56
+ features: FeatureCollection<Point, EvFeatureProperties> | null = $state.raw(null);
57
+ readonly scale = null;
58
+ stations: EvStation[] = [];
59
+
60
+ #run: Promise<void> | null = null;
61
+ #loaded = false;
62
+
63
+ constructor(
64
+ private level: EvLevel,
65
+ private fetchStations: FetchEvStations
66
+ ) {}
67
+
68
+ load(): Promise<void> {
69
+ if (this.#loaded) return Promise.resolve();
70
+ this.#run ??= this.#fetch();
71
+ return this.#run;
72
+ }
73
+
74
+ async #fetch(): Promise<void> {
75
+ if (this.status.state !== "loading") this.status = { state: "loading" };
76
+ try {
77
+ const stations = (await this.fetchStations(this.level)).filter(
78
+ (station) => station.id !== DROPPED_STATION
79
+ );
80
+ this.stations = stations;
81
+ this.features = toFeatures(stations, this.level);
82
+ this.status = { state: "ready" };
83
+ this.#loaded = true;
84
+ } catch (error) {
85
+ this.features = null;
86
+ this.status = {
87
+ state: "error",
88
+ error,
89
+ retry: () => {
90
+ this.load().catch(() => {});
91
+ }
92
+ };
93
+ throw error;
94
+ } finally {
95
+ this.#run = null;
96
+ }
97
+ }
98
+ }
99
+
100
+ /** EV chargers by level, each fetched when first wanted. */
101
+ export function createEvStore(fetchStations: FetchEvStations = fetchEvStations): EvStore {
102
+ const levels = {
103
+ lvl2: new EvLevelSource("lvl2", fetchStations),
104
+ lvl3: new EvLevelSource("lvl3", fetchStations)
105
+ };
106
+ return {
107
+ dataSource: (level) => levels[level],
108
+ load: (level) => levels[level].load(),
109
+ station(id) {
110
+ for (const source of Object.values(levels)) {
111
+ const found = source.stations.find((station) => String(station.id) === id);
112
+ if (found) return found;
113
+ }
114
+ return undefined;
115
+ }
116
+ };
117
+ }
@@ -0,0 +1,64 @@
1
+ import { mount, unmount, untrack } from "svelte";
2
+ import { Popup } from "@maptiler/sdk";
3
+ import { maptilerMap, type MapContext } from "@sjvair/map-sdk";
4
+ import EvPopup from "./EvPopup.svelte";
5
+ import type { EvStation } from "./types";
6
+
7
+ export interface StationPopups {
8
+ /** Opens `station`'s popup at `lngLat`, closing any other; it belongs to plugin `pluginId`. */
9
+ open(pluginId: string, station: EvStation, lngLat: [number, number]): void;
10
+ close(): void;
11
+ }
12
+
13
+ /**
14
+ * The EV station popup, 4.x's (a MapTiler `Popup` with a close button that a map click leaves
15
+ * open). One is open at a time; it closes on a basemap switch, when its plugin leaves the map,
16
+ * and when the GL map is replaced. Call it during component setup: its effects live as long as
17
+ * the component.
18
+ */
19
+ export function stationPopups(map: MapContext): StationPopups {
20
+ let current: { popup: Popup; pluginId: string } | null = $state.raw(null);
21
+
22
+ function close() {
23
+ const open = current;
24
+ current = null;
25
+ open?.popup.remove();
26
+ }
27
+
28
+ // A basemap switch or a new GL map closes it.
29
+ $effect(() => {
30
+ void map.basemap.current;
31
+ void map.gl;
32
+ untrack(close);
33
+ });
34
+
35
+ // So does its plugin leaving the map (its level turned off). A Marker Clusters flip removes and
36
+ // re-adds the plugin in one pass, so the popup stays: the station is still there.
37
+ $effect(() => {
38
+ const ids = map.plugins.map((plugin) => plugin.id);
39
+ const open = current;
40
+ if (open && !ids.includes(open.pluginId)) untrack(close);
41
+ });
42
+
43
+ $effect(() => () => untrack(close));
44
+
45
+ return {
46
+ open(pluginId, station, lngLat) {
47
+ close();
48
+ const gl = maptilerMap(map);
49
+ if (!gl) return;
50
+ const target = document.createElement("div");
51
+ const instance = mount(EvPopup, { target, props: { station } });
52
+ const popup = new Popup({ closeButton: true, closeOnClick: false, maxWidth: "none" })
53
+ .setLngLat(lngLat)
54
+ .setDOMContent(target);
55
+ popup.on("close", () => {
56
+ unmount(instance);
57
+ if (current?.popup === popup) current = null;
58
+ });
59
+ current = { popup, pluginId };
60
+ popup.addTo(gl);
61
+ },
62
+ close
63
+ };
64
+ }
@@ -1,6 +1,3 @@
1
- import type { Feature, Geometry } from "geojson";
2
- import type { MapGeoJSONFeature } from "@maptiler/sdk";
3
-
4
1
  export interface EvStation {
5
2
  access_code: string;
6
3
  access_days_time: string;
@@ -25,34 +22,3 @@ export interface EvStation {
25
22
  ev_network: string;
26
23
  ev_pricing: string;
27
24
  }
28
-
29
- export interface EvStationMarkerProperties {
30
- icon: string;
31
- id: number;
32
- level: "lvl2" | "lvl3";
33
- station_name: string;
34
- facility_type: string;
35
- ev_network: string;
36
- station_phone: string;
37
- street_address: string;
38
- city: string;
39
- state: string;
40
- zip: string;
41
- access_days_time: string;
42
- access_detail_code: string;
43
- cards_accepted: string | null;
44
- ev_pricing: string;
45
- ev_connector_types: string; // JSON.stringify(string[])
46
- }
47
-
48
- export type EvStationMapFeature = Feature<Geometry, EvStationMarkerProperties>;
49
-
50
- export interface EvStationClusterMapFeature extends MapGeoJSONFeature {
51
- properties: {
52
- cluster: true;
53
- cluster_id: number;
54
- point_count: number;
55
- point_count_abbreviated: string | number;
56
- };
57
- // level is derived from feature.source: "ev-stations-lvl2" | "ev-stations-lvl3"
58
- }
@@ -48,7 +48,6 @@ export const SanJoaquin = [
48
48
  export const Stanislaus = [
49
49
  "95355",
50
50
  "95350",
51
- "94550",
52
51
  "95351",
53
52
  "95307",
54
53
  "95380",
@@ -80,7 +79,6 @@ export const Stanislaus = [
80
79
  ];
81
80
 
82
81
  export const Merced = [
83
- "95023",
84
82
  "95380",
85
83
  "93635",
86
84
  "95301",
@@ -101,13 +99,11 @@ export const Merced = [
101
99
  "95369",
102
100
  "95303",
103
101
  "95317",
104
- "95325",
105
102
  "93665",
106
103
  "95342",
107
104
  "95344",
108
105
  "95343",
109
106
  "93661",
110
- "09534",
111
107
  "95312"
112
108
  ];
113
109
 
@@ -141,7 +137,6 @@ export const Fresno = [
141
137
  "93706",
142
138
  "93711",
143
139
  "93705",
144
- "93245",
145
140
  "93612",
146
141
  "93657",
147
142
  "93710",
@@ -189,7 +184,6 @@ export const Fresno = [
189
184
  "93652",
190
185
  "93627",
191
186
  "93606",
192
- "93665",
193
187
  "93628",
194
188
  "93605",
195
189
  "93607",
@@ -235,8 +229,7 @@ export const Fresno = [
235
229
  "93888",
236
230
  "93844",
237
231
  "93784",
238
- "93780",
239
- "93633"
232
+ "93780"
240
233
  ];
241
234
 
242
235
  export const Kings = [
@@ -247,7 +240,6 @@ export const Kings = [
247
240
  "93204",
248
241
  "93656",
249
242
  "93242",
250
- "93249",
251
243
  "93266",
252
244
  "93201",
253
245
  "93202",
@@ -288,12 +280,9 @@ export const Tulare = [
288
280
  "93238",
289
281
  "93261",
290
282
  "93641",
291
- "93255",
292
283
  "93603",
293
284
  "93244",
294
285
  "93262",
295
- "93549",
296
- "93628",
297
286
  "93208",
298
287
  "93227",
299
288
  "93237",
@@ -412,7 +401,6 @@ export const Calaveras = [
412
401
  export const Tuolumne = [
413
402
  "95370",
414
403
  "95327",
415
- "95247",
416
404
  "95383",
417
405
  "95321",
418
406
  "95379",
@@ -423,8 +411,6 @@ export const Tuolumne = [
423
411
  "95310",
424
412
  "95346",
425
413
  "95364",
426
- "95224",
427
- "93541",
428
414
  "95305",
429
415
  "95335",
430
416
  "95314",
@@ -438,7 +424,6 @@ export const Mono = ["93514", "93546", "93517", "96107", "93529", "96133", "9354
438
424
 
439
425
  export const Mariposa = [
440
426
  "95338",
441
- "95333",
442
427
  "93601",
443
428
  "95311",
444
429
  "95329",
@@ -468,3 +453,31 @@ export const Inyo = [
468
453
  "93522",
469
454
  "93530"
470
455
  ];
456
+
457
+ /**
458
+ * Every county's ZIPs once, sorted: the EV charger query's `zip` list. A ZIP that spans two
459
+ * counties is in both lists above; `scripts/check-ev-zips.mjs` checks the lists against the
460
+ * Census ZCTA-to-county file.
461
+ *
462
+ * The county lists began as a hand-scraped list. 5.0 only removes ZIPs with no land in their
463
+ * county; it adds none. Border ZIPs with some San Joaquin land (95632 Galt, 95641 Isleton,
464
+ * 94505 Discovery Bay) are left out (decided 2026-11-07): their chargers sit mostly outside
465
+ * the county. Revisit if users ask for chargers just over the county line.
466
+ */
467
+ export const EV_ZIP_CODES: readonly string[] = [
468
+ ...new Set([
469
+ ...SanJoaquin,
470
+ ...Stanislaus,
471
+ ...Merced,
472
+ ...Madera,
473
+ ...Fresno,
474
+ ...Kings,
475
+ ...Tulare,
476
+ ...Kern,
477
+ ...Calaveras,
478
+ ...Tuolumne,
479
+ ...Mono,
480
+ ...Mariposa,
481
+ ...Inyo
482
+ ])
483
+ ].sort();
package/src/lib/index.ts CHANGED
@@ -15,9 +15,11 @@
15
15
  * ```svelte
16
16
  * <!-- app root -->
17
17
  * <script>
18
- * import { provideMonitorMapRouter } from "@sjvair/monitor-map";
18
+ * import { configureMonitorMap, provideMonitorMapRouter } from "@sjvair/monitor-map";
19
19
  * import { route, navigate, p, isActive, Router } from "./router";
20
20
  *
21
+ * // Once at startup, before the map or the EV layer is used. The library reads no keys itself.
22
+ * configureMonitorMap({ maptilerKey, nrelKey });
21
23
  * provideMonitorMapRouter({ route, navigate, p, isActive, basePath: "/my-map" });
22
24
  * </script>
23
25
  *
@@ -25,111 +27,18 @@
25
27
  * ```
26
28
  *
27
29
  * A host that must stay light at startup imports from the subpath entries instead:
28
- * `provideMonitorMapRouter`/`monitorsManager` from "@sjvair/monitor-map/core" (no map), and
30
+ * `configureMonitorMap`/`provideMonitorMapRouter`/`monitorsManager` from
31
+ * "@sjvair/monitor-map/core" (no map), and
29
32
  * `monitorMapRoutes` from "@sjvair/monitor-map/routes" (same tree, lazy layout and routes).
30
33
  * `monitorMapRoutes` from this entry stays eager.
31
34
  */
32
35
  export { monitorMapRoutes } from "./routes";
36
+ export { configureMonitorMap, monitorMapConfig, type MonitorMapConfig } from "./config.svelte.js";
33
37
  export {
34
38
  ROUTER_KEY,
35
39
  provideMonitorMapRouter,
36
40
  useMonitorMapRouter,
37
41
  type MonitorMapRouterApi
38
42
  } from "./router-context";
39
-
40
- // State managers
41
- export { monitorsManager } from "./monitors/monitors.svelte";
42
- export type { MonitorsManager, MonitorsInitOptions } from "./monitors/monitors.svelte";
43
- export { collocationSitesManager } from "./collocation-sites/collocations.svelte";
44
- export type { CollocationSitesManager } from "./collocation-sites/collocations.svelte";
45
- export { evStationsManager } from "./ev-stations/ev-stations.svelte";
46
- export type { EvStationsManager } from "./ev-stations/ev-stations.svelte";
47
- export { hmsManager } from "./hms/hms.svelte";
48
- export type { HMSManager } from "./hms/hms.svelte";
49
- export { mapManager, DefaultMapStyle, initializeMap } from "./map/map-manager.svelte.js";
50
- export type { MapManager } from "./map/map-manager.svelte.js";
51
-
52
- // Map integrations
53
- export {
54
- monitorsMapIntegration,
55
- MonitorsMapIntegration
56
- } from "./monitors/monitors-map-integration.svelte";
57
- export { collocationSitesMapIntegration } from "./collocation-sites/collocations-map-integration.svelte";
58
- export { evStationsMapIntegration } from "./ev-stations/ev-stations-map-integration.svelte";
59
- export { windMapIntegration } from "./wind/wind.svelte";
60
- export { baseLayerSeperator } from "./map/integrations/base-layer-seperator";
61
- export { hmsFireMapIntegration } from "./hms/hms-fire-map-integration.svelte";
62
- export { hmsSmokeMapIntegration } from "./hms/hms-smoke-map-integration.svelte";
63
-
64
- // Components
65
- export { default as Map } from "./map/Map.svelte";
66
- export { default as MapShell, type MapShellProps } from "./map/MapShell.svelte";
67
- export {
68
- default as LoadScreen,
69
- loadScreenState,
70
- enable as enableLoadScreen,
71
- disable as disableLoadScreen
72
- } from "./LoadScreen.svelte";
73
- export type { LoadScreenState } from "./LoadScreen.svelte";
74
-
75
- // Options toolbar / panel (compose these inside MapShell's `menu` snippet)
76
- export { default as OptionsBar } from "./options/OptionsBar.svelte";
77
- export { default as OptionsGroup } from "./options/OptionsGroup.svelte";
78
- export { default as OptionsMenu } from "./options/OptionsMenu.svelte";
79
- export { default as CheckboxRow } from "./options/rows/CheckboxRow.svelte";
80
- export { default as RadioRow } from "./options/rows/RadioRow.svelte";
81
- export { default as SubmenuRow } from "./options/rows/SubmenuRow.svelte";
82
- export { default as GroupRow } from "./options/rows/GroupRow.svelte";
83
- export { default as MarkerIcon } from "./options/rows/MarkerIcon.svelte";
84
- export { default as PollutantMenu } from "./options/menus/PollutantMenu.svelte";
85
- export { default as MonitorsMenu } from "./options/menus/MonitorsMenu.svelte";
86
- export { default as LayersMenu } from "./options/menus/LayersMenu.svelte";
87
- export { default as OverlaysMenu } from "./options/menus/OverlaysMenu.svelte";
88
- export { default as SettingsMenu } from "./options/menus/SettingsMenu.svelte";
89
- export { resetMapOptions } from "./options/reset-map-options";
90
- export { mapStyleState } from "./map/map-style-state.svelte.js";
91
- export type { MapStyleState } from "./map/map-style-state.svelte.js";
92
-
93
- // Legend
94
- export { default as Legend } from "./legend/Legend.svelte";
95
- export { default as LegendBar } from "./legend/LegendBar.svelte";
96
- export {
97
- pollutantLegendSection,
98
- fireSmokeLegendSections,
99
- type LegendSection,
100
- type LegendBarData,
101
- type LegendCategory
102
- } from "./legend/legend-data";
103
- export { FIRE_LEGEND_CATEGORIES } from "./hms/hms-fire-icon-manager";
104
- export { SMOKE_LEGEND } from "./hms/hms-smoke-map-integration.svelte";
105
-
106
- // Integration base classes (extend these to add custom map features)
107
- export { MapIntegration } from "./map/integrations/map-integration.svelte";
108
- export { MapLayerIntegration } from "./map/integrations/map-layer-integration.svelte";
109
- export { MapIconLayerIntegration as MapGeoJSONIntegration } from "./map/integrations/map-geojson-integration.svelte";
110
-
111
- // Types
112
- export type { SomeMapIntegration } from "./map/integrations/types";
113
- export type { WindMapIntegration } from "./wind/wind.svelte";
114
- export type { EvStationsMapIntegration } from "./ev-stations/ev-stations-map-integration.svelte";
115
- export type { HMSFireMapIntegration } from "./hms/hms-fire-map-integration.svelte";
116
- export type { HMSSmokeMapIntegration } from "./hms/hms-smoke-map-integration.svelte";
117
- export type { HMSFireGroup } from "./hms/hms.svelte";
118
- export type {
119
- MonitorMapFeature,
120
- MonitorMarkerProperties,
121
- MonitorClusterMapFeature,
122
- MonitorClusterMarkerProperties,
123
- MonitorsDataSource,
124
- MonitorsPollutant
125
- } from "./monitors/types";
126
- export type {
127
- CollocationSiteMapFeature,
128
- CollocationSiteMarkerProperties
129
- } from "./collocation-sites/types";
130
- export type {
131
- EvStation,
132
- EvStationMarkerProperties,
133
- EvStationMapFeature,
134
- EvStationClusterMapFeature
135
- } from "./ev-stations/types";
43
+ export { monitorsManager } from "./monitors/monitors.svelte.js";
44
+ export type { MonitorsManager, MonitorsInitOptions } from "./monitors/monitors.svelte.js";
@@ -1,15 +1,25 @@
1
1
  <script lang="ts">
2
2
  import { ChevronDownIcon, InfoIcon } from "@lucide/svelte";
3
- import LegendBar from "./LegendBar.svelte";
4
- import type { LegendSection } from "./legend-data";
3
+ import { Legend, legendSection, type MapContext } from "@sjvair/map-sdk";
5
4
 
6
- let { sections }: { sections: Array<LegendSection> } = $props();
5
+ let { map }: { map: MapContext } = $props();
7
6
 
8
7
  // Independent of which sections are active, so toggling a layer doesn't reset it.
9
8
  let collapsed = $state(false);
9
+
10
+ // What map-sdk's Legend would list: a scale with levels, or a status line for a plugin that is
11
+ // loading or failing. The card is hidden while there is none.
12
+ const hasSections = $derived(
13
+ map.plugins.some((plugin) => {
14
+ const data = plugin.legend?.();
15
+ if (!data) return false;
16
+ if ("status" in data) return data.status.state !== "ready";
17
+ return legendSection(data, map.format) !== null;
18
+ })
19
+ );
10
20
  </script>
11
21
 
12
- {#if sections.length}
22
+ {#if hasSections}
13
23
  <div class="pointer-events-auto w-84 max-w-[calc(100vw-2rem)] rounded-md bg-white shadow">
14
24
  <button
15
25
  type="button"
@@ -21,20 +31,9 @@
21
31
  <span class="flex-1 text-left">Legend</span>
22
32
  <ChevronDownIcon size={18} class="transition-transform {collapsed ? '-rotate-90' : ''}" />
23
33
  </button>
24
- {#if !collapsed}
25
- <div class="flex flex-col gap-3 border-t border-gray-200 px-4 py-3">
26
- {#each sections as section (section.id)}
27
- <div>
28
- <p class="mb-2 text-center text-sm font-semibold">
29
- {section.title}
30
- {#if section.unit}
31
- <span class="text-xs font-normal text-gray-500">({section.unit})</span>
32
- {/if}
33
- </p>
34
- <LegendBar bar={section.bar} />
35
- </div>
36
- {/each}
37
- </div>
38
- {/if}
34
+ <!-- Kept mounted (hidden) when collapsed so the legend doesn't re-read the theme on expand. -->
35
+ <div class="border-t border-gray-200 px-4 py-3 {collapsed ? 'hidden' : ''}">
36
+ <Legend {map} order="top-first" framed={false} />
37
+ </div>
39
38
  </div>
40
39
  {/if}
@@ -0,0 +1,24 @@
1
+ import { levelFor, NO_LEVEL, type Level, type Scale } from "@sjvair/map-sdk/data";
2
+
3
+ /** The color for a reading no level covers, as map-sdk draws an inactive monitor. */
4
+ export const NEUTRAL_LEVEL_COLOR = "#969696";
5
+
6
+ /** The scale level a reading falls in, or null when there is no scale or no level covers it. */
7
+ export function readingLevel(
8
+ value: string | number | null | undefined,
9
+ scale: Scale | null
10
+ ): Level | null {
11
+ if (!scale) return null;
12
+ const level = levelFor(value, scale);
13
+ return level === NO_LEVEL ? null : level;
14
+ }
15
+
16
+ /** A reading as the panel shows it: cut off to a whole number, or "–" when not a number. */
17
+ export function readingDisplay(value: string | number | null | undefined): string {
18
+ // Number("") and Number(null) are 0, not a reading.
19
+ if (value === null || value === undefined || (typeof value === "string" && !value.trim())) {
20
+ return "–";
21
+ }
22
+ const number = Math.trunc(Number(value));
23
+ return Number.isFinite(number) ? number.toString() : "–";
24
+ }
@@ -1,13 +1,13 @@
1
1
  <script lang="ts" module>
2
2
  import type { Snippet } from "svelte";
3
- import type { SomeMapIntegration } from "$lib/map/integrations/types";
3
+ import type { MapContext } from "@sjvair/map-sdk";
4
4
 
5
5
  export interface MapShellProps {
6
- /** Map integrations to register, e.g. [monitorsMapIntegration] */
7
- integrations: Array<SomeMapIntegration>;
6
+ /** The map to show, from `createMap()` (the monitor map uses `createLiveMap()`). */
7
+ map: MapContext;
8
8
  /**
9
9
  * True once the caller's own data managers have finished their initial
10
- * load. The shell hides the load screen once the map is idle AND this
10
+ * load. The shell hides the load screen once the map is ready AND this
11
11
  * is true.
12
12
  */
13
13
  ready: boolean;
@@ -56,13 +56,12 @@
56
56
 
57
57
  <script lang="ts">
58
58
  import LoadScreen, { disable as disableLoadScreen } from "$lib/LoadScreen.svelte";
59
- import Map from "$lib/map/Map.svelte";
59
+ import { MapView } from "@sjvair/map-sdk";
60
60
  import OptionsBar from "$lib/options/OptionsBar.svelte";
61
- import { mapManager } from "$lib/map/map-manager.svelte.js";
62
61
  import { useMonitorMapRouter } from "$lib/router-context";
63
62
 
64
63
  let {
65
- integrations,
64
+ map,
66
65
  ready,
67
66
  loadError,
68
67
  panelOpen,
@@ -76,28 +75,14 @@
76
75
  }: MapShellProps = $props();
77
76
 
78
77
  const basePath = providedBasePath ?? (routerEscapeHatch ? useMonitorMapRouter().basePath : "");
79
- const TRANSITION_MS = 300;
80
78
 
81
79
  $effect(() => {
82
- // mapManager.map is only ever assigned once its own initial "load" has
83
- // already fired (see initializeMap in map-manager.svelte.ts), so by the time this
84
- // effect sees a non-null map, it's already safe to reveal — waiting for a
85
- // further "idle" event here is unreliable since one may never fire again.
86
- if (mapManager.map && ready) {
80
+ // `map.ready` is map-sdk's signal that the style loaded and the map went idle.
81
+ if (map.ready && ready) {
87
82
  disableLoadScreen();
88
83
  }
89
84
  });
90
85
 
91
- $effect(() => {
92
- if (panelOpen) {
93
- // Container width snapped to open; resize after layout settles
94
- requestAnimationFrame(() => mapManager.map?.resize());
95
- } else {
96
- // Container width snaps back after the transform transition ends
97
- setTimeout(() => mapManager.map?.resize(), TRANSITION_MS);
98
- }
99
- });
100
-
101
86
  /**
102
87
  * HACK: Fix for escaping sv-router and allowing navigation to other pages,
103
88
  * as well as navigating back
@@ -131,7 +116,15 @@
131
116
  <div class="relative flex h-full w-full flex-col md:flex-row">
132
117
  <LoadScreen error={loadError} />
133
118
  <div class="relative flex-1 overflow-hidden">
134
- <Map {integrations} />
119
+ <MapView
120
+ {map}
121
+ label="Air quality monitor map"
122
+ controls="engine"
123
+ featureList={{ position: "top-right", reveal: "focus" }}
124
+ selectionBar={false}
125
+ tooltip="pointer"
126
+ class="h-full w-full"
127
+ />
135
128
  {@render overlays?.()}
136
129
  {#if menu || search}
137
130
  <OptionsBar {search}>{@render menu?.()}</OptionsBar>