@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,16 @@
1
+ export type SignalOptions = {
2
+ signal?: AbortSignal;
3
+ };
4
+ /**
5
+ * A signal that aborts as soon as any input does, with that input's reason. Already aborted if
6
+ * any input is. Undefined inputs are ignored. Call `dispose()` to drop the listeners.
7
+ */
8
+ export declare function anySignal(...signals: Array<AbortSignal | undefined>): {
9
+ signal: AbortSignal;
10
+ dispose(): void;
11
+ };
12
+ /**
13
+ * Settles like `promise`, unless `signal` aborts first, in which case it rejects with
14
+ * `signal.reason`. The inner promise's later outcome is swallowed.
15
+ */
16
+ export declare function untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * A signal that aborts as soon as any input does, with that input's reason. Already aborted if
3
+ * any input is. Undefined inputs are ignored. Call `dispose()` to drop the listeners.
4
+ */
5
+ export function anySignal(...signals) {
6
+ const controller = new AbortController();
7
+ const cleanups = [];
8
+ const dispose = () => {
9
+ for (const cleanup of cleanups.splice(0))
10
+ cleanup();
11
+ };
12
+ for (const input of signals) {
13
+ if (!input)
14
+ continue;
15
+ if (input.aborted) {
16
+ controller.abort(input.reason);
17
+ dispose();
18
+ break;
19
+ }
20
+ const onAbort = () => {
21
+ controller.abort(input.reason);
22
+ dispose();
23
+ };
24
+ input.addEventListener("abort", onAbort, { once: true });
25
+ cleanups.push(() => input.removeEventListener("abort", onAbort));
26
+ }
27
+ return { signal: controller.signal, dispose };
28
+ }
29
+ /**
30
+ * Settles like `promise`, unless `signal` aborts first, in which case it rejects with
31
+ * `signal.reason`. The inner promise's later outcome is swallowed.
32
+ */
33
+ export function untilAborted(promise, signal) {
34
+ if (!signal)
35
+ return promise;
36
+ if (signal.aborted) {
37
+ promise.catch(() => { });
38
+ return Promise.reject(signal.reason);
39
+ }
40
+ return new Promise((resolve, reject) => {
41
+ const onAbort = () => reject(signal.reason);
42
+ signal.addEventListener("abort", onAbort, { once: true });
43
+ promise.then((value) => {
44
+ signal.removeEventListener("abort", onAbort);
45
+ resolve(value);
46
+ }, (error) => {
47
+ signal.removeEventListener("abort", onAbort);
48
+ reject(error);
49
+ });
50
+ });
51
+ }
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { darken, readableColor } from "color2k";
3
+ import { NEUTRAL_LEVEL_COLOR } from "../levels";
3
4
 
4
5
  interface DataBoxProps {
5
6
  color: string;
@@ -8,7 +9,9 @@
8
9
  value: string;
9
10
  }
10
11
 
11
- const { color, header, subheading, value }: DataBoxProps = $props();
12
+ const { color: givenColor, header, subheading, value }: DataBoxProps = $props();
13
+ // An empty color (a level without one) would make color2k throw.
14
+ const color = $derived(givenColor || NEUTRAL_LEVEL_COLOR);
12
15
  const borderColor = $derived(darken(color, 0.1));
13
16
  const textColor = $derived(readableColor(color));
14
17
  </script>
@@ -16,6 +16,6 @@ type $$ComponentProps = WithoutChildrenOrChild<RangeCalendarPrimitive.RootProps>
16
16
  outsideMonth: boolean;
17
17
  }]>;
18
18
  };
19
- declare const RangeCalendar: import("svelte").Component<$$ComponentProps, {}, "value" | "ref" | "placeholder">;
19
+ declare const RangeCalendar: import("svelte").Component<$$ComponentProps, {}, "ref" | "value" | "placeholder">;
20
20
  type RangeCalendar = ReturnType<typeof RangeCalendar>;
21
21
  export default RangeCalendar;
