mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-08-31 15:43:25 +00:00
handle waypoint symbols
This commit is contained in:
@@ -872,10 +872,11 @@ type OverpassQueryData = {
|
||||
color: string,
|
||||
},
|
||||
tags: Record<string, string | boolean | string[]> | Record<string, string | boolean | string[]>[],
|
||||
symbol?: string,
|
||||
};
|
||||
|
||||
export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
"bakery": {
|
||||
bakery: {
|
||||
icon: {
|
||||
svg: Croissant,
|
||||
color: "Coral",
|
||||
@@ -891,7 +892,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
shop: ["supermarket", "convenience"],
|
||||
}
|
||||
},
|
||||
symbol: "Convenience Store"
|
||||
},
|
||||
"eat-and-drink": {
|
||||
icon: {
|
||||
@@ -900,16 +902,18 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
amenity: ["restaurant", "fast_food", "cafe", "pub", "bar"]
|
||||
}
|
||||
},
|
||||
symbol: "Restaurant"
|
||||
},
|
||||
"toilets": {
|
||||
toilets: {
|
||||
icon: {
|
||||
svg: Droplet,
|
||||
color: "DeepSkyBlue",
|
||||
},
|
||||
tags: {
|
||||
amenity: "toilets"
|
||||
}
|
||||
},
|
||||
symbol: "Restroom"
|
||||
},
|
||||
water: {
|
||||
icon: {
|
||||
@@ -921,7 +925,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
}, {
|
||||
natural: "spring",
|
||||
drinking_water: "yes"
|
||||
}]
|
||||
}],
|
||||
symbol: "Drinking Water"
|
||||
},
|
||||
shower: {
|
||||
icon: {
|
||||
@@ -930,7 +935,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
amenity: "shower"
|
||||
}
|
||||
},
|
||||
symbol: "Shower"
|
||||
},
|
||||
"fuel-station": {
|
||||
icon: {
|
||||
@@ -939,7 +945,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
amenity: "fuel"
|
||||
}
|
||||
},
|
||||
symbol: "Gas Station"
|
||||
},
|
||||
parking: {
|
||||
icon: {
|
||||
@@ -948,7 +955,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
amenity: "parking"
|
||||
}
|
||||
},
|
||||
symbol: "Parking Area"
|
||||
},
|
||||
garage: {
|
||||
icon: {
|
||||
@@ -957,7 +965,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
shop: ["car_repair", "motorcycle_repair"]
|
||||
}
|
||||
},
|
||||
symbol: "Car Repair"
|
||||
},
|
||||
barrier: {
|
||||
icon: {
|
||||
@@ -984,7 +993,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
tourism: "viewpoint"
|
||||
}
|
||||
},
|
||||
symbol: "Scenic Area"
|
||||
},
|
||||
hotel: {
|
||||
icon: {
|
||||
@@ -993,7 +1003,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
tourism: ["hotel", "hostel", "guest_house", "motel"]
|
||||
}
|
||||
},
|
||||
symbol: "Hotel"
|
||||
},
|
||||
campsite: {
|
||||
icon: {
|
||||
@@ -1002,7 +1013,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
tourism: "camp_site"
|
||||
}
|
||||
},
|
||||
symbol: "Campground"
|
||||
},
|
||||
hut: {
|
||||
icon: {
|
||||
@@ -1011,7 +1023,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
tourism: ["alpine_hut", "wilderness_hut"]
|
||||
}
|
||||
},
|
||||
symbol: "Lodge"
|
||||
},
|
||||
summit: {
|
||||
icon: {
|
||||
@@ -1020,7 +1033,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
natural: "peak"
|
||||
}
|
||||
},
|
||||
symbol: "Summit"
|
||||
},
|
||||
pass: {
|
||||
icon: {
|
||||
@@ -1047,7 +1061,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
amenity: "bicycle_parking"
|
||||
}
|
||||
},
|
||||
symbol: "Parking Area"
|
||||
},
|
||||
"bicycle-rental": {
|
||||
icon: {
|
||||
@@ -1074,7 +1089,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
},
|
||||
tags: {
|
||||
railway: "station"
|
||||
}
|
||||
},
|
||||
symbol: "Ground Transportation"
|
||||
},
|
||||
"tram-stop": {
|
||||
icon: {
|
||||
@@ -1084,6 +1100,7 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
tags: {
|
||||
railway: "tram_stop"
|
||||
},
|
||||
symbol: "Ground Transportation"
|
||||
},
|
||||
"bus-stop": {
|
||||
icon: {
|
||||
@@ -1093,7 +1110,8 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
|
||||
tags: {
|
||||
"public_transport": ["stop_position", "platform"],
|
||||
bus: "yes"
|
||||
}
|
||||
},
|
||||
symbol: "Ground Transportation"
|
||||
},
|
||||
ferry: {
|
||||
icon: {
|
||||
|
45
website/src/lib/assets/symbols.ts
Normal file
45
website/src/lib/assets/symbols.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
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 type { ComponentType } from "svelte";
|
||||
|
||||
export type Symbol = {
|
||||
value: string;
|
||||
icon?: ComponentType<Icon>;
|
||||
};
|
||||
|
||||
export const symbols: { [key: string]: Symbol } = {
|
||||
bank: { value: 'Bank', icon: Landmark },
|
||||
beach: { value: 'Beach', icon: Shell },
|
||||
bike_trail: { value: 'Bike Trail', icon: Bike },
|
||||
bridge: { value: 'Bridge' },
|
||||
building: { value: 'Building', icon: Building },
|
||||
campground: { value: 'Campground', icon: Tent },
|
||||
car: { value: 'Car', icon: Car },
|
||||
car_repair: { value: 'Car Repair', icon: Wrench },
|
||||
convenience_store: { value: 'Convenience Store', icon: ShoppingBasket },
|
||||
crossing: { value: 'Crossing' },
|
||||
department_store: { value: 'Department Store', icon: ShoppingBasket },
|
||||
drinking_water: { value: 'Drinking Water', icon: Droplet },
|
||||
exit: { value: 'Exit', icon: DoorOpen },
|
||||
lodge: { value: 'Lodge', icon: Home },
|
||||
forest: { value: 'Forest', icon: Trees },
|
||||
gas_station: { value: 'Gas Station', icon: Fuel },
|
||||
ground_transportation: { value: 'Ground Transportation', icon: TrainFront },
|
||||
hotel: { value: 'Hotel', icon: Bed },
|
||||
house: { value: 'House', icon: Home },
|
||||
information: { value: 'Information', icon: Info },
|
||||
park: { value: 'Park', icon: TreeDeciduous },
|
||||
parking_area: { value: 'Parking Area', icon: CircleParking },
|
||||
pharmacy: { value: 'Pharmacy', icon: Cross },
|
||||
picnic_area: { value: 'Picnic Area', icon: Utensils },
|
||||
restaurant: { value: 'Restaurant', icon: Utensils },
|
||||
restricted_area: { value: 'Restricted Area', icon: Construction },
|
||||
restroom: { value: 'Restroom' },
|
||||
road: { value: 'Road', icon: BrickWall },
|
||||
scenic_area: { value: 'Scenic Area', icon: Eye },
|
||||
shopping_center: { value: 'Shopping Center', icon: ShoppingBasket },
|
||||
shower: { value: 'Shower', icon: ShowerHead },
|
||||
summit: { value: 'Summit', icon: Mountain },
|
||||
telephone: { value: 'Telephone', icon: Phone },
|
||||
tunnel: { value: 'Tunnel' },
|
||||
water_source: { value: 'Water Source', icon: Droplet },
|
||||
};
|
@@ -7,6 +7,7 @@
|
||||
import { Dot, Trash2 } from 'lucide-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { Tool, currentTool } from '$lib/stores';
|
||||
import { symbols } from '$lib/assets/symbols';
|
||||
import { _ } from 'svelte-i18n';
|
||||
|
||||
let popupElement: HTMLDivElement;
|
||||
@@ -15,6 +16,11 @@
|
||||
waypointPopup.setDOMContent(popupElement);
|
||||
popupElement.classList.remove('hidden');
|
||||
});
|
||||
|
||||
$: symbolKey =
|
||||
$currentPopupWaypoint && $currentPopupWaypoint[0].sym
|
||||
? Object.keys(symbols).find((key) => symbols[key].value === $currentPopupWaypoint[0].sym)
|
||||
: undefined;
|
||||
</script>
|
||||
|
||||
<div bind:this={popupElement} class="hidden">
|
||||
@@ -24,7 +30,22 @@
|
||||
<Card.Title class="text-md">{$currentPopupWaypoint[0].name}</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-col p-0 text-sm">
|
||||
<div class="flex flex-row items-center text-muted-foreground">
|
||||
<div class="flex flex-row items-center text-muted-foreground text-xs">
|
||||
{#if symbolKey && symbols.hasOwnProperty(symbolKey)}
|
||||
<span>
|
||||
{#if symbols[symbolKey].icon}
|
||||
<svelte:component
|
||||
this={symbols[symbolKey].icon}
|
||||
size="12"
|
||||
class="inline-block mb-0.5"
|
||||
/>
|
||||
{:else}
|
||||
<span class="w-4 inline-block" />
|
||||
{/if}
|
||||
{$_(`gpx.symbol.${symbolKey}`)}
|
||||
</span>
|
||||
<Dot size="16" />
|
||||
{/if}
|
||||
{$currentPopupWaypoint[0].getLatitude().toFixed(6)}° {$currentPopupWaypoint[0]
|
||||
.getLongitude()
|
||||
.toFixed(6)}°
|
||||
|
@@ -120,7 +120,10 @@ export class OverpassLayer {
|
||||
}
|
||||
|
||||
onHover(e: any) {
|
||||
overpassPopupPOI.set(e.features[0].properties);
|
||||
overpassPopupPOI.set({
|
||||
...e.features[0].properties,
|
||||
sym: overpassQueryData[e.features[0].properties.query].symbol ?? ''
|
||||
});
|
||||
overpassPopup.setLngLat(e.features[0].geometry.coordinates);
|
||||
overpassPopup.addTo(this.map);
|
||||
this.map.on('mousemove', this.maybeHidePopupBinded);
|
||||
|
@@ -7,7 +7,6 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import { dbUtils } from '$lib/db';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
let popupElement: HTMLDivElement;
|
||||
|
||||
@@ -89,7 +88,8 @@
|
||||
},
|
||||
name: name,
|
||||
desc: desc,
|
||||
cmt: desc
|
||||
cmt: desc,
|
||||
sym: $overpassPopupPOI.sym
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
@@ -9,6 +9,7 @@
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { selection } from '$lib/components/file-list/Selection';
|
||||
import { Waypoint } from 'gpx';
|
||||
import { _ } from 'svelte-i18n';
|
||||
@@ -20,12 +21,18 @@
|
||||
import { map } from '$lib/stores';
|
||||
import { resetCursor, setCrosshairCursor } from '$lib/utils';
|
||||
import { CirclePlus, CircleX, Save } from 'lucide-svelte';
|
||||
import { symbols } from '$lib/assets/symbols';
|
||||
|
||||
let name: string;
|
||||
let description: string;
|
||||
let longitude: number;
|
||||
let latitude: number;
|
||||
|
||||
let selectedSymbol = {
|
||||
value: '',
|
||||
label: ''
|
||||
};
|
||||
|
||||
const { verticalFileView } = settings;
|
||||
|
||||
$: canCreate = $selection.size > 0;
|
||||
@@ -60,6 +67,19 @@
|
||||
) {
|
||||
description += '\n\n' + $selectedWaypoint[0].cmt;
|
||||
}
|
||||
let symbol = $selectedWaypoint[0].sym ?? '';
|
||||
let symbolKey = Object.keys(symbols).find((key) => symbols[key].value === symbol);
|
||||
if (symbolKey) {
|
||||
selectedSymbol = {
|
||||
value: symbol,
|
||||
label: $_(`gpx.symbol.${symbolKey}`)
|
||||
};
|
||||
} else {
|
||||
selectedSymbol = {
|
||||
value: symbol,
|
||||
label: ''
|
||||
};
|
||||
}
|
||||
longitude = parseFloat($selectedWaypoint[0].getLongitude().toFixed(6));
|
||||
latitude = parseFloat($selectedWaypoint[0].getLatitude().toFixed(6));
|
||||
} else {
|
||||
@@ -74,6 +94,10 @@
|
||||
function resetWaypointData() {
|
||||
name = '';
|
||||
description = '';
|
||||
selectedSymbol = {
|
||||
value: '',
|
||||
label: ''
|
||||
};
|
||||
longitude = 0;
|
||||
latitude = 0;
|
||||
}
|
||||
@@ -111,7 +135,8 @@
|
||||
},
|
||||
name,
|
||||
desc: description,
|
||||
cmt: description
|
||||
cmt: description,
|
||||
sym: selectedSymbol.value
|
||||
},
|
||||
$selectedWaypoint
|
||||
? new ListWaypointItem($selectedWaypoint[1], $selectedWaypoint[0]._data.index)
|
||||
@@ -151,6 +176,30 @@
|
||||
<Input bind:value={name} id="name" class="font-semibold h-8" />
|
||||
<Label for="description">{$_('menu.metadata.description')}</Label>
|
||||
<Textarea bind:value={description} id="description" />
|
||||
<Label for="symbol">{$_('toolbar.waypoint.icon')}</Label>
|
||||
<Select.Root bind:selected={selectedSymbol}>
|
||||
<Select.Trigger id="symbol" class="w-full h-8">
|
||||
<Select.Value />
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-60 overflow-y-scroll">
|
||||
{#each Object.entries(symbols) as [key, symbol]}
|
||||
<Select.Item value={symbol.value}>
|
||||
<span>
|
||||
{#if symbol.icon}
|
||||
<svelte:component
|
||||
this={symbol.icon}
|
||||
size="14"
|
||||
class="inline-block align-sub mr-0.5"
|
||||
/>
|
||||
{:else}
|
||||
<span class="w-4 inline-block" />
|
||||
{/if}
|
||||
{$_(`gpx.symbol.${key}`)}
|
||||
</span>
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<div class="flex flex-row gap-2">
|
||||
<div>
|
||||
<Label for="latitude">{$_('toolbar.waypoint.latitude')}</Label>
|
||||
|
@@ -918,6 +918,7 @@ export const dbUtils = {
|
||||
wpt.name = waypoint.name;
|
||||
wpt.desc = waypoint.desc;
|
||||
wpt.cmt = waypoint.cmt;
|
||||
wpt.sym = waypoint.sym;
|
||||
wpt.setCoordinates(waypoint.attributes);
|
||||
wpt.ele = ele;
|
||||
});
|
||||
|
@@ -182,6 +182,7 @@
|
||||
},
|
||||
"waypoint": {
|
||||
"tooltip": "Create and edit points of interest",
|
||||
"icon": "Icon",
|
||||
"longitude": "Longitude",
|
||||
"latitude": "Latitude",
|
||||
"create": "Create point of interest",
|
||||
@@ -385,7 +386,44 @@
|
||||
"segment": "Segment",
|
||||
"segments": "Segments",
|
||||
"waypoint": "Point of interest",
|
||||
"waypoints": "Points of interest"
|
||||
"waypoints": "Points of interest",
|
||||
"symbol": {
|
||||
"bank": "Bank",
|
||||
"beach": "Beach",
|
||||
"bike_trail": "Bike Trail",
|
||||
"bridge": "Bridge",
|
||||
"building": "Building",
|
||||
"campground": "Campsite",
|
||||
"car": "Car",
|
||||
"car_repair": "Garage",
|
||||
"convenience_store": "Convenience Store",
|
||||
"crossing": "Crossing",
|
||||
"department_store": "Department Store",
|
||||
"drinking_water": "Water",
|
||||
"exit": "Exit",
|
||||
"lodge": "Hut",
|
||||
"forest": "Forest",
|
||||
"gas_station": "Fuel Station",
|
||||
"ground_transportation": "Ground Transportation",
|
||||
"hotel": "Hotel",
|
||||
"house": "House",
|
||||
"information": "Information",
|
||||
"park": "Park",
|
||||
"parking_area": "Parking",
|
||||
"pharmacy": "Pharmacy",
|
||||
"picnic_area": "Picnic Area",
|
||||
"restaurant": "Restaurant",
|
||||
"restricted_area": "Restricted Area",
|
||||
"restroom": "Toilets",
|
||||
"road": "Road",
|
||||
"scenic_area": "Scenic Area",
|
||||
"shopping_center": "Shopping Center",
|
||||
"shower": "Shower",
|
||||
"summit": "Summit",
|
||||
"telephone": "Telephone",
|
||||
"tunnel": "Tunnel",
|
||||
"water_source": "Water Source"
|
||||
}
|
||||
},
|
||||
"homepage": {
|
||||
"website": "Website",
|
||||
|
Reference in New Issue
Block a user