@sjvair/monitor-map 4.3.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.
- package/LICENSE +21 -0
- package/README.md +59 -138
- package/dist/lib/MonitorDetailPanel.svelte +23 -20
- package/dist/lib/MonitorMapLayout.svelte +147 -77
- package/dist/lib/components/DataBox.svelte +4 -1
- package/dist/lib/components/ui/range-calendar/range-calendar.svelte.d.ts +1 -1
- package/dist/lib/config.svelte.d.ts +7 -0
- package/dist/lib/config.svelte.js +10 -0
- package/dist/lib/core.d.ts +2 -2
- package/dist/lib/core.js +2 -1
- package/dist/lib/data-chart/DataChart.svelte +40 -25
- package/dist/lib/data-chart/DateRange.d.ts +13 -4
- package/dist/lib/data-chart/DateRange.js +50 -14
- package/dist/lib/data-chart/chart-utils.js +5 -7
- package/dist/lib/data-chart/level-colors.d.ts +3 -4
- package/dist/lib/data-chart/level-colors.js +7 -4
- package/dist/lib/data-chart/pacific-time.d.ts +9 -0
- package/dist/lib/data-chart/pacific-time.js +25 -0
- package/dist/lib/data-chart/tooltip.js +2 -2
- package/{src/lib/ev-stations/components/EvStationTooltip.svelte → dist/lib/ev-stations/EvPopup.svelte} +6 -5
- package/dist/lib/ev-stations/EvPopup.svelte.d.ts +7 -0
- package/dist/lib/ev-stations/ev-plugin.svelte.d.ts +14 -0
- package/dist/lib/ev-stations/ev-plugin.svelte.js +71 -0
- package/dist/lib/ev-stations/ev-station-icons.d.ts +1 -4
- package/dist/lib/ev-stations/ev-station-icons.js +1 -13
- package/dist/lib/ev-stations/ev-stations.d.ts +7 -0
- package/dist/lib/ev-stations/ev-stations.js +53 -0
- package/dist/lib/ev-stations/ev-store.svelte.d.ts +21 -0
- package/dist/lib/ev-stations/ev-store.svelte.js +83 -0
- package/dist/lib/ev-stations/station-popup.svelte.d.ts +14 -0
- package/dist/lib/ev-stations/station-popup.svelte.js +54 -0
- package/dist/lib/ev-stations/types.d.ts +0 -29
- package/dist/lib/ev-stations/zip-codes.d.ts +11 -0
- package/dist/lib/ev-stations/zip-codes.js +28 -16
- package/dist/lib/index.d.ts +8 -56
- package/dist/lib/index.js +7 -46
- package/dist/lib/legend/Legend.svelte +18 -19
- package/dist/lib/legend/Legend.svelte.d.ts +2 -2
- package/dist/lib/levels.d.ts +7 -0
- package/dist/lib/levels.js +19 -0
- package/dist/lib/map/MapShell.svelte +17 -24
- package/dist/lib/map/MapShell.svelte.d.ts +4 -4
- package/dist/lib/map/basemaps.d.ts +25 -0
- package/dist/lib/map/basemaps.js +66 -0
- package/dist/lib/map/live-map.svelte.d.ts +3 -0
- package/dist/lib/map/live-map.svelte.js +22 -0
- package/dist/lib/map/sync-plugins.svelte.d.ts +19 -0
- package/dist/lib/map/sync-plugins.svelte.js +52 -0
- package/dist/lib/monitors/components/MonitorSubscriptionManager.svelte +66 -61
- package/dist/lib/monitors/filtered-source.svelte.d.ts +9 -0
- package/dist/lib/monitors/filtered-source.svelte.js +28 -0
- package/dist/lib/monitors/monitors.svelte.d.ts +25 -35
- package/dist/lib/monitors/monitors.svelte.js +35 -218
- package/dist/lib/monitors/retry-load.d.ts +8 -0
- package/dist/lib/monitors/retry-load.js +11 -0
- package/dist/lib/monitors/types.d.ts +1 -41
- package/dist/lib/options/map-option-defaults.d.ts +25 -36
- package/dist/lib/options/map-option-defaults.js +12 -30
- package/dist/lib/options/map-options.svelte.d.ts +13 -0
- package/dist/lib/options/map-options.svelte.js +26 -0
- package/dist/lib/options/menus/LayersMenu.svelte +8 -20
- package/dist/lib/options/menus/LayersMenu.svelte.d.ts +17 -2
- package/dist/lib/options/menus/MonitorsMenu.svelte +34 -18
- package/dist/lib/options/menus/OverlaysMenu.svelte +2 -2
- package/dist/lib/options/menus/PollutantMenu.svelte +4 -9
- package/dist/lib/options/menus/SettingsMenu.svelte +32 -25
- package/dist/lib/options/menus/SettingsMenu.svelte.d.ts +5 -16
- package/dist/lib/options/plugin-order.d.ts +8 -0
- package/dist/lib/options/plugin-order.js +36 -0
- package/dist/lib/options/pollutant-param.d.ts +7 -5
- package/dist/lib/options/pollutant-param.js +9 -5
- package/dist/lib/options/row-icons.d.ts +8 -0
- package/dist/lib/options/row-icons.js +15 -0
- package/dist/lib/options/rows/MarkerIcon.svelte +5 -5
- package/dist/lib/options/rows/MarkerIcon.svelte.d.ts +1 -2
- package/dist/lib/search/Search.svelte +20 -33
- package/dist/lib/search/Search.svelte.d.ts +5 -1
- package/dist/lib/search/search-pin.svelte.d.ts +11 -0
- package/dist/lib/search/search-pin.svelte.js +28 -0
- package/dist/lib/search/service.d.ts +7 -2
- package/dist/lib/search/service.js +17 -10
- package/dist/lib/stores.svelte.d.ts +8 -0
- package/dist/lib/stores.svelte.js +9 -0
- package/dist/lib/wind/wind-plugin.d.ts +11 -0
- package/dist/lib/wind/wind-plugin.js +25 -0
- package/package.json +12 -10
- package/src/lib/MonitorDetailPanel.svelte +23 -20
- package/src/lib/MonitorMapLayout.svelte +147 -77
- package/src/lib/components/DataBox.svelte +4 -1
- package/src/lib/config.svelte.ts +16 -0
- package/src/lib/core.ts +2 -2
- package/src/lib/data-chart/DataChart.svelte +40 -25
- package/src/lib/data-chart/DateRange.ts +58 -15
- package/src/lib/data-chart/chart-utils.ts +6 -7
- package/src/lib/data-chart/level-colors.ts +8 -9
- package/src/lib/data-chart/pacific-time.ts +31 -0
- package/src/lib/data-chart/tooltip.ts +2 -2
- package/{dist/lib/ev-stations/components/EvStationTooltip.svelte → src/lib/ev-stations/EvPopup.svelte} +6 -5
- package/src/lib/ev-stations/ev-plugin.svelte.ts +86 -0
- package/src/lib/ev-stations/ev-station-icons.ts +1 -17
- package/src/lib/ev-stations/ev-stations.ts +72 -0
- package/src/lib/ev-stations/ev-store.svelte.ts +117 -0
- package/src/lib/ev-stations/station-popup.svelte.ts +64 -0
- package/src/lib/ev-stations/types.ts +0 -34
- package/src/lib/ev-stations/zip-codes.ts +29 -16
- package/src/lib/index.ts +8 -99
- package/src/lib/legend/Legend.svelte +18 -19
- package/src/lib/levels.ts +24 -0
- package/src/lib/map/MapShell.svelte +17 -24
- package/src/lib/map/basemaps.ts +76 -0
- package/src/lib/map/live-map.svelte.ts +24 -0
- package/src/lib/map/sync-plugins.svelte.ts +69 -0
- package/src/lib/monitors/components/MonitorSubscriptionManager.svelte +66 -61
- package/src/lib/monitors/filtered-source.svelte.ts +38 -0
- package/src/lib/monitors/monitors.svelte.ts +58 -261
- package/src/lib/monitors/retry-load.ts +14 -0
- package/src/lib/monitors/types.ts +1 -47
- package/src/lib/options/map-option-defaults.ts +30 -53
- package/src/lib/options/map-options.svelte.ts +30 -0
- package/src/lib/options/menus/LayersMenu.svelte +8 -20
- package/src/lib/options/menus/MonitorsMenu.svelte +34 -18
- package/src/lib/options/menus/OverlaysMenu.svelte +2 -2
- package/src/lib/options/menus/PollutantMenu.svelte +4 -9
- package/src/lib/options/menus/SettingsMenu.svelte +32 -25
- package/src/lib/options/plugin-order.ts +38 -0
- package/src/lib/options/pollutant-param.ts +14 -9
- package/src/lib/options/row-icons.ts +17 -0
- package/src/lib/options/rows/MarkerIcon.svelte +5 -5
- package/src/lib/search/Search.svelte +20 -33
- package/src/lib/search/search-pin.svelte.ts +35 -0
- package/src/lib/search/service.ts +34 -15
- package/src/lib/stores.svelte.ts +15 -0
- package/src/lib/wind/wind-plugin.ts +33 -0
- package/dist/lib/collocation-sites/CollocationTooltip.svelte +0 -98
- package/dist/lib/collocation-sites/CollocationTooltip.svelte.d.ts +0 -7
- package/dist/lib/collocation-sites/collocations-icon-manager.svelte.d.ts +0 -7
- package/dist/lib/collocation-sites/collocations-icon-manager.svelte.js +0 -18
- package/dist/lib/collocation-sites/collocations-map-integration.svelte.d.ts +0 -21
- package/dist/lib/collocation-sites/collocations-map-integration.svelte.js +0 -113
- package/dist/lib/collocation-sites/collocations.svelte.d.ts +0 -10
- package/dist/lib/collocation-sites/collocations.svelte.js +0 -21
- package/dist/lib/collocation-sites/types.d.ts +0 -8
- package/dist/lib/collocation-sites/types.js +0 -1
- package/dist/lib/ev-stations/components/EvStationClusterTooltip.svelte +0 -16
- package/dist/lib/ev-stations/components/EvStationClusterTooltip.svelte.d.ts +0 -7
- package/dist/lib/ev-stations/components/EvStationTooltip.svelte.d.ts +0 -7
- package/dist/lib/ev-stations/ev-stations-cluster-renderer.d.ts +0 -19
- package/dist/lib/ev-stations/ev-stations-cluster-renderer.js +0 -131
- package/dist/lib/ev-stations/ev-stations-map-integration.svelte.d.ts +0 -31
- package/dist/lib/ev-stations/ev-stations-map-integration.svelte.js +0 -226
- package/dist/lib/ev-stations/ev-stations.svelte.d.ts +0 -10
- package/dist/lib/ev-stations/ev-stations.svelte.js +0 -57
- package/dist/lib/hms/hms-fire-icon-manager.d.ts +0 -13
- package/dist/lib/hms/hms-fire-icon-manager.js +0 -39
- package/dist/lib/hms/hms-fire-map-integration.svelte.d.ts +0 -23
- package/dist/lib/hms/hms-fire-map-integration.svelte.js +0 -94
- package/dist/lib/hms/hms-smoke-map-integration.svelte.d.ts +0 -39
- package/dist/lib/hms/hms-smoke-map-integration.svelte.js +0 -87
- package/dist/lib/hms/hms.svelte.d.ts +0 -20
- package/dist/lib/hms/hms.svelte.js +0 -237
- package/dist/lib/legend/LegendBar.svelte +0 -45
- package/dist/lib/legend/LegendBar.svelte.d.ts +0 -7
- package/dist/lib/legend/legend-data.d.ts +0 -40
- package/dist/lib/legend/legend-data.js +0 -65
- package/dist/lib/map/Map.svelte +0 -13
- package/dist/lib/map/Map.svelte.d.ts +0 -8
- package/dist/lib/map/basemap.d.ts +0 -14
- package/dist/lib/map/basemap.js +0 -10
- package/dist/lib/map/icons.d.ts +0 -7
- package/dist/lib/map/icons.js +0 -33
- package/dist/lib/map/integrations/base-layer-seperator.d.ts +0 -9
- package/dist/lib/map/integrations/base-layer-seperator.js +0 -11
- package/dist/lib/map/integrations/click-manager.d.ts +0 -14
- package/dist/lib/map/integrations/click-manager.js +0 -52
- package/dist/lib/map/integrations/integrations-manager.d.ts +0 -8
- package/dist/lib/map/integrations/integrations-manager.js +0 -20
- package/dist/lib/map/integrations/map-display-option.svelte.d.ts +0 -7
- package/dist/lib/map/integrations/map-display-option.svelte.js +0 -10
- package/dist/lib/map/integrations/map-geojson-integration.svelte.d.ts +0 -32
- package/dist/lib/map/integrations/map-geojson-integration.svelte.js +0 -60
- package/dist/lib/map/integrations/map-icon-manager.d.ts +0 -15
- package/dist/lib/map/integrations/map-icon-manager.js +0 -52
- package/dist/lib/map/integrations/map-integration.svelte.d.ts +0 -6
- package/dist/lib/map/integrations/map-integration.svelte.js +0 -2
- package/dist/lib/map/integrations/map-layer-integration.svelte.d.ts +0 -11
- package/dist/lib/map/integrations/map-layer-integration.svelte.js +0 -40
- package/dist/lib/map/integrations/tooltip.svelte.d.ts +0 -36
- package/dist/lib/map/integrations/tooltip.svelte.js +0 -131
- package/dist/lib/map/integrations/types.d.ts +0 -14
- package/dist/lib/map/integrations/types.js +0 -1
- package/dist/lib/map/map-manager.svelte.d.ts +0 -20
- package/dist/lib/map/map-manager.svelte.js +0 -63
- package/dist/lib/map/map-style-state.svelte.d.ts +0 -23
- package/dist/lib/map/map-style-state.svelte.js +0 -86
- package/dist/lib/map/utils.d.ts +0 -7
- package/dist/lib/map/utils.js +0 -36
- package/dist/lib/monitors/components/MonitorClusterTooltip.svelte +0 -112
- package/dist/lib/monitors/components/MonitorClusterTooltip.svelte.d.ts +0 -7
- package/dist/lib/monitors/components/MonitorTooltip.svelte +0 -61
- package/dist/lib/monitors/components/MonitorTooltip.svelte.d.ts +0 -7
- package/dist/lib/monitors/monitor-utils.d.ts +0 -4
- package/dist/lib/monitors/monitor-utils.js +0 -41
- package/dist/lib/monitors/monitors-cluster-renderer.d.ts +0 -33
- package/dist/lib/monitors/monitors-cluster-renderer.js +0 -221
- package/dist/lib/monitors/monitors-icon-manager.svelte.d.ts +0 -15
- package/dist/lib/monitors/monitors-icon-manager.svelte.js +0 -78
- package/dist/lib/monitors/monitors-map-integration.svelte.d.ts +0 -43
- package/dist/lib/monitors/monitors-map-integration.svelte.js +0 -301
- package/dist/lib/options/reset-map-options.d.ts +0 -2
- package/dist/lib/options/reset-map-options.js +0 -24
- package/dist/lib/reactivity.svelte.d.ts +0 -0
- package/dist/lib/reactivity.svelte.js +0 -83
- package/dist/lib/wind/wind.svelte.d.ts +0 -10
- package/dist/lib/wind/wind.svelte.js +0 -13
- package/src/lib/collocation-sites/CollocationTooltip.svelte +0 -98
- package/src/lib/collocation-sites/collocations-icon-manager.svelte.ts +0 -21
- package/src/lib/collocation-sites/collocations-map-integration.svelte.ts +0 -136
- package/src/lib/collocation-sites/collocations.svelte.ts +0 -27
- package/src/lib/collocation-sites/types.ts +0 -10
- package/src/lib/ev-stations/components/EvStationClusterTooltip.svelte +0 -16
- package/src/lib/ev-stations/ev-stations-cluster-renderer.ts +0 -162
- package/src/lib/ev-stations/ev-stations-map-integration.svelte.ts +0 -248
- package/src/lib/ev-stations/ev-stations.svelte.ts +0 -84
- package/src/lib/hms/hms-fire-icon-manager.ts +0 -48
- package/src/lib/hms/hms-fire-map-integration.svelte.ts +0 -109
- package/src/lib/hms/hms-smoke-map-integration.svelte.ts +0 -105
- package/src/lib/hms/hms.svelte.ts +0 -270
- package/src/lib/legend/LegendBar.svelte +0 -45
- package/src/lib/legend/legend-data.ts +0 -101
- package/src/lib/map/Map.svelte +0 -13
- package/src/lib/map/basemap.ts +0 -24
- package/src/lib/map/icons.ts +0 -42
- package/src/lib/map/integrations/base-layer-seperator.ts +0 -17
- package/src/lib/map/integrations/click-manager.ts +0 -64
- package/src/lib/map/integrations/integrations-manager.ts +0 -25
- package/src/lib/map/integrations/map-display-option.svelte.ts +0 -13
- package/src/lib/map/integrations/map-geojson-integration.svelte.ts +0 -77
- package/src/lib/map/integrations/map-icon-manager.ts +0 -67
- package/src/lib/map/integrations/map-integration.svelte.ts +0 -8
- package/src/lib/map/integrations/map-layer-integration.svelte.ts +0 -49
- package/src/lib/map/integrations/tooltip.svelte.ts +0 -153
- package/src/lib/map/integrations/types.ts +0 -24
- package/src/lib/map/map-manager.svelte.ts +0 -83
- package/src/lib/map/map-style-state.svelte.ts +0 -94
- package/src/lib/map/utils.ts +0 -53
- package/src/lib/monitors/components/MonitorClusterTooltip.svelte +0 -112
- package/src/lib/monitors/components/MonitorTooltip.svelte +0 -61
- package/src/lib/monitors/monitor-utils.ts +0 -57
- package/src/lib/monitors/monitors-cluster-renderer.ts +0 -285
- package/src/lib/monitors/monitors-icon-manager.svelte.ts +0 -98
- package/src/lib/monitors/monitors-map-integration.svelte.ts +0 -318
- package/src/lib/options/reset-map-options.ts +0 -28
- package/src/lib/reactivity.svelte.ts +0 -82
- package/src/lib/wind/wind.svelte.ts +0 -20
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
2
|
+
/** A marker image from its SVG markup (e.g. map-sdk's `circle`). */
|
|
3
|
+
let { svg, class: className = "w-4" }: { svg: string | undefined; class?: string } = $props();
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
$props();
|
|
5
|
+
const src = $derived(svg ? `data:image/svg+xml;utf8,${encodeURIComponent(svg)}` : undefined);
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
|
-
{#if
|
|
9
|
-
<img
|
|
8
|
+
{#if src}
|
|
9
|
+
<img {src} alt="" class={className} />
|
|
10
10
|
{/if}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onDestroy } from "svelte";
|
|
3
|
-
import { Marker } from "@maptiler/sdk";
|
|
4
2
|
import { MapPinIcon, SearchIcon } from "@lucide/svelte";
|
|
3
|
+
import type { MapContext } from "@sjvair/map-sdk";
|
|
5
4
|
import { useMonitorMapRouter } from "../router-context";
|
|
6
|
-
import { mapManager } from "../map/map-manager.svelte.js";
|
|
7
5
|
import { debounce } from "../utils";
|
|
6
|
+
import { searchPin } from "./search-pin.svelte.js";
|
|
8
7
|
import { monitorSearch, geocode, type SearchResult } from "./service";
|
|
9
|
-
import { monitorsMapIntegration } from "../monitors/monitors-map-integration.svelte";
|
|
10
8
|
|
|
9
|
+
interface SearchProps {
|
|
10
|
+
map: MapContext;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { map }: SearchProps = $props();
|
|
11
14
|
const { navigate, basePath } = useMonitorMapRouter();
|
|
12
15
|
|
|
13
16
|
let collapsed = $state(true);
|
|
14
17
|
let searchText = $state("");
|
|
15
18
|
let results: SearchResult[] = $state([]);
|
|
16
|
-
|
|
19
|
+
// The layout passes one MapContext for the widget's lifetime.
|
|
20
|
+
// svelte-ignore state_referenced_locally
|
|
21
|
+
const pin = searchPin(map);
|
|
17
22
|
let inputEl: HTMLInputElement | null = $state(null);
|
|
18
23
|
|
|
19
24
|
function clickOutside(node: HTMLElement) {
|
|
@@ -42,16 +47,14 @@
|
|
|
42
47
|
function clearSearch() {
|
|
43
48
|
searchText = "";
|
|
44
49
|
results = [];
|
|
45
|
-
|
|
46
|
-
marker = null;
|
|
50
|
+
pin.clear();
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
async function handleInput() {
|
|
50
54
|
const query = searchText;
|
|
51
55
|
if (!query) {
|
|
52
56
|
results = [];
|
|
53
|
-
|
|
54
|
-
marker = null;
|
|
57
|
+
pin.clear();
|
|
55
58
|
return;
|
|
56
59
|
}
|
|
57
60
|
const monitorResults = await monitorSearch(query);
|
|
@@ -73,40 +76,24 @@
|
|
|
73
76
|
collapsed = true;
|
|
74
77
|
searchText = "";
|
|
75
78
|
results = [];
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (mapManager.map && result.monitor.position) {
|
|
79
|
+
pin.clear();
|
|
80
|
+
if (result.monitor.position) {
|
|
79
81
|
const { coordinates } = result.monitor.position;
|
|
80
|
-
|
|
81
|
-
mapManager.map.flyTo({
|
|
82
|
-
center,
|
|
83
|
-
zoom: 12
|
|
84
|
-
});
|
|
82
|
+
map.flyTo({ center: [coordinates[0], coordinates[1]], zoom: 12 });
|
|
85
83
|
}
|
|
86
84
|
await navigate(`${basePath}/monitor/:id`, { params: { id: result.monitor.id } });
|
|
87
|
-
monitorsMapIntegration.selectedMonitorId = result.monitor.id;
|
|
88
85
|
}
|
|
89
86
|
|
|
90
87
|
function selectGeocode(result: Extract<SearchResult, { type: "geocode" }>) {
|
|
91
88
|
const { feature } = result;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const isAddress = feature.place_type?.[0] === "address";
|
|
98
|
-
mapManager.map.flyTo({
|
|
99
|
-
center,
|
|
100
|
-
...(isAddress && { zoom: 16 })
|
|
101
|
-
});
|
|
102
|
-
}
|
|
89
|
+
pin.clear();
|
|
90
|
+
const center: [number, number] = [feature.center[0], feature.center[1]];
|
|
91
|
+
pin.place(center);
|
|
92
|
+
const isAddress = feature.place_type?.[0] === "address";
|
|
93
|
+
map.flyTo({ center, ...(isAddress && { zoom: 16 }) });
|
|
103
94
|
results = [];
|
|
104
95
|
collapsed = true;
|
|
105
96
|
}
|
|
106
|
-
|
|
107
|
-
onDestroy(() => {
|
|
108
|
-
marker?.remove();
|
|
109
|
-
});
|
|
110
97
|
</script>
|
|
111
98
|
|
|
112
99
|
<div
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import type { MapContext } from "@sjvair/map-sdk";
|
|
2
|
+
interface SearchProps {
|
|
3
|
+
map: MapContext;
|
|
4
|
+
}
|
|
5
|
+
declare const Search: import("svelte").Component<SearchProps, {}, "">;
|
|
2
6
|
type Search = ReturnType<typeof Search>;
|
|
3
7
|
export default Search;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type MapContext } from "@sjvair/map-sdk";
|
|
2
|
+
export interface SearchPin {
|
|
3
|
+
place(center: [number, number]): void;
|
|
4
|
+
clear(): void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The pin a geocoded place leaves on the map. It follows the map: when the GL map is replaced
|
|
8
|
+
* (a basemap or style rebuild), the pin leaves the old one and is added to the new one. Call it
|
|
9
|
+
* during component init; the pin is removed when the component is destroyed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function searchPin(map: MapContext): SearchPin;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Marker } from "@maptiler/sdk";
|
|
2
|
+
import { maptilerMap } from "@sjvair/map-sdk";
|
|
3
|
+
/**
|
|
4
|
+
* The pin a geocoded place leaves on the map. It follows the map: when the GL map is replaced
|
|
5
|
+
* (a basemap or style rebuild), the pin leaves the old one and is added to the new one. Call it
|
|
6
|
+
* during component init; the pin is removed when the component is destroyed.
|
|
7
|
+
*/
|
|
8
|
+
export function searchPin(map) {
|
|
9
|
+
let center = $state.raw(null);
|
|
10
|
+
$effect(() => {
|
|
11
|
+
const gl = maptilerMap(map);
|
|
12
|
+
const at = center;
|
|
13
|
+
if (!gl || !at)
|
|
14
|
+
return;
|
|
15
|
+
const marker = new Marker().setLngLat(at).addTo(gl);
|
|
16
|
+
return () => {
|
|
17
|
+
marker.remove();
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
place(next) {
|
|
22
|
+
center = next;
|
|
23
|
+
},
|
|
24
|
+
clear() {
|
|
25
|
+
center = null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type GeocodingFeature } from "@maptiler/sdk";
|
|
2
|
-
import type {
|
|
2
|
+
import type { MonitorData } from "@sjvair/sdk";
|
|
3
3
|
export type MonitorResult = {
|
|
4
4
|
type: "monitor";
|
|
5
|
-
monitor:
|
|
5
|
+
monitor: MonitorData;
|
|
6
6
|
logo: {
|
|
7
7
|
url: string;
|
|
8
8
|
alt: string;
|
|
@@ -13,5 +13,10 @@ export type GeocodeResult = {
|
|
|
13
13
|
feature: GeocodingFeature;
|
|
14
14
|
};
|
|
15
15
|
export type SearchResult = MonitorResult | GeocodeResult;
|
|
16
|
+
/** Monitors whose name matches `query`: exact, then prefix, then substring; at most 4. */
|
|
17
|
+
export declare function searchMonitors(query: string, monitors: Iterable<MonitorData>, latest: ReadonlyMap<string, {
|
|
18
|
+
value: number | null;
|
|
19
|
+
}>): MonitorData[];
|
|
16
20
|
export declare function monitorSearch(query: string): Promise<SearchResult[]>;
|
|
21
|
+
export declare function geocodeQuery(query: string, proximity: [number, number], key: string): Promise<SearchResult[]>;
|
|
17
22
|
export declare function geocode(query: string): Promise<SearchResult[]>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { geocoding } from "@maptiler/sdk";
|
|
2
|
-
import {
|
|
2
|
+
import { monitorMapConfig } from "../config.svelte.js";
|
|
3
|
+
import { monitorsStore } from "../stores.svelte.js";
|
|
3
4
|
import AirGradientLogo from "../assets/logos/airgradient-blue.svg";
|
|
4
5
|
import AirNowLogo from "../assets/logos/airnow-compact.jpg";
|
|
5
6
|
import CarbLogo from "../assets/logos/carb-compact.jpg";
|
|
@@ -43,14 +44,16 @@ function resolveProximity() {
|
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return [];
|
|
47
|
+
/** Monitors whose name matches `query`: exact, then prefix, then substring; at most 4. */
|
|
48
|
+
export function searchMonitors(query, monitors, latest) {
|
|
49
49
|
const normalized = query.toLowerCase();
|
|
50
50
|
const exact = [];
|
|
51
51
|
const prefix = [];
|
|
52
52
|
const substr = [];
|
|
53
|
-
for
|
|
53
|
+
// Only monitors with a reading for the current pollutant, as the map shows them.
|
|
54
|
+
for (const monitor of monitors) {
|
|
55
|
+
if (latest.get(monitor.id)?.value == null)
|
|
56
|
+
continue;
|
|
54
57
|
const name = monitor.name.toLowerCase();
|
|
55
58
|
if (name === normalized)
|
|
56
59
|
exact.push(monitor);
|
|
@@ -59,14 +62,15 @@ export async function monitorSearch(query) {
|
|
|
59
62
|
else if (name.includes(normalized))
|
|
60
63
|
substr.push(monitor);
|
|
61
64
|
}
|
|
62
|
-
return [...exact, ...prefix, ...substr]
|
|
63
|
-
.slice(0, 4)
|
|
64
|
-
.map((monitor) => ({ type: "monitor", monitor, logo: getMonitorLogo(monitor) }));
|
|
65
|
+
return [...exact, ...prefix, ...substr].slice(0, 4);
|
|
65
66
|
}
|
|
66
|
-
export async function
|
|
67
|
-
|
|
67
|
+
export async function monitorSearch(query) {
|
|
68
|
+
return searchMonitors(query, monitorsStore.monitors.values(), monitorsStore.latest).map((monitor) => ({ type: "monitor", monitor, logo: getMonitorLogo(monitor) }));
|
|
69
|
+
}
|
|
70
|
+
export async function geocodeQuery(query, proximity, key) {
|
|
68
71
|
try {
|
|
69
72
|
const result = await geocoding.forward(query, {
|
|
73
|
+
apiKey: key,
|
|
70
74
|
country: ["us"],
|
|
71
75
|
fuzzyMatch: true,
|
|
72
76
|
limit: 4,
|
|
@@ -78,3 +82,6 @@ export async function geocode(query) {
|
|
|
78
82
|
return [];
|
|
79
83
|
}
|
|
80
84
|
}
|
|
85
|
+
export async function geocode(query) {
|
|
86
|
+
return geocodeQuery(query, await getProximity(), monitorMapConfig().maptilerKey);
|
|
87
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app-wide data stores, created once. This module imports only `@sjvair/map-sdk/stores`, so
|
|
3
|
+
* the light `/core` entry can reach it without loading the map engine.
|
|
4
|
+
*/
|
|
5
|
+
export declare const monitorsStore: import("@sjvair/map-sdk/stores").MonitorsStore;
|
|
6
|
+
export declare const hmsSmokeStore: import("@sjvair/map-sdk/stores").HmsSmokeStore;
|
|
7
|
+
export declare const hmsFireStore: import("@sjvair/map-sdk/stores").HmsFireStore;
|
|
8
|
+
export declare const collocationSitesStore: import("@sjvair/map-sdk/stores").CollocationSitesStore;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createCollocationSitesStore, createHmsFireStore, createHmsSmokeStore, createMonitorsStore } from "@sjvair/map-sdk/stores";
|
|
2
|
+
/**
|
|
3
|
+
* The app-wide data stores, created once. This module imports only `@sjvair/map-sdk/stores`, so
|
|
4
|
+
* the light `/core` entry can reach it without loading the map engine.
|
|
5
|
+
*/
|
|
6
|
+
export const monitorsStore = createMonitorsStore();
|
|
7
|
+
export const hmsSmokeStore = createHmsSmokeStore();
|
|
8
|
+
export const hmsFireStore = createHmsFireStore();
|
|
9
|
+
export const collocationSitesStore = createCollocationSitesStore({ monitors: monitorsStore });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CustomLayerInterface } from "@maptiler/sdk";
|
|
2
|
+
import type { MapPlugin } from "@sjvair/map-sdk";
|
|
3
|
+
export interface WindPluginOptions {
|
|
4
|
+
/** Builds the layer; runs again on each restyle. Defaults to a `@maptiler/weather` WindLayer. */
|
|
5
|
+
createLayer?: (id: string) => CustomLayerInterface;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Wind particles as a map-sdk custom layer. `@maptiler/weather` can't be a map-sdk dependency, so
|
|
9
|
+
* the host adds it here; only the layout chunk reaches this module.
|
|
10
|
+
*/
|
|
11
|
+
export declare function windPlugin(options?: WindPluginOptions): MapPlugin;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { config } from "@maptiler/sdk";
|
|
2
|
+
import { WindLayer } from "@maptiler/weather";
|
|
3
|
+
import { monitorMapConfig } from "../config.svelte.js";
|
|
4
|
+
// The weather layer fetches its data with the SDK's global key, which map-sdk (it passes the key
|
|
5
|
+
// to the map only) never sets.
|
|
6
|
+
const defaultCreateLayer = (id) => {
|
|
7
|
+
config.apiKey = monitorMapConfig().maptilerKey;
|
|
8
|
+
return new WindLayer({ id, opacity: 0.5 });
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Wind particles as a map-sdk custom layer. `@maptiler/weather` can't be a map-sdk dependency, so
|
|
12
|
+
* the host adds it here; only the layout chunk reaches this module.
|
|
13
|
+
*/
|
|
14
|
+
export function windPlugin(options = {}) {
|
|
15
|
+
const createLayer = options.createLayer ?? defaultCreateLayer;
|
|
16
|
+
return {
|
|
17
|
+
id: "wind",
|
|
18
|
+
label: "Wind",
|
|
19
|
+
selectable: false,
|
|
20
|
+
setup(ctx) {
|
|
21
|
+
ctx.addCustomLayer("wind", createLayer);
|
|
22
|
+
return () => { };
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjvair/monitor-map",
|
|
3
3
|
"description": "SJVAir's interactive monitor map",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0-beta.0",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
8
|
"url": "https://github.com/SJVAir/monitor-map"
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
"format": "prettier --write .",
|
|
18
19
|
"lint": "prettier --check . && eslint .",
|
|
19
20
|
"test": "vitest run",
|
|
21
|
+
"map-sdk:pack": "bash scripts/pack-map-sdk.sh",
|
|
20
22
|
"test:bundle": "npm run build:lib && node tests/bundle/check-bundle.mjs"
|
|
21
23
|
},
|
|
22
24
|
"type": "module",
|
|
@@ -48,24 +50,28 @@
|
|
|
48
50
|
],
|
|
49
51
|
"peerDependencies": {
|
|
50
52
|
"@internationalized/date": "^3.12.4",
|
|
53
|
+
"@sjvair/sdk": "npm:@jsr/sjvair__sdk@^5.0.0-beta.3",
|
|
51
54
|
"@sveltejs/enhanced-img": "^0.11.0 || ^1.0.0",
|
|
52
55
|
"bits-ui": "^2.19.2",
|
|
53
56
|
"sv-router": "^0.19.0",
|
|
54
|
-
"svelte": "^5.
|
|
57
|
+
"svelte": "^5.57.0"
|
|
55
58
|
},
|
|
56
59
|
"devDependencies": {
|
|
57
60
|
"@eslint/compat": "^2.1.1",
|
|
58
61
|
"@eslint/js": "^10.0.1",
|
|
59
62
|
"@fontsource-variable/inter": "^5.3.0",
|
|
63
|
+
"@sjvair/sdk": "npm:@jsr/sjvair__sdk@5.0.0-beta.3",
|
|
60
64
|
"@sveltejs/enhanced-img": "^1.0.0",
|
|
61
65
|
"@sveltejs/package": "^2.5.8",
|
|
62
66
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
63
67
|
"@tailwindcss/vite": "^4.3.3",
|
|
64
68
|
"@types/node": "^26.6.0",
|
|
69
|
+
"@vitest/browser-playwright": "^5.1.0",
|
|
65
70
|
"eslint": "^10.10.0",
|
|
66
71
|
"eslint-config-prettier": "^10.1.8",
|
|
67
72
|
"eslint-plugin-svelte": "^3.23.0",
|
|
68
73
|
"globals": "^17.12.0",
|
|
74
|
+
"playwright": "^1.65.0",
|
|
69
75
|
"postcss": "^8.5.28",
|
|
70
76
|
"prettier": "^3.9.6",
|
|
71
77
|
"prettier-plugin-svelte": "^4.1.1",
|
|
@@ -78,18 +84,14 @@
|
|
|
78
84
|
"typescript": "^6.0.3",
|
|
79
85
|
"typescript-eslint": "^8.70.0",
|
|
80
86
|
"vite": "^8.3.0",
|
|
81
|
-
"vitest": "^5.0.3"
|
|
87
|
+
"vitest": "^5.0.3",
|
|
88
|
+
"vitest-browser-svelte": "^3.1.0"
|
|
82
89
|
},
|
|
83
90
|
"dependencies": {
|
|
84
91
|
"@lucide/svelte": "^1.46.0",
|
|
85
|
-
"@maptiler/sdk": "
|
|
92
|
+
"@maptiler/sdk": "~4.2.0",
|
|
86
93
|
"@maptiler/weather": "^4.0.1",
|
|
87
|
-
"@sjvair/sdk": "
|
|
88
|
-
"@tstk/builtin-extensions": "npm:@jsr/tstk__builtin-extensions@^1.0.8",
|
|
89
|
-
"@tstk/utils": "npm:@jsr/tstk__utils@^1.1.2",
|
|
90
|
-
"@turf/area": "^7.4.0",
|
|
91
|
-
"@turf/clusters-dbscan": "^7.4.0",
|
|
92
|
-
"@turf/helpers": "^7.4.0",
|
|
94
|
+
"@sjvair/map-sdk": "0.1.0-beta.6",
|
|
93
95
|
"@types/geojson": "^7946.0.16",
|
|
94
96
|
"clsx": "^2.1.1",
|
|
95
97
|
"color2k": "^2.0.4",
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { untrack, type ComponentProps } from "svelte";
|
|
3
|
-
import type { SJVAirEntryLevel } from "@sjvair/sdk";
|
|
4
3
|
import DataBox from "$lib/components/DataBox.svelte";
|
|
5
4
|
import LoadError from "$lib/LoadError.svelte";
|
|
6
5
|
import MonitorWidgetModal from "$lib/monitors/components/MonitorWidgetModal.svelte";
|
|
7
6
|
import MonitorSubscriptionManager from "$lib/monitors/components/MonitorSubscriptionManager.svelte";
|
|
8
7
|
import DataChart from "$lib/data-chart/DataChart.svelte";
|
|
9
|
-
import { monitorsManager } from "$lib";
|
|
8
|
+
import { monitorsManager } from "$lib/monitors/monitors.svelte.js";
|
|
10
9
|
import { MonitorDetailsState } from "$lib/monitors/monitor-details.svelte";
|
|
11
|
-
import { choosePanelPollutant } from "$lib/monitors/panel-pollutant";
|
|
10
|
+
import { choosePanelPollutant, PANEL_POLLUTANTS } from "$lib/monitors/panel-pollutant";
|
|
12
11
|
import { useMonitorMapRouter } from "./router-context";
|
|
13
12
|
import { fetchTempByCoords } from "$lib/weather";
|
|
14
13
|
import { Colors, valueToColor } from "$lib/colors";
|
|
15
|
-
import {
|
|
14
|
+
import { NEUTRAL_LEVEL_COLOR, readingDisplay, readingLevel } from "$lib/levels";
|
|
16
15
|
|
|
17
16
|
type TempDataBox = Pick<ComponentProps<typeof DataBox>, "color" | "value">;
|
|
18
17
|
|
|
@@ -28,6 +27,10 @@
|
|
|
28
27
|
// current reading for the map's pollutant.
|
|
29
28
|
const details = new MonitorDetailsState();
|
|
30
29
|
const meta = $derived(monitorsManager.meta);
|
|
30
|
+
// The map's pollutant, when it is one the panel can show.
|
|
31
|
+
const mapPollutant = $derived(
|
|
32
|
+
PANEL_POLLUTANTS.find((p) => p === monitorsManager.pollutant) ?? null
|
|
33
|
+
);
|
|
31
34
|
// Shown only once the metadata is in too; labels and levels come from it.
|
|
32
35
|
const monitor = $derived(details.status === "ready" && meta ? details.monitor : null);
|
|
33
36
|
|
|
@@ -42,14 +45,15 @@
|
|
|
42
45
|
// Bumped whenever the map's readings are replaced, i.e. its 2-minute refresh ran. The data
|
|
43
46
|
// box follows it; the chart decides for itself whether its date range can have new entries.
|
|
44
47
|
let refreshTick = $state(0);
|
|
45
|
-
let seenLatest = untrack(() => monitorsManager.latest);
|
|
48
|
+
let seenLatest = untrack(() => monitorsManager.store.latest);
|
|
46
49
|
|
|
47
50
|
$effect(() => {
|
|
48
|
-
const latest = monitorsManager.latest;
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
+
const latest = monitorsManager.store.latest;
|
|
52
|
+
// Empty before the first load and while a pollutant change is loading; wait for the
|
|
53
|
+
// readings that follow.
|
|
54
|
+
if (latest.size === 0 || latest === seenLatest) return;
|
|
51
55
|
// The first readings of a cold load arrive alongside this panel's own request.
|
|
52
|
-
const first = seenLatest ===
|
|
56
|
+
const first = seenLatest.size === 0;
|
|
53
57
|
seenLatest = latest;
|
|
54
58
|
if (first) return;
|
|
55
59
|
untrack(() => {
|
|
@@ -59,14 +63,13 @@
|
|
|
59
63
|
});
|
|
60
64
|
|
|
61
65
|
const panel = $derived(
|
|
62
|
-
monitor && meta ? choosePanelPollutant(
|
|
66
|
+
monitor && meta ? choosePanelPollutant(mapPollutant, monitor, meta) : null
|
|
63
67
|
);
|
|
64
68
|
const reading = $derived(
|
|
65
69
|
monitor && panel?.pollutant && panel.hasReading ? monitor.latest[panel.pollutant] : undefined
|
|
66
70
|
);
|
|
67
71
|
// Set when the panel shows a different pollutant than the map is coloring by.
|
|
68
72
|
const pollutantNote = $derived.by(() => {
|
|
69
|
-
const mapPollutant = monitorsManager.pollutant;
|
|
70
73
|
if (!meta || !panel?.pollutant || !panel.hasReading || !mapPollutant) return;
|
|
71
74
|
if (panel.pollutant === mapPollutant) return;
|
|
72
75
|
const shown = meta.entryType(panel.pollutant).label;
|
|
@@ -81,18 +84,15 @@
|
|
|
81
84
|
if (!monitor || !meta || !panel?.pollutant || !reading) return;
|
|
82
85
|
|
|
83
86
|
const entryType = meta.entryType(panel.pollutant);
|
|
84
|
-
const level
|
|
85
|
-
reading.value,
|
|
86
|
-
entryType.asIter.levels!
|
|
87
|
-
);
|
|
87
|
+
const level = readingLevel(reading.value, entryType.scale);
|
|
88
88
|
|
|
89
89
|
const updateDuration = meta.monitorType(monitor.type).expected_interval;
|
|
90
90
|
|
|
91
91
|
return {
|
|
92
|
-
color: level?.color ??
|
|
92
|
+
color: level?.color ?? NEUTRAL_LEVEL_COLOR,
|
|
93
93
|
header: entryType.label,
|
|
94
94
|
subheading: `(${updateDuration} avg)`,
|
|
95
|
-
value:
|
|
95
|
+
value: readingDisplay(reading.value)
|
|
96
96
|
};
|
|
97
97
|
});
|
|
98
98
|
|
|
@@ -201,9 +201,12 @@
|
|
|
201
201
|
</div>
|
|
202
202
|
{/if}
|
|
203
203
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
<!-- The widget shows current readings, and errors for an inactive monitor. -->
|
|
205
|
+
{#if panel.hasReading}
|
|
206
|
+
<div class="tall:block hidden">
|
|
207
|
+
<MonitorWidgetModal monitorId={monitor.id} />
|
|
208
|
+
</div>
|
|
209
|
+
{/if}
|
|
207
210
|
<!-- eslint-disable-next-line @typescript-eslint/no-explicit-any -->
|
|
208
211
|
{#if (globalThis as any).USER?.is_authenticated}
|
|
209
212
|
<MonitorSubscriptionManager monitorId={monitor.id} />
|