@@ -0,0 +1,7 @@
1
+ export interface MonitorMapConfig {
2
+ maptilerKey: string;
3
+ nrelKey: string;
4
+ }
5
+ /** Hosts call this once at startup, before the map or the EV layer is used. */
6
+ export declare function configureMonitorMap(config: MonitorMapConfig): void;
7
+ export declare function monitorMapConfig(): MonitorMapConfig;
@@ -0,0 +1,10 @@
1
+ let current = null;
2
+ /** Hosts call this once at startup, before the map or the EV layer is used. */
3
+ export function configureMonitorMap(config) {
4
+ current = { ...config };
5
+ }
6
+ export function monitorMapConfig() {
7
+ if (!current)
8
+ throw new Error("monitor-map: call configureMonitorMap() first");
9
+ return current;
10
+ }
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * Light entry (`@sjvair/monitor-map/core`): what a host app needs at startup, without the map.
3
3
  *
4
- * Nothing imported here may reach @maptiler/sdk, @maptiler/weather or uplot at runtime;
4
+ * Nothing imported here may reach @maptiler/sdk, maplibre, @maptiler/weather or uplot at runtime;
5
5
  * `npm run test:bundle` enforces it. Everything is re-exported from the same modules the
6
6
  * main entry uses, so `monitorsManager` and the router context stay single instances
7
7
  * whichever entries a host mixes.
8
8
  */
9
+ export { configureMonitorMap, monitorMapConfig, type MonitorMapConfig } from "./config.svelte.js";
9
10
  export { ROUTER_KEY, provideMonitorMapRouter, useMonitorMapRouter, type MonitorMapRouterApi } from "./router-context";
10
11
  export { monitorsManager } from "./monitors/monitors.svelte.js";
11
12
  export type { MonitorsManager, MonitorsInitOptions } from "./monitors/monitors.svelte.js";
12
- export type { MonitorsDataSource, MonitorsPollutant } from "./monitors/types";
package/dist/lib/core.js CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * Light entry (`@sjvair/monitor-map/core`): what a host app needs at startup, without the map.
3
3
  *
4
- * Nothing imported here may reach @maptiler/sdk, @maptiler/weather or uplot at runtime;
4
+ * Nothing imported here may reach @maptiler/sdk, maplibre, @maptiler/weather or uplot at runtime;
5
5
  * `npm run test:bundle` enforces it. Everything is re-exported from the same modules the
6
6
  * main entry uses, so `monitorsManager` and the router context stay single instances
7
7
  * whichever entries a host mixes.
8
8
  */
9
+ export { configureMonitorMap, monitorMapConfig } from "./config.svelte.js";
9
10
  export { ROUTER_KEY, provideMonitorMapRouter, useMonitorMapRouter } from "./router-context";
10
11
  export { monitorsManager } from "./monitors/monitors.svelte.js";
@@ -7,14 +7,20 @@
7
7
  import uPlot from "uplot";
8
8
  import "uplot/dist/uPlot.min.css";
9
9
  import { getMonitorEntriesCSVUrl } from "@sjvair/sdk/monitors";
10
- import { format, parseISO } from "date-fns";
11
10
  import { RangeCalendar } from "../components/ui/range-calendar";
12
11
  import type { PanelPollutant } from "../monitors/panel-pollutant";
13
12
  import { monitorsManager } from "../monitors/monitors.svelte";
14
13
  import { DataChartManager } from "./data-chart.svelte";
15
- import { createDateRange, extendToNow, includesToday } from "./DateRange";
14
+ import {
15
+ createDateRange,
16
+ dateRangeLabel as formatDateRange,
17
+ extendToNow,
18
+ fileDay,
19
+ includesToday
20
+ } from "./DateRange";
16
21
  import { fetchChartData, type ChartMonitor } from "./requests";
17
22
  import { getChartConfig } from "./chart-utils";
23
+ import { PACIFIC_TIME_ZONE } from "./pacific-time";
18
24
  import { levelColors } from "./level-colors";
19
25
 
20
26
  interface Props {
@@ -38,11 +44,7 @@
38
44
  let dateRangeContainer: HTMLElement | undefined = $state();
39
45
  let triggerRebuild: (() => void) | undefined = $state();
40
46
 
41
- const dateRangeLabel = $derived(
42
- format(parseISO(chartManager.dateRange.start), "MMM d") +
43
- " – " +
44
- format(parseISO(chartManager.dateRange.end), "MMM d, yyyy")
45
- );
47
+ const dateRangeLabel = $derived(formatDateRange(chartManager.dateRange));
46
48
 
47
49
  // A request with no entries still resolves to [[], []], so test the x axis, not the tuple.
48
50
  const noChartData = $derived(!chartManager.chartData[0]?.length);
@@ -53,17 +55,18 @@
53
55
  ? "No Data Available For Selected Date Range"
54
56
  : ""
55
57
  );
