mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2026-01-01 15:54:44 +00:00
Compare commits
5 Commits
867b6a6ac7
...
graphhoppe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ca46b9d35 | ||
|
|
7c2e24bbc4 | ||
|
|
e92e48ffde | ||
|
|
4ce7777b86 | ||
|
|
bc130ad867 |
@@ -1375,10 +1375,7 @@ export class TrackPoint {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
setExtensions(extensions: Record<string, string>) {
|
||||
if (Object.keys(extensions).length === 0) {
|
||||
return;
|
||||
}
|
||||
setExtension(key: string, value: string) {
|
||||
if (!this.extensions) {
|
||||
this.extensions = {};
|
||||
}
|
||||
@@ -1388,8 +1385,12 @@ export class TrackPoint {
|
||||
if (!this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions']) {
|
||||
this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] = {};
|
||||
}
|
||||
Object.entries(extensions).forEach(([key, value]) => {
|
||||
this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'][key] = value;
|
||||
}
|
||||
|
||||
setExtensions(extensions: Record<string, string>) {
|
||||
Object.entries(extensions).forEach(([key, value]) => {
|
||||
this.setExtension(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { Compass, Earth, Mountain, Timer } from '@lucide/svelte';
|
||||
import { i18n } from '$lib/i18n.svelte';
|
||||
import type { PopupItem } from '$lib/components/map/map-popup';
|
||||
import { map } from '$lib/components/map/map';
|
||||
|
||||
let { trackpoint }: { trackpoint: PopupItem<TrackPoint> } = $props();
|
||||
</script>
|
||||
@@ -40,7 +41,7 @@
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
href={`https://www.openstreetmap.org/edit?#map=18/${trackpoint.item.getLatitude().toFixed(5)}/${trackpoint.item.getLongitude().toFixed(5)}`}
|
||||
href={`https://www.openstreetmap.org/edit?#map=${(($map?.getZoom() ?? 17) + 1).toFixed(0)}/${trackpoint.item.getLatitude().toFixed(5)}/${trackpoint.item.getLongitude().toFixed(5)}`}
|
||||
target="_blank"
|
||||
>
|
||||
<Earth size="14" />
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
SquareArrowUpLeft,
|
||||
SquareArrowOutDownRight,
|
||||
} from '@lucide/svelte';
|
||||
import { brouterProfiles } from '$lib/components/toolbar/tools/routing/routing';
|
||||
import { routingProfiles } from '$lib/components/toolbar/tools/routing/routing';
|
||||
import { i18n } from '$lib/i18n.svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import {
|
||||
@@ -167,7 +167,7 @@
|
||||
{i18n._(`toolbar.routing.activities.${$routingProfile}`)}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each Object.keys(brouterProfiles) as profile}
|
||||
{#each Object.keys(routingProfiles) as profile}
|
||||
<Select.Item value={profile}
|
||||
>{i18n._(
|
||||
`toolbar.routing.activities.${profile}`
|
||||
|
||||
@@ -6,35 +6,141 @@ import { get } from 'svelte/store';
|
||||
|
||||
const { routing, routingProfile, privateRoads } = settings;
|
||||
|
||||
export const brouterProfiles: { [key: string]: string } = {
|
||||
bike: 'Trekking-dry',
|
||||
racing_bike: 'fastbike',
|
||||
gravel_bike: 'gravel',
|
||||
mountain_bike: 'MTB',
|
||||
foot: 'Hiking-Alpine-SAC6',
|
||||
motorcycle: 'Car-FastEco',
|
||||
water: 'river',
|
||||
railway: 'rail',
|
||||
export type RoutingProfile = {
|
||||
engine: 'graphhopper' | 'brouter';
|
||||
profile: string;
|
||||
};
|
||||
|
||||
export const routingProfiles: { [key: string]: RoutingProfile } = {
|
||||
bike: { engine: 'graphhopper', profile: 'bike' },
|
||||
racing_bike: { engine: 'graphhopper', profile: 'racingbike' },
|
||||
gravel_bike: { engine: 'brouter', profile: 'gravel' },
|
||||
mountain_bike: { engine: 'graphhopper', profile: 'mtb' },
|
||||
foot: { engine: 'graphhopper', profile: 'foot' },
|
||||
motorcycle: { engine: 'graphhopper', profile: 'motorcycle' },
|
||||
water: { engine: 'brouter', profile: 'river' },
|
||||
railway: { engine: 'brouter', profile: 'rail' },
|
||||
};
|
||||
|
||||
export function route(points: Coordinates[]): Promise<TrackPoint[]> {
|
||||
if (get(routing)) {
|
||||
return getRoute(points, brouterProfiles[get(routingProfile)], get(privateRoads));
|
||||
const profile = routingProfiles[get(routingProfile)];
|
||||
if (profile.engine === 'graphhopper') {
|
||||
return getGraphHopperRoute(points, profile.profile, get(privateRoads));
|
||||
} else {
|
||||
return getBRouterRoute(points, profile.profile);
|
||||
}
|
||||
} else {
|
||||
return getIntermediatePoints(points);
|
||||
}
|
||||
}
|
||||
|
||||
async function getRoute(
|
||||
const graphhopperDetails = ['road_class', 'surface', 'hike_rating', 'mtb_rating'];
|
||||
const hikeRatingToSACScale: { [key: string]: string } = {
|
||||
'1': 'hiking',
|
||||
'2': 'mountain_hiking',
|
||||
'3': 'demanding_mountain_hiking',
|
||||
'4': 'alpine_hiking',
|
||||
'5': 'demanding_alpine_hiking',
|
||||
'6': 'difficult_alpine_hiking',
|
||||
};
|
||||
const mtbRatingToScale: { [key: string]: string } = {
|
||||
'1': '0',
|
||||
'2': '1',
|
||||
'3': '2',
|
||||
'4': '3',
|
||||
'5': '4',
|
||||
'6': '5',
|
||||
'7': '6',
|
||||
};
|
||||
async function getGraphHopperRoute(
|
||||
points: Coordinates[],
|
||||
brouterProfile: string,
|
||||
graphHopperProfile: string,
|
||||
privateRoads: boolean
|
||||
): Promise<TrackPoint[]> {
|
||||
let url = `https://brouter.gpx.studio?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile + (privateRoads ? '-private' : '')}&format=geojson&alternativeidx=0`;
|
||||
let response = await fetch('https://graphhopper-a.gpx.studio/route', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
points: points.map((point) => [point.lon, point.lat]),
|
||||
profile: graphHopperProfile,
|
||||
elevation: true,
|
||||
points_encoded: false,
|
||||
details: graphhopperDetails,
|
||||
custom_model: privateRoads
|
||||
? {}
|
||||
: {
|
||||
priority: [
|
||||
{
|
||||
if: 'road_access == PRIVATE',
|
||||
multiply_by: '0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`${await response.text()}`);
|
||||
}
|
||||
|
||||
let json = await response.json();
|
||||
|
||||
let route: TrackPoint[] = [];
|
||||
let coordinates = json.paths[0].points.coordinates;
|
||||
let details = json.paths[0].details;
|
||||
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
route.push(
|
||||
new TrackPoint({
|
||||
attributes: {
|
||||
lat: coordinates[i][1],
|
||||
lon: coordinates[i][0],
|
||||
},
|
||||
ele: coordinates[i][2] ?? (i > 0 ? route[i - 1].ele : 0),
|
||||
extensions: {},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
for (let key of graphhopperDetails) {
|
||||
let detail = details[key];
|
||||
for (let i = 0; i < detail.length; i++) {
|
||||
for (let j = detail[i][0]; j < detail[i][1] + (i == detail.length - 1); j++) {
|
||||
if (detail[i][2] !== undefined && detail[i][2] !== 'missing') {
|
||||
if (key === 'road_class') {
|
||||
route[j].setExtension('highway', detail[i][2]);
|
||||
} else if (key === 'hike_rating') {
|
||||
const sacScale = hikeRatingToSACScale[detail[i][2]];
|
||||
if (sacScale) {
|
||||
route[j].setExtension('sac_scale', sacScale);
|
||||
}
|
||||
} else if (key === 'mtb_rating') {
|
||||
const mtbScale = mtbRatingToScale[detail[i][2]];
|
||||
if (mtbScale) {
|
||||
route[j].setExtension('mtb_scale', mtbScale);
|
||||
}
|
||||
} else if (key === 'surface' && detail[i][2] !== 'other') {
|
||||
route[j].setExtension('surface', detail[i][2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
async function getBRouterRoute(
|
||||
points: Coordinates[],
|
||||
brouterProfile: string
|
||||
): Promise<TrackPoint[]> {
|
||||
let url = `https://brouter.de/brouter?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile}&format=geojson&alternativeidx=0`;
|
||||
|
||||
let response = await fetch(url);
|
||||
|
||||
// Check if the response is ok
|
||||
if (!response.ok) {
|
||||
throw new Error(`${await response.text()}`);
|
||||
}
|
||||
@@ -52,14 +158,13 @@ async function getRoute(
|
||||
let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {};
|
||||
|
||||
for (let i = 0; i < coordinates.length; i++) {
|
||||
let coord = coordinates[i];
|
||||
route.push(
|
||||
new TrackPoint({
|
||||
attributes: {
|
||||
lat: coord[1],
|
||||
lon: coord[0],
|
||||
lat: coordinates[i][1],
|
||||
lon: coordinates[i][0],
|
||||
},
|
||||
ele: coord[2] ?? (i > 0 ? route[i - 1].ele : 0),
|
||||
ele: coordinates[i][2] ?? (i > 0 ? route[i - 1].ele : 0),
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ Beste era batez, fitxategiak zuzenean arrastatu eta jaregin ditzakezu zure fitxa
|
||||
|
||||
Sortu hautatutako fitxategien kopia bat.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Ezabatu
|
||||
|
||||
Delete the currently selected files.
|
||||
Ezabatu hautatutako fitxategiak.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Ezabatu guztiak
|
||||
|
||||
Delete all files.
|
||||
Ezabatu fitxategi guztiak.
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Esportatu...
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ cÈ inoltre possibile trascinare i file direttamente dal file system del tuo Pc
|
||||
|
||||
Crea una copia dei file attualmente selezionati.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" />Elimina
|
||||
|
||||
Delete the currently selected files.
|
||||
Elimina i file attualmente selezionati.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" />Cancella tutto
|
||||
|
||||
Delete all files.
|
||||
Elimina tutti i file.
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> Esporta...
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Deze handleiding zal je door alle componenten en gereedschappen van de interface
|
||||
<DocsImage src="getting-started/interface" alt="De gpx.studio interface." />
|
||||
|
||||
Zoals weergegeven in bovenstaande scherm, is de interface verdeeld in vier hoofddelen rond de kaart.
|
||||
Voordat we in de details van elke sectie duiken, hebben we een snel overzicht van de interface.
|
||||
Voordat we in de details van elke sectie duiken, eerst een snel overzicht van de interface.
|
||||
|
||||
## Menu
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ Deze actie is alleen beschikbaar wanneer de verticale indeling van de bestandsli
|
||||
|
||||
### <ClipboardPaste size="16" class="inline-block" style="margin-bottom: 2px" /> Plakken
|
||||
|
||||
Plak de bestandsitems van het klembord naar het huidige hiërarchie niveau indien compatibel.
|
||||
Plak de bestandsitems van het klembord naar het huidige hiërarchieniveau indien compatibel.
|
||||
|
||||
<DocsNote>
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ title: 文件
|
||||
|
||||
Delete the currently selected files.
|
||||
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> Delete all
|
||||
### <FileX size="16" class="inline-block" style="margin-bottom: 2px" /> 删除全部
|
||||
|
||||
Delete all files.
|
||||
删除全部文件。
|
||||
|
||||
### <Download size="16" class="inline-block" style="margin-bottom: 2px" /> 导出...
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"center": "Centrar",
|
||||
"open_in": "Abrir en",
|
||||
"copy_coordinates": "Copiar coordenadas",
|
||||
"edit_osm": "Edit in OpenStreetMap"
|
||||
"edit_osm": "Editar en OpenStreetMap"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -353,7 +353,7 @@
|
||||
"water": "Agua",
|
||||
"shower": "Ducha",
|
||||
"shelter": "Refugio",
|
||||
"cemetery": "Cemetery",
|
||||
"cemetery": "Cementerio",
|
||||
"motorized": "Coches y motos",
|
||||
"fuel-station": "Gasolinera",
|
||||
"parking": "Aparcamiento",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"undo": "Desegin",
|
||||
"redo": "Berregin",
|
||||
"delete": "Ezabatu",
|
||||
"delete_all": "Delete all",
|
||||
"delete_all": "Ezabatu guztiak",
|
||||
"select_all": "Hautatu dena",
|
||||
"view": "Ikusi",
|
||||
"elevation_profile": "Altuera profila",
|
||||
@@ -80,7 +80,7 @@
|
||||
"center": "Erdiratu",
|
||||
"open_in": "Ireki hemen",
|
||||
"copy_coordinates": "Kopiatu koordenatuak",
|
||||
"edit_osm": "Edit in OpenStreetMap"
|
||||
"edit_osm": "Editatu OpenStreeMapen"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -353,7 +353,7 @@
|
||||
"water": "Ura",
|
||||
"shower": "Dutxa",
|
||||
"shelter": "Babeslekua",
|
||||
"cemetery": "Cemetery",
|
||||
"cemetery": "Hilerria",
|
||||
"motorized": "Kotxeak eta motorrak",
|
||||
"fuel-station": "Gasolindegia",
|
||||
"parking": "Aparkalekua",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"undo": "Annulla",
|
||||
"redo": "Ripeti",
|
||||
"delete": "Elimina",
|
||||
"delete_all": "Delete all",
|
||||
"delete_all": "Cancella tutto",
|
||||
"select_all": "Seleziona tutto",
|
||||
"view": "Visualizza",
|
||||
"elevation_profile": "Profilo altimetrico",
|
||||
@@ -80,7 +80,7 @@
|
||||
"center": "Centra",
|
||||
"open_in": "Apri con",
|
||||
"copy_coordinates": "Copia le coordinate",
|
||||
"edit_osm": "Edit in OpenStreetMap"
|
||||
"edit_osm": "Modifica in OpenStreetMap"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -353,7 +353,7 @@
|
||||
"water": "Acqua",
|
||||
"shower": "Doccia",
|
||||
"shelter": "Riparo",
|
||||
"cemetery": "Cemetery",
|
||||
"cemetery": "Cimitero",
|
||||
"motorized": "Auto e Motocicli",
|
||||
"fuel-station": "Stazione di Rifornimento",
|
||||
"parking": "Parcheggio",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"undo": "撤销",
|
||||
"redo": "恢复",
|
||||
"delete": "删除",
|
||||
"delete_all": "",
|
||||
"delete_all": "全部删除",
|
||||
"select_all": "全选",
|
||||
"view": "显示",
|
||||
"elevation_profile": "海拔剖面图",
|
||||
@@ -80,7 +80,7 @@
|
||||
"center": "居中",
|
||||
"open_in": "打开于",
|
||||
"copy_coordinates": "复制坐标",
|
||||
"edit_osm": "Edit in OpenStreetMap"
|
||||
"edit_osm": "在 OpenStreetMap 中编辑"
|
||||
},
|
||||
"toolbar": {
|
||||
"routing": {
|
||||
@@ -353,7 +353,7 @@
|
||||
"water": "饮用水",
|
||||
"shower": "淋浴",
|
||||
"shelter": "庇护所",
|
||||
"cemetery": "Cemetery",
|
||||
"cemetery": "墓地",
|
||||
"motorized": "汽车和摩托车",
|
||||
"fuel-station": "加油站",
|
||||
"parking": "停车场",
|
||||
|
||||
Reference in New Issue
Block a user