fix overlay opacity

This commit is contained in:
vcoppe
2024-09-23 18:26:01 +02:00
parent 45bfac4f88
commit ea3d10fcc3

View File

@@ -1,219 +1,231 @@
<script lang="ts"> <script lang="ts">
import CustomControl from '$lib/components/custom-control/CustomControl.svelte'; import CustomControl from '$lib/components/custom-control/CustomControl.svelte';
import LayerTree from './LayerTree.svelte'; import LayerTree from './LayerTree.svelte';
import { Separator } from '$lib/components/ui/separator'; import { Separator } from '$lib/components/ui/separator';
import { ScrollArea } from '$lib/components/ui/scroll-area/index.js'; import { ScrollArea } from '$lib/components/ui/scroll-area/index.js';
import { Layers } from 'lucide-svelte'; import { Layers } from 'lucide-svelte';
import { basemaps, defaultBasemap, overlays } from '$lib/assets/layers'; import { basemaps, defaultBasemap, overlays } from '$lib/assets/layers';
import { settings } from '$lib/db'; import { settings } from '$lib/db';
import { map } from '$lib/stores'; import { map } from '$lib/stores';
import { get, writable } from 'svelte/store'; import { get, writable } from 'svelte/store';
import { customBasemapUpdate, getLayers } from './utils'; import { customBasemapUpdate, getLayers } from './utils';
import { OverpassLayer } from './OverpassLayer'; import { OverpassLayer } from './OverpassLayer';
import OverpassPopup from './OverpassPopup.svelte'; import OverpassPopup from './OverpassPopup.svelte';
let container: HTMLDivElement; let container: HTMLDivElement;
let overpassLayer: OverpassLayer; let overpassLayer: OverpassLayer;
const { const {
currentBasemap, currentBasemap,
previousBasemap, previousBasemap,
currentOverlays, currentOverlays,
currentOverpassQueries, currentOverpassQueries,
selectedBasemapTree, selectedBasemapTree,
selectedOverlayTree, selectedOverlayTree,
selectedOverpassTree, selectedOverpassTree,
customLayers, customLayers,
opacities opacities
} = settings; } = settings;
function setStyle() { function setStyle() {
if ($map) { if ($map) {
let basemap = basemaps.hasOwnProperty($currentBasemap) let basemap = basemaps.hasOwnProperty($currentBasemap)
? basemaps[$currentBasemap] ? basemaps[$currentBasemap]
: $customLayers[$currentBasemap]?.value ?? basemaps[defaultBasemap]; : $customLayers[$currentBasemap]?.value ?? basemaps[defaultBasemap];
$map.removeImport('basemap'); $map.removeImport('basemap');
if (typeof basemap === 'string') { if (typeof basemap === 'string') {
$map.addImport({ id: 'basemap', url: basemap }, 'overlays'); $map.addImport({ id: 'basemap', url: basemap }, 'overlays');
} else { } else {
$map.addImport( $map.addImport(
{ {
id: 'basemap', id: 'basemap',
data: basemap data: basemap
}, },
'overlays' 'overlays'
); );
} }
} }
} }
$: if ($map && ($currentBasemap || $customBasemapUpdate)) { $: if ($map && ($currentBasemap || $customBasemapUpdate)) {
setStyle(); setStyle();
} }
function addOverlay(id: string) { function addOverlay(id: string) {
try { try {
let overlay = $customLayers.hasOwnProperty(id) ? $customLayers[id].value : overlays[id]; let overlay = $customLayers.hasOwnProperty(id) ? $customLayers[id].value : overlays[id];
if (typeof overlay === 'string') { if (typeof overlay === 'string') {
$map.addImport({ id, url: overlay }); $map.addImport({ id, url: overlay });
} else { } else {
$map.addImport({ if ($opacities.hasOwnProperty(id)) {
id, overlay = {
data: overlay ...overlay,
}); layers: overlay.layers.map((layer) => {
} if (layer.type === 'raster') {
} catch (e) { if (!layer.paint) {
// No reliable way to check if the map is ready to add sources and layers layer.paint = {};
} }
} layer.paint['raster-opacity'] = $opacities[id];
}
return layer;
})
};
}
$map.addImport({
id,
data: overlay
});
}
} catch (e) {
// No reliable way to check if the map is ready to add sources and layers
}
}
function updateOverlays() { function updateOverlays() {
if ($map && $currentOverlays) { if ($map && $currentOverlays) {
let overlayLayers = getLayers($currentOverlays); let overlayLayers = getLayers($currentOverlays);
try { try {
let activeOverlays = $map let activeOverlays = $map
.getStyle() .getStyle()
.imports.filter((i) => i.id !== 'basemap' && i.id !== 'overlays'); .imports.filter((i) => i.id !== 'basemap' && i.id !== 'overlays');
let toRemove = activeOverlays.filter((i) => !overlayLayers[i.id]); let toRemove = activeOverlays.filter((i) => !overlayLayers[i.id]);
toRemove.forEach((i) => { toRemove.forEach((i) => {
$map.removeImport(i.id); $map.removeImport(i.id);
}); });
let toAdd = Object.entries(overlayLayers) let toAdd = Object.entries(overlayLayers)
.filter( .filter(([id, selected]) => selected && !activeOverlays.some((j) => j.id === id))
([id, selected]) => selected && !activeOverlays.some((j) => j.id === id) .map(([id]) => id);
) toAdd.forEach((id) => {
.map(([id]) => id); addOverlay(id);
toAdd.forEach((id) => { });
addOverlay(id); } catch (e) {
}); // No reliable way to check if the map is ready to add sources and layers
} catch (e) { }
// No reliable way to check if the map is ready to add sources and layers }
} }
}
}
$: if ($map && $currentOverlays) { $: if ($map && $currentOverlays) {
updateOverlays(); updateOverlays();
} }
$: if ($map) { $: if ($map) {
if (overpassLayer) { if (overpassLayer) {
overpassLayer.remove(); overpassLayer.remove();
} }
overpassLayer = new OverpassLayer($map); overpassLayer = new OverpassLayer($map);
overpassLayer.add(); overpassLayer.add();
$map.on('style.import.load', updateOverlays); $map.on('style.import.load', updateOverlays);
} }
let selectedBasemap = writable(get(currentBasemap)); let selectedBasemap = writable(get(currentBasemap));
selectedBasemap.subscribe((value) => { selectedBasemap.subscribe((value) => {
// Updates coming from radio buttons // Updates coming from radio buttons
if (value !== get(currentBasemap)) { if (value !== get(currentBasemap)) {
previousBasemap.set(get(currentBasemap)); previousBasemap.set(get(currentBasemap));
currentBasemap.set(value); currentBasemap.set(value);
} }
}); });
currentBasemap.subscribe((value) => { currentBasemap.subscribe((value) => {
// Updates coming from the database, or from the user swapping basemaps // Updates coming from the database, or from the user swapping basemaps
selectedBasemap.set(value); selectedBasemap.set(value);
}); });
function removeOverlayLayer(id: string) { function removeOverlayLayer(id: string) {
if ($map) { if ($map) {
let overlay = $customLayers.hasOwnProperty(id) ? $customLayers[id].value : overlays[id]; let overlay = $customLayers.hasOwnProperty(id) ? $customLayers[id].value : overlays[id];
if (overlay.layers) { if (overlay.layers) {
$map.removeImport(id); $map.removeImport(id);
} else { } else {
$map.removeLayer(id); $map.removeLayer(id);
} }
} }
} }
let open = false; let open = false;
function openLayerControl() { function openLayerControl() {
open = true; open = true;
} }
function closeLayerControl() { function closeLayerControl() {
open = false; open = false;
} }
let cancelEvents = false; let cancelEvents = false;
</script> </script>
<CustomControl class="group min-w-[29px] min-h-[29px] overflow-hidden"> <CustomControl class="group min-w-[29px] min-h-[29px] overflow-hidden">
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<div <div
bind:this={container} bind:this={container}
class="h-full w-full" class="h-full w-full"
on:mouseenter={openLayerControl} on:mouseenter={openLayerControl}
on:mouseleave={closeLayerControl} on:mouseleave={closeLayerControl}
on:pointerenter={() => { on:pointerenter={() => {
if (!open) { if (!open) {
cancelEvents = true; cancelEvents = true;
openLayerControl(); openLayerControl();
setTimeout(() => { setTimeout(() => {
cancelEvents = false; cancelEvents = false;
}, 500); }, 500);
} }
}} }}
> >
<div <div
class="flex flex-row justify-center items-center delay-100 transition-[opacity] duration-0 {open class="flex flex-row justify-center items-center delay-100 transition-[opacity] duration-0 {open
? 'opacity-0 w-0 h-0 delay-0' ? 'opacity-0 w-0 h-0 delay-0'
: 'w-[29px] h-[29px]'}" : 'w-[29px] h-[29px]'}"
> >
<Layers size="20" /> <Layers size="20" />
</div> </div>
<div <div
class="transition-[grid-template-rows grid-template-cols] grid grid-rows-[0fr] grid-cols-[0fr] duration-150 h-full {open class="transition-[grid-template-rows grid-template-cols] grid grid-rows-[0fr] grid-cols-[0fr] duration-150 h-full {open
? 'grid-rows-[1fr] grid-cols-[1fr]' ? 'grid-rows-[1fr] grid-cols-[1fr]'
: ''} {cancelEvents ? 'pointer-events-none' : ''}" : ''} {cancelEvents ? 'pointer-events-none' : ''}"
> >
<ScrollArea> <ScrollArea>
<div class="h-fit"> <div class="h-fit">
<div class="p-2"> <div class="p-2">
<LayerTree <LayerTree
layerTree={$selectedBasemapTree} layerTree={$selectedBasemapTree}
name="basemaps" name="basemaps"
bind:selected={$selectedBasemap} bind:selected={$selectedBasemap}
/> />
</div> </div>
<Separator class="w-full" /> <Separator class="w-full" />
<div class="p-2"> <div class="p-2">
{#if $currentOverlays} {#if $currentOverlays}
<LayerTree <LayerTree
layerTree={$selectedOverlayTree} layerTree={$selectedOverlayTree}
name="overlays" name="overlays"
multiple={true} multiple={true}
bind:checked={$currentOverlays} bind:checked={$currentOverlays}
/> />
{/if} {/if}
</div> </div>
<Separator class="w-full" /> <Separator class="w-full" />
<div class="p-2"> <div class="p-2">
{#if $currentOverpassQueries} {#if $currentOverpassQueries}
<LayerTree <LayerTree
layerTree={$selectedOverpassTree} layerTree={$selectedOverpassTree}
name="overpass" name="overpass"
multiple={true} multiple={true}
bind:checked={$currentOverpassQueries} bind:checked={$currentOverpassQueries}
/> />
{/if} {/if}
</div> </div>
</div> </div>
</ScrollArea> </ScrollArea>
</div> </div>
</div> </div>
</CustomControl> </CustomControl>
<OverpassPopup /> <OverpassPopup />
<svelte:window <svelte:window
on:click={(e) => { on:click={(e) => {
if (open && !cancelEvents && !container.contains(e.target)) { if (open && !cancelEvents && !container.contains(e.target)) {
closeLayerControl(); closeLayerControl();
} }
}} }}
/> />