56
- const pollutantLabel = $derived(pollutant === "pm25" ? "PM 2.5" : "Ozone");
57
- // Level definitions are shared metadata, not the map's current selection.
58
- const colors = $derived(
59
- levelColors(monitorsManager.meta?.entryType(pollutant).asIter.levels ?? [])
60
- );
61
-
62
- // Reload for another monitor or pollutant, not for a new object describing the same one.
63
- // Both go through a $derived: the owner recomputes what it passes on every refresh, and a
64
- // prop read directly would re-run this effect even when the value is unchanged.
58
+ // Props are read through a $derived: the owner recomputes what it passes on every refresh,
59
+ // and a prop read directly would re-run an effect even when the value is unchanged.
65
60
  const monitorId = $derived(monitor.id);
61
+ const monitorType = $derived(monitor.type);
66
62
  const chartPollutant = $derived(pollutant);
63
+
64
+ // Level definitions and labels are shared metadata, not the map's current selection.
65
+ const entryType = $derived(monitorsManager.meta?.entryType(chartPollutant));
66
+ const pollutantLabel = $derived(entryType?.label ?? "");
67
+ const colors = $derived(levelColors(entryType?.scale ?? null));
68
+
69
+ // Reload for another monitor or pollutant, not for a new object describing the same one.
67
70
  $effect(() => {
68
71
  void monitorId;
69
72
  void chartPollutant;
@@ -101,9 +104,15 @@
101
104
  return () => document.removeEventListener("click", handleDocClick);
102
105
  });
103
106
 
107
+ // Rebuild for new data or anything the chart draws with, and nothing else: `rebuild` runs
108
+ // untracked, so a refreshed copy of the same monitor does not recreate the chart.
104
109
  $effect(() => {
105
- void chartManager.chartData; // re-run whenever chart data updates
106
- triggerRebuild?.();
110
+ void chartManager.chartData;
111
+ void monitorType;
112
+ void colors;
113
+ void pollutantLabel;
114
+ const rebuild = triggerRebuild;
115
+ untrack(() => rebuild?.());
107
116
  });
108
117
 
109
118
  const chartAttachment: Attachment<HTMLElement> = (el) => {
@@ -121,7 +130,9 @@
121
130
  instance?.destroy?.();
122
131
  el.innerHTML = "";
123
132
  const maxDiff = Math.max(...flatData) - Math.min(...flatData);
124
- const opts = getChartConfig(monitor.type, maxDiff, width, height, colors, pollutantLabel);
133
+ const opts = getChartConfig(monitorType, maxDiff, width, height, colors, pollutantLabel);
134
+ // Axis ticks in Pacific time, like the tooltip.
135
+ opts.tzDate = (ts) => uPlot.tzDate(new Date(ts * 1000), PACIFIC_TIME_ZONE);
125
136
  instance = new uPlot(opts, chartManager.chartData, el);
126
137
  };
127
138
 
@@ -143,17 +154,36 @@
143
154
  };
144
155
 
145
156
  let chartRequest = 0;
157
+ let chartController: AbortController | undefined;
158
+
159
+ // A closed panel cancels its in-flight fetch.
160
+ $effect(() => () => chartController?.abort());
146
161
 
147
162
  // `silent` keeps the current chart on screen while a timed refresh loads.
148
163
  async function loadChartData({ silent = false } = {}) {
149
164
  const request = ++chartRequest;
150
- if (!silent) chartManager.loading = true;
151
- const data = await fetchChartData(monitor, chartManager.dateRange, pollutant);
165
+ chartController?.abort();
166
+ const controller = (chartController = new AbortController());
167
+ // A refresh that supersedes a load still in flight takes over its loading flag: the
168
+ // superseded load will never clear it.
169
+ const ownsLoading = !silent || chartManager.loading;
170
+ chartManager.loading = ownsLoading;
171
+ let data: Awaited<ReturnType<typeof fetchChartData>>;
172
+ try {
173
+ data = await fetchChartData(monitor, chartManager.dateRange, pollutant, {
174
+ signal: controller.signal
175
+ });
176
+ } catch (err) {
177
+ // Superseded or closed: the newer call owns `loading` and the chart, so leave both.
178
+ if (controller.signal.aborted || request !== chartRequest) return;
179
+ console.error("Failed to load chart data: ", err);
180
+ data = [];
181
+ }
152
182
  // A newer request (another range, monitor, pollutant or refresh) owns the chart now.
153
183
  if (request !== chartRequest) return;
154
184
  // A failed request resolves to [] (an empty range is [[], []]); a background refresh
155
185
  // that failed keeps the chart already on screen.
156
- if (silent && data.length === 0) return;
186
+ if (!ownsLoading && data.length === 0) return;
157
187
  chartManager.chartData = data;
158
188
  chartManager.loading = false;
159
189
  }
