mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-02 00:32:33 +00:00
waypoint sym icons, and change layer color design
This commit is contained in:
@@ -1,45 +1,56 @@
|
|||||||
import { Landmark, type Icon, Shell, Bike, Building, Tent, Car, Wrench, ShoppingBasket, Droplet, DoorOpen, Trees, Fuel, Home, Info, TreeDeciduous, CircleParking, Cross, Utensils, Construction, BrickWall, ShowerHead, Mountain, Phone, Eye, TrainFront, Bed } from "lucide-svelte";
|
import { Landmark, type Icon, Shell, Bike, Building, Tent, Car, Wrench, ShoppingBasket, Droplet, DoorOpen, Trees, Fuel, Home, Info, TreeDeciduous, CircleParking, Cross, Utensils, Construction, BrickWall, ShowerHead, Mountain, Phone, Eye, TrainFront, Bed } from "lucide-svelte";
|
||||||
|
import { Landmark as LandmarkSvg, Shell as ShellSvg, Bike as BikeSvg, Building as BuildingSvg, Tent as TentSvg, Car as CarSvg, Wrench as WrenchSvg, ShoppingBasket as ShoppingBasketSvg, Droplet as DropletSvg, DoorOpen as DoorOpenSvg, Trees as TreesSvg, Fuel as FuelSvg, Home as HomeSvg, Info as InfoSvg, TreeDeciduous as TreeDeciduousSvg, CircleParking as CircleParkingSvg, Cross as CrossSvg, Utensils as UtensilsSvg, Construction as ConstructionSvg, BrickWall as BrickWallSvg, ShowerHead as ShowerHeadSvg, Mountain as MountainSvg, Phone as PhoneSvg, Eye as EyeSvg, TrainFront as TrainFrontSvg, Bed as BedSvg } from "lucide-static";
|
||||||
import type { ComponentType } from "svelte";
|
import type { ComponentType } from "svelte";
|
||||||
|
|
||||||
export type Symbol = {
|
export type Symbol = {
|
||||||
value: string;
|
value: string;
|
||||||
icon?: ComponentType<Icon>;
|
icon?: ComponentType<Icon>;
|
||||||
|
iconSvg?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const symbols: { [key: string]: Symbol } = {
|
export const symbols: { [key: string]: Symbol } = {
|
||||||
bank: { value: 'Bank', icon: Landmark },
|
bank: { value: 'Bank', icon: Landmark, iconSvg: LandmarkSvg },
|
||||||
beach: { value: 'Beach', icon: Shell },
|
beach: { value: 'Beach', icon: Shell, iconSvg: ShellSvg },
|
||||||
bike_trail: { value: 'Bike Trail', icon: Bike },
|
bike_trail: { value: 'Bike Trail', icon: Bike, iconSvg: BikeSvg },
|
||||||
bridge: { value: 'Bridge' },
|
bridge: { value: 'Bridge' },
|
||||||
building: { value: 'Building', icon: Building },
|
building: { value: 'Building', icon: Building, iconSvg: BuildingSvg },
|
||||||
campground: { value: 'Campground', icon: Tent },
|
campground: { value: 'Campground', icon: Tent, iconSvg: TentSvg },
|
||||||
car: { value: 'Car', icon: Car },
|
car: { value: 'Car', icon: Car, iconSvg: CarSvg },
|
||||||
car_repair: { value: 'Car Repair', icon: Wrench },
|
car_repair: { value: 'Car Repair', icon: Wrench, iconSvg: WrenchSvg },
|
||||||
convenience_store: { value: 'Convenience Store', icon: ShoppingBasket },
|
convenience_store: { value: 'Convenience Store', icon: ShoppingBasket, iconSvg: ShoppingBasketSvg },
|
||||||
crossing: { value: 'Crossing' },
|
crossing: { value: 'Crossing' },
|
||||||
department_store: { value: 'Department Store', icon: ShoppingBasket },
|
department_store: { value: 'Department Store', icon: ShoppingBasket, iconSvg: ShoppingBasketSvg },
|
||||||
drinking_water: { value: 'Drinking Water', icon: Droplet },
|
drinking_water: { value: 'Drinking Water', icon: Droplet, iconSvg: DropletSvg },
|
||||||
exit: { value: 'Exit', icon: DoorOpen },
|
exit: { value: 'Exit', icon: DoorOpen, iconSvg: DoorOpenSvg },
|
||||||
lodge: { value: 'Lodge', icon: Home },
|
lodge: { value: 'Lodge', icon: Home, iconSvg: HomeSvg },
|
||||||
forest: { value: 'Forest', icon: Trees },
|
lodging: { value: 'Lodging', icon: Bed, iconSvg: BedSvg },
|
||||||
gas_station: { value: 'Gas Station', icon: Fuel },
|
forest: { value: 'Forest', icon: Trees, iconSvg: TreesSvg },
|
||||||
ground_transportation: { value: 'Ground Transportation', icon: TrainFront },
|
gas_station: { value: 'Gas Station', icon: Fuel, iconSvg: FuelSvg },
|
||||||
hotel: { value: 'Hotel', icon: Bed },
|
ground_transportation: { value: 'Ground Transportation', icon: TrainFront, iconSvg: TrainFrontSvg },
|
||||||
house: { value: 'House', icon: Home },
|
hotel: { value: 'Hotel', icon: Bed, iconSvg: BedSvg },
|
||||||
information: { value: 'Information', icon: Info },
|
house: { value: 'House', icon: Home, iconSvg: HomeSvg },
|
||||||
park: { value: 'Park', icon: TreeDeciduous },
|
information: { value: 'Information', icon: Info, iconSvg: InfoSvg },
|
||||||
parking_area: { value: 'Parking Area', icon: CircleParking },
|
park: { value: 'Park', icon: TreeDeciduous, iconSvg: TreeDeciduousSvg },
|
||||||
pharmacy: { value: 'Pharmacy', icon: Cross },
|
parking_area: { value: 'Parking Area', icon: CircleParking, iconSvg: CircleParkingSvg },
|
||||||
picnic_area: { value: 'Picnic Area', icon: Utensils },
|
pharmacy: { value: 'Pharmacy', icon: Cross, iconSvg: CrossSvg },
|
||||||
restaurant: { value: 'Restaurant', icon: Utensils },
|
picnic_area: { value: 'Picnic Area', icon: Utensils, iconSvg: UtensilsSvg },
|
||||||
restricted_area: { value: 'Restricted Area', icon: Construction },
|
restaurant: { value: 'Restaurant', icon: Utensils, iconSvg: UtensilsSvg },
|
||||||
|
restricted_area: { value: 'Restricted Area', icon: Construction, iconSvg: ConstructionSvg },
|
||||||
restroom: { value: 'Restroom' },
|
restroom: { value: 'Restroom' },
|
||||||
road: { value: 'Road', icon: BrickWall },
|
road: { value: 'Road', icon: BrickWall, iconSvg: BrickWallSvg },
|
||||||
scenic_area: { value: 'Scenic Area', icon: Eye },
|
scenic_area: { value: 'Scenic Area', icon: Eye },
|
||||||
shopping_center: { value: 'Shopping Center', icon: ShoppingBasket },
|
shopping_center: { value: 'Shopping Center', icon: ShoppingBasket },
|
||||||
shower: { value: 'Shower', icon: ShowerHead },
|
shower: { value: 'Shower', icon: ShowerHead, iconSvg: ShowerHeadSvg },
|
||||||
summit: { value: 'Summit', icon: Mountain },
|
summit: { value: 'Summit', icon: Mountain, iconSvg: MountainSvg },
|
||||||
telephone: { value: 'Telephone', icon: Phone },
|
telephone: { value: 'Telephone', icon: Phone, iconSvg: PhoneSvg },
|
||||||
tunnel: { value: 'Tunnel' },
|
tunnel: { value: 'Tunnel' },
|
||||||
water_source: { value: 'Water Source', icon: Droplet },
|
water_source: { value: 'Water Source', icon: Droplet, iconSvg: DropletSvg },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function getSymbolKey(value: string | undefined): string | undefined {
|
||||||
|
if (value === undefined) {
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return Object.keys(symbols).find(key => symbols[key].value === value);
|
||||||
|
}
|
||||||
|
}
|
@@ -9,7 +9,8 @@ import type { Waypoint } from "gpx";
|
|||||||
import { getElevation, resetCursor, setGrabbingCursor, setPointerCursor, setScissorsCursor } from "$lib/utils";
|
import { getElevation, resetCursor, setGrabbingCursor, setPointerCursor, setScissorsCursor } from "$lib/utils";
|
||||||
import { font } from "$lib/assets/layers";
|
import { font } from "$lib/assets/layers";
|
||||||
import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte";
|
import { selectedWaypoint } from "$lib/components/toolbar/tools/Waypoint.svelte";
|
||||||
import { MapPin } from "lucide-static";
|
import { MapPin, Square } from "lucide-static";
|
||||||
|
import { getSymbolKey, symbols } from "$lib/assets/symbols";
|
||||||
|
|
||||||
const colors = [
|
const colors = [
|
||||||
'#ff0000',
|
'#ff0000',
|
||||||
@@ -43,6 +44,25 @@ function decrementColor(color: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMarkerForSymbol(symbol: string | undefined, layerColor: string) {
|
||||||
|
let symbolSvg = symbol ? symbols[symbol]?.iconSvg : undefined;
|
||||||
|
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
${Square
|
||||||
|
.replace('stroke="currentColor"', 'stroke="SteelBlue" stroke-width="1.5" transform="translate(9.6, 0.4) scale(0.5)"')
|
||||||
|
.replace('fill="none"', `fill="${layerColor}"`)}
|
||||||
|
|
||||||
|
${MapPin
|
||||||
|
.replace('width="24"', '')
|
||||||
|
.replace('height="24"', '')
|
||||||
|
.replace('stroke="currentColor"', '')
|
||||||
|
.replace('path', `path fill="#3fb1ce" stroke="SteelBlue" stroke-width="1"`)
|
||||||
|
.replace('circle', `circle fill="${symbolSvg ? 'none' : 'white'}" stroke="${symbolSvg ? 'none' : 'white'}" stroke-width="2"`)}
|
||||||
|
|
||||||
|
${symbolSvg?.replace('stroke="currentColor"', 'stroke="white" stroke-width="2.5" transform="translate(7.2, 5) scale(0.4)"') ?? ''}
|
||||||
|
|
||||||
|
</svg>`
|
||||||
|
}
|
||||||
|
|
||||||
const { directionMarkers, verticalFileView, currentBasemap, defaultOpacity, defaultWeight } = settings;
|
const { directionMarkers, verticalFileView, currentBasemap, defaultOpacity, defaultWeight } = settings;
|
||||||
|
|
||||||
export class GPXLayer {
|
export class GPXLayer {
|
||||||
@@ -184,19 +204,15 @@ export class GPXLayer {
|
|||||||
|
|
||||||
if (get(selection).hasAnyChildren(new ListFileItem(this.fileId))) {
|
if (get(selection).hasAnyChildren(new ListFileItem(this.fileId))) {
|
||||||
file.wpt.forEach((waypoint) => { // Update markers
|
file.wpt.forEach((waypoint) => { // Update markers
|
||||||
|
let symbolKey = getSymbolKey(waypoint.sym);
|
||||||
if (markerIndex < this.markers.length) {
|
if (markerIndex < this.markers.length) {
|
||||||
this.markers[markerIndex].getElement().querySelector('circle')?.setAttribute('fill', this.layerColor);
|
this.markers[markerIndex].getElement().innerHTML = getMarkerForSymbol(symbolKey, this.layerColor);
|
||||||
this.markers[markerIndex].setLngLat(waypoint.getCoordinates());
|
this.markers[markerIndex].setLngLat(waypoint.getCoordinates());
|
||||||
Object.defineProperty(this.markers[markerIndex], '_waypoint', { value: waypoint, writable: true });
|
Object.defineProperty(this.markers[markerIndex], '_waypoint', { value: waypoint, writable: true });
|
||||||
} else {
|
} else {
|
||||||
let element = document.createElement('div');
|
let element = document.createElement('div');
|
||||||
element.classList.add('w-8', 'h-8', 'drop-shadow-xl');
|
element.classList.add('w-8', 'h-8', 'drop-shadow-xl');
|
||||||
element.innerHTML = MapPin
|
element.innerHTML = getMarkerForSymbol(symbolKey, this.layerColor);
|
||||||
.replace('width="24"', '')
|
|
||||||
.replace('height="24"', '')
|
|
||||||
.replace('stroke="currentColor"', '')
|
|
||||||
.replace('path', `path fill="#3fb1ce" stroke="SteelBlue" stroke-width="1"`)
|
|
||||||
.replace('circle', `circle fill="${this.layerColor}" stroke="white" stroke-width="2.5"`);
|
|
||||||
let marker = new mapboxgl.Marker({
|
let marker = new mapboxgl.Marker({
|
||||||
draggable: this.draggable,
|
draggable: this.draggable,
|
||||||
element,
|
element,
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
import { Dot, Trash2 } from 'lucide-svelte';
|
import { Dot, Trash2 } from 'lucide-svelte';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { Tool, currentTool } from '$lib/stores';
|
import { Tool, currentTool } from '$lib/stores';
|
||||||
import { symbols } from '$lib/assets/symbols';
|
import { getSymbolKey, symbols } from '$lib/assets/symbols';
|
||||||
import { _ } from 'svelte-i18n';
|
import { _ } from 'svelte-i18n';
|
||||||
|
|
||||||
let popupElement: HTMLDivElement;
|
let popupElement: HTMLDivElement;
|
||||||
@@ -17,10 +17,7 @@
|
|||||||
popupElement.classList.remove('hidden');
|
popupElement.classList.remove('hidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
$: symbolKey =
|
$: symbolKey = $currentPopupWaypoint ? getSymbolKey($currentPopupWaypoint[0].sym) : undefined;
|
||||||
$currentPopupWaypoint && $currentPopupWaypoint[0].sym
|
|
||||||
? Object.keys(symbols).find((key) => symbols[key].value === $currentPopupWaypoint[0].sym)
|
|
||||||
: undefined;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={popupElement} class="hidden">
|
<div bind:this={popupElement} class="hidden">
|
||||||
@@ -30,8 +27,8 @@
|
|||||||
<Card.Title class="text-md">{$currentPopupWaypoint[0].name}</Card.Title>
|
<Card.Title class="text-md">{$currentPopupWaypoint[0].name}</Card.Title>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Content class="flex flex-col p-0 text-sm">
|
<Card.Content class="flex flex-col p-0 text-sm">
|
||||||
<div class="flex flex-row items-center text-muted-foreground text-xs">
|
<div class="flex flex-row items-center text-muted-foreground text-xs whitespace-nowrap">
|
||||||
{#if symbolKey && symbols.hasOwnProperty(symbolKey)}
|
{#if symbolKey}
|
||||||
<span>
|
<span>
|
||||||
{#if symbols[symbolKey].icon}
|
{#if symbols[symbolKey].icon}
|
||||||
<svelte:component
|
<svelte:component
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
import { map } from '$lib/stores';
|
import { map } from '$lib/stores';
|
||||||
import { resetCursor, setCrosshairCursor } from '$lib/utils';
|
import { resetCursor, setCrosshairCursor } from '$lib/utils';
|
||||||
import { CirclePlus, CircleX, Save } from 'lucide-svelte';
|
import { CirclePlus, CircleX, Save } from 'lucide-svelte';
|
||||||
import { symbols } from '$lib/assets/symbols';
|
import { getSymbolKey, symbols } from '$lib/assets/symbols';
|
||||||
|
|
||||||
let name: string;
|
let name: string;
|
||||||
let description: string;
|
let description: string;
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
description += '\n\n' + $selectedWaypoint[0].cmt;
|
description += '\n\n' + $selectedWaypoint[0].cmt;
|
||||||
}
|
}
|
||||||
let symbol = $selectedWaypoint[0].sym ?? '';
|
let symbol = $selectedWaypoint[0].sym ?? '';
|
||||||
let symbolKey = Object.keys(symbols).find((key) => symbols[key].value === symbol);
|
let symbolKey = getSymbolKey(symbol);
|
||||||
if (symbolKey) {
|
if (symbolKey) {
|
||||||
selectedSymbol = {
|
selectedSymbol = {
|
||||||
value: symbol,
|
value: symbol,
|
||||||
|
@@ -402,6 +402,7 @@
|
|||||||
"drinking_water": "Water",
|
"drinking_water": "Water",
|
||||||
"exit": "Exit",
|
"exit": "Exit",
|
||||||
"lodge": "Hut",
|
"lodge": "Hut",
|
||||||
|
"lodging": "Accommodation",
|
||||||
"forest": "Forest",
|
"forest": "Forest",
|
||||||
"gas_station": "Fuel Station",
|
"gas_station": "Fuel Station",
|
||||||
"ground_transportation": "Ground Transportation",
|
"ground_transportation": "Ground Transportation",
|
||||||
|
Reference in New Issue
Block a user