wxbox/wxbox-web/src/lib/map/Map.svelte

94 lines
2.4 KiB
Svelte

<script lang="ts">
import type { ActionReturn } from 'svelte/action';
import type { TileLayer, Map as LeafletMap } from 'leaflet';
import { tilerLayerAttribution, tilerLayerUrl } from '$lib/map/layer';
import type * as Leaflet from 'leaflet';
interface Props {
map: LeafletMap | null;
selected: boolean;
baseLayer: 'osm';
dataLayer: 'noaa_mrms_merged_composite_reflectivity_qc' | null;
overlayLayers: string[];
}
let { map = $bindable(null), selected, baseLayer, dataLayer }: Props = $props();
let mapContainerElement: HTMLElement;
// await import('leaflet') done at runtime
let L: Leaflet | null = $state(null);
// Base layer - openstreetmap, carto, etc
let layer0: TileLayer;
// Data layer - composite reflectivity, velocity (the actual data)
let layer1: TileLayer;
// Layer0 (base) updating
$effect(() => {
// if leaflet hasn't been imported yet, skip
// this also sets a dependency, so we'll be re-ran once it has
if (!L) return;
if (!map) return;
// if there is already a layer0 and a map, remove the old one
if (layer0 && map) {
layer0.removeFrom(map);
}
// OpenStreetMap
if (baseLayer === 'osm') {
layer0 = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution:
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
});
layer0.addTo(map);
}
});
// Layer1 (data) updating
$effect(() => {
if (!L) return;
if (!map) return;
// remove existing layer1, if there is one
if (layer1 && map) {
layer1.removeFrom(map);
}
if (dataLayer) {
layer1 = L.tileLayer(tilerLayerUrl(dataLayer), {
attribution: tilerLayerAttribution(dataLayer)
});
layer1.addTo(map);
}
});
// ran when the div below (see use:mapAction) is created
async function mapAction(): Promise<ActionReturn> {
// dynamically imports leaflet, as it's a browser lib
L = await import('leaflet');
// imports leaflet.sync, for syncing
await import('leaflet.sync');
// create the map
map = L.map(mapContainerElement, {
// geo center of CONUS
center: [39.83, -98.583],
zoom: 5
});
if (!map) return {};
}
</script>
<div class="map" class:mapselected={selected} bind:this={mapContainerElement} use:mapAction></div>
<style>
.map {
flex: 1;
box-sizing: border-box;
border: 2px solid transparent;
}
.mapselected {
box-sizing: border-box;
border: 2px solid red;
}
</style>