@@ -186,8 +216,8 @@
186
216
  if (!canvas) return;
187
217
  const link = document.createElement("a");
188
218
  const monitorName = monitor.name.split(" ").join("-");
189
- const start = format(parseISO(chartManager.dateRange.start), "dd.MMM.yyyy");
190
- const end = format(parseISO(chartManager.dateRange.end), "dd.MMM.yyyy");
219
+ const start = fileDay(chartManager.dateRange.start);
220
+ const end = fileDay(chartManager.dateRange.end);
191
221
  link.download = `${monitorName}_${start}-${end}.png`;
192
222
  link.href = canvas.toDataURL();
193
223
  link.click();
@@ -1,10 +1,19 @@
1
1
  import type { DateValue } from "@internationalized/date";
2
+ /** ISO instants. Days are Pacific days, whatever zone the device is in. */
2
3
  export type DateRange = {
3
4
  start: string;
4
5
  end: string;
5
6
  };
6
- export declare function createDateRange(start?: DateValue, end?: DateValue): DateRange;
7
- /** Whether the range ends on the current calendar day, i.e. it can still gain entries. */
8
- export declare function includesToday(range: DateRange): boolean;
7
+ /**
8
+ * The chart's range: with no days picked, the start of yesterday through now; otherwise the
9
+ * whole picked days, ending now when the end day is today.
10
+ */
11
+ export declare function createDateRange(start?: DateValue, end?: DateValue, now?: Date): DateRange;
12
+ /** Whether the range ends on the current Pacific day, i.e. it can still gain entries. */
13
+ export declare function includesToday(range: DateRange, now?: Date): boolean;
9
14
  /** The same range with its end moved to the present. */
