gpx.studio/website/src/lib/components/GPXMapLayer.svelte
2024-04-24 22:35:53 +02:00

202 lines
4.1 KiB
Svelte

<script context="module" lang="ts">
let id = 0;
function getLayerId() {
return `gpx-${id++}`;
}
let defaultWeight = 6;
let defaultOpacity = 1;
const colors = [
'#ff0000',
'#0000ff',
'#46e646',
'#00ccff',
'#ff9900',
'#ff00ff',
'#ffff32',
'#288228',
'#9933ff',
'#50f0be',
'#8c645a'
];
const colorCount: { [key: string]: number } = {};
for (let color of colors) {
colorCount[color] = 0;
}
// Get the color with the least amount of uses
function getColor() {
let color = colors.reduce((a, b) => (colorCount[a] <= colorCount[b] ? a : b));
colorCount[color]++;
return color;
}
function decrementColor(color: string) {
colorCount[color]--;
}
</script>
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import { GPXFile } from 'gpx';
import { map, selectedFiles, selectFiles, files } from '$lib/stores';
import { get, type Writable } from 'svelte/store';
export let file: Writable<GPXFile>;
let layerId = getLayerId();
let layerColor = getColor();
file.update((f) => {
Object.defineProperty(f, 'layerId', {
value: layerId,
writable: false
});
return f;
});
function selectOnClick(e: any) {
if (e.originalEvent.shiftKey) {
get(selectFiles).addSelect(get(file));
} else {
get(selectFiles).select(get(file));
}
}
function toPointerCursor() {
if ($map) {
$map.getCanvas().style.cursor = 'pointer';
}
}
function toDefaultCursor() {
if ($map) {
$map.getCanvas().style.cursor = '';
}
}
function extendGeoJSON(data: any) {
for (let feature of data.features) {
if (!feature.properties.color) {
feature.properties.color = layerColor;
}
if (!feature.properties.weight) {
feature.properties.weight = defaultWeight;
}
if (!feature.properties.opacity) {
feature.properties.opacity = defaultOpacity;
}
}
return data;
}
function addGPXLayer() {
if ($map) {
if (!$map.getSource(layerId)) {
let data = extendGeoJSON($file.toGeoJSON());
$map.addSource(layerId, {
type: 'geojson',
data
});
}
if (!$map.getLayer(layerId)) {
$map.addLayer({
id: layerId,
type: 'line',
source: layerId,
layout: {
'line-join': 'round',
'line-cap': 'round'
},
paint: {
'line-color': ['get', 'color'],
'line-width': ['get', 'weight'],
'line-opacity': ['get', 'opacity']
}
});
$map.on('click', layerId, selectOnClick);
$map.on('mouseenter', layerId, toPointerCursor);
$map.on('mouseleave', layerId, toDefaultCursor);
}
}
}
$: if ($selectedFiles.has(get(file))) {
if ($map) {
$map.moveLayer(layerId);
}
}
$: if ($map) {
let source = $map.getSource(layerId);
if (source) {
source.setData(extendGeoJSON($file.toGeoJSON()));
}
}
onMount(() => {
addGPXLayer();
if ($map) {
if ($files.length == 1) {
$map.fitBounds(
[get(file).statistics.bounds.southWest, get(file).statistics.bounds.northEast],
{
padding: 60,
linear: true,
easing: () => 1
}
);
} else {
let mapBounds = $map.getBounds();
if (
mapBounds.contains(get(file).statistics.bounds.southWest) &&
mapBounds.contains(get(file).statistics.bounds.northEast) &&
mapBounds.contains([
get(file).statistics.bounds.southWest.lon,
get(file).statistics.bounds.northEast.lat
]) &&
mapBounds.contains([
get(file).statistics.bounds.northEast.lon,
get(file).statistics.bounds.southWest.lat
])
) {
return;
}
$map.fitBounds(
$map
.getBounds()
.extend([
get(file).statistics.bounds.southWest.lon,
get(file).statistics.bounds.southWest.lat,
get(file).statistics.bounds.northEast.lon,
get(file).statistics.bounds.northEast.lat
]),
{
padding: 60
}
);
}
$map.on('style.load', addGPXLayer);
}
});
onDestroy(() => {
if ($map) {
$map.off('click', layerId, selectOnClick);
$map.off('mouseenter', layerId, toPointerCursor);
$map.off('mouseleave', layerId, toDefaultCursor);
$map.off('style.load', addGPXLayer);
$map.removeLayer(layerId);
$map.removeSource(layerId);
}
decrementColor(layerColor);
});
</script>