10
- export declare function extendToNow(range: DateRange): DateRange;
15
+ export declare function extendToNow(range: DateRange, now?: Date): DateRange;
16
+ /** The range's Pacific days for display, e.g. "Nov 3 – Nov 4, 2026". */
17
+ export declare function dateRangeLabel(range: DateRange): string;
18
+ /** An instant's Pacific day for a file name, e.g. "04.Nov.2026". */
19
+ export declare function fileDay(iso: string): string;
@@ -1,24 +1,60 @@
1
- import { subDays, startOfDay, endOfDay, isToday, parseISO } from "date-fns";
2
- export function createDateRange(start, end) {
1
+ import { addPacificDays, endOfPacificDay, startOfPacificDay } from "@sjvair/sdk/datetime/pacific";
2
+ /** An instant on the given calendar day in Pacific time (noon UTC is 4–5 AM Pacific). */
3
+ function onPacificDay(day) {
4
+ return new Date(Date.UTC(day.year, day.month - 1, day.day, 12));
5
+ }
6
+ function samePacificDay(a, b) {
7
+ return startOfPacificDay(a).getTime() === startOfPacificDay(b).getTime();
8
+ }
9
+ /**
10
+ * The chart's range: with no days picked, the start of yesterday through now; otherwise the
11
+ * whole picked days, ending now when the end day is today.
12
+ */
13
+ export function createDateRange(start, end, now = new Date()) {
3
14
  if (!start || !end) {
4
15
  return {
5
- start: startOfDay(subDays(new Date(), 1)).toISOString(),
6
- end: new Date().toISOString()
16
+ start: startOfPacificDay(addPacificDays(now, -1)).toISOString(),
17
+ end: now.toISOString()
7
18
  };
8
19
  }
9
- const startDate = startOfDay(new Date(start.year, start.month - 1, start.day));
10
- let endDate = new Date(end.year, end.month - 1, end.day);
11
- endDate = isToday(endDate) ? new Date() : endOfDay(endDate);
20
+ const endDay = onPacificDay(end);
12
21
  return {
13
- start: startDate.toISOString(),
14
- end: endDate.toISOString()
22
+ start: startOfPacificDay(onPacificDay(start)).toISOString(),
23
+ end: (samePacificDay(endDay, now) ? now : endOfPacificDay(endDay)).toISOString()
15
24
  };
16
25
  }
17
- /** Whether the range ends on the current calendar day, i.e. it can still gain entries. */
18
- export function includesToday(range) {
19
- return isToday(parseISO(range.end));
26
+ /** Whether the range ends on the current Pacific day, i.e. it can still gain entries. */
27
+ export function includesToday(range, now = new Date()) {
28
+ return samePacificDay(new Date(range.end), now);
20
29
  }
21
30
  /** The same range with its end moved to the present. */
22
- export function extendToNow(range) {
23
- return { start: range.start, end: new Date().toISOString() };
31
+ export function extendToNow(range, now = new Date()) {
32
+ return { start: range.start, end: now.toISOString() };
33
+ }
34
+ const PACIFIC = "America/Los_Angeles";
35
+ const shortDay = new Intl.DateTimeFormat("en-US", {
36
+ timeZone: PACIFIC,
37
+ month: "short",
38
+ day: "numeric"
39
+ });
40
+ const shortDayYear = new Intl.DateTimeFormat("en-US", {
41
+ timeZone: PACIFIC,
42
+ month: "short",
43
+ day: "numeric",
44
+ year: "numeric"
45
+ });
46
+ const fileDayParts = new Intl.DateTimeFormat("en-US", {
47
+ timeZone: PACIFIC,
48
+ day: "2-digit",
49
+ month: "short",
50
+ year: "numeric"
51
+ });
52
+ /** The range's Pacific days for display, e.g. "Nov 3 – Nov 4, 2026". */
53
+ export function dateRangeLabel(range) {
54
+ return `${shortDay.format(new Date(range.start))} – ${shortDayYear.format(new Date(range.end))}`;
55
+ }
56
+ /** An instant's Pacific day for a file name, e.g. "04.Nov.2026". */
57
+ export function fileDay(iso) {
58
+ const parts = Object.fromEntries(fileDayParts.formatToParts(new Date(iso)).map((part) => [part.type, part.value]));
59
+ return `${parts.day}.${parts.month}.${parts.year}`;
24
60
  }
@@ -1,5 +1,5 @@
1
1
  import { tooltipsPlugin, uPlotCursorConfig } from "./tooltip";
2
- import { fromUnixTime, getUnixTime, isToday, isAfter } from "date-fns";
2
+ import { chartRangeMax } from "./pacific-time";
3
3
  const PM25_AVG_60_STROKE = "3572b0";
4
4
  const baseXSeriesConfig = {};
5
5
  const baseYSeriesConfig = {
@@ -46,12 +46,7 @@ export function getChartConfig(sourceName, maxDiff, width, height, colors, label
46
46
  },
47
47
  scales: {
48
48
  x: {
49
- range: (_, min, max) => {
50
- const maxDate = fromUnixTime(max);
51
- const now = new Date();
52
- max = isToday(maxDate) && isAfter(now, maxDate) ? getUnixTime(now) : max;
53
- return [min, max];
54
- }
49
+ range: (_, min, max) => [min, chartRangeMax(max)]
55
50
  },
56
51
  y: {
57
52
  range: {
@@ -96,6 +91,9 @@ function getSeriesConfigs(monitorType, stops) {
96
91
  case "airgradient":
97
92
  case "purpleair":
98
93
  return [baseXSeriesConfig, pm25SeriesConfig(stops)];
94
+ // Types without a real-time series of their own (CIMIS weather stations, from SDK 5.0).
95
+ default:
96
+ return [baseXSeriesConfig, singleSeriesConfig];
99
97
  }
100
98
  }
101
99
  function autograd(u, fallback, stops) {
@@ -1,6 +1,5 @@
1
+ import type { Scale } from "@sjvair/map-sdk/data";
1
2
  /** Color stops for a chart: each level's lower bound and its hex color without the `#`. */
2
3
  export type LevelColors = Array<[number, string]>;
3
- export declare function levelColors(levels: ReadonlyArray<{
4
- range: readonly [number, ...unknown[]] | readonly number[];
5
- color: string;
6
- }>): LevelColors;
4
+ /** The chart's color stops from a range scale; a level with no lower bound or color is skipped. */
5
+ export declare function levelColors(scale: Scale | null): LevelColors;
@@ -1,5 +1,8 @@
1
- // `range` is loose on purpose: the SDK types it as a tuple with an open tail, and only its
2
- // first member (the level's lower bound) is read.
3
- export function levelColors(levels) {
4
- return levels.map((level) => [level.range[0], level.color.replace(/^#/, "")]);
1
+ /** The chart's color stops from a range scale; a level with no lower bound or color is skipped. */
2
+ export function levelColors(scale) {
3
+ return (scale?.levels ?? []).flatMap((level) => {
4
+ if (level.min === null || !level.color)
5
+ return [];
6
+ return [[level.min, level.color.replace(/^#/, "")]];
7
+ });
5
8
  }
@@ -0,0 +1,9 @@
1
+ /** The chart's zone: times and days are Pacific, whatever zone the device is in. */
2
+ export declare const PACIFIC_TIME_ZONE = "America/Los_Angeles";
3
+ /**
4
+ * The x-axis end: `now` when the data ends earlier on the current Pacific day (so the axis
5
+ * reaches the present), else the data's end. Both are Unix seconds.
6
+ */
7
+ export declare function chartRangeMax(max: number, now?: Date): number;
8
+ /** A Unix time as Pacific wall-clock time, e.g. "Nov 4, 2026 8:05 PM". */
9
+ export declare function formatPacificTime(unixSeconds: number): string;
@@ -0,0 +1,25 @@
1
+ import { startOfPacificDay } from "@sjvair/sdk/datetime/pacific";
2
+ /** The chart's zone: times and days are Pacific, whatever zone the device is in. */
3
+ export const PACIFIC_TIME_ZONE = "America/Los_Angeles";
4
+ /**
5
+ * The x-axis end: `now` when the data ends earlier on the current Pacific day (so the axis
6
+ * reaches the present), else the data's end. Both are Unix seconds.
7
+ */
8
+ export function chartRangeMax(max, now = new Date()) {
9
+ const end = new Date(max * 1000);
10
+ const today = startOfPacificDay(end).getTime() === startOfPacificDay(now).getTime();
11
+ return today && now > end ? Math.floor(now.getTime() / 1000) : max;
12
+ }
13
+ const formatter = new Intl.DateTimeFormat("en-US", {
14
+ timeZone: PACIFIC_TIME_ZONE,
15
+ month: "short",
16
+ day: "numeric",
17
+ year: "numeric",
18
+ hour: "numeric",
19
+ minute: "2-digit"
20
+ });
21
+ /** A Unix time as Pacific wall-clock time, e.g. "Nov 4, 2026 8:05 PM". */
22
+ export function formatPacificTime(unixSeconds) {
23
+ const part = Object.fromEntries(formatter.formatToParts(new Date(unixSeconds * 1000)).map((p) => [p.type, p.value]));
24
+ return `${part.month} ${part.day}, ${part.year} ${part.hour}:${part.minute} ${part.dayPeriod}`;
25
+ }
@@ -1,6 +1,7 @@
1
1
  import { type MonitorDetails } from "@sjvair/sdk";
2
2
  import type { PanelPollutant } from "../monitors/panel-pollutant";
3
3
  import type { DateRange } from "./DateRange";
4
+ import type { SignalOptions } from "../abort";
4
5
  /** The monitor fields the chart reads. */
5
6
  export type ChartMonitor = Pick<MonitorDetails, "id" | "name" | "type" | "data_source">;
6
- export declare function fetchChartData(m: ChartMonitor, d: DateRange, entryType: PanelPollutant): Promise<uPlot.AlignedData>;
7
+ export declare function fetchChartData(m: ChartMonitor, d: DateRange, entryType: PanelPollutant, options?: SignalOptions): Promise<uPlot.AlignedData>;
@@ -1,12 +1,12 @@
1
1
  import { getMonitorEntries } from "@sjvair/sdk";
2
2
  import { getUnixTime, parseISO, differenceInMinutes, addMinutes } from "date-fns";
3
- export async function fetchChartData(m, d, entryType) {
3
+ export async function fetchChartData(m, d, entryType, options = {}) {
4
4
  return await getMonitorEntries({
5
5
  entryType,
6
6
  monitorId: m.id,
7
7
  timestampGte: d.start,
8
8
  timestampLte: d.end
9
- })
9
+ }, { signal: options.signal })
10
10
  .then((entries) => {
11
11
  const xAxisData = [];
12
12
  const updateDuration = m.data_source.name === "PurpleAir" ? 2 : 60;
@@ -26,6 +26,9 @@ export async function fetchChartData(m, d, entryType) {
26
26
  return [xAxisData.map((d) => getUnixTime(d)), yAxisRecord];
27
27
  })
28
28
  .catch((err) => {
29
+ // A cancelled request is not a failure: let the caller see the reason, log nothing.
30
+ if (options.signal?.aborted)
31
+ throw options.signal.reason;
29
32
  console.error("Failed to load chart data: ", err);
30
33
  return [];
31
34
  });
@@ -1,4 +1,4 @@
1
- import { format, fromUnixTime } from "date-fns";
1
+ import { formatPacificTime } from "./pacific-time";
2
2
  let cursLeft = -100;
3
3
  let cursTop = -100;
4
4
  export const uPlotCursorConfig = {
@@ -124,7 +124,7 @@ export function tooltipsPlugin() {
124
124
  tt.style.display = "block";
125
125
  let label = u.series[i].label;
126
126
  label = label?.substring(label.indexOf("(") + 1, label.indexOf(")")) || label;
127
- ttHeader.textContent = format(fromUnixTime(xVal), "MMM d, yyyy h:mm a");
127
+ ttHeader.textContent = formatPacificTime(xVal);
128
128
  tt.textContent = `${label}: ${yVal}`;
129
129
  }
130
130
  }
@@ -1,18 +1,19 @@
1
1
  <script lang="ts">
2
2
  import CreditCard from "@lucide/svelte/icons/credit-card";
3
3
  import Zap from "@lucide/svelte/icons/zap";
4
- import type { EvStationMapFeature } from "../types";
4
+ import type { EvStation } from "./types";
5
5
 
6
6
  interface Props {
7
- feature: EvStationMapFeature;
7
+ station: EvStation;
8
8
  }
9
9
 
10
- const { feature }: Props = $props();
10
+ const { station }: Props = $props();
11
11
 
12
+ // 4.x's station popup, from the station NREL returned (codes already turned into labels).
12
13
  const computed = $derived.by(() => {
13
- const p = feature.properties;
14
+ const p = station;
14
15
 
15
- const connectors: string[] = JSON.parse(p.ev_connector_types || "[]");
16
+ const connectors: string[] = (p.ev_connector_types ?? []).filter(Boolean);
16
17
 
17
18
  const cards: string[] = p.cards_accepted
18
19
  ? p.cards_accepted.split(",").map((c) => c.trim())
@@ -0,0 +1,7 @@
1
+ import type { EvStation } from "./types";
2
+ interface Props {
3
+ station: EvStation;
4
+ }
5
+ declare const EvPopup: import("svelte").Component<Props, {}, "">;
6
+ type EvPopup = ReturnType<typeof EvPopup>;
7
+ export default EvPopup;
@@ -0,0 +1,14 @@
1
+ import { type MapPlugin } from "@sjvair/map-sdk";
2
+ import type { DataSource } from "@sjvair/map-sdk/data";
3
+ import type { EvFeatureProperties, EvLevel } from "./ev-store.svelte.js";
4
+ export type EvPluginId = "ev-l2" | "ev-l3";
5
+ export declare const EV_PLUGIN_IDS: Record<EvLevel, EvPluginId>;
6
+ /**
7
+ * One level of EV chargers, as 4.x drew them: the level's charger icon and, clustered, bubbles in
8
+ * the level's color (radius 15/20/25 by count, with a white count) that zoom in on click. Stations
9
+ * are listed for the feature list; a click on one is a `featureclick` with a `feature` ref whose
10
+ * id is the station id (the layout opens the popup).
11
+ */
12
+ export declare function evPlugin(level: EvLevel, source: DataSource<EvFeatureProperties>, options: {
13
+ clustered: boolean;
14
+ }): MapPlugin;