From 6a718fccea73ec6310be4eef1de8742e770dc1c6 Mon Sep 17 00:00:00 2001 From: jubnl Date: Sat, 18 Apr 2026 01:28:37 +0200 Subject: [PATCH] feat(import): selective GPX/KML element import and performance improvements Add type-selector UI in the file import modal letting users choose which GPX elements (waypoints, routes, tracks) or KML/KMZ elements (points, paths) to import. KML LineString placemarks are now imported as path places with route_geometry. Performance improvements: - Extract MemoPlaceRow with React.memo and contentVisibility:auto to cut unnecessary re-renders in PlacesSidebar - Add weatherQueue to cap concurrent weather fetches at 3 - Replace sequential per-place deletes with a single bulkDelete API call (new DELETE /places/bulk endpoint + deletePlacesMany service) - Memoize atlas/photo/weather service calls to avoid redundant requests - Add multi-select mode to PlacesSidebar for bulk operations Add large GPX/KML/KMZ fixtures for integration/perf testing and two profiler analysis scripts under scripts/. --- client/src/api/client.ts | 17 +- client/src/components/Map/MapView.test.tsx | 20 +- client/src/components/Map/MapView.tsx | 199 +- .../src/components/Planner/DayPlanSidebar.tsx | 52 +- .../components/Planner/FileImportModal.tsx | 73 +- .../src/components/Planner/PlacesSidebar.tsx | 326 +- .../src/components/Weather/WeatherWidget.tsx | 6 +- .../src/components/shared/ConfirmDialog.tsx | 2 +- client/src/hooks/useRouteCalculation.ts | 8 +- client/src/i18n/translations/ar.ts | 18 + client/src/i18n/translations/br.ts | 18 + client/src/i18n/translations/cs.ts | 18 + client/src/i18n/translations/de.ts | 18 + client/src/i18n/translations/en.ts | 18 + client/src/i18n/translations/es.ts | 18 + client/src/i18n/translations/fr.ts | 18 + client/src/i18n/translations/hu.ts | 18 + client/src/i18n/translations/id.ts | 18 + client/src/i18n/translations/it.ts | 18 + client/src/i18n/translations/nl.ts | 18 + client/src/i18n/translations/pl.ts | 18 + client/src/i18n/translations/ru.ts | 18 + client/src/i18n/translations/zh.ts | 18 + client/src/i18n/translations/zhTw.ts | 18 + client/src/pages/TripPlannerPage.tsx | 47 +- client/src/repo/placeRepo.ts | 22 + client/src/services/photoService.ts | 79 +- client/src/services/weatherQueue.ts | 25 + client/src/store/slices/placesSlice.ts | 25 + .../hooks/useRouteCalculation.test.ts | 12 +- .../tests/unit/services/photoService.test.ts | 2 + scripts/analyze-devtool-profiler.cjs | 133 + scripts/analyze-react-profiler.cjs | 96 + server/src/routes/places.ts | 51 +- server/src/services/atlasService.ts | 51 +- server/src/services/kmlImport.ts | 39 +- server/src/services/mapsService.ts | 29 + server/src/services/placePhotoCache.ts | 35 +- server/src/services/placeService.ts | 125 +- server/src/services/weatherService.ts | 41 +- server/tests/fixtures/large-test.gpx | 17773 ++++++++++++++++ server/tests/fixtures/large-test.kml | 3181 +++ server/tests/fixtures/large-test.kmz | Bin 0 -> 26416 bytes server/tests/integration/places.test.ts | 4 +- .../tests/unit/services/placeService.test.ts | 13 +- 45 files changed, 22471 insertions(+), 285 deletions(-) create mode 100644 client/src/services/weatherQueue.ts create mode 100644 scripts/analyze-devtool-profiler.cjs create mode 100644 scripts/analyze-react-profiler.cjs create mode 100644 server/tests/fixtures/large-test.gpx create mode 100644 server/tests/fixtures/large-test.kml create mode 100644 server/tests/fixtures/large-test.kmz diff --git a/client/src/api/client.ts b/client/src/api/client.ts index 179e02ff..576477ce 100644 --- a/client/src/api/client.ts +++ b/client/src/api/client.ts @@ -190,18 +190,27 @@ export const placesApi = { update: (tripId: number | string, id: number | string, data: Record) => apiClient.put(`/trips/${tripId}/places/${id}`, data).then(r => r.data), delete: (tripId: number | string, id: number | string) => apiClient.delete(`/trips/${tripId}/places/${id}`).then(r => r.data), searchImage: (tripId: number | string, id: number | string) => apiClient.get(`/trips/${tripId}/places/${id}/image`).then(r => r.data), - importGpx: (tripId: number | string, file: File) => { - const fd = new FormData(); fd.append('file', file) + importGpx: (tripId: number | string, file: File, opts?: { waypoints?: boolean; routes?: boolean; tracks?: boolean }) => { + const fd = new FormData() + fd.append('file', file) + if (opts?.waypoints !== undefined) fd.append('importWaypoints', String(opts.waypoints)) + if (opts?.routes !== undefined) fd.append('importRoutes', String(opts.routes)) + if (opts?.tracks !== undefined) fd.append('importTracks', String(opts.tracks)) return apiClient.post(`/trips/${tripId}/places/import/gpx`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data) }, - importMapFile: (tripId: number | string, file: File) => { - const fd = new FormData(); fd.append('file', file) + importMapFile: (tripId: number | string, file: File, opts?: { points?: boolean; paths?: boolean }) => { + const fd = new FormData() + fd.append('file', file) + if (opts?.points !== undefined) fd.append('importPoints', String(opts.points)) + if (opts?.paths !== undefined) fd.append('importPaths', String(opts.paths)) return apiClient.post(`/trips/${tripId}/places/import/map`, fd, { headers: { 'Content-Type': 'multipart/form-data' } }).then(r => r.data) }, importGoogleList: (tripId: number | string, url: string) => apiClient.post(`/trips/${tripId}/places/import/google-list`, { url }).then(r => r.data), importNaverList: (tripId: number | string, url: string) => apiClient.post(`/trips/${tripId}/places/import/naver-list`, { url }).then(r => r.data), + bulkDelete: (tripId: number | string, ids: number[]) => + apiClient.post(`/trips/${tripId}/places/bulk-delete`, { ids }).then(r => r.data), } export const assignmentsApi = { diff --git a/client/src/components/Map/MapView.test.tsx b/client/src/components/Map/MapView.test.tsx index 36ee4869..dde68ec4 100644 --- a/client/src/components/Map/MapView.test.tsx +++ b/client/src/components/Map/MapView.test.tsx @@ -1,7 +1,8 @@ import React from 'react' import { describe, it, expect, vi, afterEach } from 'vitest' import { render, screen } from '../../../tests/helpers/render' -import { fireEvent } from '@testing-library/react' +import { fireEvent, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' import { resetAllStores } from '../../../tests/helpers/store' import { buildPlace } from '../../../tests/helpers/factories' import * as photoService from '../../services/photoService' @@ -16,10 +17,13 @@ vi.mock('react-leaflet', () => ({ data-lng={position[1]} onClick={() => eventHandlers?.click?.()} > + @@ -1471,7 +1479,6 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({ const TransportIcon = RES_ICONS[res.type] || Ticket const color = '#3b82f6' const meta = typeof res.metadata === 'string' ? JSON.parse(res.metadata || '{}') : (res.metadata || {}) - const isTransportHovered = hoveredId === `transport-${res.id}` // Subtitle aus Metadaten zusammensetzen let subtitle = '' @@ -1518,15 +1525,15 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({ } setDraggingId(null); setDropTargetKey(null); dragDataRef.current = null; window.__dragData = null }} - onMouseEnter={() => setHoveredId(`transport-${res.id}`)} - onMouseLeave={() => setHoveredId(null)} + onMouseEnter={e => { e.currentTarget.style.background = `${color}12` }} + onMouseLeave={e => { e.currentTarget.style.background = `${color}08` }} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '7px 8px 7px 10px', margin: '1px 8px', borderRadius: 6, border: `1px solid ${color}33`, - background: isTransportHovered ? `${color}12` : `${color}08`, + background: `${color}08`, cursor: 'pointer', userSelect: 'none', transition: 'background 0.1s', opacity: spanPhase === 'middle' ? 0.65 : 1, @@ -1578,7 +1585,6 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({ // Notizkarte const note = item.data - const isNoteHovered = hoveredId === `note-${note.id}` const NoteIcon = getNoteIcon(note.icon) const noteIdx = idx return ( @@ -1615,20 +1621,30 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({ { divider: true }, { label: t('common.delete'), icon: Trash2, danger: true, onClick: () => deleteNote(day.id, note.id) }, ]) : undefined} - onMouseEnter={() => setHoveredId(`note-${note.id}`)} - onMouseLeave={() => setHoveredId(null)} + onMouseEnter={e => { + const grip = e.currentTarget.querySelector('.dp-grip') as HTMLElement | null + if (grip) grip.style.opacity = '1' + const editBtns = e.currentTarget.querySelector('.note-edit-buttons') as HTMLElement | null + if (editBtns) editBtns.style.opacity = '1' + }} + onMouseLeave={e => { + const grip = e.currentTarget.querySelector('.dp-grip') as HTMLElement | null + if (grip) grip.style.opacity = '0.3' + const editBtns = e.currentTarget.querySelector('.note-edit-buttons') as HTMLElement | null + if (editBtns) editBtns.style.opacity = '0' + }} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '7px 8px 7px 2px', margin: '1px 8px', borderRadius: 6, border: '1px solid var(--border-faint)', - background: isNoteHovered ? 'var(--bg-hover)' : 'var(--bg-hover)', + background: 'var(--bg-hover)', opacity: draggingId === `note-${note.id}` ? 0.4 : 1, transition: 'background 0.1s', cursor: 'grab', userSelect: 'none', }} > - {canEditDays &&
+ {canEditDays &&
}
@@ -1642,11 +1658,11 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar({
{note.time}
)}
- {canEditDays &&
+ {canEditDays &&
} - {canEditDays &&
+ {canEditDays &&
} diff --git a/client/src/components/Planner/FileImportModal.tsx b/client/src/components/Planner/FileImportModal.tsx index 687e1d14..3be41691 100644 --- a/client/src/components/Planner/FileImportModal.tsx +++ b/client/src/components/Planner/FileImportModal.tsx @@ -36,6 +36,8 @@ export default function FileImportModal({ isOpen, onClose, tripId, pushUndo, ini const [loading, setLoading] = useState(false) const [error, setError] = useState('') const [summary, setSummary] = useState(null) + const [gpxOpts, setGpxOpts] = useState({ waypoints: true, routes: true, tracks: true }) + const [kmlOpts, setKmlOpts] = useState({ points: true, paths: true }) const validateFile = (f: File): string | null => { const ext = f.name.toLowerCase().split('.').pop() @@ -127,7 +129,7 @@ export default function FileImportModal({ isOpen, onClose, tripId, pushUndo, ini try { if (ext === 'gpx') { - const result = await placesApi.importGpx(tripId, file) + const result = await placesApi.importGpx(tripId, file, gpxOpts) await loadTrip(tripId) if (result.count === 0 && result.skipped > 0) { toast.warning(t('places.importAllSkipped')) @@ -137,15 +139,13 @@ export default function FileImportModal({ isOpen, onClose, tripId, pushUndo, ini if (result.places?.length > 0) { const importedIds: number[] = result.places.map((p: { id: number }) => p.id) pushUndo?.(t('undo.importGpx'), async () => { - for (const id of importedIds) { - try { await placesApi.delete(tripId, id) } catch {} - } + try { await placesApi.bulkDelete(tripId, importedIds) } catch {} await loadTrip(tripId) }) } handleClose() } else { - const result = await placesApi.importMapFile(tripId, file) + const result = await placesApi.importMapFile(tripId, file, kmlOpts) await loadTrip(tripId) setSummary(result.summary || null) if (result.count === 0 && (result.summary?.skippedCount ?? 0) > 0) { @@ -159,9 +159,7 @@ export default function FileImportModal({ isOpen, onClose, tripId, pushUndo, ini if (result.places?.length > 0) { const importedIds: number[] = result.places.map((p: { id: number }) => p.id) pushUndo?.(t('undo.importKeyholeMarkup'), async () => { - for (const id of importedIds) { - try { await placesApi.delete(tripId, id) } catch {} - } + try { await placesApi.bulkDelete(tripId, importedIds) } catch {} await loadTrip(tripId) }) } @@ -177,7 +175,12 @@ export default function FileImportModal({ isOpen, onClose, tripId, pushUndo, ini } } - const canImport = !!file && !loading + const fileExt = file?.name.toLowerCase().split('.').pop() ?? '' + const isGpx = fileExt === 'gpx' + const isKml = fileExt === 'kml' || fileExt === 'kmz' + const gpxNoneSelected = isGpx && !gpxOpts.waypoints && !gpxOpts.routes && !gpxOpts.tracks + const kmlNoneSelected = isKml && !kmlOpts.points && !kmlOpts.paths + const canImport = !!file && !loading && !gpxNoneSelected && !kmlNoneSelected if (!isOpen) return null @@ -242,6 +245,58 @@ export default function FileImportModal({ isOpen, onClose, tripId, pushUndo, ini )}
+ {isGpx && ( +
+
+ {t('places.gpxImportTypes')} +
+ {(['waypoints', 'routes', 'tracks'] as const).map(key => ( + + ))} + {gpxNoneSelected && ( +
{t('places.gpxImportNoneSelected')}
+ )} +
+ )} + + {isKml && ( +
+
+ {t('places.kmlImportTypes')} +
+ {(['points', 'paths'] as const).map(key => ( + + ))} + {kmlNoneSelected && ( +
{t('places.kmlImportNoneSelected')}
+ )} +
+ )} + {summary && (
void onEditPlace: (place: Place) => void onDeletePlace: (placeId: number) => void + onBulkDeletePlaces?: (ids: number[]) => void + onBulkDeleteConfirm?: (ids: number[]) => void days: Day[] isMobile: boolean onCategoryFilterChange?: (categoryIds: Set) => void @@ -32,9 +35,115 @@ interface PlacesSidebarProps { pushUndo?: (label: string, undoFn: () => Promise | void) => void } +interface MemoPlaceRowProps { + place: Place + category: Category | undefined + isSelected: boolean + isPlanned: boolean + inDay: boolean + isChecked: boolean + selectMode: boolean + selectedDayId: number | null + canEditPlaces: boolean + isMobile: boolean + t: (key: string, params?: Record) => string + onPlaceClick: (id: number | null) => void + onContextMenu: (e: React.MouseEvent, place: Place) => void + onAssignToDay: (placeId: number, dayId?: number) => void + toggleSelected: (id: number) => void + setDayPickerPlace: (place: any) => void +} + +const MemoPlaceRow = React.memo(function MemoPlaceRow({ + place, category: cat, isSelected, isPlanned, inDay, isChecked, + selectMode, selectedDayId, canEditPlaces, isMobile, t, + onPlaceClick, onContextMenu, onAssignToDay, toggleSelected, setDayPickerPlace, +}: MemoPlaceRowProps) { + const hasGeometry = Boolean(place.route_geometry) + return ( +
{ + e.dataTransfer.setData('placeId', String(place.id)) + e.dataTransfer.effectAllowed = 'copy' + window.__dragData = { placeId: String(place.id) } + }} + onClick={() => { + if (selectMode) { + toggleSelected(place.id) + } else if (isMobile) { + setDayPickerPlace(place) + } else { + onPlaceClick(isSelected ? null : place.id) + } + }} + onContextMenu={selectMode ? undefined : e => onContextMenu(e, place)} + style={{ + display: 'flex', alignItems: 'center', gap: 10, + padding: '9px 14px 9px 16px', + cursor: selectMode ? 'pointer' : 'grab', + background: isChecked ? 'color-mix(in srgb, var(--accent) 8%, transparent)' : isSelected ? 'var(--border-faint)' : 'transparent', + borderBottom: '1px solid var(--border-faint)', + transition: 'background 0.1s', + contentVisibility: 'auto', + containIntrinsicSize: '0 52px', + }} + onMouseEnter={e => { if (!isSelected && !isChecked) e.currentTarget.style.background = 'var(--bg-hover)' }} + onMouseLeave={e => { if (!isSelected && !isChecked) e.currentTarget.style.background = 'transparent' }} + > + {selectMode && ( +
+ {isChecked && } +
+ )} + +
+
+ {hasGeometry && } + {cat && (() => { + const CatIcon = getCategoryIcon(cat.icon) + return + })()} + + {place.name} + +
+ {(place.description || place.address || cat?.name) && ( +
+ + {place.description || place.address || cat?.name} + +
+ )} +
+
+ {!selectMode && !inDay && selectedDayId && ( + + )} +
+
+ ) +}) + const PlacesSidebar = React.memo(function PlacesSidebar({ tripId, places, categories, assignments, selectedDayId, selectedPlaceId, - onPlaceClick, onAddPlace, onAssignToDay, onEditPlace, onDeletePlace, days, isMobile, onCategoryFilterChange, onPlacesFilterChange, pushUndo, + onPlaceClick, onAddPlace, onAssignToDay, onEditPlace, onDeletePlace, onBulkDeletePlaces, onBulkDeleteConfirm, days, isMobile, onCategoryFilterChange, onPlacesFilterChange, pushUndo, }: PlacesSidebarProps) { const { t } = useTranslation() const toast = useToast() @@ -110,9 +219,7 @@ const PlacesSidebar = React.memo(function PlacesSidebar({ if (result.places?.length > 0) { const importedIds: number[] = result.places.map((p: { id: number }) => p.id) pushUndo?.(t(provider === 'google' ? 'undo.importGoogleList' : 'undo.importNaverList'), async () => { - for (const id of importedIds) { - try { await placesApi.delete(tripId, id) } catch {} - } + try { await placesApi.bulkDelete(tripId, importedIds) } catch {} await loadTrip(tripId) }) } @@ -126,6 +233,28 @@ const PlacesSidebar = React.memo(function PlacesSidebar({ const [search, setSearch] = useState('') const [filter, setFilter] = useState('all') const [categoryFilters, setCategoryFiltersLocal] = useState>(new Set()) + const [selectMode, setSelectMode] = useState(false) + const [selectedIds, setSelectedIds] = useState>(new Set()) + const [pendingDeleteIds, setPendingDeleteIds] = useState(null) + + const exitSelectMode = () => { setSelectMode(false); setSelectedIds(new Set()) } + + // Auto-exit when all selected places have been removed from the store (e.g. after bulk delete) + useEffect(() => { + if (!selectMode || selectedIds.size === 0) return + const placeIdSet = new Set(places.map(p => p.id)) + if ([...selectedIds].every(id => !placeIdSet.has(id))) { + setSelectMode(false) + setSelectedIds(new Set()) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [places]) + + const toggleSelected = useCallback((id: number) => setSelectedIds(prev => { + const next = new Set(prev) + if (next.has(id)) next.delete(id); else next.add(id) + return next + }), []) const toggleCategoryFilter = (catId: string) => { setCategoryFiltersLocal(prev => { @@ -140,12 +269,16 @@ const PlacesSidebar = React.memo(function PlacesSidebar({ const [mobileShowDays, setMobileShowDays] = useState(false) // Alle geplanten Ort-IDs abrufen (einem Tag zugewiesen) + const hasTracks = useMemo(() => places.some(p => p.route_geometry), [places]) + useEffect(() => { if (filter === 'tracks' && !hasTracks) setFilter('all') }, [hasTracks, filter]) + const plannedIds = useMemo(() => new Set( Object.values(assignments).flatMap(da => da.map(a => a.place?.id).filter(Boolean)) ), [assignments]) const filtered = useMemo(() => places.filter(p => { if (filter === 'unplanned' && plannedIds.has(p.id)) return false + if (filter === 'tracks' && !p.route_geometry) return false if (categoryFilters.size > 0) { if (p.category_id == null) { if (!categoryFilters.has('uncategorized')) return false @@ -159,6 +292,26 @@ const PlacesSidebar = React.memo(function PlacesSidebar({ const isAssignedToSelectedDay = (placeId) => selectedDayId && (assignments[String(selectedDayId)] || []).some(a => a.place?.id === placeId) + const selectedDayIdRef = useRef(selectedDayId) + useEffect(() => { selectedDayIdRef.current = selectedDayId }, [selectedDayId]) + + const inDaySet = useMemo(() => { + if (!selectedDayId) return new Set() + return new Set((assignments[String(selectedDayId)] || []).map((a: any) => a.place?.id).filter(Boolean)) + }, [assignments, selectedDayId]) + + const openContextMenu = useCallback((e: React.MouseEvent, place: Place) => { + const selDayId = selectedDayIdRef.current + ctxMenu.open(e, [ + canEditPlaces && { label: t('common.edit'), icon: Pencil, onClick: () => onEditPlace(place) }, + selDayId && { label: t('planner.addToDay'), icon: CalendarDays, onClick: () => onAssignToDay(place.id, selDayId) }, + place.website && { label: t('inspector.website'), icon: ExternalLink, onClick: () => window.open(place.website, '_blank') }, + (place.lat && place.lng) && { label: 'Google Maps', icon: Navigation, onClick: () => window.open(`https://www.google.com/maps/search/?api=1&query=${(place as any).google_place_id ? encodeURIComponent(place.name) + '&query_place_id=' + (place as any).google_place_id : place.lat + ',' + place.lng}`, '_blank') }, + { divider: true }, + canEditPlaces && { label: t('common.delete'), icon: Trash2, danger: true, onClick: () => onDeletePlace(place.id) }, + ]) + }, [ctxMenu.open, canEditPlaces, t, onEditPlace, onAssignToDay, onDeletePlace]) + return (
{t(hasMultipleListImportProviders ? 'places.importList' : 'places.importGoogleList')} +
+ {selectMode && ( +
+ + {t('places.selectionCount', { count: selectedIds.size })} + + + + +
+ )} } {/* Filter-Tabs */}
- {[{ id: 'all', label: t('places.all') }, { id: 'unplanned', label: t('places.unplanned') }].map(f => ( - - )} -
-
+ place={place} + category={cat} + isSelected={isSelected} + isPlanned={isPlanned} + inDay={inDay} + isChecked={isChecked} + selectMode={selectMode} + selectedDayId={selectedDayId} + canEditPlaces={canEditPlaces} + isMobile={isMobile} + t={t} + onPlaceClick={onPlaceClick} + onContextMenu={openContextMenu} + onAssignToDay={onAssignToDay} + toggleSelected={toggleSelected} + setDayPickerPlace={setDayPickerPlace} + /> ) }) )} @@ -602,6 +756,14 @@ const PlacesSidebar = React.memo(function PlacesSidebar({ initialFile={sidebarDropFile} /> + {isMobile && ( + setPendingDeleteIds(null)} + onConfirm={() => { onBulkDeleteConfirm?.(pendingDeleteIds!); setPendingDeleteIds(null) }} + message={t('trip.confirm.deletePlaces', { count: pendingDeleteIds?.length ?? 0 })} + /> + )}
) }) diff --git a/client/src/components/Weather/WeatherWidget.tsx b/client/src/components/Weather/WeatherWidget.tsx index ccdc295c..669f1875 100644 --- a/client/src/components/Weather/WeatherWidget.tsx +++ b/client/src/components/Weather/WeatherWidget.tsx @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react' import { Sun, Cloud, CloudRain, CloudSnow, CloudDrizzle, CloudLightning, Wind } from 'lucide-react' -import { weatherApi } from '../../api/client' +import { fetchWeather } from '../../services/weatherQueue' import { useSettingsStore } from '../../store/settingsStore' const WEATHER_ICON_MAP = { @@ -61,7 +61,7 @@ export default function WeatherWidget({ lat, lng, date, compact = false }: Weath // Climate data: use from cache but re-fetch in background to upgrade to forecast else if (cached.type === 'climate') { setWeather(cached) - weatherApi.get(lat, lng, date) + fetchWeather(lat, lng, date) .then(data => { if (!data.error && data.temp !== undefined && data.type === 'forecast') { setWeatherCache(cacheKey, data) @@ -77,7 +77,7 @@ export default function WeatherWidget({ lat, lng, date, compact = false }: Weath return } setLoading(true) - weatherApi.get(lat, lng, date) + fetchWeather(lat, lng, date) .then(data => { if (data.error || data.temp === undefined) { setFailed(true) diff --git a/client/src/components/shared/ConfirmDialog.tsx b/client/src/components/shared/ConfirmDialog.tsx index 144fbb55..1a71cebd 100644 --- a/client/src/components/shared/ConfirmDialog.tsx +++ b/client/src/components/shared/ConfirmDialog.tsx @@ -40,7 +40,7 @@ export default function ConfirmDialog({ return (
diff --git a/client/src/hooks/useRouteCalculation.ts b/client/src/hooks/useRouteCalculation.ts index 60ce403b..bf3c763b 100644 --- a/client/src/hooks/useRouteCalculation.ts +++ b/client/src/hooks/useRouteCalculation.ts @@ -1,5 +1,6 @@ import { useState, useCallback, useRef, useEffect } from 'react' import { useSettingsStore } from '../store/settingsStore' +import { useTripStore } from '../store/tripStore' import { calculateSegments } from '../components/Map/RouteCalculator' import type { TripStoreState } from '../store/tripStore' import type { RouteSegment, RouteResult } from '../types' @@ -15,14 +16,13 @@ export function useRouteCalculation(tripStore: TripStoreState, selectedDayId: nu const [routeSegments, setRouteSegments] = useState([]) const routeCalcEnabled = useSettingsStore((s) => s.settings.route_calculation) !== false const routeAbortRef = useRef(null) - // Keep a ref to the latest tripStore so updateRouteForDay never has a stale closure - const tripStoreRef = useRef(tripStore) - tripStoreRef.current = tripStore const updateRouteForDay = useCallback(async (dayId: number | null) => { if (routeAbortRef.current) routeAbortRef.current.abort() if (!dayId) { setRoute(null); setRouteSegments([]); return } - const currentAssignments = tripStoreRef.current.assignments || {} + // Read directly from store (not a render-phase ref) so callers after optimistic + // updates or non-optimistic deletes always see the latest assignments. + const currentAssignments = useTripStore.getState().assignments || {} const da = (currentAssignments[String(dayId)] || []).slice().sort((a, b) => a.order_index - b.order_index) const waypoints = da.map((a) => a.place).filter((p) => p?.lat && p?.lng) if (waypoints.length < 2) { setRoute(null); setRouteSegments([]); return } diff --git a/client/src/i18n/translations/ar.ts b/client/src/i18n/translations/ar.ts index 218536cc..05227221 100644 --- a/client/src/i18n/translations/ar.ts +++ b/client/src/i18n/translations/ar.ts @@ -14,6 +14,9 @@ const ar: Record = { 'common.add': 'إضافة', 'common.loading': 'جارٍ التحميل...', 'common.import': 'استيراد', + 'common.select': 'تحديد', + 'common.selectAll': 'تحديد الكل', + 'common.deselectAll': 'إلغاء تحديد الكل', 'common.error': 'خطأ', 'common.unknownError': 'خطأ غير معروف', 'common.tooManyAttempts': 'محاولات كثيرة جدًا. يرجى المحاولة لاحقًا.', @@ -879,6 +882,8 @@ const ar: Record = { 'trip.toast.reservationAdded': 'تمت إضافة الحجز', 'trip.toast.deleted': 'تم الحذف', 'trip.confirm.deletePlace': 'هل تريد حذف هذا المكان؟', + 'trip.confirm.deletePlaces': 'حذف {count} أماكن؟', + 'trip.toast.placesDeleted': 'تم حذف {count} أماكن', // Day Plan Sidebar 'dayplan.emptyDay': 'لا توجد أماكن مخططة لهذا اليوم', @@ -923,6 +928,17 @@ const ar: Record = { 'places.importFileError': 'فشل الاستيراد', 'places.importAllSkipped': 'جميع الأماكن موجودة بالفعل في الرحلة.', 'places.gpxImported': 'تم استيراد {count} مكان من GPX', + 'places.gpxImportTypes': 'ما الذي تريد استيراده؟', + 'places.gpxImportWaypoints': 'نقاط الطريق', + 'places.gpxImportRoutes': 'المسارات', + 'places.gpxImportTracks': 'المسارات (مع هندسة الطريق)', + 'places.gpxImportNoneSelected': 'اختر نوعاً واحداً على الأقل للاستيراد.', + 'places.kmlImportTypes': 'ما الذي تريد استيراده؟', + 'places.kmlImportPoints': 'نقاط (Placemarks)', + 'places.kmlImportPaths': 'مسارات (LineStrings)', + 'places.kmlImportNoneSelected': 'اختر نوعًا واحدًا على الأقل.', + 'places.selectionCount': '{count} محدد', + 'places.deleteSelected': 'حذف المحدد', 'places.kmlKmzImported': 'تم استيراد {count} مكان من KMZ/KML', 'places.urlResolved': 'تم استيراد المكان من الرابط', 'places.importList': 'استيراد قائمة', @@ -939,6 +955,7 @@ const ar: Record = { 'places.assignToDay': 'إلى أي يوم تريد الإضافة؟', 'places.all': 'الكل', 'places.unplanned': 'غير مخطط', + 'places.filterTracks': 'المسارات', 'places.search': 'ابحث عن أماكن...', 'places.allCategories': 'كل الفئات', 'places.categoriesSelected': 'فئات', @@ -1754,6 +1771,7 @@ const ar: Record = { 'undo.reorder': 'تمت إعادة ترتيب الأماكن', 'undo.optimize': 'تم تحسين المسار', 'undo.deletePlace': 'تم حذف المكان', + 'undo.deletePlaces': 'تم حذف الأماكن', 'undo.moveDay': 'تم نقل المكان إلى يوم آخر', 'undo.lock': 'تم تبديل قفل المكان', 'undo.importGpx': 'استيراد GPX', diff --git a/client/src/i18n/translations/br.ts b/client/src/i18n/translations/br.ts index 9264943f..75737f5f 100644 --- a/client/src/i18n/translations/br.ts +++ b/client/src/i18n/translations/br.ts @@ -10,6 +10,9 @@ const br: Record = { 'common.add': 'Adicionar', 'common.loading': 'Carregando...', 'common.import': 'Importar', + 'common.select': 'Selecionar', + 'common.selectAll': 'Selecionar tudo', + 'common.deselectAll': 'Desmarcar tudo', 'common.error': 'Erro', 'common.unknownError': 'Erro desconhecido', 'common.tooManyAttempts': 'Muitas tentativas. Tente novamente mais tarde.', @@ -848,6 +851,8 @@ const br: Record = { 'trip.toast.reservationAdded': 'Reserva adicionada', 'trip.toast.deleted': 'Excluído', 'trip.confirm.deletePlace': 'Tem certeza de que deseja excluir este lugar?', + 'trip.confirm.deletePlaces': 'Excluir {count} lugares?', + 'trip.toast.placesDeleted': '{count} lugares excluídos', 'trip.loadingPhotos': 'Carregando fotos dos lugares...', // Day Plan Sidebar @@ -893,6 +898,17 @@ const br: Record = { 'places.importFileError': 'Importação falhou', 'places.importAllSkipped': 'Todos os lugares já estavam na viagem.', 'places.gpxImported': '{count} lugares importados do GPX', + 'places.gpxImportTypes': 'O que deseja importar?', + 'places.gpxImportWaypoints': 'Pontos de caminho', + 'places.gpxImportRoutes': 'Rotas', + 'places.gpxImportTracks': 'Trilhas (com geometria de percurso)', + 'places.gpxImportNoneSelected': 'Selecione pelo menos um tipo para importar.', + 'places.kmlImportTypes': 'O que deseja importar?', + 'places.kmlImportPoints': 'Pontos (Placemarks)', + 'places.kmlImportPaths': 'Caminhos (LineStrings)', + 'places.kmlImportNoneSelected': 'Selecione pelo menos um tipo.', + 'places.selectionCount': '{count} selecionado(s)', + 'places.deleteSelected': 'Excluir seleção', 'places.kmlKmzImported': '{count} lugares importados de KMZ/KML', 'places.urlResolved': 'Lugar importado da URL', 'places.importList': 'Importar lista', @@ -909,6 +925,7 @@ const br: Record = { 'places.assignToDay': 'Adicionar a qual dia?', 'places.all': 'Todos', 'places.unplanned': 'Não planejados', + 'places.filterTracks': 'Trilhas', 'places.search': 'Buscar lugares...', 'places.allCategories': 'Todas as categorias', 'places.categoriesSelected': 'categorias', @@ -1695,6 +1712,7 @@ const br: Record = { 'undo.reorder': 'Locais reordenados', 'undo.optimize': 'Rota otimizada', 'undo.deletePlace': 'Local excluído', + 'undo.deletePlaces': 'Lugares excluídos', 'undo.moveDay': 'Local movido para outro dia', 'undo.lock': 'Bloqueio do local alternado', 'undo.importGpx': 'Importação de GPX', diff --git a/client/src/i18n/translations/cs.ts b/client/src/i18n/translations/cs.ts index 7aca6d21..14109f4c 100644 --- a/client/src/i18n/translations/cs.ts +++ b/client/src/i18n/translations/cs.ts @@ -10,6 +10,9 @@ const cs: Record = { 'common.add': 'Přidat', 'common.loading': 'Načítání...', 'common.import': 'Importovat', + 'common.select': 'Vybrat', + 'common.selectAll': 'Vybrat vše', + 'common.deselectAll': 'Zrušit výběr všeho', 'common.error': 'Chyba', 'common.unknownError': 'Neznámá chyba', 'common.tooManyAttempts': 'Příliš mnoho pokusů. Zkuste to prosím znovu.', @@ -877,6 +880,8 @@ const cs: Record = { 'trip.toast.reservationAdded': 'Rezervace přidána', 'trip.toast.deleted': 'Smazáno', 'trip.confirm.deletePlace': 'Opravdu chcete toto místo smazat?', + 'trip.confirm.deletePlaces': 'Smazat {count} míst?', + 'trip.toast.placesDeleted': '{count} míst smazáno', // Denní plán (Day Plan) 'dayplan.emptyDay': 'Na tento den nejsou naplánována žádná místa', @@ -921,6 +926,17 @@ const cs: Record = { 'places.importFileError': 'Import se nezdařil', 'places.importAllSkipped': 'Všechna místa již byla v cestě.', 'places.gpxImported': '{count} míst importováno z GPX', + 'places.gpxImportTypes': 'Co chcete importovat?', + 'places.gpxImportWaypoints': 'Trasové body', + 'places.gpxImportRoutes': 'Trasy', + 'places.gpxImportTracks': 'Trasy GPS (s geometrií)', + 'places.gpxImportNoneSelected': 'Vyberte alespoň jeden typ k importu.', + 'places.kmlImportTypes': 'Co chcete importovat?', + 'places.kmlImportPoints': 'Body (Placemarks)', + 'places.kmlImportPaths': 'Trasy (LineStrings)', + 'places.kmlImportNoneSelected': 'Vyberte alespoň jeden typ.', + 'places.selectionCount': '{count} vybráno', + 'places.deleteSelected': 'Smazat vybrané', 'places.kmlKmzImported': 'Importováno {count} míst z KMZ/KML', 'places.urlResolved': 'Místo importováno z URL', 'places.importList': 'Import seznamu', @@ -937,6 +953,7 @@ const cs: Record = { 'places.assignToDay': 'Přidat do kterého dne?', 'places.all': 'Vše', 'places.unplanned': 'Nezařazené', + 'places.filterTracks': 'Trasy', 'places.search': 'Hledat místa...', 'places.allCategories': 'Všechny kategorie', 'places.categoriesSelected': 'kategorií', @@ -1698,6 +1715,7 @@ const cs: Record = { 'undo.reorder': 'Místa přeseřazena', 'undo.optimize': 'Trasa optimalizována', 'undo.deletePlace': 'Místo smazáno', + 'undo.deletePlaces': 'Místa smazána', 'undo.moveDay': 'Místo přesunuto na jiný den', 'undo.lock': 'Zámek místa přepnut', 'undo.importGpx': 'Import GPX', diff --git a/client/src/i18n/translations/de.ts b/client/src/i18n/translations/de.ts index c0e65fde..75f2a4af 100644 --- a/client/src/i18n/translations/de.ts +++ b/client/src/i18n/translations/de.ts @@ -10,6 +10,9 @@ const de: Record = { 'common.add': 'Hinzufügen', 'common.loading': 'Laden...', 'common.import': 'Importieren', + 'common.select': 'Auswählen', + 'common.selectAll': 'Alle auswählen', + 'common.deselectAll': 'Alle abwählen', 'common.error': 'Fehler', 'common.unknownError': 'Unbekannter Fehler', 'common.tooManyAttempts': 'Zu viele Versuche. Bitte versuchen Sie es später erneut.', @@ -880,6 +883,8 @@ const de: Record = { 'trip.toast.reservationAdded': 'Reservierung hinzugefügt', 'trip.toast.deleted': 'Gelöscht', 'trip.confirm.deletePlace': 'Möchtest du diesen Ort wirklich löschen?', + 'trip.confirm.deletePlaces': '{count} Orte löschen?', + 'trip.toast.placesDeleted': '{count} Orte gelöscht', // Day Plan Sidebar 'dayplan.emptyDay': 'Keine Orte für diesen Tag geplant', @@ -924,6 +929,17 @@ const de: Record = { 'places.importFileError': 'Import fehlgeschlagen', 'places.importAllSkipped': 'Alle Orte waren bereits in der Reise.', 'places.gpxImported': '{count} Orte aus GPX importiert', + 'places.gpxImportTypes': 'Was soll importiert werden?', + 'places.gpxImportWaypoints': 'Wegpunkte', + 'places.gpxImportRoutes': 'Routen', + 'places.gpxImportTracks': 'Tracks (mit Streckenverlauf)', + 'places.gpxImportNoneSelected': 'Wähle mindestens einen Typ zum Importieren.', + 'places.kmlImportTypes': 'Was möchtest du importieren?', + 'places.kmlImportPoints': 'Punkte (Placemarks)', + 'places.kmlImportPaths': 'Pfade (LineStrings)', + 'places.kmlImportNoneSelected': 'Wähle mindestens einen Typ aus.', + 'places.selectionCount': '{count} ausgewählt', + 'places.deleteSelected': 'Auswahl löschen', 'places.kmlKmzImported': '{count} Orte aus KMZ/KML importiert', 'places.urlResolved': 'Ort aus URL importiert', 'places.importList': 'Listenimport', @@ -940,6 +956,7 @@ const de: Record = { 'places.assignToDay': 'Zu welchem Tag hinzufügen?', 'places.all': 'Alle', 'places.unplanned': 'Ungeplant', + 'places.filterTracks': 'Tracks', 'places.search': 'Orte suchen...', 'places.allCategories': 'Alle Kategorien', 'places.categoriesSelected': 'Kategorien', @@ -1703,6 +1720,7 @@ const de: Record = { 'undo.reorder': 'Orte neu sortiert', 'undo.optimize': 'Route optimiert', 'undo.deletePlace': 'Ort gelöscht', + 'undo.deletePlaces': 'Orte gelöscht', 'undo.moveDay': 'Ort zu anderem Tag verschoben', 'undo.lock': 'Ortssperre umgeschaltet', 'undo.importGpx': 'GPX-Import', diff --git a/client/src/i18n/translations/en.ts b/client/src/i18n/translations/en.ts index fcf3eedf..d9784602 100644 --- a/client/src/i18n/translations/en.ts +++ b/client/src/i18n/translations/en.ts @@ -10,6 +10,9 @@ const en: Record = { 'common.add': 'Add', 'common.loading': 'Loading...', 'common.import': 'Import', + 'common.select': 'Select', + 'common.selectAll': 'Select all', + 'common.deselectAll': 'Deselect all', 'common.error': 'Error', 'common.unknownError': 'Unknown error', 'common.tooManyAttempts': 'Too many attempts. Please try again later.', @@ -937,6 +940,8 @@ const en: Record = { 'trip.toast.reservationAdded': 'Reservation added', 'trip.toast.deleted': 'Deleted', 'trip.confirm.deletePlace': 'Are you sure you want to delete this place?', + 'trip.confirm.deletePlaces': 'Delete {count} places?', + 'trip.toast.placesDeleted': '{count} places deleted', // Day Plan Sidebar 'dayplan.emptyDay': 'No places planned for this day', @@ -981,6 +986,17 @@ const en: Record = { 'places.importFileError': 'Import failed', 'places.importAllSkipped': 'All places were already in the trip.', 'places.gpxImported': '{count} places imported from GPX', + 'places.gpxImportTypes': 'What do you want to import?', + 'places.gpxImportWaypoints': 'Waypoints', + 'places.gpxImportRoutes': 'Routes', + 'places.gpxImportTracks': 'Tracks (with path geometry)', + 'places.gpxImportNoneSelected': 'Select at least one type to import.', + 'places.kmlImportTypes': 'What do you want to import?', + 'places.kmlImportPoints': 'Points (Placemarks)', + 'places.kmlImportPaths': 'Paths (LineStrings)', + 'places.kmlImportNoneSelected': 'Select at least one type to import.', + 'places.selectionCount': '{count} selected', + 'places.deleteSelected': 'Delete selected', 'places.kmlKmzImported': '{count} places imported from KMZ/KML', 'places.urlResolved': 'Place imported from URL', 'places.importList': 'List Import', @@ -997,6 +1013,7 @@ const en: Record = { 'places.assignToDay': 'Add to which day?', 'places.all': 'All', 'places.unplanned': 'Unplanned', + 'places.filterTracks': 'Tracks', 'places.search': 'Search places...', 'places.allCategories': 'All Categories', 'places.categoriesSelected': 'categories', @@ -1772,6 +1789,7 @@ const en: Record = { 'undo.reorder': 'Places reordered', 'undo.optimize': 'Route optimized', 'undo.deletePlace': 'Place deleted', + 'undo.deletePlaces': 'Places deleted', 'undo.moveDay': 'Place moved to another day', 'undo.lock': 'Place lock toggled', 'undo.importGpx': 'GPX import', diff --git a/client/src/i18n/translations/es.ts b/client/src/i18n/translations/es.ts index 84dd3c9d..3992e326 100644 --- a/client/src/i18n/translations/es.ts +++ b/client/src/i18n/translations/es.ts @@ -10,6 +10,9 @@ const es: Record = { 'common.add': 'Añadir', 'common.loading': 'Cargando...', 'common.import': 'Importar', + 'common.select': 'Seleccionar', + 'common.selectAll': 'Seleccionar todo', + 'common.deselectAll': 'Deseleccionar todo', 'common.error': 'Error', 'common.unknownError': 'Error desconocido', 'common.tooManyAttempts': 'Demasiados intentos. Inténtelo de nuevo más tarde.', @@ -852,6 +855,8 @@ const es: Record = { 'trip.toast.reservationAdded': 'Reserva añadida', 'trip.toast.deleted': 'Eliminado', 'trip.confirm.deletePlace': '¿Seguro que quieres eliminar este lugar?', + 'trip.confirm.deletePlaces': '¿Eliminar {count} lugares?', + 'trip.toast.placesDeleted': '{count} lugares eliminados', // Day Plan Sidebar 'dayplan.emptyDay': 'No hay lugares planificados para este día', @@ -896,6 +901,17 @@ const es: Record = { 'places.importFileError': 'Importación fallida', 'places.importAllSkipped': 'Todos los lugares ya estaban en el viaje.', 'places.gpxImported': '{count} lugares importados desde GPX', + 'places.gpxImportTypes': '¿Qué deseas importar?', + 'places.gpxImportWaypoints': 'Puntos de ruta', + 'places.gpxImportRoutes': 'Rutas', + 'places.gpxImportTracks': 'Tracks (con geometría de ruta)', + 'places.gpxImportNoneSelected': 'Selecciona al menos un tipo para importar.', + 'places.kmlImportTypes': '¿Qué deseas importar?', + 'places.kmlImportPoints': 'Puntos (Placemarks)', + 'places.kmlImportPaths': 'Rutas (LineStrings)', + 'places.kmlImportNoneSelected': 'Selecciona al menos un tipo.', + 'places.selectionCount': '{count} seleccionado(s)', + 'places.deleteSelected': 'Eliminar selección', 'places.kmlKmzImported': '{count} lugares importados desde KMZ/KML', 'places.urlResolved': 'Lugar importado desde URL', 'places.importList': 'Importar lista', @@ -912,6 +928,7 @@ const es: Record = { 'places.assignToDay': '¿A qué día añadirlo?', 'places.all': 'Todo', 'places.unplanned': 'Sin planificar', + 'places.filterTracks': 'Rutas', 'places.search': 'Buscar lugares...', 'places.allCategories': 'Todas las categorías', 'places.categoriesSelected': 'categorías', @@ -1705,6 +1722,7 @@ const es: Record = { 'undo.reorder': 'Lugares reordenados', 'undo.optimize': 'Ruta optimizada', 'undo.deletePlace': 'Lugar eliminado', + 'undo.deletePlaces': 'Lugares eliminados', 'undo.moveDay': 'Lugar movido a otro día', 'undo.lock': 'Bloqueo de lugar activado/desactivado', 'undo.importGpx': 'Importación GPX', diff --git a/client/src/i18n/translations/fr.ts b/client/src/i18n/translations/fr.ts index 436bd107..cf68e2aa 100644 --- a/client/src/i18n/translations/fr.ts +++ b/client/src/i18n/translations/fr.ts @@ -10,6 +10,9 @@ const fr: Record = { 'common.add': 'Ajouter', 'common.loading': 'Chargement…', 'common.import': 'Importer', + 'common.select': 'Sélectionner', + 'common.selectAll': 'Tout sélectionner', + 'common.deselectAll': 'Tout désélectionner', 'common.error': 'Erreur', 'common.unknownError': 'Erreur inconnue', 'common.tooManyAttempts': 'Trop de tentatives. Veuillez réessayer plus tard.', @@ -876,6 +879,8 @@ const fr: Record = { 'trip.toast.reservationAdded': 'Réservation ajoutée', 'trip.toast.deleted': 'Supprimé', 'trip.confirm.deletePlace': 'Voulez-vous vraiment supprimer ce lieu ?', + 'trip.confirm.deletePlaces': 'Supprimer {count} lieux?', + 'trip.toast.placesDeleted': '{count} lieux supprimés', // Day Plan Sidebar 'dayplan.emptyDay': 'Aucun lieu prévu pour ce jour', @@ -920,6 +925,17 @@ const fr: Record = { 'places.importFileError': 'Importation échouée', 'places.importAllSkipped': 'Tous les lieux étaient déjà dans le voyage.', 'places.gpxImported': '{count} lieux importés depuis GPX', + 'places.gpxImportTypes': 'Que voulez-vous importer?', + 'places.gpxImportWaypoints': 'Points de passage', + 'places.gpxImportRoutes': 'Itinéraires', + 'places.gpxImportTracks': 'Traces (avec géométrie)', + 'places.gpxImportNoneSelected': 'Sélectionnez au moins un type à importer.', + 'places.kmlImportTypes': 'Que souhaitez-vous importer ?', + 'places.kmlImportPoints': 'Points (Placemarks)', + 'places.kmlImportPaths': 'Chemins (LineStrings)', + 'places.kmlImportNoneSelected': 'Sélectionnez au moins un type.', + 'places.selectionCount': '{count} sélectionné(s)', + 'places.deleteSelected': 'Supprimer la sélection', 'places.kmlKmzImported': '{count} lieux importés depuis KMZ/KML', 'places.urlResolved': 'Lieu importé depuis l\'URL', 'places.importList': 'Import de liste', @@ -936,6 +952,7 @@ const fr: Record = { 'places.assignToDay': 'Ajouter à quel jour ?', 'places.all': 'Tous', 'places.unplanned': 'Non planifiés', + 'places.filterTracks': 'Traces', 'places.search': 'Rechercher des lieux…', 'places.allCategories': 'Toutes les catégories', 'places.categoriesSelected': 'catégories', @@ -1699,6 +1716,7 @@ const fr: Record = { 'undo.reorder': 'Lieux réorganisés', 'undo.optimize': 'Itinéraire optimisé', 'undo.deletePlace': 'Lieu supprimé', + 'undo.deletePlaces': 'Lieux supprimés', 'undo.moveDay': 'Lieu déplacé vers un autre jour', 'undo.lock': 'Verrouillage du lieu modifié', 'undo.importGpx': 'Import GPX', diff --git a/client/src/i18n/translations/hu.ts b/client/src/i18n/translations/hu.ts index e3b19545..9b693601 100644 --- a/client/src/i18n/translations/hu.ts +++ b/client/src/i18n/translations/hu.ts @@ -10,6 +10,9 @@ const hu: Record = { 'common.add': 'Hozzáadás', 'common.loading': 'Betöltés...', 'common.import': 'Importálás', + 'common.select': 'Kiválaszt', + 'common.selectAll': 'Mindet kiválaszt', + 'common.deselectAll': 'Összes kijelölés megszüntetése', 'common.error': 'Hiba', 'common.unknownError': 'Ismeretlen hiba', 'common.tooManyAttempts': 'Túl sok próbálkozás. Kérjük, próbálja újra később.', @@ -876,6 +879,8 @@ const hu: Record = { 'trip.toast.reservationAdded': 'Foglalás hozzáadva', 'trip.toast.deleted': 'Törölve', 'trip.confirm.deletePlace': 'Biztosan törölni szeretnéd ezt a helyet?', + 'trip.confirm.deletePlaces': '{count} helyet töröl?', + 'trip.toast.placesDeleted': '{count} hely törölve', 'trip.loadingPhotos': 'Helyek fotóinak betöltése...', // Napi terv oldalsáv @@ -921,6 +926,17 @@ const hu: Record = { 'places.importFileError': 'Importálás sikertelen', 'places.importAllSkipped': 'Minden hely már szerepel az utazásban.', 'places.gpxImported': '{count} hely importálva GPX-ből', + 'places.gpxImportTypes': 'Mit szeretnél importálni?', + 'places.gpxImportWaypoints': 'Útpontok', + 'places.gpxImportRoutes': 'Útvonalak', + 'places.gpxImportTracks': 'Nyomvonalak (útvonalgeometriával)', + 'places.gpxImportNoneSelected': 'Válassz legalább egy típust az importáláshoz.', + 'places.kmlImportTypes': 'Mit szeretnél importálni?', + 'places.kmlImportPoints': 'Pontok (Placemarks)', + 'places.kmlImportPaths': 'Útvonalak (LineStrings)', + 'places.kmlImportNoneSelected': 'Válassz legalább egy típust.', + 'places.selectionCount': '{count} kiválasztva', + 'places.deleteSelected': 'Kijelöltek törlése', 'places.kmlKmzImported': '{count} hely importálva KMZ/KML-ből', 'places.urlResolved': 'Hely importálva URL-ből', 'places.importList': 'Lista importálás', @@ -937,6 +953,7 @@ const hu: Record = { 'places.assignToDay': 'Melyik naphoz adod?', 'places.all': 'Összes', 'places.unplanned': 'Nem tervezett', + 'places.filterTracks': 'Nyomvonalak', 'places.search': 'Helyek keresése...', 'places.allCategories': 'Összes kategória', 'places.categoriesSelected': 'kategória', @@ -1697,6 +1714,7 @@ const hu: Record = { 'undo.reorder': 'Helyek átrendezve', 'undo.optimize': 'Útvonal optimalizálva', 'undo.deletePlace': 'Hely törölve', + 'undo.deletePlaces': 'Helyek törölve', 'undo.moveDay': 'Hely áthelyezve másik napra', 'undo.lock': 'Hely zárolása váltva', 'undo.importGpx': 'GPX importálás', diff --git a/client/src/i18n/translations/id.ts b/client/src/i18n/translations/id.ts index bdba0cf8..8ed9190c 100644 --- a/client/src/i18n/translations/id.ts +++ b/client/src/i18n/translations/id.ts @@ -10,6 +10,9 @@ const id: Record = { 'common.add': 'Tambah', 'common.loading': 'Memuat...', 'common.import': 'Impor', + 'common.select': 'Pilih', + 'common.selectAll': 'Pilih semua', + 'common.deselectAll': 'Batalkan semua pilihan', 'common.error': 'Kesalahan', 'common.unknownError': 'Kesalahan tidak diketahui', 'common.tooManyAttempts': 'Terlalu banyak percobaan. Coba lagi nanti.', @@ -937,6 +940,8 @@ const id: Record = { 'trip.toast.reservationAdded': 'Reservasi ditambahkan', 'trip.toast.deleted': 'Dihapus', 'trip.confirm.deletePlace': 'Apakah kamu yakin ingin menghapus tempat ini?', + 'trip.confirm.deletePlaces': 'Hapus {count} tempat?', + 'trip.toast.placesDeleted': '{count} tempat dihapus', // Day Plan Sidebar 'dayplan.emptyDay': 'Belum ada tempat yang direncanakan untuk hari ini', @@ -981,6 +986,17 @@ const id: Record = { 'places.importFileError': 'Impor gagal', 'places.importAllSkipped': 'Semua tempat sudah ada di perjalanan.', 'places.gpxImported': '{count} tempat diimpor dari GPX', + 'places.gpxImportTypes': 'Apa yang ingin diimpor?', + 'places.gpxImportWaypoints': 'Titik jalan', + 'places.gpxImportRoutes': 'Rute', + 'places.gpxImportTracks': 'Trek (dengan geometri jalur)', + 'places.gpxImportNoneSelected': 'Pilih setidaknya satu jenis untuk diimpor.', + 'places.kmlImportTypes': 'Apa yang ingin diimpor?', + 'places.kmlImportPoints': 'Titik (Placemarks)', + 'places.kmlImportPaths': 'Jalur (LineStrings)', + 'places.kmlImportNoneSelected': 'Pilih setidaknya satu jenis.', + 'places.selectionCount': '{count} dipilih', + 'places.deleteSelected': 'Hapus yang dipilih', 'places.kmlKmzImported': '{count} tempat diimpor dari KMZ/KML', 'places.urlResolved': 'Tempat diimpor dari URL', 'places.importList': 'Impor Daftar', @@ -997,6 +1013,7 @@ const id: Record = { 'places.assignToDay': 'Tambah ke hari mana?', 'places.all': 'Semua', 'places.unplanned': 'Belum direncanakan', + 'places.filterTracks': 'Trek', 'places.search': 'Cari tempat...', 'places.allCategories': 'Semua Kategori', 'places.categoriesSelected': 'kategori', @@ -1772,6 +1789,7 @@ const id: Record = { 'undo.reorder': 'Tempat diurutkan ulang', 'undo.optimize': 'Rute dioptimalkan', 'undo.deletePlace': 'Tempat dihapus', + 'undo.deletePlaces': 'Tempat dihapus', 'undo.moveDay': 'Tempat dipindah ke hari lain', 'undo.lock': 'Kunci tempat diubah', 'undo.importGpx': 'Impor GPX', diff --git a/client/src/i18n/translations/it.ts b/client/src/i18n/translations/it.ts index 178305e5..d8fc3d9f 100644 --- a/client/src/i18n/translations/it.ts +++ b/client/src/i18n/translations/it.ts @@ -10,6 +10,9 @@ const it: Record = { 'common.add': 'Aggiungi', 'common.loading': 'Caricamento...', 'common.import': 'Importa', + 'common.select': 'Seleziona', + 'common.selectAll': 'Seleziona tutto', + 'common.deselectAll': 'Deseleziona tutto', 'common.error': 'Errore', 'common.unknownError': 'Errore sconosciuto', 'common.tooManyAttempts': 'Troppi tentativi. Riprova più tardi.', @@ -876,6 +879,8 @@ const it: Record = { 'trip.toast.reservationAdded': 'Prenotazione aggiunta', 'trip.toast.deleted': 'Eliminato', 'trip.confirm.deletePlace': 'Sei sicuro di voler eliminare questo luogo?', + 'trip.confirm.deletePlaces': 'Eliminare {count} luoghi?', + 'trip.toast.placesDeleted': '{count} luoghi eliminati', 'trip.loadingPhotos': 'Caricamento foto dei luoghi...', // Day Plan Sidebar @@ -921,6 +926,17 @@ const it: Record = { 'places.importFileError': 'Importazione non riuscita', 'places.importAllSkipped': 'Tutti i luoghi erano già nel viaggio.', 'places.gpxImported': '{count} luoghi importati da GPX', + 'places.gpxImportTypes': 'Cosa vuoi importare?', + 'places.gpxImportWaypoints': 'Waypoint', + 'places.gpxImportRoutes': 'Percorsi', + 'places.gpxImportTracks': 'Tracce (con geometria percorso)', + 'places.gpxImportNoneSelected': 'Seleziona almeno un tipo da importare.', + 'places.kmlImportTypes': 'Cosa vuoi importare?', + 'places.kmlImportPoints': 'Punti (Placemarks)', + 'places.kmlImportPaths': 'Percorsi (LineStrings)', + 'places.kmlImportNoneSelected': 'Seleziona almeno un tipo.', + 'places.selectionCount': '{count} selezionato/i', + 'places.deleteSelected': 'Elimina selezionati', 'places.kmlKmzImported': '{count} luoghi importati da KMZ/KML', 'places.urlResolved': 'Luogo importato dall\'URL', 'places.importList': 'Importa lista', @@ -937,6 +953,7 @@ const it: Record = { 'places.assignToDay': 'A quale giorno aggiungere?', 'places.all': 'Tutti', 'places.unplanned': 'Non pianificati', + 'places.filterTracks': 'Tracce', 'places.search': 'Cerca luoghi...', 'places.allCategories': 'Tutte le categorie', 'places.categoriesSelected': 'categorie', @@ -1701,6 +1718,7 @@ const it: Record = { 'undo.reorder': 'Luoghi riordinati', 'undo.optimize': 'Percorso ottimizzato', 'undo.deletePlace': 'Luogo eliminato', + 'undo.deletePlaces': 'Luoghi eliminati', 'undo.moveDay': 'Luogo spostato in altro giorno', 'undo.lock': 'Blocco luogo modificato', 'undo.importGpx': 'Importazione GPX', diff --git a/client/src/i18n/translations/nl.ts b/client/src/i18n/translations/nl.ts index 17a7165c..880485e2 100644 --- a/client/src/i18n/translations/nl.ts +++ b/client/src/i18n/translations/nl.ts @@ -10,6 +10,9 @@ const nl: Record = { 'common.add': 'Toevoegen', 'common.loading': 'Laden...', 'common.import': 'Importeren', + 'common.select': 'Selecteren', + 'common.selectAll': 'Alles selecteren', + 'common.deselectAll': 'Alles deselecteren', 'common.error': 'Fout', 'common.unknownError': 'Onbekende fout', 'common.tooManyAttempts': 'Te veel pogingen. Probeer het later opnieuw.', @@ -876,6 +879,8 @@ const nl: Record = { 'trip.toast.reservationAdded': 'Reservering toegevoegd', 'trip.toast.deleted': 'Verwijderd', 'trip.confirm.deletePlace': 'Weet je zeker dat je deze plaats wilt verwijderen?', + 'trip.confirm.deletePlaces': '{count} plaatsen verwijderen?', + 'trip.toast.placesDeleted': '{count} plaatsen verwijderd', // Day Plan Sidebar 'dayplan.emptyDay': 'Geen plaatsen gepland voor deze dag', @@ -920,6 +925,17 @@ const nl: Record = { 'places.importFileError': 'Importeren mislukt', 'places.importAllSkipped': 'Alle plaatsen waren al in de reis.', 'places.gpxImported': '{count} plaatsen geïmporteerd uit GPX', + 'places.gpxImportTypes': 'Wat wil je importeren?', + 'places.gpxImportWaypoints': 'Waypoints', + 'places.gpxImportRoutes': 'Routes', + 'places.gpxImportTracks': 'Tracks (met routegeometrie)', + 'places.gpxImportNoneSelected': 'Selecteer minstens één type om te importeren.', + 'places.kmlImportTypes': 'Wat wil je importeren?', + 'places.kmlImportPoints': 'Punten (Placemarks)', + 'places.kmlImportPaths': 'Paden (LineStrings)', + 'places.kmlImportNoneSelected': 'Selecteer minstens één type.', + 'places.selectionCount': '{count} geselecteerd', + 'places.deleteSelected': 'Selectie verwijderen', 'places.kmlKmzImported': '{count} plaatsen geïmporteerd uit KMZ/KML', 'places.urlResolved': 'Plaats geïmporteerd van URL', 'places.importList': 'Lijst importeren', @@ -936,6 +952,7 @@ const nl: Record = { 'places.assignToDay': 'Aan welke dag toevoegen?', 'places.all': 'Alle', 'places.unplanned': 'Ongepland', + 'places.filterTracks': 'Tracks', 'places.search': 'Plaatsen zoeken...', 'places.allCategories': 'Alle categorieën', 'places.categoriesSelected': 'categorieën', @@ -1699,6 +1716,7 @@ const nl: Record = { 'undo.reorder': 'Locaties hergeordend', 'undo.optimize': 'Route geoptimaliseerd', 'undo.deletePlace': 'Locatie verwijderd', + 'undo.deletePlaces': 'Plaatsen verwijderd', 'undo.moveDay': 'Locatie naar andere dag verplaatst', 'undo.lock': 'Vergrendeling locatie gewijzigd', 'undo.importGpx': 'GPX-import', diff --git a/client/src/i18n/translations/pl.ts b/client/src/i18n/translations/pl.ts index 080d5cb3..09829707 100644 --- a/client/src/i18n/translations/pl.ts +++ b/client/src/i18n/translations/pl.ts @@ -843,6 +843,8 @@ const pl: Record = { 'trip.toast.reservationAdded': 'Rezerwacja została dodana', 'trip.toast.deleted': 'Usunięto', 'trip.confirm.deletePlace': 'Czy na pewno chcesz usunąć to miejsce?', + 'trip.confirm.deletePlaces': 'Usunąć {count} miejsc?', + 'trip.toast.placesDeleted': '{count} miejsc usunięto', // Day Plan Sidebar 'dayplan.emptyDay': 'Brak miejsc zaplanowanych na ten dzień', @@ -887,6 +889,17 @@ const pl: Record = { 'places.importFileError': 'Import nie powiódł się', 'places.importAllSkipped': 'Wszystkie miejsca były już w podróży.', 'places.gpxImported': '{count} miejsc zaimportowanych z GPX', + 'places.gpxImportTypes': 'Co chcesz zaimportować?', + 'places.gpxImportWaypoints': 'Punkty trasy', + 'places.gpxImportRoutes': 'Trasy', + 'places.gpxImportTracks': 'Trasy GPS (ze śladem)', + 'places.gpxImportNoneSelected': 'Wybierz co najmniej jeden typ do importu.', + 'places.kmlImportTypes': 'Co chcesz zaimportować?', + 'places.kmlImportPoints': 'Punkty (Placemarks)', + 'places.kmlImportPaths': 'Ścieżki (LineStrings)', + 'places.kmlImportNoneSelected': 'Wybierz co najmniej jeden typ.', + 'places.selectionCount': '{count} zaznaczono', + 'places.deleteSelected': 'Usuń wybrane', 'places.kmlKmzImported': 'Zaimportowano {count} miejsc z KMZ/KML', 'places.urlResolved': 'Miejsce zaimportowane z URL', 'places.kmlKmzSummaryValues': 'Placemarks: {total} • Zaimportowano: {created} • Pominięto: {skipped}', @@ -894,6 +907,7 @@ const pl: Record = { 'places.assignToDay': 'Do którego dnia dodać?', 'places.all': 'Wszystkie', 'places.unplanned': 'Niezaplanowane', + 'places.filterTracks': 'Trasy', 'places.search': 'Szukaj miejsc...', 'places.allCategories': 'Wszystkie kategorie', 'places.categoriesSelected': 'kategorii', @@ -1586,6 +1600,9 @@ const pl: Record = { 'collab.polls.delete': 'Usuń', 'collab.polls.closedSection': 'Zamknięte', 'common.import': 'Importuj', + 'common.select': 'Wybierz', + 'common.selectAll': 'Zaznacz wszystko', + 'common.deselectAll': 'Odznacz wszystko', 'common.saved': 'Zapisano', 'trips.reminder': 'Przypomnienie', 'trips.reminderNone': 'Brak', @@ -1759,6 +1776,7 @@ const pl: Record = { 'undo.reorder': 'Kolejność zmieniona', 'undo.optimize': 'Trasa zoptymalizowana', 'undo.deletePlace': 'Miejsce usunięte', + 'undo.deletePlaces': 'Miejsca usunięte', 'undo.moveDay': 'Miejsce przeniesione', 'undo.lock': 'Blokada przełączona', 'undo.importGpx': 'Import GPX', diff --git a/client/src/i18n/translations/ru.ts b/client/src/i18n/translations/ru.ts index 9961de3d..3b32e53a 100644 --- a/client/src/i18n/translations/ru.ts +++ b/client/src/i18n/translations/ru.ts @@ -10,6 +10,9 @@ const ru: Record = { 'common.add': 'Добавить', 'common.loading': 'Загрузка...', 'common.import': 'Импорт', + 'common.select': 'Выбрать', + 'common.selectAll': 'Выбрать всё', + 'common.deselectAll': 'Снять выделение со всех', 'common.error': 'Ошибка', 'common.unknownError': 'Неизвестная ошибка', 'common.tooManyAttempts': 'Слишком много попыток. Попробуйте позже.', @@ -876,6 +879,8 @@ const ru: Record = { 'trip.toast.reservationAdded': 'Бронирование добавлено', 'trip.toast.deleted': 'Удалено', 'trip.confirm.deletePlace': 'Вы уверены, что хотите удалить это место?', + 'trip.confirm.deletePlaces': 'Удалить {count} мест?', + 'trip.toast.placesDeleted': '{count} мест удалено', // Day Plan Sidebar 'dayplan.emptyDay': 'На этот день мест не запланировано', @@ -920,6 +925,17 @@ const ru: Record = { 'places.importFileError': 'Ошибка импорта', 'places.importAllSkipped': 'Все места уже были в поездке.', 'places.gpxImported': '{count} мест импортировано из GPX', + 'places.gpxImportTypes': 'Что импортировать?', + 'places.gpxImportWaypoints': 'Путевые точки', + 'places.gpxImportRoutes': 'Маршруты', + 'places.gpxImportTracks': 'Треки (с геометрией пути)', + 'places.gpxImportNoneSelected': 'Выберите хотя бы один тип для импорта.', + 'places.kmlImportTypes': 'Что вы хотите импортировать?', + 'places.kmlImportPoints': 'Точки (Placemarks)', + 'places.kmlImportPaths': 'Маршруты (LineStrings)', + 'places.kmlImportNoneSelected': 'Выберите хотя бы один тип.', + 'places.selectionCount': '{count} выбрано', + 'places.deleteSelected': 'Удалить выбранные', 'places.kmlKmzImported': '{count} мест импортировано из KMZ/KML', 'places.urlResolved': 'Место импортировано из URL', 'places.importList': 'Импорт списка', @@ -936,6 +952,7 @@ const ru: Record = { 'places.assignToDay': 'Добавить в какой день?', 'places.all': 'Все', 'places.unplanned': 'Незапланированные', + 'places.filterTracks': 'Треки', 'places.search': 'Поиск мест...', 'places.allCategories': 'Все категории', 'places.categoriesSelected': 'категорий', @@ -1696,6 +1713,7 @@ const ru: Record = { 'undo.reorder': 'Места переупорядочены', 'undo.optimize': 'Маршрут оптимизирован', 'undo.deletePlace': 'Место удалено', + 'undo.deletePlaces': 'Места удалены', 'undo.moveDay': 'Место перемещено в другой день', 'undo.lock': 'Блокировка места изменена', 'undo.importGpx': 'Импорт GPX', diff --git a/client/src/i18n/translations/zh.ts b/client/src/i18n/translations/zh.ts index 98d5b4db..f363980b 100644 --- a/client/src/i18n/translations/zh.ts +++ b/client/src/i18n/translations/zh.ts @@ -10,6 +10,9 @@ const zh: Record = { 'common.add': '添加', 'common.loading': '加载中...', 'common.import': '导入', + 'common.select': '选择', + 'common.selectAll': '全选', + 'common.deselectAll': '取消全选', 'common.error': '错误', 'common.unknownError': '未知错误', 'common.tooManyAttempts': '尝试次数过多,请稍后再试。', @@ -876,6 +879,8 @@ const zh: Record = { 'trip.toast.reservationAdded': '预订已添加', 'trip.toast.deleted': '已删除', 'trip.confirm.deletePlace': '确定要删除这个地点吗?', + 'trip.confirm.deletePlaces': '删除 {count} 个地点?', + 'trip.toast.placesDeleted': '已删除 {count} 个地点', // Day Plan Sidebar 'dayplan.emptyDay': '当天暂无计划', @@ -920,6 +925,17 @@ const zh: Record = { 'places.importFileError': '导入失败', 'places.importAllSkipped': '所有地点已在行程中。', 'places.gpxImported': '已从 GPX 导入 {count} 个地点', + 'places.gpxImportTypes': '要导入什么?', + 'places.gpxImportWaypoints': '路点', + 'places.gpxImportRoutes': '路线', + 'places.gpxImportTracks': '轨迹(含路径几何)', + 'places.gpxImportNoneSelected': '请至少选择一种导入类型。', + 'places.kmlImportTypes': '要导入什么?', + 'places.kmlImportPoints': '点(Placemarks)', + 'places.kmlImportPaths': '路径(LineStrings)', + 'places.kmlImportNoneSelected': '请至少选择一种类型。', + 'places.selectionCount': '已选 {count} 项', + 'places.deleteSelected': '删除所选', 'places.kmlKmzImported': '已从 KMZ/KML 导入 {count} 个地点', 'places.urlResolved': '已从 URL 导入地点', 'places.importList': '列表导入', @@ -936,6 +952,7 @@ const zh: Record = { 'places.assignToDay': '添加到哪一天?', 'places.all': '全部', 'places.unplanned': '未规划', + 'places.filterTracks': '路线', 'places.search': '搜索地点...', 'places.allCategories': '所有分类', 'places.categoriesSelected': '个分类', @@ -1696,6 +1713,7 @@ const zh: Record = { 'undo.reorder': '地点已重新排序', 'undo.optimize': '路线已优化', 'undo.deletePlace': '地点已删除', + 'undo.deletePlaces': '地点已删除', 'undo.moveDay': '地点已移至另一天', 'undo.lock': '地点锁定已切换', 'undo.importGpx': 'GPX 导入', diff --git a/client/src/i18n/translations/zhTw.ts b/client/src/i18n/translations/zhTw.ts index 9d83ae40..ac29b628 100644 --- a/client/src/i18n/translations/zhTw.ts +++ b/client/src/i18n/translations/zhTw.ts @@ -10,6 +10,9 @@ const zhTw: Record = { 'common.add': '新增', 'common.loading': '載入中...', 'common.import': '匯入', + 'common.select': '選擇', + 'common.selectAll': '全選', + 'common.deselectAll': '取消全選', 'common.error': '錯誤', 'common.unknownError': '未知錯誤', 'common.tooManyAttempts': '嘗試次數過多,請稍後再試。', @@ -936,6 +939,8 @@ const zhTw: Record = { 'trip.toast.reservationAdded': '預訂已新增', 'trip.toast.deleted': '已刪除', 'trip.confirm.deletePlace': '確定要刪除這個地點嗎?', + 'trip.confirm.deletePlaces': '刪除 {count} 個地點?', + 'trip.toast.placesDeleted': '已刪除 {count} 個地點', // Day Plan Sidebar 'dayplan.emptyDay': '當天暫無計劃', @@ -980,6 +985,17 @@ const zhTw: Record = { 'places.importFileError': '匯入失敗', 'places.importAllSkipped': '所有地點已在行程中。', 'places.gpxImported': '已從 GPX 匯入 {count} 個地點', + 'places.gpxImportTypes': '要匯入什麼?', + 'places.gpxImportWaypoints': '路點', + 'places.gpxImportRoutes': '路線', + 'places.gpxImportTracks': '軌跡(含路徑幾何)', + 'places.gpxImportNoneSelected': '請至少選擇一種匯入類型。', + 'places.kmlImportTypes': '要匯入什麼?', + 'places.kmlImportPoints': '點(Placemarks)', + 'places.kmlImportPaths': '路徑(LineStrings)', + 'places.kmlImportNoneSelected': '請至少選擇一種類型。', + 'places.selectionCount': '已選 {count} 項', + 'places.deleteSelected': '刪除所選', 'places.kmlKmzImported': '已從 KMZ/KML 匯入 {count} 個地點', 'places.urlResolved': '已從 URL 匯入地點', 'places.importList': '列表匯入', @@ -996,6 +1012,7 @@ const zhTw: Record = { 'places.assignToDay': '新增到哪一天?', 'places.all': '全部', 'places.unplanned': '未規劃', + 'places.filterTracks': '路線', 'places.search': '搜尋地點...', 'places.allCategories': '所有分類', 'places.categoriesSelected': '個分類', @@ -1756,6 +1773,7 @@ const zhTw: Record = { 'undo.reorder': '地點已重新排序', 'undo.optimize': '路線已最佳化', 'undo.deletePlace': '地點已刪除', + 'undo.deletePlaces': '地點已刪除', 'undo.moveDay': '地點已移至另一天', 'undo.lock': '地點鎖定已切換', 'undo.importGpx': 'GPX 匯入', diff --git a/client/src/pages/TripPlannerPage.tsx b/client/src/pages/TripPlannerPage.tsx index 020be261..933e8086 100644 --- a/client/src/pages/TripPlannerPage.tsx +++ b/client/src/pages/TripPlannerPage.tsx @@ -169,6 +169,7 @@ export default function TripPlannerPage(): React.ReactElement | null { const [fitKey, setFitKey] = useState(0) const [mobileSidebarOpen, setMobileSidebarOpen] = useState<'left' | 'right' | null>(null) const [deletePlaceId, setDeletePlaceId] = useState(null) + const [deletePlaceIds, setDeletePlaceIds] = useState(null) const [isMobile, setIsMobile] = useState(() => window.innerWidth < 768) useEffect(() => { @@ -250,6 +251,7 @@ export default function TripPlannerPage(): React.ReactElement | null { return places.filter(p => { if (!p.lat || !p.lng) return false + if (mapPlacesFilter === 'tracks' && !p.route_geometry) return false if (mapCategoryFilter.size > 0) { if (p.category_id == null) { if (!mapCategoryFilter.has('uncategorized')) return false @@ -363,6 +365,7 @@ export default function TripPlannerPage(): React.ReactElement | null { try { await tripActions.deletePlace(tripId, deletePlaceId) if (selectedPlaceId === deletePlaceId) setSelectedPlaceId(null) + updateRouteForDay(selectedDayId) toast.success(t('trip.toast.placeDeleted')) if (capturedPlace) { pushUndo(t('undo.deletePlace'), async () => { @@ -382,7 +385,38 @@ export default function TripPlannerPage(): React.ReactElement | null { }) } } catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) } - }, [deletePlaceId, tripId, toast, selectedPlaceId, pushUndo]) + }, [deletePlaceId, tripId, toast, selectedPlaceId, selectedDayId, updateRouteForDay, pushUndo]) + + const confirmDeletePlaces = useCallback(async (ids?: number[]) => { + const targetIds = ids ?? deletePlaceIds + if (!targetIds?.length) return + const state = useTripStore.getState() + const capturedPlaces = state.places.filter(p => targetIds.includes(p.id)) + const capturedAssignments = Object.entries(state.assignments).flatMap(([dayId, as]) => + as.filter(a => a.place?.id != null && targetIds.includes(a.place.id)).map(a => ({ dayId: Number(dayId), placeId: a.place!.id, orderIndex: a.order_index })) + ) + try { + await tripActions.deletePlacesMany(tripId, targetIds) + if (selectedPlaceId != null && targetIds.includes(selectedPlaceId)) setSelectedPlaceId(null) + if (!ids) setDeletePlaceIds(null) + updateRouteForDay(selectedDayId) + toast.success(t('trip.toast.placesDeleted', { count: capturedPlaces.length })) + if (capturedPlaces.length > 0) { + pushUndo(t('undo.deletePlaces'), async () => { + for (const place of capturedPlaces) { + const newPlace = await tripActions.addPlace(tripId, { + name: place.name, description: place.description, + lat: place.lat, lng: place.lng, address: place.address, + category_id: place.category_id, icon: place.icon, price: place.price, + }) + for (const a of capturedAssignments.filter(x => x.placeId === place.id)) { + await tripActions.assignPlaceToDay(tripId, a.dayId, newPlace.id, a.orderIndex) + } + } + }) + } + } catch (err: unknown) { toast.error(err instanceof Error ? err.message : t('common.unknownError')) } + }, [deletePlaceIds, tripId, toast, selectedPlaceId, selectedDayId, updateRouteForDay, pushUndo]) const handleAssignToDay = useCallback(async (placeId, dayId, position) => { const target = dayId || selectedDayId @@ -408,6 +442,7 @@ export default function TripPlannerPage(): React.ReactElement | null { const capturedOrderIndex = capturedAssignment?.order_index ?? 0 try { await tripActions.removeAssignment(tripId, dayId, assignmentId) + updateRouteForDay(dayId) if (capturedPlaceId != null) { const capturedDayId = dayId const capturedPos = capturedOrderIndex @@ -745,6 +780,7 @@ export default function TripPlannerPage(): React.ReactElement | null { onAssignToDay={handleAssignToDay} onEditPlace={(place) => { setEditingPlace(place); setEditingAssignmentId(null); setShowPlaceForm(true) }} onDeletePlace={(placeId) => handleDeletePlace(placeId)} + onBulkDeletePlaces={(ids) => setDeletePlaceIds(ids)} onCategoryFilterChange={setMapCategoryFilter} onPlacesFilterChange={setMapPlacesFilter} pushUndo={pushUndo} @@ -903,7 +939,7 @@ export default function TripPlannerPage(): React.ReactElement | null {
{mobileSidebarOpen === 'left' ? { handleSelectDay(id); setMobileSidebarOpen(null) }} onPlaceClick={(placeId, assignmentId) => { handlePlaceClick(placeId, assignmentId); setMobileSidebarOpen(null) }} onReorder={handleReorder} onUpdateDayTitle={handleUpdateDayTitle} onAssignToDay={handleAssignToDay} onRouteCalculated={(r) => { if (r) { setRoute(r.coordinates); setRouteInfo({ distance: r.distanceText, duration: r.durationText }) } }} reservations={reservations} onAddReservation={(dayId) => { setEditingReservation(null); tripActions.setSelectedDay(dayId); setShowReservationModal(true); setMobileSidebarOpen(null) }} onAddPlace={() => { setEditingPlace(null); setShowPlaceForm(true); setMobileSidebarOpen(null) }} onDayDetail={(day) => { setShowDayDetail(day); setSelectedPlaceId(null); selectAssignment(null); setMobileSidebarOpen(null) }} accommodations={tripAccommodations} onNavigateToFiles={() => { setMobileSidebarOpen(null); handleTabChange('dateien') }} onExpandedDaysChange={setExpandedDayIds} pushUndo={pushUndo} canUndo={canUndo} lastActionLabel={lastActionLabel} onUndo={handleUndo} /> - : { handlePlaceClick(placeId); setMobileSidebarOpen(null) }} onAddPlace={() => { setEditingPlace(null); setShowPlaceForm(true); setMobileSidebarOpen(null) }} onAssignToDay={handleAssignToDay} onEditPlace={(place) => { setEditingPlace(place); setEditingAssignmentId(null); setShowPlaceForm(true); setMobileSidebarOpen(null) }} onDeletePlace={(placeId) => handleDeletePlace(placeId)} days={days} isMobile onCategoryFilterChange={setMapCategoryFilter} onPlacesFilterChange={setMapPlacesFilter} pushUndo={pushUndo} /> + : { handlePlaceClick(placeId); setMobileSidebarOpen(null) }} onAddPlace={() => { setEditingPlace(null); setShowPlaceForm(true); setMobileSidebarOpen(null) }} onAssignToDay={handleAssignToDay} onEditPlace={(place) => { setEditingPlace(place); setEditingAssignmentId(null); setShowPlaceForm(true); setMobileSidebarOpen(null) }} onDeletePlace={(placeId) => handleDeletePlace(placeId)} onBulkDeletePlaces={(ids) => setDeletePlaceIds(ids)} onBulkDeleteConfirm={(ids) => confirmDeletePlaces(ids)} days={days} isMobile onCategoryFilterChange={setMapCategoryFilter} onPlacesFilterChange={setMapPlacesFilter} pushUndo={pushUndo} /> }
@@ -976,6 +1012,13 @@ export default function TripPlannerPage(): React.ReactElement | null { title={t('common.delete')} message={t('trip.confirm.deletePlace')} /> + setDeletePlaceIds(null)} + onConfirm={confirmDeletePlaces} + title={t('common.delete')} + message={t('trip.confirm.deletePlaces', { count: deletePlaceIds?.length ?? 0 })} + />
) } diff --git a/client/src/repo/placeRepo.ts b/client/src/repo/placeRepo.ts index 3e5e1d1f..36b1acc2 100644 --- a/client/src/repo/placeRepo.ts +++ b/client/src/repo/placeRepo.ts @@ -84,4 +84,26 @@ export const placeRepo = { offlineDb.places.delete(Number(id)) return result }, + + async deleteMany(tripId: number | string, ids: number[]): Promise { + if (!navigator.onLine) { + await offlineDb.places.bulkDelete(ids) + for (const id of ids) { + const mutId = generateUUID() + await mutationQueue.enqueue({ + id: mutId, + tripId: Number(tripId), + method: 'DELETE', + url: `/trips/${tripId}/places/${id}`, + body: undefined, + resource: 'places', + entityId: id, + }) + } + return { deleted: ids, count: ids.length } + } + const result = await placesApi.bulkDelete(tripId, ids) + await offlineDb.places.bulkDelete(ids) + return result + }, } diff --git a/client/src/services/photoService.ts b/client/src/services/photoService.ts index c80c7fa4..24eaba81 100644 --- a/client/src/services/photoService.ts +++ b/client/src/services/photoService.ts @@ -12,6 +12,29 @@ const listeners = new Map void>>() // Separate thumb listeners — called when thumbDataUrl becomes available after initial load const thumbListeners = new Map void>>() +// Concurrency limiter — at most N photo API requests in flight at once. +// Prevents flooding the server (and external APIs it calls) when many places appear at once. +const MAX_CONCURRENT = 5 +let activeRequests = 0 +const requestQueue: Array<() => void> = [] + +function acquireRequestSlot(): Promise { + if (activeRequests < MAX_CONCURRENT) { + activeRequests++ + return Promise.resolve() + } + return new Promise(resolve => requestQueue.push(resolve)) +} + +function releaseRequestSlot(): void { + const next = requestQueue.shift() + if (next) { + next() + } else { + activeRequests-- + } +} + function notify(key: string, entry: PhotoEntry) { listeners.get(key)?.forEach(fn => fn(entry)) listeners.delete(key) @@ -99,37 +122,39 @@ export function fetchPhoto( } inFlight.add(cacheKey) - mapsApi.placePhoto(photoId, lat, lng, name) - .then(async (data: { photoUrl?: string }) => { - const photoUrl = data.photoUrl || null - if (!photoUrl) { + acquireRequestSlot().then(() => + mapsApi.placePhoto(photoId, lat, lng, name) + .then(async (data: { photoUrl?: string }) => { + const photoUrl = data.photoUrl || null + if (!photoUrl) { + const entry: PhotoEntry = { photoUrl: null, thumbDataUrl: null } + cache.set(cacheKey, entry) + callback?.(entry) + notify(cacheKey, entry) + return + } + + // Store URL first — sidebar can show immediately + const entry: PhotoEntry = { photoUrl, thumbDataUrl: null } + cache.set(cacheKey, entry) + callback?.(entry) + notify(cacheKey, entry) + + // Generate base64 thumb in background + const thumb = await urlToBase64(photoUrl) + if (thumb) { + entry.thumbDataUrl = thumb + notifyThumb(cacheKey, thumb) + } + }) + .catch(() => { const entry: PhotoEntry = { photoUrl: null, thumbDataUrl: null } cache.set(cacheKey, entry) callback?.(entry) notify(cacheKey, entry) - return - } - - // Store URL first — sidebar can show immediately - const entry: PhotoEntry = { photoUrl, thumbDataUrl: null } - cache.set(cacheKey, entry) - callback?.(entry) - notify(cacheKey, entry) - - // Generate base64 thumb in background - const thumb = await urlToBase64(photoUrl) - if (thumb) { - entry.thumbDataUrl = thumb - notifyThumb(cacheKey, thumb) - } - }) - .catch(() => { - const entry: PhotoEntry = { photoUrl: null, thumbDataUrl: null } - cache.set(cacheKey, entry) - callback?.(entry) - notify(cacheKey, entry) - }) - .finally(() => { inFlight.delete(cacheKey) }) + }) + .finally(() => { inFlight.delete(cacheKey); releaseRequestSlot() }) + ) } export function getAllThumbs(): Record { diff --git a/client/src/services/weatherQueue.ts b/client/src/services/weatherQueue.ts new file mode 100644 index 00000000..56b034db --- /dev/null +++ b/client/src/services/weatherQueue.ts @@ -0,0 +1,25 @@ +import { weatherApi } from '../api/client' + +const MAX_CONCURRENT = 3 +let active = 0 +const queue: Array<() => void> = [] + +function acquire(): Promise { + if (active < MAX_CONCURRENT) { active++; return Promise.resolve() } + return new Promise(resolve => queue.push(resolve)) +} + +function release(): void { + const next = queue.shift() + if (next) next() + else active-- +} + +export async function fetchWeather(lat: number, lng: number, date: string) { + await acquire() + try { + return await weatherApi.get(lat, lng, date) + } finally { + release() + } +} diff --git a/client/src/store/slices/placesSlice.ts b/client/src/store/slices/placesSlice.ts index 27e6df21..224a0488 100644 --- a/client/src/store/slices/placesSlice.ts +++ b/client/src/store/slices/placesSlice.ts @@ -12,6 +12,7 @@ export interface PlacesSlice { addPlace: (tripId: number | string, placeData: Partial) => Promise updatePlace: (tripId: number | string, placeId: number, placeData: Partial) => Promise deletePlace: (tripId: number | string, placeId: number) => Promise + deletePlacesMany: (tripId: number | string, placeIds: number[]) => Promise } export const createPlacesSlice = (set: SetState, get: GetState): PlacesSlice => ({ @@ -80,4 +81,28 @@ export const createPlacesSlice = (set: SetState, get: GetState): PlacesSlice => throw new Error(getApiErrorMessage(err, 'Error deleting place')) } }, + + deletePlacesMany: async (tripId, placeIds) => { + if (placeIds.length === 0) return + try { + await placeRepo.deleteMany(tripId, placeIds) + const idSet = new Set(placeIds) + set(state => { + const updatedAssignments = { ...state.assignments } + let changed = false + for (const [dayId, items] of Object.entries(state.assignments)) { + if (items.some((a: Assignment) => a.place?.id != null && idSet.has(a.place.id))) { + updatedAssignments[dayId] = items.filter((a: Assignment) => !idSet.has(a.place?.id!)) + changed = true + } + } + return { + places: state.places.filter(p => !idSet.has(p.id)), + ...(changed ? { assignments: updatedAssignments } : {}), + } + }) + } catch (err: unknown) { + throw new Error(getApiErrorMessage(err, 'Error deleting places')) + } + }, }) diff --git a/client/tests/integration/hooks/useRouteCalculation.test.ts b/client/tests/integration/hooks/useRouteCalculation.test.ts index fb26a1c3..d439c682 100644 --- a/client/tests/integration/hooks/useRouteCalculation.test.ts +++ b/client/tests/integration/hooks/useRouteCalculation.test.ts @@ -2,6 +2,7 @@ import { renderHook, act } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { useRouteCalculation } from '../../../src/hooks/useRouteCalculation'; import { useSettingsStore } from '../../../src/store/settingsStore'; +import { useTripStore } from '../../../src/store/tripStore'; import { buildAssignment, buildPlace } from '../../helpers/factories'; import type { TripStoreState } from '../../../src/store/tripStore'; import type { RouteSegment } from '../../../src/types'; @@ -17,6 +18,9 @@ vi.mock('../../../src/components/Map/RouteCalculator', () => ({ const { calculateSegments } = await import('../../../src/components/Map/RouteCalculator'); function buildMockStore(assignments: Record[]> = {}): Partial { + // Also populate the real Zustand store so updateRouteForDay (which reads from + // useTripStore.getState()) sees the same assignments as the hook's tripStore param. + useTripStore.setState({ assignments } as any); return { assignments } as Partial; } @@ -35,6 +39,8 @@ describe('useRouteCalculation', () => { vi.clearAllMocks(); // Default: route_calculation disabled useSettingsStore.setState({ settings: { route_calculation: false } as any }); + // Reset trip store assignments so each test starts clean + useTripStore.setState({ assignments: {} } as any); (calculateSegments as ReturnType).mockResolvedValue(MOCK_SEGMENTS); }); @@ -266,7 +272,7 @@ describe('useRouteCalculation', () => { expect(result.current.setRouteInfo).toBeTypeOf('function'); }); - it('FE-HOOK-ROUTE-013: hook uses tripStoreRef — late store updates reflected correctly', async () => { + it('FE-HOOK-ROUTE-013: route recalculates when assignments change via store update', async () => { useSettingsStore.setState({ settings: { route_calculation: true } as any }); const p1 = buildPlace({ lat: 10, lng: 10 }); @@ -287,10 +293,10 @@ describe('useRouteCalculation', () => { [p2.lat, p2.lng], ]); - // Now add a third place + // Now add a third place — update both the local store object and the Zustand store const p3 = buildPlace({ lat: 30, lng: 30 }); const a3 = buildAssignment({ day_id: 5, order_index: 2, place: p3 }); - storeData = buildMockStore({ '5': [a1, a2, a3] }); + storeData = buildMockStore({ '5': [a1, a2, a3] }); // also calls useTripStore.setState await act(async () => { rerender(); diff --git a/client/tests/unit/services/photoService.test.ts b/client/tests/unit/services/photoService.test.ts index 94646e8b..953078cf 100644 --- a/client/tests/unit/services/photoService.test.ts +++ b/client/tests/unit/services/photoService.test.ts @@ -134,6 +134,8 @@ describe('fetchPhoto — in-flight deduplication', () => { svc.fetchPhoto('k', 'pid', undefined, undefined, undefined, cb1); svc.fetchPhoto('k', 'pid', undefined, undefined, undefined, cb2); + // acquireRequestSlot() is async (Promise.resolve), so flush microtasks before asserting + await flush(); expect(mockPlacePhoto).toHaveBeenCalledTimes(1); resolve({ photoUrl: 'https://example.com/photo.jpg' }); diff --git a/scripts/analyze-devtool-profiler.cjs b/scripts/analyze-devtool-profiler.cjs new file mode 100644 index 00000000..62737669 --- /dev/null +++ b/scripts/analyze-devtool-profiler.cjs @@ -0,0 +1,133 @@ +#!/usr/bin/env node +// Chrome Performance Trace Analyzer — outputs a compact summary +// Usage: node analyze-trace.js + +const fs = require('fs') +const path = require('path') + +const file = process.argv[2] +if (!file) { console.error('Usage: node analyze-trace.js '); process.exit(1) } + +console.log(`Reading ${path.basename(file)} (${(fs.statSync(file).size / 1e6).toFixed(1)} MB)...`) +const raw = fs.readFileSync(file, 'utf8') +console.log('Parsing...') +const trace = JSON.parse(raw) +const events = Array.isArray(trace) ? trace : (trace.traceEvents || []) +console.log(`Total events: ${events.length.toLocaleString()}\n`) + +// ── 1. Long Tasks (> 50ms on main thread) ──────────────────────────────────── +const LONG_TASK_MS = 50 +const tasks = events + .filter(e => e.ph === 'X' && e.dur && e.dur > LONG_TASK_MS * 1000) + .sort((a, b) => b.dur - a.dur) + .slice(0, 30) + +console.log(`═══ TOP LONG TASKS (>${LONG_TASK_MS}ms) ═══`) +for (const t of tasks) { + const ms = (t.dur / 1000).toFixed(1) + const name = t.name || '(unknown)' + const cat = t.cat || '' + console.log(` ${ms.padStart(8)}ms ${name} [${cat}]`) +} + +// ── 2. Summarise all complete events by name ────────────────────────────────── +const byName = new Map() +for (const e of events) { + if (e.ph !== 'X' || !e.dur) continue + const key = e.name + const existing = byName.get(key) + if (existing) { + existing.totalMs += e.dur / 1000 + existing.count++ + if (e.dur > existing.maxMs * 1000) existing.maxMs = e.dur / 1000 + } else { + byName.set(key, { totalMs: e.dur / 1000, count: 1, maxMs: e.dur / 1000 }) + } +} +const topByTotal = [...byName.entries()] + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .slice(0, 40) + +console.log('\n═══ TOP EVENTS BY TOTAL TIME ═══') +console.log(' Total(ms) Max(ms) Count Name') +for (const [name, s] of topByTotal) { + console.log( + ` ${s.totalMs.toFixed(1).padStart(9)} ${s.maxMs.toFixed(1).padStart(7)} ${String(s.count).padStart(5)} ${name}` + ) +} + +// ── 3. React-specific events ────────────────────────────────────────────────── +const reactKeywords = ['react', 'React', 'setState', 'useState', 'useMemo', 'useEffect', + 'reconcil', 'Reconcil', 'render', 'Render', 'commit', 'Commit', 'fiber', 'Fiber', + 'Marker', 'MapView', 'photoUrl', 'createPlace', 'markers'] +const reactEvents = [...byName.entries()] + .filter(([name]) => reactKeywords.some(k => name.includes(k))) + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .slice(0, 30) + +if (reactEvents.length > 0) { + console.log('\n═══ REACT / MAP EVENTS ═══') + for (const [name, s] of reactEvents) { + console.log(` ${s.totalMs.toFixed(1).padStart(9)}ms total ${s.maxMs.toFixed(1).padStart(7)}ms max ${s.count}x ${name}`) + } +} + +// ── 4. V8 / JS heavy hitters ───────────────────────────────────────────────── +const jsEvents = [...byName.entries()] + .filter(([, s]) => s.totalMs > 20) + .filter(([name]) => { + const cat = (events.find(e => e.name === name)?.cat || '') + return cat.includes('v8') || cat.includes('devtools.timeline') || name.includes('JS') || name.includes('Compile') || name.includes('GC') + }) + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .slice(0, 20) + +if (jsEvents.length > 0) { + console.log('\n═══ V8 / JS EVENTS (>20ms total) ═══') + for (const [name, s] of jsEvents) { + console.log(` ${s.totalMs.toFixed(1).padStart(9)}ms ${s.count}x ${name}`) + } +} + +// ── 5. CPU profile — top self-time functions ───────────────────────────────── +const profileChunks = events.filter(e => e.name === 'ProfileChunk') +if (profileChunks.length > 0) { + const selfTime = new Map() + for (const chunk of profileChunks) { + const nodes = chunk.args?.data?.cpuProfile?.nodes || [] + const samples = chunk.args?.data?.cpuProfile?.samples || [] + const timeDeltas = chunk.args?.data?.timeDeltas || [] + // Build node map + const nodeMap = new Map(nodes.map(n => [n.id, n])) + // Accumulate self time per node + for (let i = 0; i < samples.length; i++) { + const nodeId = samples[i] + const dt = (timeDeltas[i] || 0) / 1000 // µs → ms + const node = nodeMap.get(nodeId) + if (!node) continue + const fn = node.callFrame?.functionName || '(anonymous)' + const url = node.callFrame?.url || '' + const line = node.callFrame?.lineNumber || 0 + const key = `${fn} @ ${url.split('/').slice(-2).join('/')}:${line}` + selfTime.set(key, (selfTime.get(key) || 0) + dt) + } + } + const topSelf = [...selfTime.entries()] + .sort((a, b) => b[1] - a[1]) + .slice(0, 40) + + console.log('\n═══ CPU PROFILE — TOP SELF-TIME FUNCTIONS ═══') + for (const [name, ms] of topSelf) { + console.log(` ${ms.toFixed(1).padStart(8)}ms ${name}`) + } +} + +// ── 6. Paint / Layout costs ─────────────────────────────────────────────────── +const renderCats = ['Layout', 'UpdateLayoutTree', 'Paint', 'CompositeLayers', 'RasterTask'] +console.log('\n═══ RENDERING COSTS ═══') +for (const cat of renderCats) { + const s = byName.get(cat) + if (s) console.log(` ${s.totalMs.toFixed(1).padStart(9)}ms total ${s.maxMs.toFixed(1).padStart(7)}ms max ${s.count}x ${cat}`) +} + +console.log('\nDone.') diff --git a/scripts/analyze-react-profiler.cjs b/scripts/analyze-react-profiler.cjs new file mode 100644 index 00000000..98f021fa --- /dev/null +++ b/scripts/analyze-react-profiler.cjs @@ -0,0 +1,96 @@ +#!/usr/bin/env node +const fs = require('fs') +const path = require('path') + +const file = process.argv[2] +if (!file) { console.error('Usage: node analyze-react-profiler.cjs '); process.exit(1) } + +const raw = JSON.parse(fs.readFileSync(path.resolve(file), 'utf8')) +const root = raw.dataForRoots[0] +const commits = root.commitData + +// snapshots: array of [fiberId, {displayName, ...}] +const nameMap = new Map() +for (const snap of root.snapshots) { + const id = snap[0] + const data = snap[1] + if (data?.displayName) nameMap.set(id, data.displayName) +} + +console.log(`Commits: ${commits.length} Tracked components: ${nameMap.size}`) + +// Probe the unit of fiberActualDurations against the known commit duration +// fiberActualDurations contains durations for the subtree — the root fiber's +// actual duration should be >= commit.duration. Find a plausible scale factor. +const c0 = commits[0] +const knownDur = c0.duration // already in ms per React DevTools spec +const rootId = root.rootID ?? 1 +// Check a few values to pick scale +const sampleDurs = c0.fiberActualDurations.slice(0, 10).map(e => e[1]) +console.log(`\nDebug — commit[0].duration=${knownDur}ms, first 5 raw fiberActualDurations values:`, sampleDurs.slice(0,5)) +// If max sample > 10*knownDur, values are in units of 1/100 ms; otherwise already ms +const maxSample = Math.max(...c0.fiberActualDurations.map(e => e[1])) +const scale = maxSample > knownDur * 10 ? 0.01 : 1 + +console.log(`Unit scale: ${scale === 0.01 ? '1/100 ms (dividing by 100)' : 'ms (no conversion)'}\n`) + +// --- 1. Commit summary --- +const fmt = (v) => v == null ? ' -' : (v * 1).toFixed(1).padStart(7) +console.log('=== Commit summary ===') +console.log(' # t(s) dur(ms) passive(ms) effects(ms) priority') +const sorted = [...commits].map((c, i) => ({ i, ...c })).sort((a, b) => b.duration - a.duration) +for (const c of sorted.slice(0, 15)) { + const ts = (c.timestamp / 1000).toFixed(3) + console.log(` ${String(c.i).padStart(2)} ${ts} ${fmt(c.duration)} ${fmt(c.passiveEffectDuration)} ${fmt(c.effectDuration)} ${c.priorityLevel ?? ''}`) +} + +// --- 2. Aggregate self + actual duration per component --- +const selfTotals = new Map() // name → { total, count, max } +const actualTotals = new Map() + +for (const commit of commits) { + for (const [id, raw] of commit.fiberActualDurations) { + const dur = raw * scale + const name = nameMap.get(id) ?? `(fiber#${id})` + const e = actualTotals.get(name) ?? { total: 0, count: 0, max: 0 } + e.total += dur; e.count += 1; e.max = Math.max(e.max, dur) + actualTotals.set(name, e) + } + for (const [id, raw] of commit.fiberSelfDurations) { + const dur = raw * scale + const name = nameMap.get(id) ?? `(fiber#${id})` + const e = selfTotals.get(name) ?? { total: 0, count: 0, max: 0 } + e.total += dur; e.count += 1; e.max = Math.max(e.max, dur) + selfTotals.set(name, e) + } +} + +const ranked = [...selfTotals.entries()] + .sort((a, b) => b[1].total - a[1].total) + .filter(([, s]) => s.total > 0.5) + +console.log('\n=== Top 40 components by SELF render time (excludes children) ===') +console.log(' Component Self total Renders Self max Actual total') +for (const [name, s] of ranked.slice(0, 40)) { + const actual = actualTotals.get(name) ?? { total: 0 } + console.log( + ` ${name.padEnd(48)} ${s.total.toFixed(1).padStart(8)} ms` + + ` ${String(s.count).padStart(6)}x` + + ` ${s.max.toFixed(1).padStart(7)} ms` + + ` ${actual.total.toFixed(1).padStart(10)} ms` + ) +} + +console.log('\n=== Most frequently re-rendering components (top 20) ===') +const byCount = [...selfTotals.entries()].sort((a, b) => b[1].count - a[1].count) +console.log(' Component Renders Self total') +for (const [name, s] of byCount.slice(0, 20)) { + console.log(` ${name.padEnd(48)} ${String(s.count).padStart(6)}x ${s.total.toFixed(1).padStart(8)} ms`) +} + +const totalPassive = commits.reduce((a, c) => a + (c.passiveEffectDuration ?? 0), 0) +const totalCommit = commits.reduce((a, c) => a + c.duration, 0) +console.log(`\n=== Totals ===`) +console.log(` Total commit render time: ${totalCommit.toFixed(1)} ms (${commits.length} commits)`) +console.log(` Total passive effect time: ${totalPassive.toFixed(1)} ms (useEffect)`) +console.log(` Avg commit duration: ${(totalCommit / commits.length).toFixed(1)} ms`) diff --git a/server/src/routes/places.ts b/server/src/routes/places.ts index be0d8f1f..3346fa14 100644 --- a/server/src/routes/places.ts +++ b/server/src/routes/places.ts @@ -12,11 +12,13 @@ import { getPlace, updatePlace, deletePlace, + deletePlacesMany, importGpx, importMapFile, importGoogleList, importNaverList, searchPlaceImage, + type KmlImportOptions, } from '../services/placeService'; import { onPlaceCreated, onPlaceUpdated, onPlaceDeleted } from '../services/journeyService'; @@ -65,9 +67,18 @@ router.post('/import/gpx', authenticate, requireTripAccess, uploadMulter.single( const file = req.file as Express.Multer.File | undefined; if (!file) return res.status(400).json({ error: 'No file uploaded' }); - const result = importGpx(tripId, file.buffer); + const parseBool = (v: unknown, defaultVal: boolean) => v === undefined || v === null ? defaultVal : String(v) === 'true'; + const importWaypoints = parseBool(req.body.importWaypoints, true); + const importRoutes = parseBool(req.body.importRoutes, true); + const importTracks = parseBool(req.body.importTracks, true); + + if (!importWaypoints && !importRoutes && !importTracks) { + return res.status(400).json({ error: 'No import types selected' }); + } + + const result = importGpx(tripId, file.buffer, { importWaypoints, importRoutes, importTracks }); if (!result) { - return res.status(400).json({ error: 'No waypoints found in GPX file' }); + return res.status(400).json({ error: 'No matching places found in GPX file' }); } res.status(201).json({ places: result.places, count: result.count, skipped: result.skipped }); @@ -86,8 +97,18 @@ router.post('/import/map', authenticate, requireTripAccess, uploadMulter.single( const file = req.file as Express.Multer.File | undefined; if (!file) return res.status(400).json({ error: 'No file uploaded' }); + const parseBool = (v: unknown, defaultVal: boolean) => v === undefined || v === null ? defaultVal : String(v) === 'true'; + const importPoints = parseBool(req.body.importPoints, true); + const importPaths = parseBool(req.body.importPaths, true); + + if (!importPoints && !importPaths) { + return res.status(400).json({ error: 'No import types selected' }); + } + + const kmlOpts: KmlImportOptions = { importPoints, importPaths }; + try { - const result = await importMapFile(tripId, file.buffer, file.originalname); + const result = await importMapFile(tripId, file.buffer, file.originalname, kmlOpts); if (result.summary?.totalPlacemarks === 0) { return res.status(400).json({ error: 'No valid Placemarks found in map file', summary: result.summary }); } @@ -201,6 +222,30 @@ router.put('/:id', authenticate, requireTripAccess, validateStringLengths({ name try { onPlaceUpdated(place.id); } catch {} }); +// Bulk delete (must be before /:id) +router.post('/bulk-delete', authenticate, requireTripAccess, (req: Request, res: Response) => { + const authReq = req as AuthRequest; + if (!checkPermission('place_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id)) + return res.status(403).json({ error: 'No permission' }); + + const { tripId } = req.params; + const { ids } = req.body as { ids?: unknown }; + if (!Array.isArray(ids) || ids.some(v => typeof v !== 'number')) + return res.status(400).json({ error: 'ids must be an array of numbers' }); + + const idList = ids as number[]; + if (idList.length === 0) return res.json({ deleted: [], count: 0 }); + + for (const id of idList) { try { onPlaceDeleted(id); } catch {} } + const deleted = deletePlacesMany(tripId, idList); + + res.json({ deleted, count: deleted.length }); + const socketId = req.headers['x-socket-id'] as string; + for (const id of deleted) { + broadcast(tripId, 'place:deleted', { placeId: id }, socketId); + } +}); + router.delete('/:id', authenticate, requireTripAccess, (req: Request, res: Response) => { const authReq = req as AuthRequest; if (!checkPermission('place_edit', authReq.user.role, authReq.trip!.user_id, authReq.user.id, authReq.trip!.user_id !== authReq.user.id)) diff --git a/server/src/services/atlasService.ts b/server/src/services/atlasService.ts index c05d060f..95f70d27 100644 --- a/server/src/services/atlasService.ts +++ b/server/src/services/atlasService.ts @@ -264,6 +264,54 @@ function getPlacesForTrips(tripIds: number[]): Place[] { return db.prepare(`SELECT * FROM places WHERE trip_id IN (${placeholders})`).all(...tripIds) as Place[]; } +// ── Country resolution (batch DB cache + sync fallback + background geocoding) ── + +function resolvePlaceCountries(places: Place[]): Map { + const out = new Map(); + const geoPlaces = places.filter(p => p.lat && p.lng); + const placeIds = geoPlaces.map(p => p.id); + + const cached = placeIds.length > 0 + ? (db.prepare( + `SELECT place_id, country_code FROM place_regions WHERE place_id IN (${placeIds.map(() => '?').join(',')})` + ).all(...placeIds) as { place_id: number; country_code: string }[]) + : []; + const cachedMap = new Map(cached.map(r => [r.place_id, r.country_code])); + + const uncachedForGeocode: Place[] = []; + for (const p of places) { + const fromDb = cachedMap.get(p.id); + if (fromDb) { out.set(p.id, fromDb); continue; } + const sync = resolveCountryCodeSync(p); + if (sync) { out.set(p.id, sync); continue; } + if (p.lat && p.lng && !geocodingInFlight.has(p.id)) { + uncachedForGeocode.push(p); + } + } + + if (uncachedForGeocode.length > 0) { + const insertStmt = db.prepare( + 'INSERT OR REPLACE INTO place_regions (place_id, country_code, region_code, region_name) VALUES (?, ?, ?, ?)' + ); + for (const p of uncachedForGeocode) geocodingInFlight.add(p.id); + void (async () => { + try { + for (const place of uncachedForGeocode) { + try { + const info = await reverseGeocodeRegion(place.lat!, place.lng!); + if (info) insertStmt.run(place.id, info.country_code, info.region_code, info.region_name); + } catch { /* continue */ } + finally { geocodingInFlight.delete(place.id); } + } + } catch { + for (const p of uncachedForGeocode) geocodingInFlight.delete(p.id); + } + })(); + } + + return out; +} + // ── getStats ──────────────────────────────────────────────────────────────── export async function getStats(userId: number) { @@ -279,9 +327,10 @@ export async function getStats(userId: number) { const places = getPlacesForTrips(tripIds); interface CountryEntry { code: string; places: { id: number; name: string; lat: number | null; lng: number | null }[]; tripIds: Set } + const placeCountries = resolvePlaceCountries(places); const countrySet = new Map(); for (const place of places) { - const code = await resolveCountryCode(place); + const code = placeCountries.get(place.id); if (code) { if (!countrySet.has(code)) { countrySet.set(code, { code, places: [], tripIds: new Set() }); diff --git a/server/src/services/kmlImport.ts b/server/src/services/kmlImport.ts index 9fe38eb2..599cffff 100644 --- a/server/src/services/kmlImport.ts +++ b/server/src/services/kmlImport.ts @@ -6,6 +6,7 @@ export interface ParsedKmlPlacemark { lat: number | null; lng: number | null; folderName: string | null; + routeGeometry: string | null; } export interface KmlPlacemarkNode { @@ -97,6 +98,26 @@ export function sanitizeKmlDescription(value: unknown): string | null { return decoded || null; } +export function parseKmlLineStringCoordinates(value: unknown): Array<{ lat: number; lng: number; ele: number | null }> | null { + const coordinates = asTrimmedString(value); + if (!coordinates) return null; + + const points = coordinates + .trim() + .split(/\s+/) + .map(coord => { + const parts = coord.split(','); + const lng = Number.parseFloat(parts[0] ?? ''); + const lat = Number.parseFloat(parts[1] ?? ''); + const eleRaw = parts[2] != null ? Number.parseFloat(parts[2]) : NaN; + if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null; + return { lat, lng, ele: Number.isFinite(eleRaw) ? eleRaw : null }; + }) + .filter((p): p is { lat: number; lng: number; ele: number | null } => p !== null); + + return points.length >= 2 ? points : null; +} + export function parseKmlPointCoordinates(value: unknown): { lat: number; lng: number } | null { const coordinates = asTrimmedString(value); if (!coordinates) return null; @@ -167,13 +188,25 @@ export function extractKmlPlacemarkNodes(kmlRoot: any): KmlPlacemarkNode[] { } export function parsePlacemarkNode(node: KmlPlacemarkNode): ParsedKmlPlacemark { - const coordinates = parseKmlPointCoordinates(node.placemark?.Point?.coordinates); + const pointCoords = parseKmlPointCoordinates(node.placemark?.Point?.coordinates); + + let routeGeometry: string | null = null; + let pathFirstPt: { lat: number; lng: number } | null = null; + if (!pointCoords) { + const linePts = parseKmlLineStringCoordinates(node.placemark?.LineString?.coordinates); + if (linePts) { + pathFirstPt = { lat: linePts[0].lat, lng: linePts[0].lng }; + const hasAllEle = linePts.every(p => p.ele !== null); + routeGeometry = JSON.stringify(linePts.map(p => hasAllEle ? [p.lat, p.lng, p.ele] : [p.lat, p.lng])); + } + } return { name: asTrimmedString(node.placemark?.name), description: sanitizeKmlDescription(node.placemark?.description), - lat: coordinates?.lat ?? null, - lng: coordinates?.lng ?? null, + lat: pointCoords?.lat ?? pathFirstPt?.lat ?? null, + lng: pointCoords?.lng ?? pathFirstPt?.lng ?? null, folderName: node.folderName, + routeGeometry, }; } diff --git a/server/src/services/mapsService.ts b/server/src/services/mapsService.ts index 30827857..ed484d0c 100644 --- a/server/src/services/mapsService.ts +++ b/server/src/services/mapsService.ts @@ -71,6 +71,30 @@ const UA = 'TREK Travel Planner (https://github.com/mauriceboe/TREK)'; // ── Photo cache (disk-backed) ──────────────────────────────────────────────── import * as placePhotoCache from './placePhotoCache'; +// ── Concurrency limiter for outbound photo fetches ─────────────────────────── +// Caps simultaneous Wikimedia/Google photo requests so a bulk import of hundreds +// of places cannot monopolise the event loop or trigger external API rate limits. +const MAX_CONCURRENT_PHOTO_FETCHES = 5; +let photoFetchActive = 0; +const photoFetchQueue: Array<() => void> = []; + +function acquirePhotoFetchSlot(): Promise { + if (photoFetchActive < MAX_CONCURRENT_PHOTO_FETCHES) { + photoFetchActive++; + return Promise.resolve(); + } + return new Promise(resolve => photoFetchQueue.push(resolve)); +} + +function releasePhotoFetchSlot(): void { + const next = photoFetchQueue.shift(); + if (next) { + next(); + } else { + photoFetchActive--; + } +} + // ── API key retrieval ──────────────────────────────────────────────────────── export function getMapsKey(userId: number): string | null { @@ -597,6 +621,8 @@ export async function getPlacePhoto( } const fetchPromise = (async (): Promise<{ filePath: string; attribution: string | null } | null> => { + await acquirePhotoFetchSlot(); + try { const apiKey = getMapsKey(userId); const isCoordLookup = placeId.startsWith('coords:'); @@ -676,6 +702,9 @@ export async function getPlacePhoto( } return { filePath: cached.filePath, attribution }; + } finally { + releasePhotoFetchSlot(); + } })(); placePhotoCache.setInFlight(placeId, fetchPromise); diff --git a/server/src/services/placePhotoCache.ts b/server/src/services/placePhotoCache.ts index 06995e57..59522d8f 100644 --- a/server/src/services/placePhotoCache.ts +++ b/server/src/services/placePhotoCache.ts @@ -10,11 +10,14 @@ const ERROR_TTL = 5 * 60 * 1000; // In-flight dedup — prevents stampedes when multiple requests hit the same uncached placeId simultaneously const inFlight = new Map>(); -function ensureDir(): void { - if (!fs.existsSync(GOOGLE_PHOTO_DIR)) { - fs.mkdirSync(GOOGLE_PHOTO_DIR, { recursive: true }); - } -} +// In-memory set of placeIds whose file is confirmed on disk this session. +// Avoids a synchronous fs.existsSync() call on every cache hit after the first verification. +const knownOnDisk = new Set(); + +// Ensure upload dir exists once at startup — avoids sync FS calls inside put() on every write. +try { + fs.mkdirSync(GOOGLE_PHOTO_DIR, { recursive: true }); +} catch { /* already exists */ } function filePath(placeId: string): string { // Hash to avoid filename collisions — coords:lat:lng pseudo-IDs contain characters that @@ -41,10 +44,15 @@ export function get(placeId: string): CachedPhoto | null { if (!row) return null; const fp = filePath(placeId); - if (!fs.existsSync(fp)) { - // File missing (e.g. volume wiped) — clear row so it refetches - db.prepare('DELETE FROM google_place_photo_meta WHERE place_id = ?').run(placeId); - return null; + + if (!knownOnDisk.has(placeId)) { + // First time this placeId is checked this session — verify the file exists on disk. + // (Guards against volume wipes or manual deletion between server restarts.) + if (!fs.existsSync(fp)) { + db.prepare('DELETE FROM google_place_photo_meta WHERE place_id = ?').run(placeId); + return null; + } + knownOnDisk.add(placeId); } return { photoUrl: proxyUrl(placeId), filePath: fp, attribution: row.attribution }; @@ -60,19 +68,21 @@ export function getErrored(placeId: string): boolean { } export function markError(placeId: string): void { + knownOnDisk.delete(placeId); db.prepare( 'INSERT OR REPLACE INTO google_place_photo_meta (place_id, attribution, fetched_at, error_at) VALUES (?, NULL, ?, ?)' ).run(placeId, Date.now(), Date.now()); } export async function put(placeId: string, bytes: Buffer, attribution: string | null): Promise { - ensureDir(); const fp = filePath(placeId); const tmp = fp + '.tmp'; await fsPromises.writeFile(tmp, bytes); await fsPromises.rename(tmp, fp); + knownOnDisk.add(placeId); + db.prepare( 'INSERT OR REPLACE INTO google_place_photo_meta (place_id, attribution, fetched_at, error_at) VALUES (?, ?, ?, NULL)' ).run(placeId, attribution, Date.now()); @@ -90,6 +100,9 @@ export function setInFlight(placeId: string, promise: Promise<{ filePath: string } export function serveFilePath(placeId: string): string | null { + if (knownOnDisk.has(placeId)) return filePath(placeId); const fp = filePath(placeId); - return fs.existsSync(fp) ? fp : null; + if (!fs.existsSync(fp)) return null; + knownOnDisk.add(placeId); + return fp; } diff --git a/server/src/services/placeService.ts b/server/src/services/placeService.ts index f2305bb1..c14fc273 100644 --- a/server/src/services/placeService.ts +++ b/server/src/services/placeService.ts @@ -240,6 +240,22 @@ export function deletePlace(tripId: string, placeId: string): boolean { return true; } +export function deletePlacesMany(tripId: string, ids: number[]): number[] { + if (ids.length === 0) return []; + const selectStmt = db.prepare('SELECT id FROM places WHERE id = ? AND trip_id = ?'); + const deleteStmt = db.prepare('DELETE FROM places WHERE id = ?'); + const deleted: number[] = []; + const run = db.transaction((list: number[]) => { + for (const id of list) { + if (!selectStmt.get(id, tripId)) continue; + deleteStmt.run(id); + deleted.push(id); + } + }); + run(ids); + return deleted; +} + // --------------------------------------------------------------------------- // Import GPX // --------------------------------------------------------------------------- @@ -326,7 +342,20 @@ function trackInsertedInDedupSet( } } -export function importGpx(tripId: string, fileBuffer: Buffer) { +export interface GpxImportOptions { + importWaypoints?: boolean; + importRoutes?: boolean; + importTracks?: boolean; +} + +export interface KmlImportOptions { + importPoints?: boolean; + importPaths?: boolean; +} + +export function importGpx(tripId: string, fileBuffer: Buffer, opts: GpxImportOptions = {}) { + const { importWaypoints = true, importRoutes = true, importTracks = true } = opts; + const parsed = gpxParser.parse(fileBuffer.toString('utf-8')); const gpx = parsed?.gpx; if (!gpx) return null; @@ -338,41 +367,46 @@ export function importGpx(tripId: string, fileBuffer: Buffer) { const waypoints: WaypointEntry[] = []; // 1) Parse elements (named waypoints / POIs) - for (const wpt of gpx.wpt ?? []) { - const lat = num(wpt['@_lat']); - const lng = num(wpt['@_lon']); - if (lat === null || lng === null) continue; - waypoints.push({ lat, lng, name: str(wpt.name) || `Waypoint ${waypoints.length + 1}`, description: str(wpt.desc) }); + if (importWaypoints) { + for (const wpt of gpx.wpt ?? []) { + const lat = num(wpt['@_lat']); + const lng = num(wpt['@_lon']); + if (lat === null || lng === null) continue; + waypoints.push({ lat, lng, name: str(wpt.name) || `Waypoint ${waypoints.length + 1}`, description: str(wpt.desc) }); + } } - // 2) If no , try route points as individual places - if (waypoints.length === 0) { + // 2) Parse routes as polyline-places (one place per route with route_geometry) + if (importRoutes) { for (const rte of gpx.rte ?? []) { - for (const rtept of rte.rtept ?? []) { - const lat = num(rtept['@_lat']); - const lng = num(rtept['@_lon']); - if (lat === null || lng === null) continue; - waypoints.push({ lat, lng, name: str(rtept.name) || `Route Point ${waypoints.length + 1}`, description: str(rtept.desc) }); - } + const pts = (rte.rtept ?? []) + .map((pt: Record) => ({ lat: num(pt['@_lat']), lng: num(pt['@_lon']), ele: num(pt['ele']) })) + .filter((p: { lat: number | null; lng: number | null; ele: number | null }) => p.lat !== null && p.lng !== null) as Array<{ lat: number; lng: number; ele: number | null }>; + if (pts.length === 0) continue; + const hasAllEle = pts.every(p => p.ele !== null); + const routeGeometry = pts.map(p => hasAllEle ? [p.lat, p.lng, p.ele] : [p.lat, p.lng]); + waypoints.push({ lat: pts[0].lat, lng: pts[0].lng, name: str(rte.name) || 'GPX Route', description: str(rte.desc), routeGeometry: JSON.stringify(routeGeometry) }); } } - // 3) Extract full track geometry from (always, even if were found) - for (const trk of gpx.trk ?? []) { - const trackPoints: { lat: number; lng: number; ele: number | null }[] = []; - for (const seg of trk.trkseg ?? []) { - for (const pt of seg.trkpt ?? []) { - const lat = num(pt['@_lat']); - const lng = num(pt['@_lon']); - if (lat === null || lng === null) continue; - trackPoints.push({ lat, lng, ele: num(pt.ele) }); + // 3) Extract full track geometry from + if (importTracks) { + for (const trk of gpx.trk ?? []) { + const trackPoints: { lat: number; lng: number; ele: number | null }[] = []; + for (const seg of trk.trkseg ?? []) { + for (const pt of seg.trkpt ?? []) { + const lat = num(pt['@_lat']); + const lng = num(pt['@_lon']); + if (lat === null || lng === null) continue; + trackPoints.push({ lat, lng, ele: num(pt.ele) }); + } } + if (trackPoints.length === 0) continue; + const start = trackPoints[0]; + const hasAllEle = trackPoints.every(p => p.ele !== null); + const routeGeometry = trackPoints.map(p => hasAllEle ? [p.lat, p.lng, p.ele] : [p.lat, p.lng]); + waypoints.push({ lat: start.lat, lng: start.lng, name: str(trk.name) || 'GPX Track', description: str(trk.desc), routeGeometry: JSON.stringify(routeGeometry) }); } - if (trackPoints.length === 0) continue; - const start = trackPoints[0]; - const hasAllEle = trackPoints.every(p => p.ele !== null); - const routeGeometry = trackPoints.map(p => hasAllEle ? [p.lat, p.lng, p.ele] : [p.lat, p.lng]); - waypoints.push({ lat: start.lat, lng: start.lng, name: str(trk.name) || 'GPX Track', description: str(trk.desc), routeGeometry: JSON.stringify(routeGeometry) }); } if (waypoints.length === 0) return null; @@ -401,7 +435,8 @@ export function importGpx(tripId: string, fileBuffer: Buffer) { return { places: created, count: created.length, skipped }; } -export function importKmlPlaces(tripId: string, fileBuffer: Buffer): PlaceImportResult { +export function importKmlPlaces(tripId: string, fileBuffer: Buffer, opts: KmlImportOptions = {}): PlaceImportResult { + const { importPoints = true, importPaths = true } = opts; const decoded = decodeUtf8WithWarning(fileBuffer); const validationResult = XMLValidator.validate(decoded.text); @@ -430,19 +465,32 @@ export function importKmlPlaces(tripId: string, fileBuffer: Buffer): PlaceImport let dupCount = 0; const insertStmt = db.prepare(` - INSERT INTO places (trip_id, name, description, lat, lng, category_id, transport_mode) - VALUES (?, ?, ?, ?, ?, ?, 'walking') + INSERT INTO places (trip_id, name, description, lat, lng, category_id, transport_mode, route_geometry) + VALUES (?, ?, ?, ?, ?, ?, 'walking', ?) `); const insertAll = db.transaction(() => { let fallbackIndex = 1; for (const node of placemarkNodes) { const parsedPlacemark = parsePlacemarkNode(node); + const isPath = parsedPlacemark.routeGeometry !== null; - // KML geometry support is intentionally limited to coordinates. + // Unsupported geometry type (polygon, multi-geometry, no geometry, etc.) if (parsedPlacemark.lat === null || parsedPlacemark.lng === null) { summary.skippedCount += 1; - summary.errors.push(`Skipped Placemark ${fallbackIndex}: missing Point coordinates.`); + summary.errors.push(`Skipped Placemark ${fallbackIndex}: unsupported geometry type.`); + fallbackIndex += 1; + continue; + } + + // Type filtering: respect importPoints / importPaths opts + if (isPath && !importPaths) { + summary.skippedCount += 1; + fallbackIndex += 1; + continue; + } + if (!isPath && !importPoints) { + summary.skippedCount += 1; fallbackIndex += 1; continue; } @@ -466,6 +514,7 @@ export function importKmlPlaces(tripId: string, fileBuffer: Buffer): PlaceImport parsedPlacemark.lat, parsedPlacemark.lng, categoryId, + parsedPlacemark.routeGeometry, ); const place = getPlaceWithTags(Number(result.lastInsertRowid)); @@ -514,15 +563,15 @@ export async function unpackKmzToKml( return preferredEntry.buffer(); } -export async function importKmzPlaces(tripId: string, kmzBuffer: Buffer): Promise { +export async function importKmzPlaces(tripId: string, kmzBuffer: Buffer, opts: KmlImportOptions = {}): Promise { const kmlBuffer = await unpackKmzToKml(kmzBuffer); - return importKmlPlaces(tripId, kmlBuffer); + return importKmlPlaces(tripId, kmlBuffer, opts); } -export async function importMapFile(tripId: string, fileBuffer: Buffer, filename: string): Promise { +export async function importMapFile(tripId: string, fileBuffer: Buffer, filename: string, opts: KmlImportOptions = {}): Promise { const ext = filename.toLowerCase().split('.').pop(); - if (ext === 'kmz') return importKmzPlaces(tripId, fileBuffer); - if (ext === 'kml') return importKmlPlaces(tripId, fileBuffer); + if (ext === 'kmz') return importKmzPlaces(tripId, fileBuffer, opts); + if (ext === 'kml') return importKmlPlaces(tripId, fileBuffer, opts); throw new Error(`Unsupported map file format: .${ext}. Please upload a .kml or .kmz file.`); } diff --git a/server/src/services/weatherService.ts b/server/src/services/weatherService.ts index 3791a354..a7f2fde0 100644 --- a/server/src/services/weatherService.ts +++ b/server/src/services/weatherService.ts @@ -95,6 +95,7 @@ const WMO_DESCRIPTION_EN: Record = { // ── Cache management ──────────────────────────────────────────────────── const weatherCache = new Map(); +const inFlight = new Map>(); const CACHE_MAX_ENTRIES = 1000; const CACHE_PRUNE_TARGET = 500; const CACHE_CLEANUP_INTERVAL = 5 * 60 * 1000; // 5 minutes @@ -146,7 +147,7 @@ export function estimateCondition(tempAvg: number, precipMm: number): string { // ── getWeather ────────────────────────────────────────────────────────── -export async function getWeather( +async function _getWeatherImpl( lat: string, lng: string, date: string | undefined, @@ -281,9 +282,27 @@ export async function getWeather( return result; } +export async function getWeather( + lat: string, + lng: string, + date: string | undefined, + lang: string, +): Promise { + const ck = cacheKey(lat, lng, date); + const cached = getCached(ck); + if (cached) return cached; + + const inFlightKey = `${ck}:${lang}`; + const existing = inFlight.get(inFlightKey); + if (existing) return existing; + const promise = _getWeatherImpl(lat, lng, date, lang); + inFlight.set(inFlightKey, promise); + try { return await promise; } finally { inFlight.delete(inFlightKey); } +} + // ── getDetailedWeather ────────────────────────────────────────────────── -export async function getDetailedWeather( +async function _getDetailedWeatherImpl( lat: string, lng: string, date: string, @@ -434,6 +453,24 @@ export async function getDetailedWeather( return result; } +export async function getDetailedWeather( + lat: string, + lng: string, + date: string, + lang: string, +): Promise { + const ck = `detailed_${cacheKey(lat, lng, date)}`; + const cached = getCached(ck); + if (cached) return cached; + + const inFlightKey = `${ck}:${lang}`; + const existing = inFlight.get(inFlightKey); + if (existing) return existing; + const promise = _getDetailedWeatherImpl(lat, lng, date, lang); + inFlight.set(inFlightKey, promise); + try { return await promise; } finally { inFlight.delete(inFlightKey); } +} + // ── ApiError ──────────────────────────────────────────────────────────── export class ApiError extends Error { diff --git a/server/tests/fixtures/large-test.gpx b/server/tests/fixtures/large-test.gpx new file mode 100644 index 00000000..00336239 --- /dev/null +++ b/server/tests/fixtures/large-test.gpx @@ -0,0 +1,17773 @@ + + + + + 1335.1 + Wegweiser 1 + Test waypoint 1 — category: Shelter + + + 3433.4 + Gipfel 2 + Test waypoint 2 — category: Transport + + + 1143.4 + Kapelle 3 + Test waypoint 3 — category: Transport + + + 2609.6 + Refugio 4 + Test waypoint 4 — category: Water + + + 3152.1 + Alpengasthof 5 + Test waypoint 5 — category: Shelter + + + 928.6 + Parkplatz 6 + Test waypoint 6 — category: POI + + + 3281.5 + Refuge 7 + Test waypoint 7 — category: Summit + + + 2223.2 + Kirche 8 + Test waypoint 8 — category: POI + + + 2503.0 + Bushaltestelle 9 + Test waypoint 9 — category: Transport + + + 2648.3 + Quelle 10 + Test waypoint 10 — category: POI + + + 1692.4 + Gasthaus 11 + Test waypoint 11 — category: Culture + + + 1607.9 + Brücke 12 + Test waypoint 12 — category: Culture + + + 2471.0 + Hütte 13 + Test waypoint 13 — category: Transport + + + 1690.1 + Refugio 14 + Test waypoint 14 — category: Shelter + + + 3038.4 + Wegweiser 15 + Test waypoint 15 — category: POI + + + 625.0 + Chalet 16 + Test waypoint 16 — category: Culture + + + 1745.1 + Gasthaus 17 + Test waypoint 17 — category: Shelter + + + 2308.7 + Brücke 18 + Test waypoint 18 — category: Culture + + + 1758.0 + Wegweiser 19 + Test waypoint 19 — category: Shelter + + + 560.2 + Talstation 20 + Test waypoint 20 — category: Shelter + + + 1835.3 + Gipfel 21 + Test waypoint 21 — category: Water + + + 2199.0 + Refugio 22 + Test waypoint 22 — category: POI + + + 3408.3 + Brücke 23 + Test waypoint 23 — category: Culture + + + 937.7 + Alpengasthof 24 + Test waypoint 24 — category: Culture + + + 2102.0 + Hütte 25 + Test waypoint 25 — category: Transport + + + 1414.7 + Kapelle 26 + Test waypoint 26 — category: Transport + + + 949.3 + Kapelle 27 + Test waypoint 27 — category: POI + + + 780.4 + Bushaltestelle 28 + Test waypoint 28 — category: Summit + + + 2329.0 + Gipfel 29 + Test waypoint 29 — category: POI + + + 3725.1 + Refugio 30 + Test waypoint 30 — category: Shelter + + + 2269.3 + Brücke 31 + Test waypoint 31 — category: Transport + + + 1120.1 + Refugio 32 + Test waypoint 32 — category: Culture + + + 1756.6 + Bushaltestelle 33 + Test waypoint 33 — category: Water + + + 1242.9 + Gipfel 34 + Test waypoint 34 — category: Summit + + + 2400.7 + Alpengasthof 35 + Test waypoint 35 — category: POI + + + 629.2 + Bergstation 36 + Test waypoint 36 — category: Summit + + + 2674.5 + Aussichtspunkt 37 + Test waypoint 37 — category: Transport + + + 2341.5 + Alm 38 + Test waypoint 38 — category: Shelter + + + 1047.4 + Talstation 39 + Test waypoint 39 — category: Culture + + + 1987.9 + Bergstation 40 + Test waypoint 40 — category: Culture + + + 606.1 + Parkplatz 41 + Test waypoint 41 — category: POI + + + 1925.3 + Museum 42 + Test waypoint 42 — category: Shelter + + + 3539.1 + Gasthaus 43 + Test waypoint 43 — category: Transport + + + 2238.0 + Alpengasthof 44 + Test waypoint 44 — category: POI + + + 965.5 + Alm 45 + Test waypoint 45 — category: Water + + + 599.4 + Kirche 46 + Test waypoint 46 — category: POI + + + 3065.8 + Gasthaus 47 + Test waypoint 47 — category: Summit + + + 3784.4 + Refugio 48 + Test waypoint 48 — category: Culture + + + 1408.8 + Bergstation 49 + Test waypoint 49 — category: Transport + + + 1466.3 + Bergstation 50 + Test waypoint 50 — category: Transport + + + 2205.8 + Bergstation 51 + Test waypoint 51 — category: Transport + + + 2423.1 + Wegweiser 52 + Test waypoint 52 — category: Water + + + 1237.1 + Refuge 53 + Test waypoint 53 — category: POI + + + 2384.5 + Kapelle 54 + Test waypoint 54 — category: Summit + + + 2835.1 + Wegweiser 55 + Test waypoint 55 — category: Summit + + + 1420.0 + Parkplatz 56 + Test waypoint 56 — category: POI + + + 2314.2 + Talstation 57 + Test waypoint 57 — category: POI + + + 850.4 + Bushaltestelle 58 + Test waypoint 58 — category: POI + + + 936.3 + Refugio 59 + Test waypoint 59 — category: Culture + + + 3144.1 + Kapelle 60 + Test waypoint 60 — category: POI + + + 3568.0 + Talstation 61 + Test waypoint 61 — category: Shelter + + + 2924.2 + Jochpass 62 + Test waypoint 62 — category: Summit + + + 1565.8 + Brücke 63 + Test waypoint 63 — category: Transport + + + 3276.8 + Gipfel 64 + Test waypoint 64 — category: Culture + + + 412.1 + Jochpass 65 + Test waypoint 65 — category: Culture + + + 1902.2 + Museum 66 + Test waypoint 66 — category: Transport + + + 3402.6 + Jochpass 67 + Test waypoint 67 — category: Transport + + + 2278.6 + Museum 68 + Test waypoint 68 — category: Shelter + + + 3572.5 + Bergstation 69 + Test waypoint 69 — category: Summit + + + 749.5 + Refugio 70 + Test waypoint 70 — category: Water + + + 3547.6 + Wegweiser 71 + Test waypoint 71 — category: Shelter + + + 3396.8 + Alpengasthof 72 + Test waypoint 72 — category: Shelter + + + 3564.8 + Wegweiser 73 + Test waypoint 73 — category: Summit + + + 1700.6 + Bergstation 74 + Test waypoint 74 — category: Water + + + 1965.0 + Quelle 75 + Test waypoint 75 — category: Shelter + + + 1754.8 + Brücke 76 + Test waypoint 76 — category: POI + + + 2581.0 + Kirche 77 + Test waypoint 77 — category: Culture + + + 3593.4 + Talstation 78 + Test waypoint 78 — category: Summit + + + 1302.6 + Talstation 79 + Test waypoint 79 — category: Transport + + + 1466.5 + Refuge 80 + Test waypoint 80 — category: Transport + + + 1046.3 + Bergstation 81 + Test waypoint 81 — category: Culture + + + 3142.1 + Aussichtspunkt 82 + Test waypoint 82 — category: Summit + + + 3530.6 + Refuge 83 + Test waypoint 83 — category: Summit + + + 3460.2 + Kapelle 84 + Test waypoint 84 — category: Summit + + + 2770.5 + Refugio 85 + Test waypoint 85 — category: Shelter + + + 1689.1 + Hütte 86 + Test waypoint 86 — category: Transport + + + 3234.7 + Quelle 87 + Test waypoint 87 — category: Summit + + + 2462.6 + Parkplatz 88 + Test waypoint 88 — category: Water + + + 2138.0 + Hütte 89 + Test waypoint 89 — category: Culture + + + 2552.0 + Parkplatz 90 + Test waypoint 90 — category: POI + + + 2687.5 + Wegweiser 91 + Test waypoint 91 — category: Shelter + + + 3745.4 + Refuge 92 + Test waypoint 92 — category: POI + + + 2357.3 + Kirche 93 + Test waypoint 93 — category: Water + + + 2737.9 + Talstation 94 + Test waypoint 94 — category: Water + + + 2766.1 + Gasthaus 95 + Test waypoint 95 — category: POI + + + 812.6 + Jochpass 96 + Test waypoint 96 — category: Water + + + 2424.5 + Gasthaus 97 + Test waypoint 97 — category: Transport + + + 1850.9 + Refugio 98 + Test waypoint 98 — category: Water + + + 2013.9 + Brücke 99 + Test waypoint 99 — category: Shelter + + + 3044.1 + Alm 100 + Test waypoint 100 — category: Culture + + + 2826.1 + Wegweiser 101 + Test waypoint 101 — category: Summit + + + 674.0 + Jochpass 102 + Test waypoint 102 — category: Shelter + + + 1127.4 + Museum 103 + Test waypoint 103 — category: Water + + + 611.7 + Museum 104 + Test waypoint 104 — category: Water + + + 2764.8 + Chalet 105 + Test waypoint 105 — category: Water + + + 1415.3 + Kirche 106 + Test waypoint 106 — category: Water + + + 3119.6 + Wegweiser 107 + Test waypoint 107 — category: Water + + + 498.7 + Parkplatz 108 + Test waypoint 108 — category: Culture + + + 961.2 + Gasthaus 109 + Test waypoint 109 — category: Shelter + + + 3483.6 + Chalet 110 + Test waypoint 110 — category: Transport + + + 1857.3 + Gasthaus 111 + Test waypoint 111 — category: Shelter + + + 1119.6 + Parkplatz 112 + Test waypoint 112 — category: Summit + + + 2956.8 + Museum 113 + Test waypoint 113 — category: Summit + + + 2946.7 + Bergstation 114 + Test waypoint 114 — category: Summit + + + 3654.8 + Bergstation 115 + Test waypoint 115 — category: POI + + + 469.3 + Jochpass 116 + Test waypoint 116 — category: Shelter + + + 2317.5 + Aussichtspunkt 117 + Test waypoint 117 — category: Water + + + 970.5 + Refuge 118 + Test waypoint 118 — category: Culture + + + 956.8 + Quelle 119 + Test waypoint 119 — category: POI + + + 2357.7 + Kirche 120 + Test waypoint 120 — category: Water + + + 2413.1 + Wegweiser 121 + Test waypoint 121 — category: POI + + + 2726.5 + Talstation 122 + Test waypoint 122 — category: Transport + + + 1856.5 + Bushaltestelle 123 + Test waypoint 123 — category: POI + + + 3288.6 + Alm 124 + Test waypoint 124 — category: POI + + + 3431.4 + Gasthaus 125 + Test waypoint 125 — category: Culture + + + 2173.9 + Brücke 126 + Test waypoint 126 — category: Transport + + + 2043.9 + Museum 127 + Test waypoint 127 — category: Culture + + + 1234.7 + Gipfel 128 + Test waypoint 128 — category: Summit + + + 1980.0 + Refuge 129 + Test waypoint 129 — category: Culture + + + 3293.2 + Hütte 130 + Test waypoint 130 — category: Water + + + 1557.2 + Refuge 131 + Test waypoint 131 — category: Culture + + + 2156.5 + Aussichtspunkt 132 + Test waypoint 132 — category: POI + + + 2287.6 + Wegweiser 133 + Test waypoint 133 — category: Culture + + + 1923.8 + Alpengasthof 134 + Test waypoint 134 — category: POI + + + 1227.2 + Chalet 135 + Test waypoint 135 — category: Summit + + + 1846.7 + Refugio 136 + Test waypoint 136 — category: Summit + + + 1442.5 + Wegweiser 137 + Test waypoint 137 — category: POI + + + 2925.8 + Hütte 138 + Test waypoint 138 — category: Shelter + + + 2863.6 + Kapelle 139 + Test waypoint 139 — category: Transport + + + 1060.0 + Wegweiser 140 + Test waypoint 140 — category: Summit + + + 2216.1 + Brücke 141 + Test waypoint 141 — category: POI + + + 2771.2 + Jochpass 142 + Test waypoint 142 — category: Culture + + + 3367.2 + Chalet 143 + Test waypoint 143 — category: Summit + + + 1026.8 + Bergstation 144 + Test waypoint 144 — category: Summit + + + 3195.2 + Kirche 145 + Test waypoint 145 — category: Water + + + 582.3 + Jochpass 146 + Test waypoint 146 — category: Transport + + + 1243.8 + Refugio 147 + Test waypoint 147 — category: Water + + + 1167.3 + Kapelle 148 + Test waypoint 148 — category: Water + + + 3324.6 + Museum 149 + Test waypoint 149 — category: Summit + + + 3664.4 + Talstation 150 + Test waypoint 150 — category: Shelter + + + 676.0 + Wegweiser 151 + Test waypoint 151 — category: Shelter + + + 1789.0 + Refuge 152 + Test waypoint 152 — category: Water + + + 2803.6 + Gasthaus 153 + Test waypoint 153 — category: POI + + + 3078.6 + Chalet 154 + Test waypoint 154 — category: Culture + + + 790.2 + Wegweiser 155 + Test waypoint 155 — category: Culture + + + 883.6 + Bergstation 156 + Test waypoint 156 — category: Shelter + + + 3202.4 + Quelle 157 + Test waypoint 157 — category: Water + + + 2779.1 + Brücke 158 + Test waypoint 158 — category: Transport + + + 2433.3 + Museum 159 + Test waypoint 159 — category: Culture + + + 710.5 + Chalet 160 + Test waypoint 160 — category: Transport + + + 2685.8 + Brücke 161 + Test waypoint 161 — category: Transport + + + 1999.7 + Gasthaus 162 + Test waypoint 162 — category: Summit + + + 2558.3 + Alm 163 + Test waypoint 163 — category: POI + + + 1491.6 + Talstation 164 + Test waypoint 164 — category: Shelter + + + 1379.9 + Kirche 165 + Test waypoint 165 — category: Transport + + + 1258.2 + Talstation 166 + Test waypoint 166 — category: Water + + + 403.9 + Refugio 167 + Test waypoint 167 — category: Water + + + 1630.0 + Alm 168 + Test waypoint 168 — category: Culture + + + 1308.0 + Jochpass 169 + Test waypoint 169 — category: Summit + + + 812.8 + Refuge 170 + Test waypoint 170 — category: Shelter + + + 446.5 + Museum 171 + Test waypoint 171 — category: POI + + + 785.8 + Talstation 172 + Test waypoint 172 — category: Culture + + + 2836.5 + Kapelle 173 + Test waypoint 173 — category: Culture + + + 3732.8 + Wegweiser 174 + Test waypoint 174 — category: Water + + + 3534.4 + Kapelle 175 + Test waypoint 175 — category: Culture + + + 1339.2 + Museum 176 + Test waypoint 176 — category: Summit + + + 3189.9 + Quelle 177 + Test waypoint 177 — category: Culture + + + 3796.3 + Alm 178 + Test waypoint 178 — category: Shelter + + + 1530.3 + Refugio 179 + Test waypoint 179 — category: Water + + + 1041.4 + Wegweiser 180 + Test waypoint 180 — category: Transport + + + 1796.6 + Parkplatz 181 + Test waypoint 181 — category: Culture + + + 1696.2 + Jochpass 182 + Test waypoint 182 — category: Water + + + 3682.2 + Parkplatz 183 + Test waypoint 183 — category: POI + + + 2188.2 + Gasthaus 184 + Test waypoint 184 — category: POI + + + 2626.3 + Jochpass 185 + Test waypoint 185 — category: POI + + + 1551.1 + Kirche 186 + Test waypoint 186 — category: Culture + + + 2692.8 + Refugio 187 + Test waypoint 187 — category: Water + + + 3417.3 + Alm 188 + Test waypoint 188 — category: Transport + + + 2545.9 + Quelle 189 + Test waypoint 189 — category: Shelter + + + 734.7 + Talstation 190 + Test waypoint 190 — category: Water + + + 498.4 + Parkplatz 191 + Test waypoint 191 — category: POI + + + 894.9 + Kapelle 192 + Test waypoint 192 — category: Water + + + 977.9 + Gipfel 193 + Test waypoint 193 — category: Transport + + + 1218.8 + Chalet 194 + Test waypoint 194 — category: Shelter + + + 1962.5 + Alpengasthof 195 + Test waypoint 195 — category: Water + + + 937.1 + Gasthaus 196 + Test waypoint 196 — category: Summit + + + 3670.9 + Brücke 197 + Test waypoint 197 — category: Water + + + 1708.0 + Alm 198 + Test waypoint 198 — category: POI + + + 2354.4 + Quelle 199 + Test waypoint 199 — category: POI + + + 1333.2 + Chalet 200 + Test waypoint 200 — category: Culture + + + + Via Alpina Stage 1 + Scenic alpine route 1 with 79 waypoints through mountain terrain + + 3241.2 + RP1-1 + + + 2439.1 + RP1-2 + + + 2476.4 + RP1-3 + + + 2579.8 + RP1-4 + + + 3239.2 + RP1-5 + + + 1990.2 + RP1-6 + + + 1043.4 + RP1-7 + + + 1274.2 + RP1-8 + + + 3423.0 + RP1-9 + + + 3042.3 + RP1-10 + + + 2101.0 + RP1-11 + + + 1714.5 + RP1-12 + + + 1180.0 + RP1-13 + + + 2755.4 + RP1-14 + + + 2972.4 + RP1-15 + + + 837.6 + RP1-16 + + + 2616.6 + RP1-17 + + + 3020.5 + RP1-18 + + + 1431.3 + RP1-19 + + + 2315.8 + RP1-20 + + + 970.7 + RP1-21 + + + 1615.9 + RP1-22 + + + 3354.9 + RP1-23 + + + 1062.4 + RP1-24 + + + 2024.8 + RP1-25 + + + 3354.1 + RP1-26 + + + 1217.2 + RP1-27 + + + 2121.2 + RP1-28 + + + 888.3 + RP1-29 + + + 3366.5 + RP1-30 + + + 2651.3 + RP1-31 + + + 1768.4 + RP1-32 + + + 1129.8 + RP1-33 + + + 2589.5 + RP1-34 + + + 857.7 + RP1-35 + + + 1140.0 + RP1-36 + + + 2865.9 + RP1-37 + + + 3148.2 + RP1-38 + + + 3231.4 + RP1-39 + + + 1190.7 + RP1-40 + + + 1271.1 + RP1-41 + + + 840.1 + RP1-42 + + + 1674.2 + RP1-43 + + + 2802.6 + RP1-44 + + + 2085.9 + RP1-45 + + + 1094.4 + RP1-46 + + + 917.1 + RP1-47 + + + 2207.9 + RP1-48 + + + 964.2 + RP1-49 + + + 2652.5 + RP1-50 + + + 998.4 + RP1-51 + + + 977.3 + RP1-52 + + + 2280.6 + RP1-53 + + + 2232.5 + RP1-54 + + + 1067.8 + RP1-55 + + + 2557.5 + RP1-56 + + + 1380.6 + RP1-57 + + + 1715.1 + RP1-58 + + + 1056.7 + RP1-59 + + + 1810.6 + RP1-60 + + + 981.3 + RP1-61 + + + 1060.7 + RP1-62 + + + 2302.2 + RP1-63 + + + 1690.0 + RP1-64 + + + 2596.4 + RP1-65 + + + 2746.9 + RP1-66 + + + 1749.4 + RP1-67 + + + 2573.2 + RP1-68 + + + 3082.5 + RP1-69 + + + 2350.1 + RP1-70 + + + 3431.3 + RP1-71 + + + 2535.3 + RP1-72 + + + 3039.5 + RP1-73 + + + 2697.3 + RP1-74 + + + 816.5 + RP1-75 + + + 1882.1 + RP1-76 + + + 882.8 + RP1-77 + + + 1933.5 + RP1-78 + + + 2748.3 + RP1-79 + + + + + Haute Route West + Scenic alpine route 2 with 79 waypoints through mountain terrain + + 2837.0 + RP2-1 + + + 1996.4 + RP2-2 + + + 1918.3 + RP2-3 + + + 1732.0 + RP2-4 + + + 2401.5 + RP2-5 + + + 1395.4 + RP2-6 + + + 1351.9 + RP2-7 + + + 2863.2 + RP2-8 + + + 2142.9 + RP2-9 + + + 2238.0 + RP2-10 + + + 2603.7 + RP2-11 + + + 2803.5 + RP2-12 + + + 1010.2 + RP2-13 + + + 1891.9 + RP2-14 + + + 1893.6 + RP2-15 + + + 3358.6 + RP2-16 + + + 1777.0 + RP2-17 + + + 1976.3 + RP2-18 + + + 3150.4 + RP2-19 + + + 2903.6 + RP2-20 + + + 2232.6 + RP2-21 + + + 1748.0 + RP2-22 + + + 1198.8 + RP2-23 + + + 1268.4 + RP2-24 + + + 1003.5 + RP2-25 + + + 2052.7 + RP2-26 + + + 2639.1 + RP2-27 + + + 3463.6 + RP2-28 + + + 1653.6 + RP2-29 + + + 2504.7 + RP2-30 + + + 1750.2 + RP2-31 + + + 901.5 + RP2-32 + + + 2540.6 + RP2-33 + + + 2404.0 + RP2-34 + + + 3376.4 + RP2-35 + + + 3254.4 + RP2-36 + + + 2433.5 + RP2-37 + + + 2016.4 + RP2-38 + + + 3452.4 + RP2-39 + + + 905.7 + RP2-40 + + + 2215.0 + RP2-41 + + + 3278.4 + RP2-42 + + + 2633.0 + RP2-43 + + + 1703.6 + RP2-44 + + + 1843.7 + RP2-45 + + + 3403.9 + RP2-46 + + + 1020.1 + RP2-47 + + + 1637.6 + RP2-48 + + + 1148.2 + RP2-49 + + + 2309.1 + RP2-50 + + + 1145.0 + RP2-51 + + + 3492.3 + RP2-52 + + + 2610.2 + RP2-53 + + + 1633.9 + RP2-54 + + + 2868.6 + RP2-55 + + + 1218.4 + RP2-56 + + + 2170.8 + RP2-57 + + + 2221.6 + RP2-58 + + + 2468.7 + RP2-59 + + + 1288.3 + RP2-60 + + + 3237.2 + RP2-61 + + + 1783.6 + RP2-62 + + + 2448.6 + RP2-63 + + + 1431.8 + RP2-64 + + + 2917.8 + RP2-65 + + + 2631.4 + RP2-66 + + + 2877.5 + RP2-67 + + + 1930.4 + RP2-68 + + + 2432.6 + RP2-69 + + + 2097.6 + RP2-70 + + + 2720.8 + RP2-71 + + + 1026.5 + RP2-72 + + + 2186.1 + RP2-73 + + + 3258.4 + RP2-74 + + + 3312.3 + RP2-75 + + + 1809.7 + RP2-76 + + + 1406.3 + RP2-77 + + + 2729.7 + RP2-78 + + + 903.4 + RP2-79 + + + + + Tour du Mont Blanc Section A + Scenic alpine route 3 with 60 waypoints through mountain terrain + + 814.2 + RP3-1 + + + 3054.4 + RP3-2 + + + 2225.0 + RP3-3 + + + 1613.1 + RP3-4 + + + 2943.5 + RP3-5 + + + 3243.9 + RP3-6 + + + 2191.5 + RP3-7 + + + 1958.9 + RP3-8 + + + 1924.7 + RP3-9 + + + 3431.6 + RP3-10 + + + 2169.8 + RP3-11 + + + 1854.2 + RP3-12 + + + 3448.8 + RP3-13 + + + 2929.0 + RP3-14 + + + 2496.3 + RP3-15 + + + 1697.8 + RP3-16 + + + 2996.5 + RP3-17 + + + 2054.5 + RP3-18 + + + 2991.0 + RP3-19 + + + 2035.7 + RP3-20 + + + 1338.6 + RP3-21 + + + 2191.5 + RP3-22 + + + 1483.9 + RP3-23 + + + 1763.8 + RP3-24 + + + 1696.2 + RP3-25 + + + 3201.6 + RP3-26 + + + 1843.9 + RP3-27 + + + 2143.3 + RP3-28 + + + 2963.1 + RP3-29 + + + 2426.6 + RP3-30 + + + 1667.3 + RP3-31 + + + 1627.5 + RP3-32 + + + 2662.4 + RP3-33 + + + 3149.1 + RP3-34 + + + 1541.3 + RP3-35 + + + 1002.6 + RP3-36 + + + 2963.0 + RP3-37 + + + 1185.5 + RP3-38 + + + 894.0 + RP3-39 + + + 1789.1 + RP3-40 + + + 2433.8 + RP3-41 + + + 1804.4 + RP3-42 + + + 2229.5 + RP3-43 + + + 1474.0 + RP3-44 + + + 2147.4 + RP3-45 + + + 1506.8 + RP3-46 + + + 3490.2 + RP3-47 + + + 2126.2 + RP3-48 + + + 2020.5 + RP3-49 + + + 1037.0 + RP3-50 + + + 1662.9 + RP3-51 + + + 2263.9 + RP3-52 + + + 2721.9 + RP3-53 + + + 2943.0 + RP3-54 + + + 1342.6 + RP3-55 + + + 871.0 + RP3-56 + + + 2969.9 + RP3-57 + + + 1032.8 + RP3-58 + + + 2740.9 + RP3-59 + + + 3023.0 + RP3-60 + + + + + Rheinsteig Etappe 3 + Scenic alpine route 4 with 34 waypoints through mountain terrain + + 2196.9 + RP4-1 + + + 2288.5 + RP4-2 + + + 1148.4 + RP4-3 + + + 3044.8 + RP4-4 + + + 1237.3 + RP4-5 + + + 3358.2 + RP4-6 + + + 1012.9 + RP4-7 + + + 1541.0 + RP4-8 + + + 1052.3 + RP4-9 + + + 2367.7 + RP4-10 + + + 3235.4 + RP4-11 + + + 911.2 + RP4-12 + + + 3361.1 + RP4-13 + + + 1369.2 + RP4-14 + + + 881.8 + RP4-15 + + + 2533.1 + RP4-16 + + + 2979.8 + RP4-17 + + + 2659.3 + RP4-18 + + + 2054.0 + RP4-19 + + + 3126.0 + RP4-20 + + + 1732.5 + RP4-21 + + + 2190.4 + RP4-22 + + + 1130.8 + RP4-23 + + + 936.6 + RP4-24 + + + 1929.0 + RP4-25 + + + 2996.4 + RP4-26 + + + 1312.6 + RP4-27 + + + 2060.4 + RP4-28 + + + 1043.1 + RP4-29 + + + 1450.0 + RP4-30 + + + 1559.7 + RP4-31 + + + 2615.8 + RP4-32 + + + 1090.2 + RP4-33 + + + 1787.6 + RP4-34 + + + + + Inntal Wanderweg + Scenic alpine route 5 with 55 waypoints through mountain terrain + + 2293.7 + RP5-1 + + + 2863.9 + RP5-2 + + + 2637.1 + RP5-3 + + + 1863.5 + RP5-4 + + + 3335.2 + RP5-5 + + + 2174.6 + RP5-6 + + + 1170.0 + RP5-7 + + + 2033.9 + RP5-8 + + + 1685.5 + RP5-9 + + + 3474.7 + RP5-10 + + + 1708.2 + RP5-11 + + + 2713.6 + RP5-12 + + + 1794.6 + RP5-13 + + + 2376.0 + RP5-14 + + + 2657.7 + RP5-15 + + + 3411.1 + RP5-16 + + + 2359.6 + RP5-17 + + + 1260.4 + RP5-18 + + + 3071.2 + RP5-19 + + + 885.2 + RP5-20 + + + 851.1 + RP5-21 + + + 2664.4 + RP5-22 + + + 3285.4 + RP5-23 + + + 968.2 + RP5-24 + + + 1677.6 + RP5-25 + + + 1647.1 + RP5-26 + + + 1724.3 + RP5-27 + + + 2746.5 + RP5-28 + + + 2447.2 + RP5-29 + + + 2306.8 + RP5-30 + + + 1603.6 + RP5-31 + + + 2703.5 + RP5-32 + + + 3321.0 + RP5-33 + + + 1129.1 + RP5-34 + + + 3446.4 + RP5-35 + + + 2972.5 + RP5-36 + + + 2728.2 + RP5-37 + + + 1492.4 + RP5-38 + + + 2254.2 + RP5-39 + + + 1170.6 + RP5-40 + + + 1454.6 + RP5-41 + + + 2522.5 + RP5-42 + + + 2736.9 + RP5-43 + + + 814.0 + RP5-44 + + + 1898.7 + RP5-45 + + + 2089.1 + RP5-46 + + + 1667.2 + RP5-47 + + + 1041.5 + RP5-48 + + + 2752.4 + RP5-49 + + + 1272.7 + RP5-50 + + + 3283.0 + RP5-51 + + + 2137.5 + RP5-52 + + + 3300.3 + RP5-53 + + + 2333.5 + RP5-54 + + + 1568.3 + RP5-55 + + + + + Zermatt Runde + Scenic alpine route 6 with 71 waypoints through mountain terrain + + 2063.7 + RP6-1 + + + 1238.7 + RP6-2 + + + 1302.7 + RP6-3 + + + 2333.0 + RP6-4 + + + 3111.6 + RP6-5 + + + 2191.3 + RP6-6 + + + 1298.9 + RP6-7 + + + 1469.7 + RP6-8 + + + 2835.1 + RP6-9 + + + 2751.8 + RP6-10 + + + 2670.5 + RP6-11 + + + 1628.4 + RP6-12 + + + 3216.8 + RP6-13 + + + 1117.0 + RP6-14 + + + 2459.5 + RP6-15 + + + 1213.6 + RP6-16 + + + 1922.8 + RP6-17 + + + 2251.3 + RP6-18 + + + 1468.2 + RP6-19 + + + 1021.0 + RP6-20 + + + 1010.9 + RP6-21 + + + 2278.7 + RP6-22 + + + 1204.6 + RP6-23 + + + 1113.8 + RP6-24 + + + 1045.5 + RP6-25 + + + 2024.3 + RP6-26 + + + 955.9 + RP6-27 + + + 2752.9 + RP6-28 + + + 2813.7 + RP6-29 + + + 1730.2 + RP6-30 + + + 3323.4 + RP6-31 + + + 885.7 + RP6-32 + + + 2867.7 + RP6-33 + + + 1935.0 + RP6-34 + + + 3148.0 + RP6-35 + + + 2643.4 + RP6-36 + + + 2564.5 + RP6-37 + + + 2250.6 + RP6-38 + + + 1401.3 + RP6-39 + + + 1605.6 + RP6-40 + + + 2236.2 + RP6-41 + + + 1734.6 + RP6-42 + + + 2757.6 + RP6-43 + + + 1931.9 + RP6-44 + + + 842.2 + RP6-45 + + + 890.7 + RP6-46 + + + 931.4 + RP6-47 + + + 1432.7 + RP6-48 + + + 952.4 + RP6-49 + + + 3002.6 + RP6-50 + + + 2414.4 + RP6-51 + + + 1614.8 + RP6-52 + + + 1396.8 + RP6-53 + + + 3361.0 + RP6-54 + + + 2490.2 + RP6-55 + + + 3425.2 + RP6-56 + + + 2921.7 + RP6-57 + + + 1202.9 + RP6-58 + + + 2078.5 + RP6-59 + + + 2710.5 + RP6-60 + + + 2318.4 + RP6-61 + + + 1162.1 + RP6-62 + + + 2546.1 + RP6-63 + + + 1049.7 + RP6-64 + + + 1524.8 + RP6-65 + + + 3158.8 + RP6-66 + + + 2755.6 + RP6-67 + + + 3147.6 + RP6-68 + + + 841.4 + RP6-69 + + + 1116.8 + RP6-70 + + + 1455.0 + RP6-71 + + + + + Graubünden Panoramaweg + Scenic alpine route 7 with 68 waypoints through mountain terrain + + 1564.3 + RP7-1 + + + 1632.7 + RP7-2 + + + 2058.2 + RP7-3 + + + 2856.4 + RP7-4 + + + 1644.9 + RP7-5 + + + 2801.4 + RP7-6 + + + 2376.9 + RP7-7 + + + 1329.7 + RP7-8 + + + 1463.5 + RP7-9 + + + 1711.4 + RP7-10 + + + 2753.6 + RP7-11 + + + 2778.5 + RP7-12 + + + 2275.7 + RP7-13 + + + 3473.9 + RP7-14 + + + 3065.5 + RP7-15 + + + 931.0 + RP7-16 + + + 2502.9 + RP7-17 + + + 1390.5 + RP7-18 + + + 2521.9 + RP7-19 + + + 2580.1 + RP7-20 + + + 1910.6 + RP7-21 + + + 2246.5 + RP7-22 + + + 2684.2 + RP7-23 + + + 1284.0 + RP7-24 + + + 1450.6 + RP7-25 + + + 1757.0 + RP7-26 + + + 3390.1 + RP7-27 + + + 2682.5 + RP7-28 + + + 1017.7 + RP7-29 + + + 885.5 + RP7-30 + + + 2085.2 + RP7-31 + + + 1023.3 + RP7-32 + + + 1694.4 + RP7-33 + + + 3215.9 + RP7-34 + + + 3233.4 + RP7-35 + + + 1532.0 + RP7-36 + + + 1557.0 + RP7-37 + + + 1284.3 + RP7-38 + + + 1359.3 + RP7-39 + + + 3016.4 + RP7-40 + + + 2322.7 + RP7-41 + + + 2628.0 + RP7-42 + + + 2797.3 + RP7-43 + + + 1011.8 + RP7-44 + + + 1063.8 + RP7-45 + + + 2434.0 + RP7-46 + + + 3416.1 + RP7-47 + + + 2915.6 + RP7-48 + + + 2293.2 + RP7-49 + + + 2381.9 + RP7-50 + + + 1597.5 + RP7-51 + + + 1523.5 + RP7-52 + + + 1216.5 + RP7-53 + + + 1047.1 + RP7-54 + + + 2855.6 + RP7-55 + + + 1963.6 + RP7-56 + + + 1078.9 + RP7-57 + + + 2411.7 + RP7-58 + + + 833.3 + RP7-59 + + + 2757.1 + RP7-60 + + + 1455.4 + RP7-61 + + + 1257.7 + RP7-62 + + + 1194.0 + RP7-63 + + + 1609.3 + RP7-64 + + + 3349.4 + RP7-65 + + + 2465.0 + RP7-66 + + + 818.3 + RP7-67 + + + 1825.7 + RP7-68 + + + + + Südtiroler Höhenweg + Scenic alpine route 8 with 51 waypoints through mountain terrain + + 2404.9 + RP8-1 + + + 2677.3 + RP8-2 + + + 1875.2 + RP8-3 + + + 2508.8 + RP8-4 + + + 1679.9 + RP8-5 + + + 1785.5 + RP8-6 + + + 3492.6 + RP8-7 + + + 872.7 + RP8-8 + + + 1366.1 + RP8-9 + + + 986.7 + RP8-10 + + + 2617.0 + RP8-11 + + + 1482.2 + RP8-12 + + + 1817.9 + RP8-13 + + + 2372.1 + RP8-14 + + + 3148.0 + RP8-15 + + + 3414.8 + RP8-16 + + + 2403.3 + RP8-17 + + + 1470.2 + RP8-18 + + + 2454.2 + RP8-19 + + + 2546.2 + RP8-20 + + + 2949.8 + RP8-21 + + + 1399.4 + RP8-22 + + + 2656.7 + RP8-23 + + + 2050.3 + RP8-24 + + + 1207.0 + RP8-25 + + + 1597.7 + RP8-26 + + + 3044.6 + RP8-27 + + + 1726.3 + RP8-28 + + + 1740.4 + RP8-29 + + + 2021.6 + RP8-30 + + + 2719.1 + RP8-31 + + + 1095.3 + RP8-32 + + + 2799.3 + RP8-33 + + + 1693.7 + RP8-34 + + + 2953.9 + RP8-35 + + + 2574.7 + RP8-36 + + + 3184.4 + RP8-37 + + + 812.0 + RP8-38 + + + 2700.2 + RP8-39 + + + 2958.7 + RP8-40 + + + 1332.1 + RP8-41 + + + 2121.6 + RP8-42 + + + 1849.3 + RP8-43 + + + 1750.4 + RP8-44 + + + 2618.7 + RP8-45 + + + 2393.5 + RP8-46 + + + 1444.1 + RP8-47 + + + 1181.7 + RP8-48 + + + 2352.0 + RP8-49 + + + 2250.9 + RP8-50 + + + 1267.7 + RP8-51 + + + + + Salzburger Almenweg + Scenic alpine route 9 with 40 waypoints through mountain terrain + + 944.8 + RP9-1 + + + 1168.6 + RP9-2 + + + 2164.7 + RP9-3 + + + 1922.2 + RP9-4 + + + 1023.2 + RP9-5 + + + 1588.6 + RP9-6 + + + 1254.2 + RP9-7 + + + 2868.0 + RP9-8 + + + 1108.4 + RP9-9 + + + 1790.5 + RP9-10 + + + 2741.2 + RP9-11 + + + 1076.5 + RP9-12 + + + 1603.8 + RP9-13 + + + 1741.2 + RP9-14 + + + 3483.7 + RP9-15 + + + 2988.9 + RP9-16 + + + 2719.0 + RP9-17 + + + 1175.9 + RP9-18 + + + 960.3 + RP9-19 + + + 2254.7 + RP9-20 + + + 1581.2 + RP9-21 + + + 3460.8 + RP9-22 + + + 3399.5 + RP9-23 + + + 3479.7 + RP9-24 + + + 3138.4 + RP9-25 + + + 3036.9 + RP9-26 + + + 2121.5 + RP9-27 + + + 1918.4 + RP9-28 + + + 3176.3 + RP9-29 + + + 1551.2 + RP9-30 + + + 3365.7 + RP9-31 + + + 2890.2 + RP9-32 + + + 2037.8 + RP9-33 + + + 971.0 + RP9-34 + + + 1960.0 + RP9-35 + + + 3036.5 + RP9-36 + + + 2328.6 + RP9-37 + + + 2642.9 + RP9-38 + + + 2916.5 + RP9-39 + + + 3115.7 + RP9-40 + + + + + Lechweg Etappe 2 + Scenic alpine route 10 with 58 waypoints through mountain terrain + + 3255.4 + RP10-1 + + + 3160.9 + RP10-2 + + + 1773.2 + RP10-3 + + + 2552.1 + RP10-4 + + + 2760.2 + RP10-5 + + + 1046.2 + RP10-6 + + + 1010.4 + RP10-7 + + + 2001.7 + RP10-8 + + + 1125.6 + RP10-9 + + + 2089.8 + RP10-10 + + + 2976.1 + RP10-11 + + + 1608.0 + RP10-12 + + + 3161.1 + RP10-13 + + + 984.7 + RP10-14 + + + 1243.1 + RP10-15 + + + 2820.3 + RP10-16 + + + 1994.4 + RP10-17 + + + 1178.0 + RP10-18 + + + 1749.1 + RP10-19 + + + 1469.0 + RP10-20 + + + 2422.7 + RP10-21 + + + 3341.5 + RP10-22 + + + 3164.7 + RP10-23 + + + 1309.3 + RP10-24 + + + 2916.3 + RP10-25 + + + 1841.9 + RP10-26 + + + 991.7 + RP10-27 + + + 1775.0 + RP10-28 + + + 3065.2 + RP10-29 + + + 1266.5 + RP10-30 + + + 997.1 + RP10-31 + + + 1382.5 + RP10-32 + + + 1874.5 + RP10-33 + + + 2871.2 + RP10-34 + + + 2529.5 + RP10-35 + + + 1025.7 + RP10-36 + + + 3430.6 + RP10-37 + + + 2383.4 + RP10-38 + + + 2536.5 + RP10-39 + + + 3374.3 + RP10-40 + + + 3296.5 + RP10-41 + + + 948.6 + RP10-42 + + + 1274.2 + RP10-43 + + + 830.6 + RP10-44 + + + 2196.5 + RP10-45 + + + 1804.0 + RP10-46 + + + 2778.9 + RP10-47 + + + 1704.5 + RP10-48 + + + 1510.0 + RP10-49 + + + 2139.6 + RP10-50 + + + 3120.3 + RP10-51 + + + 2888.0 + RP10-52 + + + 1111.4 + RP10-53 + + + 3118.5 + RP10-54 + + + 1782.7 + RP10-55 + + + 3392.9 + RP10-56 + + + 2727.9 + RP10-57 + + + 1470.3 + RP10-58 + + + + + Karwendel-Überquerung + Scenic alpine route 11 with 76 waypoints through mountain terrain + + 2618.0 + RP11-1 + + + 2192.6 + RP11-2 + + + 3167.9 + RP11-3 + + + 1638.9 + RP11-4 + + + 1796.1 + RP11-5 + + + 2796.9 + RP11-6 + + + 3424.0 + RP11-7 + + + 2777.7 + RP11-8 + + + 1206.5 + RP11-9 + + + 2262.2 + RP11-10 + + + 825.9 + RP11-11 + + + 1815.9 + RP11-12 + + + 1354.0 + RP11-13 + + + 3395.2 + RP11-14 + + + 1555.5 + RP11-15 + + + 3082.9 + RP11-16 + + + 2037.8 + RP11-17 + + + 2164.7 + RP11-18 + + + 2398.1 + RP11-19 + + + 1152.9 + RP11-20 + + + 3300.3 + RP11-21 + + + 1680.9 + RP11-22 + + + 3347.6 + RP11-23 + + + 1094.2 + RP11-24 + + + 1647.9 + RP11-25 + + + 2879.9 + RP11-26 + + + 3101.7 + RP11-27 + + + 1621.4 + RP11-28 + + + 1380.6 + RP11-29 + + + 2720.1 + RP11-30 + + + 932.2 + RP11-31 + + + 1269.4 + RP11-32 + + + 2301.1 + RP11-33 + + + 1075.1 + RP11-34 + + + 966.5 + RP11-35 + + + 1335.0 + RP11-36 + + + 3331.5 + RP11-37 + + + 3277.1 + RP11-38 + + + 1333.9 + RP11-39 + + + 1048.3 + RP11-40 + + + 1130.8 + RP11-41 + + + 3295.2 + RP11-42 + + + 2139.5 + RP11-43 + + + 1755.6 + RP11-44 + + + 2004.2 + RP11-45 + + + 2465.8 + RP11-46 + + + 3183.5 + RP11-47 + + + 1834.1 + RP11-48 + + + 2952.4 + RP11-49 + + + 2267.9 + RP11-50 + + + 2726.1 + RP11-51 + + + 1728.9 + RP11-52 + + + 1530.2 + RP11-53 + + + 879.1 + RP11-54 + + + 2983.4 + RP11-55 + + + 2376.8 + RP11-56 + + + 1469.2 + RP11-57 + + + 2936.3 + RP11-58 + + + 3359.1 + RP11-59 + + + 1848.7 + RP11-60 + + + 1269.0 + RP11-61 + + + 2792.9 + RP11-62 + + + 2699.3 + RP11-63 + + + 1183.7 + RP11-64 + + + 2988.1 + RP11-65 + + + 1274.0 + RP11-66 + + + 2424.5 + RP11-67 + + + 1695.7 + RP11-68 + + + 1720.0 + RP11-69 + + + 3144.2 + RP11-70 + + + 2702.7 + RP11-71 + + + 2221.0 + RP11-72 + + + 2702.3 + RP11-73 + + + 1708.7 + RP11-74 + + + 2777.4 + RP11-75 + + + 2115.4 + RP11-76 + + + + + Dolomiten Höhenweg + Scenic alpine route 12 with 50 waypoints through mountain terrain + + 2924.9 + RP12-1 + + + 1273.4 + RP12-2 + + + 1143.8 + RP12-3 + + + 3361.5 + RP12-4 + + + 3406.0 + RP12-5 + + + 3286.8 + RP12-6 + + + 2327.0 + RP12-7 + + + 2812.4 + RP12-8 + + + 1253.3 + RP12-9 + + + 2714.2 + RP12-10 + + + 2005.9 + RP12-11 + + + 983.0 + RP12-12 + + + 803.5 + RP12-13 + + + 3416.6 + RP12-14 + + + 1822.5 + RP12-15 + + + 2094.6 + RP12-16 + + + 2655.3 + RP12-17 + + + 3083.6 + RP12-18 + + + 3369.5 + RP12-19 + + + 1813.7 + RP12-20 + + + 3224.9 + RP12-21 + + + 2456.8 + RP12-22 + + + 3344.6 + RP12-23 + + + 1224.2 + RP12-24 + + + 1313.0 + RP12-25 + + + 2770.9 + RP12-26 + + + 1441.4 + RP12-27 + + + 3067.6 + RP12-28 + + + 1954.5 + RP12-29 + + + 2420.6 + RP12-30 + + + 1261.2 + RP12-31 + + + 3078.2 + RP12-32 + + + 2120.6 + RP12-33 + + + 2269.1 + RP12-34 + + + 1797.4 + RP12-35 + + + 2055.7 + RP12-36 + + + 966.4 + RP12-37 + + + 1031.4 + RP12-38 + + + 3420.9 + RP12-39 + + + 3070.2 + RP12-40 + + + 2245.9 + RP12-41 + + + 3180.5 + RP12-42 + + + 3237.5 + RP12-43 + + + 3231.2 + RP12-44 + + + 2474.7 + RP12-45 + + + 1266.8 + RP12-46 + + + 2982.8 + RP12-47 + + + 3468.4 + RP12-48 + + + 2493.5 + RP12-49 + + + 3259.2 + RP12-50 + + + + + Bregenzerwald Käsestraße + Scenic alpine route 13 with 34 waypoints through mountain terrain + + 2203.2 + RP13-1 + + + 2691.1 + RP13-2 + + + 1103.8 + RP13-3 + + + 2373.2 + RP13-4 + + + 1085.2 + RP13-5 + + + 3142.2 + RP13-6 + + + 1513.2 + RP13-7 + + + 2077.2 + RP13-8 + + + 2469.3 + RP13-9 + + + 1868.6 + RP13-10 + + + 933.1 + RP13-11 + + + 1805.6 + RP13-12 + + + 1639.6 + RP13-13 + + + 2649.1 + RP13-14 + + + 1611.3 + RP13-15 + + + 1666.3 + RP13-16 + + + 1041.1 + RP13-17 + + + 1899.4 + RP13-18 + + + 1809.6 + RP13-19 + + + 977.2 + RP13-20 + + + 2907.4 + RP13-21 + + + 1269.1 + RP13-22 + + + 955.6 + RP13-23 + + + 1773.0 + RP13-24 + + + 2053.4 + RP13-25 + + + 2639.2 + RP13-26 + + + 2004.5 + RP13-27 + + + 2288.6 + RP13-28 + + + 1498.4 + RP13-29 + + + 874.1 + RP13-30 + + + 2905.5 + RP13-31 + + + 1407.8 + RP13-32 + + + 2185.9 + RP13-33 + + + 2641.9 + RP13-34 + + + + + Allgäuer Alpenweg + Scenic alpine route 14 with 60 waypoints through mountain terrain + + 1481.5 + RP14-1 + + + 3411.0 + RP14-2 + + + 1816.5 + RP14-3 + + + 2060.1 + RP14-4 + + + 1872.3 + RP14-5 + + + 2588.4 + RP14-6 + + + 2547.8 + RP14-7 + + + 1091.9 + RP14-8 + + + 2529.6 + RP14-9 + + + 2720.0 + RP14-10 + + + 1204.8 + RP14-11 + + + 2974.7 + RP14-12 + + + 2583.4 + RP14-13 + + + 2569.6 + RP14-14 + + + 1930.1 + RP14-15 + + + 2711.8 + RP14-16 + + + 2761.7 + RP14-17 + + + 2919.2 + RP14-18 + + + 2169.8 + RP14-19 + + + 3166.5 + RP14-20 + + + 1145.4 + RP14-21 + + + 2859.9 + RP14-22 + + + 1706.2 + RP14-23 + + + 2809.4 + RP14-24 + + + 1694.6 + RP14-25 + + + 3098.3 + RP14-26 + + + 2375.7 + RP14-27 + + + 3201.5 + RP14-28 + + + 1830.5 + RP14-29 + + + 3106.4 + RP14-30 + + + 1904.8 + RP14-31 + + + 2035.4 + RP14-32 + + + 2903.1 + RP14-33 + + + 1277.4 + RP14-34 + + + 1538.5 + RP14-35 + + + 2864.1 + RP14-36 + + + 1037.7 + RP14-37 + + + 1889.2 + RP14-38 + + + 901.9 + RP14-39 + + + 838.0 + RP14-40 + + + 1314.6 + RP14-41 + + + 1471.8 + RP14-42 + + + 1148.1 + RP14-43 + + + 1251.0 + RP14-44 + + + 1585.2 + RP14-45 + + + 1712.2 + RP14-46 + + + 1725.4 + RP14-47 + + + 1308.8 + RP14-48 + + + 1889.2 + RP14-49 + + + 1154.3 + RP14-50 + + + 1256.3 + RP14-51 + + + 900.9 + RP14-52 + + + 2243.8 + RP14-53 + + + 2587.3 + RP14-54 + + + 2662.3 + RP14-55 + + + 2277.7 + RP14-56 + + + 2677.0 + RP14-57 + + + 2586.5 + RP14-58 + + + 2435.2 + RP14-59 + + + 1083.3 + RP14-60 + + + + + Berchtesgadener Land Tour + Scenic alpine route 15 with 76 waypoints through mountain terrain + + 2444.3 + RP15-1 + + + 3427.4 + RP15-2 + + + 1233.0 + RP15-3 + + + 2658.2 + RP15-4 + + + 834.4 + RP15-5 + + + 2350.2 + RP15-6 + + + 2904.7 + RP15-7 + + + 3242.9 + RP15-8 + + + 3438.7 + RP15-9 + + + 2995.6 + RP15-10 + + + 1772.6 + RP15-11 + + + 1112.9 + RP15-12 + + + 826.1 + RP15-13 + + + 1687.4 + RP15-14 + + + 1524.4 + RP15-15 + + + 1725.4 + RP15-16 + + + 2032.7 + RP15-17 + + + 2091.3 + RP15-18 + + + 1250.3 + RP15-19 + + + 3450.2 + RP15-20 + + + 3260.3 + RP15-21 + + + 1983.0 + RP15-22 + + + 2450.7 + RP15-23 + + + 2763.7 + RP15-24 + + + 2117.9 + RP15-25 + + + 971.0 + RP15-26 + + + 3144.5 + RP15-27 + + + 2754.2 + RP15-28 + + + 3157.5 + RP15-29 + + + 2469.2 + RP15-30 + + + 2101.2 + RP15-31 + + + 1201.4 + RP15-32 + + + 2454.9 + RP15-33 + + + 2165.0 + RP15-34 + + + 1246.6 + RP15-35 + + + 1693.4 + RP15-36 + + + 2286.3 + RP15-37 + + + 1012.8 + RP15-38 + + + 1037.7 + RP15-39 + + + 3447.8 + RP15-40 + + + 1889.3 + RP15-41 + + + 3030.0 + RP15-42 + + + 2521.4 + RP15-43 + + + 2567.4 + RP15-44 + + + 3028.3 + RP15-45 + + + 1696.3 + RP15-46 + + + 2735.0 + RP15-47 + + + 1063.1 + RP15-48 + + + 1758.9 + RP15-49 + + + 2278.8 + RP15-50 + + + 3221.6 + RP15-51 + + + 2804.8 + RP15-52 + + + 1329.0 + RP15-53 + + + 1382.1 + RP15-54 + + + 1712.4 + RP15-55 + + + 2509.7 + RP15-56 + + + 3207.7 + RP15-57 + + + 2161.3 + RP15-58 + + + 2787.8 + RP15-59 + + + 2379.8 + RP15-60 + + + 3257.5 + RP15-61 + + + 3001.2 + RP15-62 + + + 2436.6 + RP15-63 + + + 1930.4 + RP15-64 + + + 1470.2 + RP15-65 + + + 1594.3 + RP15-66 + + + 2426.8 + RP15-67 + + + 3076.3 + RP15-68 + + + 2789.4 + RP15-69 + + + 1366.2 + RP15-70 + + + 3287.2 + RP15-71 + + + 3433.0 + RP15-72 + + + 2519.6 + RP15-73 + + + 2318.2 + RP15-74 + + + 2717.3 + RP15-75 + + + 2241.5 + RP15-76 + + + + + Glacier Express Walk + GPS-recorded track 1 + + + 2878.9 + + + 1147.2 + + + 3453.9 + + + 3162.2 + + + 948.4 + + + 3540.5 + + + 1407.8 + + + 1578.0 + + + 1917.7 + + + 1663.0 + + + 1454.6 + + + 3558.4 + + + 2995.7 + + + 2458.5 + + + 3878.6 + + + 2921.0 + + + 2347.1 + + + 2492.4 + + + 1467.1 + + + 1126.3 + + + 772.9 + + + 2850.4 + + + 1817.5 + + + 1347.9 + + + 2317.5 + + + 912.6 + + + 1728.4 + + + 3159.0 + + + 3400.5 + + + 2874.4 + + + 2686.6 + + + 2920.7 + + + 3813.0 + + + 1561.6 + + + 1874.9 + + + 749.1 + + + 2347.2 + + + 3851.3 + + + 822.6 + + + 3540.8 + + + 2428.4 + + + 3235.7 + + + 3139.4 + + + 1727.8 + + + 3300.3 + + + 1374.0 + + + 2742.7 + + + 2889.2 + + + 2702.4 + + + 2983.0 + + + 960.9 + + + 894.1 + + + 1943.4 + + + 2413.8 + + + 3311.4 + + + 1089.9 + + + 3303.1 + + + 3828.2 + + + 1019.0 + + + 2426.3 + + + 3144.2 + + + 2003.0 + + + 1147.2 + + + 1260.7 + + + 3497.7 + + + 3980.7 + + + 1588.2 + + + 3067.0 + + + 1035.9 + + + 2706.4 + + + 2132.2 + + + 2586.2 + + + 1675.5 + + + 1437.0 + + + 2082.8 + + + 3092.1 + + + 1819.7 + + + 1197.6 + + + 3519.6 + + + 1495.0 + + + 856.4 + + + 706.1 + + + 2606.1 + + + 2447.8 + + + 3235.7 + + + 3225.3 + + + 2180.0 + + + 2307.4 + + + 3342.2 + + + 2348.1 + + + 3825.6 + + + 3102.1 + + + 1344.0 + + + 1856.1 + + + 3686.3 + + + 2145.8 + + + 1701.8 + + + 3420.8 + + + 2450.8 + + + 1842.0 + + + 2356.7 + + + 2355.7 + + + 2255.3 + + + 2306.2 + + + 862.1 + + + 1235.5 + + + 1002.3 + + + 3048.9 + + + 1316.4 + + + 676.3 + + + 1147.8 + + + 2545.7 + + + 1212.1 + + + 2516.3 + + + 2849.9 + + + 2127.3 + + + 1179.9 + + + 3301.7 + + + 2842.8 + + + 1765.1 + + + 1381.4 + + + 1288.0 + + + 2027.3 + + + 2218.0 + + + 3265.2 + + + 2239.6 + + + 3413.6 + + + 2807.4 + + + 747.7 + + + 3327.8 + + + 2602.8 + + + 2968.8 + + + 2222.3 + + + 1563.1 + + + 1242.7 + + + 2565.2 + + + 2825.5 + + + 1831.7 + + + 1462.4 + + + 1843.2 + + + 3166.9 + + + 2240.4 + + + 1462.8 + + + 2998.7 + + + 2841.6 + + + 2977.7 + + + 1961.9 + + + 2970.0 + + + 3879.5 + + + 1094.1 + + + 817.9 + + + 728.4 + + + 2687.5 + + + 2631.4 + + + 2777.0 + + + 2833.4 + + + 714.3 + + + 1387.7 + + + 1413.5 + + + 3989.2 + + + 1943.5 + + + 3105.5 + + + 1567.3 + + + 1783.5 + + + 2661.6 + + + 2554.7 + + + 962.0 + + + 3819.9 + + + 3578.8 + + + 2960.3 + + + 3217.9 + + + 2160.1 + + + 693.0 + + + 2754.5 + + + 1381.8 + + + 2356.1 + + + 1444.1 + + + 3391.7 + + + 2220.3 + + + 1514.9 + + + 3693.8 + + + 3100.5 + + + 1289.4 + + + 1130.4 + + + 2897.8 + + + 3198.4 + + + 2239.8 + + + 3950.2 + + + 2268.7 + + + 1552.9 + + + 784.1 + + + 2438.6 + + + 2933.6 + + + 2974.0 + + + 640.2 + + + 1706.5 + + + 2465.1 + + + 2684.5 + + + 1315.8 + + + 3504.7 + + + 1045.9 + + + 939.6 + + + 2069.5 + + + 3861.1 + + + 1200.5 + + + 3487.9 + + + 3270.1 + + + 2530.6 + + + + + 3452.5 + + + 1483.2 + + + 3354.1 + + + 1793.7 + + + 2561.3 + + + 3929.9 + + + 2335.5 + + + 866.6 + + + 3852.4 + + + 3757.1 + + + 1976.2 + + + 1768.3 + + + 3015.5 + + + 3618.2 + + + 3142.8 + + + 1168.9 + + + 1397.7 + + + 3149.1 + + + 924.9 + + + 783.0 + + + 3986.4 + + + 886.7 + + + 1379.8 + + + 1029.8 + + + 719.5 + + + 3538.4 + + + 3778.5 + + + 3824.9 + + + 2152.2 + + + 2078.9 + + + 3690.3 + + + 904.5 + + + 1129.4 + + + 3178.2 + + + 2419.3 + + + 3168.6 + + + 3313.1 + + + 2463.9 + + + 2086.2 + + + 2796.4 + + + 869.3 + + + 3319.9 + + + 1524.3 + + + 1514.0 + + + 1064.5 + + + 2139.4 + + + 1690.2 + + + 675.2 + + + 1025.1 + + + 906.7 + + + 2028.8 + + + 1862.8 + + + 872.8 + + + 1506.1 + + + 1217.5 + + + 1897.4 + + + 3029.9 + + + 1440.4 + + + 3100.1 + + + 1308.7 + + + 3783.1 + + + 3132.1 + + + 2236.2 + + + 777.1 + + + 1402.3 + + + 3300.9 + + + 1426.5 + + + 2948.6 + + + 3220.7 + + + 2932.2 + + + 3182.5 + + + 1123.3 + + + 2258.7 + + + 2791.8 + + + 3092.8 + + + 3063.0 + + + 3002.5 + + + 2266.3 + + + 1700.5 + + + 2635.5 + + + 2405.9 + + + 2441.5 + + + 2320.3 + + + 2707.1 + + + 1690.4 + + + 3384.8 + + + 3203.5 + + + 1993.3 + + + 2840.2 + + + 1207.4 + + + 1251.5 + + + 1921.2 + + + 2607.0 + + + 3040.7 + + + 2829.5 + + + 2191.4 + + + 3127.8 + + + 3300.4 + + + 2398.7 + + + 865.0 + + + 944.2 + + + 1256.6 + + + 3821.5 + + + 3136.8 + + + 2893.3 + + + 953.9 + + + 2316.0 + + + 2639.7 + + + 2470.3 + + + 2002.0 + + + 1699.7 + + + 1875.2 + + + 1824.0 + + + 600.7 + + + 655.4 + + + 622.3 + + + 1670.7 + + + 3395.9 + + + 2540.3 + + + 2165.3 + + + 3624.7 + + + 1621.0 + + + 1339.3 + + + 2240.6 + + + 2989.7 + + + 2783.7 + + + 1897.3 + + + 1736.9 + + + 3762.8 + + + 3142.1 + + + 2755.2 + + + 2382.9 + + + 1711.2 + + + 2890.4 + + + 3159.7 + + + 2068.6 + + + 1743.0 + + + 2463.7 + + + 2077.3 + + + 2235.1 + + + 2542.5 + + + 2382.4 + + + 2062.2 + + + 982.6 + + + 1247.7 + + + 3216.9 + + + 3393.4 + + + 2097.6 + + + 2930.2 + + + 891.8 + + + 2711.4 + + + 1578.1 + + + 1637.4 + + + 3116.0 + + + 3151.2 + + + 3973.8 + + + 1880.5 + + + 1468.6 + + + 2968.2 + + + 3813.6 + + + 2854.1 + + + 966.8 + + + 1914.2 + + + 3012.9 + + + 2630.2 + + + 3887.1 + + + 735.2 + + + 2265.8 + + + 2926.7 + + + 3986.4 + + + 810.8 + + + 3999.8 + + + 3827.0 + + + 1935.7 + + + 2370.4 + + + 2934.2 + + + 2050.0 + + + 2127.2 + + + 2575.5 + + + 1069.6 + + + 3692.5 + + + 3218.2 + + + 1137.4 + + + 3046.1 + + + 1094.8 + + + 3907.8 + + + 3135.1 + + + 3110.4 + + + 1047.3 + + + 2697.6 + + + 2627.6 + + + 870.2 + + + 3476.9 + + + 1225.5 + + + 1156.9 + + + 3894.0 + + + 1356.7 + + + 1295.5 + + + 2093.6 + + + 3547.0 + + + 3239.1 + + + + + + Eagle Trail Recording + GPS-recorded track 2 + + + 3124.4 + + + 3577.7 + + + 2135.5 + + + 3985.8 + + + 3099.6 + + + 2744.2 + + + 1953.3 + + + 2168.6 + + + 785.5 + + + 3421.6 + + + 984.8 + + + 2966.8 + + + 2133.0 + + + 3023.0 + + + 1450.3 + + + 3308.1 + + + 988.2 + + + 2381.0 + + + 2339.7 + + + 1894.1 + + + 2522.7 + + + 3554.0 + + + 2026.6 + + + 2117.3 + + + 1047.3 + + + 1460.6 + + + 2447.9 + + + 1408.8 + + + 1994.5 + + + 1432.0 + + + 2495.7 + + + 1104.1 + + + 3503.0 + + + 1614.7 + + + 2017.7 + + + 2677.7 + + + 3276.8 + + + 2568.5 + + + 1617.6 + + + 830.1 + + + 1595.6 + + + 1533.0 + + + 2965.8 + + + 3081.1 + + + 2244.5 + + + 3953.3 + + + 1676.1 + + + 1803.1 + + + 2367.0 + + + 1211.7 + + + 3929.5 + + + 2726.6 + + + 3318.5 + + + 1734.9 + + + 638.4 + + + 3286.7 + + + 1701.3 + + + 1760.6 + + + 699.0 + + + 1749.0 + + + 3310.7 + + + 2732.5 + + + 1739.4 + + + 3124.8 + + + 3064.9 + + + 1308.1 + + + 3619.3 + + + 1553.6 + + + 2730.1 + + + 1924.5 + + + 3155.2 + + + 707.5 + + + 1408.0 + + + 1334.9 + + + 3268.8 + + + 2108.8 + + + 2300.2 + + + 3090.3 + + + 1087.2 + + + 3789.3 + + + 3063.5 + + + 2376.0 + + + 2936.0 + + + 2187.4 + + + 2840.5 + + + 1661.3 + + + 1254.7 + + + 2099.8 + + + 1530.4 + + + 2347.8 + + + 1682.8 + + + 1563.1 + + + 2685.1 + + + 1042.3 + + + 960.2 + + + 1208.5 + + + 3443.0 + + + 1662.3 + + + 2881.0 + + + 3169.5 + + + 3363.7 + + + 2149.0 + + + 2182.3 + + + 3527.7 + + + 1933.1 + + + 3281.8 + + + 702.9 + + + 2168.2 + + + 2655.1 + + + 3379.7 + + + 2808.1 + + + 1962.8 + + + 2463.0 + + + 1758.4 + + + 3266.7 + + + 2451.4 + + + 864.8 + + + 2254.5 + + + 2487.3 + + + 3850.0 + + + 3367.8 + + + 2280.6 + + + 3377.9 + + + 2762.9 + + + 1107.3 + + + 2565.0 + + + 1676.4 + + + 1635.5 + + + 3940.8 + + + 627.6 + + + 1033.7 + + + 2466.2 + + + 3390.6 + + + 3543.3 + + + 1940.5 + + + 1897.3 + + + 2214.6 + + + 3743.2 + + + 3991.4 + + + 843.9 + + + 2254.8 + + + 887.9 + + + 2150.5 + + + 2047.4 + + + 2477.0 + + + 1231.7 + + + 3479.1 + + + 1996.8 + + + 3926.0 + + + 984.8 + + + 1083.1 + + + 3944.5 + + + 866.0 + + + 2206.3 + + + 865.5 + + + 1662.5 + + + 3247.0 + + + 2195.4 + + + 3866.0 + + + 720.1 + + + 3071.7 + + + 2208.7 + + + 3967.2 + + + 1575.2 + + + 3494.9 + + + 1555.3 + + + 979.3 + + + 1113.9 + + + 1488.3 + + + 3317.9 + + + 887.0 + + + 1124.1 + + + 3219.3 + + + 2767.8 + + + 1773.3 + + + 994.4 + + + 1624.6 + + + 2410.6 + + + 2213.6 + + + 640.3 + + + 2906.9 + + + 739.5 + + + 3851.0 + + + 3518.4 + + + 934.1 + + + 3228.7 + + + 2601.7 + + + 2946.9 + + + 2887.5 + + + 932.2 + + + 1346.0 + + + 680.6 + + + 2832.5 + + + 2769.5 + + + 3511.9 + + + 3659.7 + + + 3646.4 + + + 2187.6 + + + 3737.2 + + + 3671.5 + + + 1150.7 + + + 1901.5 + + + 3849.1 + + + 2271.9 + + + 1206.7 + + + 3213.6 + + + 3702.9 + + + 847.9 + + + 2116.0 + + + 2777.1 + + + 1505.9 + + + 2553.8 + + + 2268.3 + + + 3255.2 + + + 611.2 + + + 2239.0 + + + 1820.3 + + + 2749.7 + + + 2417.7 + + + 2162.8 + + + 1200.5 + + + 1249.4 + + + 2744.1 + + + 2751.5 + + + 1256.5 + + + 2263.4 + + + 1646.3 + + + 2127.6 + + + 790.6 + + + 1001.8 + + + 2158.5 + + + 1106.4 + + + 725.4 + + + 2202.1 + + + 2984.4 + + + 769.9 + + + 2151.0 + + + 3380.5 + + + 3317.2 + + + 2049.1 + + + 2906.6 + + + 2558.3 + + + 3296.5 + + + 1740.7 + + + 2011.5 + + + 2091.4 + + + 939.9 + + + 3244.0 + + + 621.6 + + + 2816.0 + + + 3345.2 + + + 1300.9 + + + 2000.9 + + + 727.9 + + + 3831.0 + + + + + 1252.5 + + + 2704.3 + + + 2325.6 + + + 1307.6 + + + 1107.3 + + + 2291.6 + + + 783.7 + + + 1454.7 + + + 2140.7 + + + 2427.0 + + + 2350.7 + + + 2112.4 + + + 3140.1 + + + 974.5 + + + 2002.8 + + + 850.0 + + + 2081.9 + + + 1205.4 + + + 1260.1 + + + 1742.0 + + + 1554.1 + + + 2022.0 + + + 1603.5 + + + 3839.4 + + + 2479.4 + + + 2374.0 + + + 2209.9 + + + 2055.6 + + + 2076.4 + + + 3817.3 + + + 2020.4 + + + 3093.8 + + + 1940.7 + + + 933.2 + + + 3160.5 + + + 1341.2 + + + 3996.4 + + + 1150.1 + + + 2584.4 + + + 1071.3 + + + 3563.3 + + + 604.6 + + + 2948.6 + + + 3159.8 + + + 2555.8 + + + 2709.5 + + + 2350.1 + + + 775.3 + + + 739.0 + + + 2996.6 + + + 1070.6 + + + 2806.7 + + + 3561.8 + + + 3187.0 + + + 2312.1 + + + 1867.6 + + + 3420.5 + + + 3406.3 + + + 1851.7 + + + 3925.5 + + + 3605.5 + + + 1208.0 + + + 1160.2 + + + 1111.7 + + + 677.1 + + + 3767.9 + + + 3108.2 + + + 674.9 + + + 2366.7 + + + 2829.6 + + + 2863.9 + + + 2157.2 + + + 1642.1 + + + 1864.5 + + + 1104.4 + + + 2703.3 + + + 1219.8 + + + 3676.0 + + + 997.5 + + + 2099.0 + + + 3004.3 + + + 1103.0 + + + 2414.4 + + + 3765.6 + + + 3754.6 + + + 2456.9 + + + 1604.9 + + + 3164.1 + + + 3318.6 + + + 1751.5 + + + 736.3 + + + 684.3 + + + 1815.1 + + + 1263.5 + + + 3208.1 + + + 3488.1 + + + 3917.9 + + + 2650.8 + + + 2867.0 + + + 2054.0 + + + 1772.7 + + + 2507.5 + + + 3121.2 + + + 1156.9 + + + 3077.3 + + + 2633.0 + + + 3960.5 + + + 3772.7 + + + 622.5 + + + 1194.6 + + + 3710.6 + + + 1584.2 + + + 1626.8 + + + 3115.5 + + + 1123.7 + + + 2764.3 + + + 1578.1 + + + 2792.3 + + + 2276.4 + + + 1830.0 + + + 1476.9 + + + 3382.2 + + + 659.7 + + + 1096.9 + + + 1815.1 + + + 2763.2 + + + 1591.7 + + + 1259.0 + + + 2635.8 + + + 1675.1 + + + 1523.0 + + + 2417.0 + + + 1065.6 + + + 2000.8 + + + 1340.8 + + + 3303.4 + + + 3838.5 + + + 2987.7 + + + 3714.5 + + + 659.1 + + + 1072.6 + + + 2378.6 + + + 1635.9 + + + 1037.6 + + + 1645.3 + + + 2044.3 + + + 663.9 + + + 1267.6 + + + 2616.8 + + + 747.2 + + + 928.1 + + + 2597.9 + + + 699.5 + + + 1543.1 + + + 3507.5 + + + 2316.7 + + + 3195.0 + + + 3878.5 + + + 614.7 + + + 3729.6 + + + 2973.7 + + + 964.9 + + + 2041.9 + + + 3188.8 + + + 2367.1 + + + 1580.1 + + + 2278.6 + + + 3103.0 + + + 1368.0 + + + 2055.1 + + + 3328.3 + + + 643.5 + + + 1947.7 + + + 1005.3 + + + 3923.7 + + + 1173.7 + + + 1280.9 + + + 1391.8 + + + 3200.4 + + + 3171.4 + + + 1810.7 + + + 3957.6 + + + 2145.8 + + + 1384.8 + + + 3108.5 + + + 690.9 + + + 1711.7 + + + 3184.6 + + + 2273.4 + + + 2292.9 + + + 3352.2 + + + 3197.5 + + + 1005.1 + + + 2104.7 + + + 2842.0 + + + 1158.1 + + + 1922.9 + + + 2267.9 + + + 2197.4 + + + 789.5 + + + 1884.5 + + + 1567.1 + + + 1035.9 + + + 2585.5 + + + 1893.9 + + + 2000.6 + + + 1397.7 + + + 2939.2 + + + 1177.2 + + + 2836.4 + + + 2032.0 + + + 675.6 + + + 1696.2 + + + 2948.9 + + + 3359.0 + + + 1618.4 + + + 2760.5 + + + 3491.1 + + + 1610.9 + + + 2723.5 + + + 2565.9 + + + 2525.9 + + + 1780.9 + + + 2315.8 + + + 1665.4 + + + 2352.3 + + + 3595.0 + + + 1234.2 + + + 3401.3 + + + 2768.9 + + + 3683.2 + + + 803.6 + + + 1417.3 + + + 3470.0 + + + 3697.9 + + + 1507.6 + + + 2935.3 + + + 1109.9 + + + 1655.8 + + + 2034.9 + + + 1527.2 + + + 1544.4 + + + 743.6 + + + 788.1 + + + 1613.1 + + + 3649.0 + + + 1949.4 + + + 1402.5 + + + 1010.2 + + + 3235.4 + + + 906.8 + + + 1665.6 + + + 3339.9 + + + 1341.2 + + + 1533.5 + + + 790.1 + + + 984.1 + + + 1437.6 + + + 1092.7 + + + 1647.8 + + + + + + Morning Run — Innsbruck + GPS-recorded track 3 + + + 3798.9 + + + 1414.9 + + + 3858.8 + + + 1015.0 + + + 1920.8 + + + 3925.2 + + + 3397.2 + + + 3626.8 + + + 3590.2 + + + 715.2 + + + 3797.7 + + + 3460.0 + + + 3388.8 + + + 2321.5 + + + 3365.7 + + + 693.0 + + + 2742.5 + + + 1962.3 + + + 879.6 + + + 3896.9 + + + 3479.7 + + + 1686.0 + + + 1486.3 + + + 3318.4 + + + 1218.4 + + + 3606.8 + + + 3738.6 + + + 3683.3 + + + 2984.3 + + + 2058.3 + + + 1850.9 + + + 3043.2 + + + 2142.6 + + + 3026.1 + + + 3259.2 + + + 1042.7 + + + 3983.7 + + + 2022.3 + + + 1586.7 + + + 3684.9 + + + 2157.8 + + + 1699.0 + + + 3025.3 + + + 1471.2 + + + 1731.7 + + + 3988.9 + + + 2634.0 + + + 1462.4 + + + 1942.0 + + + 2502.5 + + + 2312.7 + + + 1048.5 + + + 1082.1 + + + 1034.8 + + + 3142.6 + + + 3581.6 + + + 1496.8 + + + 1672.8 + + + 1114.3 + + + 3313.9 + + + 1381.5 + + + 3000.3 + + + 3752.5 + + + 3010.7 + + + 3475.9 + + + 918.2 + + + 1172.2 + + + 3629.0 + + + 3903.5 + + + 2015.8 + + + 2901.5 + + + 2916.5 + + + 2607.2 + + + 1122.8 + + + 2842.4 + + + 2620.3 + + + 2831.0 + + + 3087.3 + + + 2535.1 + + + 2788.4 + + + 840.5 + + + 1956.1 + + + 3542.8 + + + 1154.0 + + + 3158.7 + + + 2268.9 + + + 1654.3 + + + 2862.8 + + + 2264.7 + + + 2514.4 + + + 3357.4 + + + 2048.5 + + + 2176.0 + + + 1127.2 + + + 2383.9 + + + 3684.5 + + + 1034.5 + + + 2378.5 + + + 860.2 + + + 2044.7 + + + 2532.8 + + + 878.0 + + + 2849.4 + + + 1936.1 + + + 1612.0 + + + 2878.9 + + + 3886.8 + + + 1229.3 + + + 3563.5 + + + 619.9 + + + 2222.2 + + + 3883.7 + + + 2702.9 + + + 2890.9 + + + 1629.0 + + + 980.5 + + + 3520.6 + + + 2809.1 + + + 3286.0 + + + 2175.6 + + + 1164.8 + + + 3877.1 + + + 2589.8 + + + 3435.1 + + + 2743.4 + + + 3485.3 + + + 2484.6 + + + 2252.7 + + + 2907.5 + + + 3028.6 + + + 1673.9 + + + 2018.3 + + + 3792.5 + + + 670.7 + + + 1769.1 + + + 3690.8 + + + 3464.4 + + + 1597.5 + + + 3432.5 + + + 3306.3 + + + 2714.3 + + + 2868.0 + + + 2659.5 + + + 3978.8 + + + 760.0 + + + 1813.9 + + + 3641.2 + + + 2064.1 + + + 1361.9 + + + 1807.8 + + + 1509.5 + + + 2352.3 + + + 3750.5 + + + 3461.4 + + + 2675.9 + + + 3400.3 + + + 2741.7 + + + 1133.9 + + + 2320.0 + + + 2800.1 + + + 3485.2 + + + 3214.7 + + + 1760.5 + + + 1866.3 + + + 2861.0 + + + 2033.9 + + + 2005.6 + + + 3498.1 + + + 701.2 + + + 3341.2 + + + 1670.3 + + + 809.0 + + + 2781.0 + + + 2747.5 + + + 2367.2 + + + 2915.0 + + + 3125.2 + + + 1551.3 + + + 2981.0 + + + 2451.0 + + + 2971.4 + + + 1975.3 + + + 725.7 + + + 3116.2 + + + 1565.4 + + + 2372.5 + + + 694.8 + + + 1420.6 + + + 3955.6 + + + 1868.1 + + + 762.3 + + + 1613.1 + + + 3942.0 + + + 2523.0 + + + 2413.2 + + + 1567.4 + + + 2886.3 + + + 1442.0 + + + 3397.7 + + + 3019.6 + + + 811.5 + + + 2117.0 + + + 2600.0 + + + 1496.6 + + + 2096.3 + + + 3909.1 + + + 1032.2 + + + 2995.4 + + + 1323.0 + + + 3809.8 + + + 2645.4 + + + 2544.0 + + + 1863.0 + + + 2252.1 + + + 2604.7 + + + 3622.8 + + + 2261.9 + + + 3559.5 + + + 2523.6 + + + 2215.8 + + + 3190.6 + + + 2129.8 + + + 1705.7 + + + 1992.7 + + + 2439.4 + + + 3102.1 + + + 3004.5 + + + 3005.2 + + + 2501.8 + + + 2561.3 + + + 1243.9 + + + 1009.0 + + + 664.5 + + + 2999.1 + + + 3343.5 + + + 752.9 + + + 2085.9 + + + 1679.1 + + + 937.7 + + + 1468.9 + + + 1984.7 + + + 1706.0 + + + 1925.8 + + + 1702.3 + + + 1011.3 + + + 2592.5 + + + 1909.2 + + + 2180.4 + + + 1985.5 + + + 1515.1 + + + 1437.5 + + + 1635.7 + + + 2761.7 + + + 1869.2 + + + 2427.6 + + + 2135.9 + + + 3419.9 + + + 601.8 + + + 2632.8 + + + 1794.8 + + + 2335.1 + + + 2514.5 + + + 1018.1 + + + 1550.2 + + + 3855.2 + + + + + 3006.4 + + + 1693.6 + + + 1716.8 + + + 3735.7 + + + 632.6 + + + 2968.1 + + + 875.9 + + + 2431.4 + + + 2113.6 + + + 1879.7 + + + 3453.1 + + + 1024.2 + + + 631.8 + + + 2913.9 + + + 3375.4 + + + 1351.9 + + + 725.1 + + + 2660.5 + + + 2770.7 + + + 2301.1 + + + 3112.0 + + + 1196.8 + + + 1033.3 + + + 3595.4 + + + 3482.1 + + + 2904.3 + + + 3602.5 + + + 2888.5 + + + 1698.2 + + + 1013.8 + + + 2358.7 + + + 759.7 + + + 1675.2 + + + 3592.3 + + + 1367.1 + + + 3717.8 + + + 813.7 + + + 2068.6 + + + 3779.2 + + + 2210.2 + + + 3393.6 + + + 847.9 + + + 925.3 + + + 3913.4 + + + 3626.3 + + + 810.8 + + + 2676.5 + + + 789.7 + + + 1965.7 + + + 3539.6 + + + 2141.6 + + + 2708.7 + + + 2897.6 + + + 2598.6 + + + 3025.2 + + + 3674.9 + + + 1808.3 + + + 1232.1 + + + 1034.1 + + + 2815.8 + + + 2439.9 + + + 2651.3 + + + 2360.1 + + + 1407.1 + + + 785.7 + + + 2777.9 + + + 1243.8 + + + 2189.2 + + + 2524.7 + + + 1717.3 + + + 3695.8 + + + 2771.8 + + + 1324.7 + + + 3369.8 + + + 1961.1 + + + 633.6 + + + 1084.2 + + + 3703.2 + + + 1277.0 + + + 1490.8 + + + 1436.3 + + + 1490.1 + + + 1066.6 + + + 1750.5 + + + 1856.8 + + + 2391.6 + + + 2074.6 + + + 3601.3 + + + 2410.7 + + + 2060.4 + + + 3194.5 + + + 1600.2 + + + 2740.2 + + + 2328.1 + + + 3072.8 + + + 2690.6 + + + 3600.2 + + + 2364.2 + + + 2386.6 + + + 2210.8 + + + 3288.1 + + + 3642.6 + + + 1343.2 + + + 1134.5 + + + 888.2 + + + 2364.0 + + + 2341.7 + + + 2070.5 + + + 1528.3 + + + 1503.1 + + + 1601.4 + + + 865.7 + + + 1003.2 + + + 1734.8 + + + 3133.7 + + + 1248.2 + + + 3507.0 + + + 1487.9 + + + 1778.6 + + + 3394.3 + + + 2823.9 + + + 3542.6 + + + 1313.6 + + + 2478.9 + + + 1758.2 + + + 2057.9 + + + 2091.5 + + + 1072.4 + + + 3108.5 + + + 1805.9 + + + 3158.9 + + + 1618.8 + + + 709.5 + + + 3980.5 + + + 3706.8 + + + 1896.5 + + + 1913.9 + + + 3870.6 + + + 1182.8 + + + 3410.0 + + + 1276.6 + + + 1300.0 + + + 2145.9 + + + 1711.0 + + + 3964.6 + + + 3126.7 + + + 1302.0 + + + 3380.8 + + + 2555.3 + + + 3037.0 + + + 3969.7 + + + 2656.5 + + + 2535.4 + + + 732.9 + + + 3724.2 + + + 3294.0 + + + 3489.5 + + + 2700.3 + + + 2840.9 + + + 2975.4 + + + 1137.0 + + + 2866.6 + + + 3667.6 + + + 3190.3 + + + 3168.3 + + + 1994.3 + + + 751.8 + + + 2897.1 + + + 1957.8 + + + 1452.1 + + + 2879.0 + + + 880.7 + + + 2088.3 + + + 1148.6 + + + 1202.6 + + + 1172.7 + + + 1529.1 + + + 3029.0 + + + 2103.0 + + + 2377.8 + + + 949.5 + + + + + + E-Bike Tour Tirol + GPS-recorded track 4 + + + 3208.2 + + + 1411.0 + + + 3963.6 + + + 2874.4 + + + 3309.5 + + + 1168.0 + + + 1712.3 + + + 1304.2 + + + 1010.7 + + + 1190.3 + + + 1923.2 + + + 3371.3 + + + 828.9 + + + 2636.2 + + + 2423.0 + + + 1183.0 + + + 3936.8 + + + 3906.0 + + + 2760.2 + + + 3495.3 + + + 2669.0 + + + 2778.0 + + + 3960.5 + + + 3649.1 + + + 3174.6 + + + 1873.4 + + + 1294.9 + + + 2633.5 + + + 1717.5 + + + 3185.7 + + + 2803.4 + + + 3479.6 + + + 2433.7 + + + 1444.3 + + + 2667.4 + + + 2104.0 + + + 837.9 + + + 1081.2 + + + 2614.9 + + + 1403.5 + + + 1659.0 + + + 1199.9 + + + 2124.1 + + + 3256.4 + + + 1954.0 + + + 2318.0 + + + 3386.7 + + + 1896.8 + + + 3733.2 + + + 3070.6 + + + 2142.3 + + + 844.6 + + + 1126.1 + + + 2302.4 + + + 3747.3 + + + 1868.7 + + + 2600.3 + + + 3733.5 + + + 1260.4 + + + 3704.5 + + + 2261.3 + + + 2813.1 + + + 1424.0 + + + 2745.2 + + + 2794.1 + + + 2915.6 + + + 3836.0 + + + 3221.3 + + + 1952.1 + + + 2363.0 + + + 3421.1 + + + 3890.1 + + + 1647.1 + + + 3074.0 + + + 3240.7 + + + 3145.7 + + + 1647.5 + + + 605.4 + + + 2384.9 + + + 1180.2 + + + 1273.3 + + + 2132.2 + + + 800.5 + + + 1481.8 + + + 2731.2 + + + 1164.8 + + + 1541.0 + + + 2224.9 + + + 807.6 + + + 2580.3 + + + 2375.9 + + + 3955.4 + + + 2209.0 + + + 1860.7 + + + 3377.3 + + + 1886.7 + + + 1722.9 + + + 2974.2 + + + 1072.1 + + + 1550.2 + + + 1852.7 + + + 2215.7 + + + 3953.0 + + + 656.5 + + + 3345.7 + + + 2663.0 + + + 876.7 + + + 3764.9 + + + 3677.7 + + + 1263.3 + + + 2544.4 + + + 1034.7 + + + 2888.4 + + + 1171.4 + + + 2853.0 + + + 3161.1 + + + 2078.6 + + + 3758.5 + + + 1349.3 + + + 3651.5 + + + 2417.4 + + + 2952.1 + + + 3205.5 + + + 1881.8 + + + 1897.6 + + + 3510.9 + + + 993.3 + + + 3118.0 + + + 994.8 + + + 3843.8 + + + 810.4 + + + 3931.5 + + + 2909.6 + + + 2645.5 + + + 2430.2 + + + 3805.8 + + + 2149.7 + + + 2862.2 + + + 2636.2 + + + 1019.8 + + + 2226.2 + + + 2994.1 + + + 1732.7 + + + 3091.0 + + + 778.7 + + + 2257.5 + + + 2841.5 + + + 2463.0 + + + 3967.1 + + + 2394.7 + + + 1263.3 + + + 1045.1 + + + 3337.2 + + + 2155.6 + + + 2966.2 + + + 1341.7 + + + 631.1 + + + 2906.3 + + + 3831.4 + + + 3748.4 + + + 1210.8 + + + 2810.3 + + + 2731.7 + + + 1418.9 + + + 3845.5 + + + 2952.1 + + + 3244.3 + + + 1535.7 + + + 1008.0 + + + 2873.9 + + + 2548.1 + + + 3134.2 + + + 2246.3 + + + 2542.8 + + + 3690.2 + + + 3363.8 + + + 2423.8 + + + 2364.3 + + + 3286.2 + + + 3883.3 + + + 2636.9 + + + 3321.7 + + + 1301.2 + + + 3494.9 + + + 2320.9 + + + 2038.4 + + + 1622.0 + + + 3261.5 + + + 2562.0 + + + 3952.5 + + + 1313.5 + + + 1406.1 + + + 3430.9 + + + 1334.5 + + + 2715.9 + + + 1898.1 + + + 883.1 + + + 2961.4 + + + 3091.1 + + + 1254.9 + + + 3389.7 + + + 1121.9 + + + 688.1 + + + 1023.7 + + + 824.3 + + + 1734.0 + + + 1367.6 + + + 3893.8 + + + 3747.2 + + + 1642.3 + + + 2398.5 + + + 2475.2 + + + 1180.5 + + + 1773.6 + + + 2407.7 + + + 2221.3 + + + 2346.1 + + + 1887.9 + + + 3066.4 + + + 1447.0 + + + 1222.6 + + + 2307.6 + + + 3801.7 + + + 1637.6 + + + 1982.2 + + + 870.2 + + + 1161.4 + + + 2484.1 + + + 3871.4 + + + 3084.7 + + + 3993.0 + + + 1978.6 + + + 3136.1 + + + 3148.9 + + + 3833.0 + + + 2395.6 + + + 3879.8 + + + 2380.0 + + + 3454.0 + + + 2037.3 + + + 985.0 + + + 2433.0 + + + 1617.2 + + + 3109.3 + + + 1636.4 + + + 3407.5 + + + 2700.3 + + + 2692.8 + + + 617.5 + + + 1805.4 + + + 888.1 + + + 1021.1 + + + 2832.6 + + + 1617.6 + + + 3359.2 + + + 3453.8 + + + 2075.5 + + + 1274.8 + + + 1941.5 + + + 1145.1 + + + 2861.8 + + + 2470.4 + + + 1704.6 + + + 2306.3 + + + 1922.1 + + + 671.9 + + + 3209.1 + + + 1763.3 + + + 2076.7 + + + 3187.1 + + + 2338.2 + + + 3538.2 + + + 3390.1 + + + 3353.8 + + + 1216.0 + + + 2757.2 + + + + + 1097.1 + + + 3538.8 + + + 2980.5 + + + 2048.7 + + + 775.6 + + + 3806.2 + + + 3643.4 + + + 2845.5 + + + 1552.5 + + + 1457.7 + + + 979.2 + + + 3219.6 + + + 3354.1 + + + 2656.0 + + + 2586.7 + + + 2740.5 + + + 3892.3 + + + 1256.0 + + + 3802.2 + + + 2212.3 + + + 1122.5 + + + 3019.5 + + + 3476.7 + + + 1844.4 + + + 2082.8 + + + 3838.3 + + + 1532.1 + + + 3849.7 + + + 1246.6 + + + 2410.4 + + + 2088.3 + + + 1926.8 + + + 612.2 + + + 630.1 + + + 2313.0 + + + 3498.4 + + + 1005.0 + + + 1592.3 + + + 1544.4 + + + 775.2 + + + 1313.9 + + + 2751.1 + + + 3817.9 + + + 1054.3 + + + 3949.9 + + + 1808.6 + + + 1470.4 + + + 3551.1 + + + 3056.8 + + + 1475.0 + + + 1300.9 + + + 1293.3 + + + 1944.6 + + + 2075.3 + + + 2286.3 + + + 1198.8 + + + 1213.4 + + + 3609.9 + + + 1238.7 + + + 2147.1 + + + 3062.6 + + + 640.1 + + + 974.1 + + + 3777.9 + + + 1510.0 + + + 3294.2 + + + 2444.1 + + + 888.9 + + + 3634.1 + + + 2228.3 + + + 1509.4 + + + 751.4 + + + 3614.0 + + + 1362.7 + + + 1801.3 + + + 2927.0 + + + 3665.5 + + + 1363.4 + + + 859.4 + + + 3744.0 + + + 1113.8 + + + 1216.6 + + + 2936.3 + + + 3576.4 + + + 1855.0 + + + 3315.5 + + + 3925.4 + + + 1102.5 + + + 3919.6 + + + 2053.3 + + + 1698.4 + + + 1798.9 + + + 3907.7 + + + 1070.0 + + + 1902.8 + + + 2274.8 + + + 884.3 + + + 3240.4 + + + 896.7 + + + 3492.0 + + + 1054.2 + + + 1826.9 + + + 1073.8 + + + 2475.1 + + + 1316.5 + + + 998.6 + + + 3092.5 + + + 783.9 + + + 2221.2 + + + 1099.4 + + + 642.4 + + + 3202.4 + + + 2393.3 + + + 3056.7 + + + 644.9 + + + 2784.3 + + + 1145.2 + + + 3824.0 + + + 1477.4 + + + 1372.9 + + + 912.6 + + + 3126.7 + + + 1933.8 + + + 2657.3 + + + 2223.2 + + + 1893.6 + + + 2042.4 + + + 1833.2 + + + 1414.4 + + + 1320.5 + + + 1238.3 + + + 1417.1 + + + 752.6 + + + 763.4 + + + 3933.8 + + + 2300.7 + + + 1809.8 + + + 1726.5 + + + 2307.4 + + + 3306.6 + + + 727.2 + + + 867.8 + + + 1387.3 + + + 1078.9 + + + 2305.5 + + + 2379.2 + + + 1046.1 + + + 3796.5 + + + 2921.8 + + + 2663.4 + + + 3981.0 + + + 3205.5 + + + 3311.1 + + + 2146.3 + + + 831.8 + + + 841.9 + + + 3952.9 + + + 2103.0 + + + + + + Alpine Ski Descent + GPS-recorded track 5 + + + 1574.5 + + + 2914.0 + + + 2723.7 + + + 2004.9 + + + 3970.2 + + + 794.0 + + + 2506.7 + + + 1877.8 + + + 1358.4 + + + 1786.4 + + + 3382.2 + + + 968.4 + + + 2072.6 + + + 1764.2 + + + 3120.4 + + + 2453.3 + + + 1325.8 + + + 3232.3 + + + 3284.5 + + + 1319.1 + + + 2037.9 + + + 1081.9 + + + 3570.8 + + + 1328.0 + + + 782.7 + + + 3875.4 + + + 3802.6 + + + 3265.1 + + + 3702.8 + + + 2507.1 + + + 3057.3 + + + 2857.8 + + + 3405.8 + + + 3648.1 + + + 1675.8 + + + 1493.5 + + + 3807.3 + + + 2772.3 + + + 3135.4 + + + 3232.9 + + + 1313.2 + + + 3847.4 + + + 1626.6 + + + 1024.1 + + + 2007.8 + + + 2047.9 + + + 3749.6 + + + 2479.4 + + + 1569.1 + + + 2756.0 + + + 3680.0 + + + 3446.6 + + + 3770.5 + + + 2721.0 + + + 2311.4 + + + 3495.8 + + + 2475.4 + + + 3831.9 + + + 2177.4 + + + 3267.8 + + + 3259.5 + + + 2839.5 + + + 3691.7 + + + 3690.8 + + + 3287.7 + + + 3120.2 + + + 2545.4 + + + 1930.6 + + + 775.4 + + + 3793.9 + + + 1970.6 + + + 1385.3 + + + 1002.5 + + + 3191.4 + + + 1140.8 + + + 687.2 + + + 1120.4 + + + 3906.2 + + + 3096.2 + + + 3243.8 + + + 1354.0 + + + 3069.5 + + + 1485.6 + + + 1088.8 + + + 3547.6 + + + 1017.1 + + + 1214.1 + + + 3703.2 + + + 3304.5 + + + 2727.3 + + + 3625.0 + + + 1147.8 + + + 2842.9 + + + 998.5 + + + 3982.7 + + + 2373.9 + + + 2660.8 + + + 3187.3 + + + 3655.1 + + + 883.7 + + + 1530.5 + + + 3007.5 + + + 3350.6 + + + 3736.0 + + + 3476.5 + + + 949.8 + + + 1163.2 + + + 2073.8 + + + 1523.7 + + + 1902.4 + + + 3652.8 + + + 3624.1 + + + 1993.3 + + + 3831.3 + + + 989.8 + + + 1232.1 + + + 1863.6 + + + 632.8 + + + 1326.8 + + + 1391.0 + + + 1321.4 + + + 3035.3 + + + 2882.6 + + + 2919.2 + + + 1273.7 + + + 759.1 + + + 2139.5 + + + 1403.8 + + + 3263.2 + + + 2170.0 + + + 725.8 + + + 1747.1 + + + 3421.5 + + + 1678.4 + + + 3233.7 + + + + + 3485.5 + + + 1871.3 + + + 762.6 + + + 1697.7 + + + 1178.7 + + + 1854.2 + + + 3776.0 + + + 3268.8 + + + 855.1 + + + 3362.5 + + + 1068.4 + + + 3530.4 + + + 3883.8 + + + 1016.1 + + + 2848.3 + + + 3084.9 + + + 1607.2 + + + 3567.8 + + + 1300.0 + + + 804.9 + + + 3382.9 + + + 1309.4 + + + 3518.8 + + + 2143.7 + + + 3708.0 + + + 3915.2 + + + 3252.1 + + + 1158.3 + + + 2804.5 + + + 3133.1 + + + 1235.9 + + + 2139.0 + + + 885.8 + + + 2307.4 + + + 1526.8 + + + 3088.8 + + + 1969.2 + + + 2278.3 + + + 2797.6 + + + 780.7 + + + 823.9 + + + 2708.8 + + + 944.4 + + + 1512.1 + + + 1782.0 + + + 3095.2 + + + 2981.6 + + + 3682.7 + + + 836.1 + + + 1082.2 + + + 1735.8 + + + 861.7 + + + 2619.6 + + + 2260.2 + + + 3384.3 + + + 3440.2 + + + 1098.4 + + + 3103.0 + + + 3965.3 + + + 1838.6 + + + 3014.5 + + + 3513.7 + + + 983.4 + + + 670.5 + + + 2841.8 + + + 1430.9 + + + 1822.0 + + + 3846.4 + + + 3582.3 + + + 2127.5 + + + 1502.1 + + + 1929.5 + + + 1651.7 + + + 1767.1 + + + 2147.1 + + + 1580.6 + + + 2930.8 + + + 3886.7 + + + 1632.4 + + + 653.0 + + + 2616.4 + + + 2063.1 + + + 1454.9 + + + 3520.6 + + + 1127.1 + + + 3986.1 + + + 874.4 + + + 1805.7 + + + 2036.7 + + + 1488.3 + + + 2517.3 + + + 2565.8 + + + 1061.3 + + + 891.0 + + + 2455.8 + + + 2085.4 + + + 1665.1 + + + 2192.5 + + + 1994.9 + + + 3855.5 + + + 2691.7 + + + 2341.5 + + + 2523.0 + + + 1935.0 + + + 3345.4 + + + 784.8 + + + 2809.6 + + + 1588.0 + + + 806.9 + + + 3113.8 + + + 1090.1 + + + 1360.0 + + + 1623.0 + + + 621.9 + + + 990.0 + + + 1011.5 + + + 2210.7 + + + 789.2 + + + 3941.2 + + + 2458.8 + + + 3521.7 + + + 2763.1 + + + 2835.4 + + + 2458.7 + + + 2869.6 + + + 2870.2 + + + 964.9 + + + 695.1 + + + 637.5 + + + 3637.0 + + + 3684.0 + + + 3058.4 + + + 1816.3 + + + 1786.0 + + + 813.4 + + + 3045.3 + + + 3274.8 + + + 1630.0 + + + 2793.5 + + + 2840.5 + + + 3236.4 + + + 1701.9 + + + 2699.4 + + + 3496.9 + + + 961.1 + + + 3183.7 + + + 3091.0 + + + 1191.6 + + + 2028.2 + + + 989.2 + + + 3195.4 + + + 3740.1 + + + 1883.9 + + + 1279.1 + + + 1572.7 + + + 3367.0 + + + 2579.2 + + + 1292.0 + + + 1822.5 + + + 844.7 + + + 3697.4 + + + 3430.2 + + + 1797.2 + + + 1068.8 + + + 1878.8 + + + 3260.1 + + + 1383.9 + + + 1361.7 + + + 1454.7 + + + 3563.0 + + + 2556.2 + + + 2300.2 + + + 2971.6 + + + 3719.1 + + + 1749.4 + + + 3200.1 + + + 3095.2 + + + 3586.1 + + + 3823.2 + + + 3203.0 + + + 3829.7 + + + 874.0 + + + 1519.7 + + + 2325.9 + + + 1631.6 + + + 672.9 + + + 3358.6 + + + 2918.6 + + + 777.3 + + + 2061.0 + + + 2089.0 + + + 1850.2 + + + 796.9 + + + 1372.2 + + + 3296.8 + + + 650.7 + + + 860.5 + + + 748.6 + + + 1430.4 + + + 2227.6 + + + 1450.7 + + + 3099.7 + + + 1349.0 + + + 3252.7 + + + 2391.3 + + + 2311.7 + + + 2998.7 + + + 1292.1 + + + 1383.8 + + + 1866.1 + + + 3924.9 + + + 2142.8 + + + 714.8 + + + 3412.4 + + + 2673.1 + + + 1907.9 + + + 1227.1 + + + 1637.5 + + + 688.4 + + + 3737.2 + + + 2609.3 + + + 3419.4 + + + 3079.4 + + + 971.3 + + + 2905.1 + + + 2175.9 + + + + + + MTB Singletrack Davos + GPS-recorded track 6 + + + 2188.9 + + + 3686.2 + + + 1532.9 + + + 1605.8 + + + 1532.5 + + + 2820.3 + + + 1643.4 + + + 3481.8 + + + 630.2 + + + 2768.3 + + + 2570.5 + + + 2474.9 + + + 1477.2 + + + 2282.8 + + + 3562.2 + + + 2825.6 + + + 2097.1 + + + 1375.0 + + + 3916.0 + + + 3488.0 + + + 1398.0 + + + 984.8 + + + 2539.2 + + + 842.7 + + + 684.0 + + + 3915.3 + + + 3804.4 + + + 1852.2 + + + 2610.2 + + + 1664.6 + + + 3263.8 + + + 1888.1 + + + 1110.3 + + + 3847.6 + + + 2522.4 + + + 3095.4 + + + 3586.8 + + + 2841.3 + + + 1263.1 + + + 1717.5 + + + 1385.0 + + + 2956.4 + + + 1993.7 + + + 859.9 + + + 1383.2 + + + 3414.6 + + + 1432.8 + + + 2864.1 + + + 1761.3 + + + 2317.1 + + + 3596.9 + + + 2143.8 + + + 3202.7 + + + 943.4 + + + 681.4 + + + 2350.7 + + + 2036.0 + + + 1420.9 + + + 3404.0 + + + 966.4 + + + 3730.5 + + + 2744.8 + + + 3176.0 + + + 3060.6 + + + 2331.9 + + + 2616.4 + + + 1689.5 + + + 1549.6 + + + 2277.2 + + + 2385.7 + + + 1569.9 + + + 3841.9 + + + 3600.1 + + + 1546.1 + + + 1781.0 + + + 2254.6 + + + 3793.3 + + + 960.8 + + + 926.3 + + + 1174.1 + + + 3628.2 + + + 2260.4 + + + 1558.2 + + + 2112.4 + + + 3721.2 + + + 1007.8 + + + 895.3 + + + 2873.6 + + + 3130.5 + + + 1132.9 + + + 3100.8 + + + 2939.0 + + + 941.5 + + + 1247.4 + + + 2728.7 + + + 1436.1 + + + 1348.7 + + + 2247.3 + + + 1048.1 + + + 2355.4 + + + 2076.7 + + + 3014.0 + + + 2685.5 + + + 1785.6 + + + 3584.8 + + + 2871.1 + + + 2632.7 + + + 1344.4 + + + 3197.4 + + + 1441.1 + + + 3851.5 + + + 1953.5 + + + 3456.4 + + + 2936.6 + + + 1331.4 + + + 2511.7 + + + 1998.2 + + + 1486.4 + + + 1344.4 + + + 2122.0 + + + 2422.4 + + + 3892.1 + + + 2435.2 + + + 1889.2 + + + 1799.2 + + + 767.3 + + + 3949.7 + + + 3040.6 + + + 1871.2 + + + 1445.6 + + + 3167.4 + + + 3290.6 + + + 682.4 + + + 714.8 + + + 1884.7 + + + 3580.5 + + + 2315.3 + + + 668.5 + + + 3724.5 + + + 1929.8 + + + 3268.2 + + + 1627.3 + + + 3895.5 + + + 2060.4 + + + 1547.1 + + + 2322.4 + + + 3073.7 + + + 3029.6 + + + 684.6 + + + 2221.1 + + + 1088.8 + + + 2507.0 + + + 2617.6 + + + 3449.4 + + + 3186.0 + + + 2000.3 + + + 724.1 + + + 3562.9 + + + 2512.2 + + + 3862.2 + + + 2242.2 + + + 3092.4 + + + 863.5 + + + 2367.8 + + + 3557.1 + + + 2283.6 + + + 1075.5 + + + 631.5 + + + 3047.8 + + + 1696.1 + + + 1164.2 + + + 3329.6 + + + 2655.4 + + + 1722.6 + + + 3754.7 + + + 2211.2 + + + 2199.7 + + + 2985.0 + + + 2159.2 + + + 2621.1 + + + 3603.4 + + + 1255.0 + + + 2049.0 + + + 3207.8 + + + 2781.4 + + + 950.1 + + + 2516.4 + + + 2792.4 + + + 3501.6 + + + 3106.7 + + + 1311.6 + + + 1335.9 + + + 1087.3 + + + 864.5 + + + 3344.6 + + + 1628.7 + + + 1937.0 + + + 1807.0 + + + 865.9 + + + 1164.8 + + + 1085.8 + + + 2963.3 + + + 3659.2 + + + 3544.9 + + + 1776.4 + + + 3579.4 + + + 1872.3 + + + 1265.8 + + + 2593.0 + + + 3713.7 + + + 1528.3 + + + 1954.2 + + + 1532.2 + + + 703.5 + + + 2665.7 + + + 3149.8 + + + 953.2 + + + 3025.3 + + + 3493.2 + + + 1006.1 + + + 1668.4 + + + 3498.8 + + + 1731.1 + + + 3652.3 + + + 3055.3 + + + 2859.3 + + + 809.0 + + + 1101.5 + + + 751.3 + + + 1991.5 + + + 3799.7 + + + 1000.2 + + + 892.7 + + + 3721.2 + + + 1679.5 + + + 1175.7 + + + 2165.2 + + + 1646.5 + + + 3539.6 + + + 1655.7 + + + 1548.5 + + + 3439.9 + + + 3929.5 + + + 914.2 + + + 3226.1 + + + 2935.9 + + + + + 1316.3 + + + 3357.1 + + + 2978.8 + + + 3300.6 + + + 1477.9 + + + 992.0 + + + 1075.4 + + + 1884.9 + + + 2984.4 + + + 626.9 + + + 2802.8 + + + 1641.3 + + + 673.9 + + + 2558.3 + + + 3435.9 + + + 1748.8 + + + 3900.6 + + + 3662.9 + + + 3231.8 + + + 697.1 + + + 3614.4 + + + 1274.8 + + + 1041.6 + + + 3899.1 + + + 3325.1 + + + 602.9 + + + 1624.4 + + + 1291.3 + + + 2928.0 + + + 2223.0 + + + 2935.1 + + + 2872.7 + + + 3194.4 + + + 3194.4 + + + 3653.3 + + + 2043.6 + + + 2260.3 + + + 1353.8 + + + 1855.1 + + + 2675.9 + + + 2795.0 + + + 1318.2 + + + 2823.8 + + + 1915.2 + + + 3858.9 + + + 2920.0 + + + 881.0 + + + 3532.4 + + + 1684.7 + + + 2146.0 + + + 1965.8 + + + 1479.4 + + + 3328.2 + + + 2210.1 + + + 1840.5 + + + 2407.8 + + + 1111.3 + + + 2606.5 + + + 3073.9 + + + 1557.8 + + + 1848.2 + + + 1457.9 + + + 3385.2 + + + 1813.4 + + + 2762.3 + + + 724.5 + + + 885.8 + + + 2751.9 + + + 3904.3 + + + 3406.9 + + + 2380.1 + + + 1867.5 + + + 2768.6 + + + 3932.9 + + + 3147.5 + + + 3831.2 + + + 3601.9 + + + 995.8 + + + 1630.9 + + + 1428.8 + + + 2207.9 + + + 3139.4 + + + 2882.9 + + + 2905.9 + + + 663.6 + + + 1508.0 + + + 1681.7 + + + 1703.7 + + + 3020.9 + + + 2758.2 + + + 3761.6 + + + 1777.6 + + + 3124.7 + + + 2888.2 + + + 3497.8 + + + 2508.5 + + + 3948.6 + + + 1672.1 + + + 2724.5 + + + 2067.2 + + + 3909.4 + + + 1398.0 + + + 2664.2 + + + 1342.2 + + + 1207.3 + + + 3097.2 + + + 3154.4 + + + 1686.0 + + + 3608.2 + + + 1680.4 + + + 3520.8 + + + 3408.9 + + + 1203.9 + + + 1735.8 + + + 3684.8 + + + 3912.4 + + + 2704.2 + + + 3996.3 + + + 3814.7 + + + 2535.6 + + + 1859.0 + + + 2824.0 + + + 3491.1 + + + 3573.8 + + + 3732.7 + + + 3641.1 + + + 1433.2 + + + 3309.0 + + + 977.9 + + + 3728.5 + + + 3872.4 + + + 2901.4 + + + 3531.4 + + + 1484.1 + + + 3240.4 + + + 680.6 + + + 1596.1 + + + 3213.8 + + + 2339.3 + + + 1021.8 + + + 3405.8 + + + 1174.4 + + + 3704.1 + + + + + + Paraglide GPS Log + GPS-recorded track 7 + + + 3657.3 + + + 1484.0 + + + 3277.1 + + + 2621.1 + + + 1580.7 + + + 1833.3 + + + 1138.2 + + + 2095.5 + + + 2963.3 + + + 1906.4 + + + 1319.7 + + + 1482.0 + + + 3986.8 + + + 1179.4 + + + 3498.9 + + + 3542.4 + + + 2385.8 + + + 648.4 + + + 1651.6 + + + 3245.2 + + + 1812.1 + + + 3681.4 + + + 3262.5 + + + 1288.6 + + + 936.1 + + + 2481.2 + + + 3566.9 + + + 2526.8 + + + 3114.1 + + + 1433.3 + + + 736.8 + + + 910.6 + + + 816.5 + + + 806.9 + + + 2576.3 + + + 3256.8 + + + 1267.8 + + + 1271.1 + + + 2256.6 + + + 3527.9 + + + 3243.1 + + + 2689.0 + + + 3165.5 + + + 1200.2 + + + 3834.9 + + + 1446.7 + + + 1649.9 + + + 3815.4 + + + 3912.8 + + + 822.9 + + + 3746.0 + + + 2431.2 + + + 3193.7 + + + 1688.1 + + + 1620.2 + + + 1125.6 + + + 3271.8 + + + 1134.3 + + + 2821.9 + + + 1515.9 + + + 3511.2 + + + 1734.7 + + + 2129.0 + + + 2018.2 + + + 3893.5 + + + 3915.0 + + + 2994.8 + + + 1602.3 + + + 3289.1 + + + 1278.1 + + + 3511.8 + + + 1706.6 + + + 2197.6 + + + 2842.0 + + + 2255.9 + + + 1875.1 + + + 1538.6 + + + 977.5 + + + 1703.5 + + + 1116.0 + + + 1280.0 + + + 2547.3 + + + 2405.5 + + + 3225.1 + + + 3018.8 + + + 3677.4 + + + 1226.1 + + + 1732.2 + + + 1468.2 + + + 1005.2 + + + 2253.6 + + + 3704.1 + + + 2086.5 + + + 3314.1 + + + 2554.4 + + + 2522.0 + + + 2408.0 + + + 898.3 + + + 3976.0 + + + 2065.2 + + + 3593.8 + + + 848.3 + + + 2394.2 + + + 2070.2 + + + 621.6 + + + 1365.8 + + + 1357.8 + + + 2156.4 + + + 2553.1 + + + 3548.6 + + + 1300.9 + + + 2646.5 + + + 2436.3 + + + 1672.1 + + + 1630.0 + + + 3783.4 + + + 1066.9 + + + 3436.1 + + + 3129.4 + + + 2119.2 + + + 1893.7 + + + 1195.3 + + + 1013.8 + + + 2263.0 + + + 1677.4 + + + 3319.3 + + + 980.8 + + + 3331.6 + + + 1805.9 + + + 2076.1 + + + 3999.0 + + + 1924.9 + + + 1534.0 + + + 1078.5 + + + 1035.0 + + + 3191.8 + + + 3545.6 + + + 2010.6 + + + 3783.4 + + + 3355.1 + + + 971.9 + + + 1640.6 + + + 1501.6 + + + 1727.4 + + + 3564.9 + + + 1881.9 + + + 1137.8 + + + 913.2 + + + 2217.3 + + + 2424.9 + + + 2765.3 + + + 3669.8 + + + 1843.0 + + + 2224.5 + + + 1267.4 + + + 3803.8 + + + 922.3 + + + 957.8 + + + 2841.9 + + + 2872.4 + + + 3604.2 + + + 2034.9 + + + 3777.7 + + + 1909.1 + + + 1219.4 + + + 3302.2 + + + 2545.1 + + + 2737.6 + + + 2137.2 + + + 2456.0 + + + 3212.9 + + + 3634.6 + + + 2257.0 + + + 2871.2 + + + 814.6 + + + 3880.2 + + + 2155.3 + + + 1605.4 + + + 2083.0 + + + 3547.7 + + + 3800.2 + + + 3456.0 + + + 3093.6 + + + 1116.0 + + + 3320.9 + + + 3820.4 + + + 940.5 + + + 2701.8 + + + 1117.0 + + + 3518.2 + + + 3826.3 + + + 3904.8 + + + 1062.8 + + + 1454.5 + + + 3738.4 + + + 801.4 + + + 3396.3 + + + 3833.1 + + + 2796.7 + + + 3436.7 + + + 1539.1 + + + 3114.8 + + + 2107.5 + + + 636.6 + + + 948.9 + + + 2845.4 + + + 3125.9 + + + 3928.6 + + + 1525.1 + + + 3629.8 + + + 2588.9 + + + 3301.1 + + + 950.4 + + + 2636.8 + + + 2860.7 + + + 2033.7 + + + 1552.7 + + + 1455.7 + + + 3443.4 + + + 943.8 + + + 2282.9 + + + 953.5 + + + 2506.3 + + + + + 2645.7 + + + 3843.8 + + + 3197.7 + + + 3672.9 + + + 3130.7 + + + 3497.6 + + + 3796.7 + + + 3243.9 + + + 2527.6 + + + 935.9 + + + 3078.8 + + + 3382.5 + + + 1445.6 + + + 2157.9 + + + 3298.4 + + + 2460.7 + + + 1785.0 + + + 3512.6 + + + 3115.5 + + + 3417.5 + + + 2774.1 + + + 1233.1 + + + 3005.1 + + + 3645.7 + + + 1697.4 + + + 3690.0 + + + 3173.0 + + + 2965.2 + + + 2633.1 + + + 1495.5 + + + 1949.0 + + + 3850.7 + + + 1599.3 + + + 3998.1 + + + 2463.2 + + + 2281.3 + + + 1434.2 + + + 1334.3 + + + 670.6 + + + 2009.4 + + + 2001.9 + + + 2732.7 + + + 3139.1 + + + 3331.6 + + + 3660.2 + + + 1814.1 + + + 3523.8 + + + 2430.1 + + + 3266.3 + + + 1665.8 + + + 3048.4 + + + 2483.8 + + + 2552.5 + + + 2290.3 + + + 1182.6 + + + 795.7 + + + 1102.8 + + + 3044.2 + + + 3074.4 + + + 2994.3 + + + 1331.7 + + + 2893.9 + + + 3578.3 + + + 2434.7 + + + 623.0 + + + 1818.4 + + + 2687.1 + + + 3090.7 + + + 3180.0 + + + 2808.0 + + + 903.0 + + + 3826.7 + + + 2967.8 + + + 925.9 + + + 3588.8 + + + 1964.9 + + + 2752.2 + + + 2381.1 + + + 3938.8 + + + 1172.9 + + + 615.8 + + + 3677.5 + + + 3849.4 + + + 1218.1 + + + 3565.1 + + + 2551.5 + + + 3590.9 + + + 3384.6 + + + 2427.8 + + + 3344.5 + + + 1786.8 + + + 3660.8 + + + 3991.0 + + + 3757.3 + + + 2123.0 + + + 1750.4 + + + 3379.8 + + + 1818.3 + + + 3357.1 + + + 3452.8 + + + 2855.9 + + + 2555.3 + + + 2457.7 + + + 2122.9 + + + 2049.5 + + + 681.0 + + + 2672.9 + + + 3220.5 + + + 2393.7 + + + 1644.2 + + + 2445.5 + + + 2416.6 + + + 1240.0 + + + 3406.3 + + + 2381.5 + + + 2977.2 + + + 3829.9 + + + 848.1 + + + 2777.4 + + + 1300.5 + + + 616.1 + + + 3426.4 + + + 3410.3 + + + 752.2 + + + 2873.0 + + + 2539.8 + + + 1296.6 + + + 1526.9 + + + 2006.4 + + + 3376.9 + + + 2730.2 + + + 2127.0 + + + 2767.8 + + + 2261.0 + + + 3807.9 + + + 3764.1 + + + 1568.0 + + + 1006.0 + + + 3830.7 + + + 1032.2 + + + 971.8 + + + 1227.2 + + + 1422.0 + + + 1085.4 + + + 3774.3 + + + 1315.5 + + + 3825.2 + + + 3734.3 + + + 2790.1 + + + 1592.1 + + + 1260.8 + + + 3562.5 + + + 2397.1 + + + 3878.2 + + + 1993.7 + + + 1177.7 + + + 1087.9 + + + 1896.1 + + + 3213.6 + + + 2736.2 + + + 2806.1 + + + 2792.3 + + + 2128.9 + + + 2682.9 + + + 2146.9 + + + 2423.9 + + + 2766.1 + + + 3997.1 + + + 671.2 + + + 2163.8 + + + 1371.8 + + + 1800.0 + + + 2020.3 + + + 1920.7 + + + 1956.2 + + + 1337.7 + + + 3570.3 + + + 3661.2 + + + 1987.9 + + + 1080.5 + + + 1621.1 + + + 2720.2 + + + 2475.8 + + + 2359.5 + + + 2959.5 + + + 1529.3 + + + 1129.9 + + + 1272.6 + + + 3551.3 + + + 1675.1 + + + 3100.1 + + + 3275.7 + + + 3531.6 + + + 1665.2 + + + 1112.0 + + + 1834.0 + + + 3416.1 + + + 1585.1 + + + 3662.8 + + + 2409.8 + + + 2477.9 + + + 2945.9 + + + 2857.2 + + + 3169.3 + + + 1889.9 + + + 2939.6 + + + 2588.6 + + + 3899.0 + + + 3611.1 + + + 834.2 + + + 1938.3 + + + 3289.3 + + + 1690.4 + + + 882.4 + + + 654.2 + + + 1174.8 + + + 1552.8 + + + 2264.7 + + + 1411.9 + + + 2072.0 + + + 3183.2 + + + 2474.3 + + + 2722.2 + + + 1894.3 + + + 722.3 + + + 1741.2 + + + 3266.0 + + + 3805.9 + + + 2262.0 + + + 3723.8 + + + 1683.3 + + + 3356.6 + + + 853.0 + + + 3325.0 + + + 632.8 + + + 3558.5 + + + 2728.8 + + + 3569.6 + + + 1014.7 + + + 3136.6 + + + 3834.7 + + + 1392.5 + + + 1689.4 + + + 1270.8 + + + + + + Snowshoe Tour Salzburg + GPS-recorded track 8 + + + 3541.0 + + + 621.5 + + + 1089.2 + + + 2979.9 + + + 1236.0 + + + 1152.2 + + + 2645.3 + + + 2050.2 + + + 2403.9 + + + 1455.7 + + + 813.5 + + + 2707.5 + + + 3199.1 + + + 3130.5 + + + 3715.5 + + + 2905.9 + + + 3455.1 + + + 2233.5 + + + 1665.8 + + + 2596.6 + + + 2180.8 + + + 3478.0 + + + 3656.9 + + + 3151.9 + + + 1074.1 + + + 2749.5 + + + 3290.7 + + + 2616.0 + + + 1930.4 + + + 673.7 + + + 3847.7 + + + 2254.2 + + + 3820.9 + + + 803.2 + + + 1699.2 + + + 1399.5 + + + 2015.6 + + + 1139.2 + + + 2058.6 + + + 1558.5 + + + 3537.9 + + + 814.6 + + + 1312.2 + + + 3564.4 + + + 3906.6 + + + 2562.5 + + + 1032.3 + + + 3901.9 + + + 1405.9 + + + 2197.2 + + + 925.0 + + + 876.4 + + + 3629.1 + + + 2128.2 + + + 1539.1 + + + 800.7 + + + 1712.5 + + + 1096.8 + + + 3950.0 + + + 3402.1 + + + 2631.8 + + + 3100.7 + + + 1560.4 + + + 3383.2 + + + 1089.8 + + + 3192.1 + + + 3693.3 + + + 2487.9 + + + 966.3 + + + 3001.4 + + + 1037.0 + + + 3550.3 + + + 1671.9 + + + 2738.2 + + + 1710.5 + + + 1561.9 + + + 3603.1 + + + 1048.3 + + + 1115.3 + + + 1399.0 + + + 1749.7 + + + 2274.3 + + + 2782.2 + + + 3397.7 + + + 991.8 + + + 1413.5 + + + 778.4 + + + 744.1 + + + 1118.9 + + + 1698.0 + + + 665.5 + + + 2876.8 + + + 736.4 + + + 2581.3 + + + 2577.4 + + + 1158.2 + + + 3711.7 + + + 2491.3 + + + 1285.4 + + + 1712.0 + + + 1570.3 + + + 2814.8 + + + 1228.4 + + + 3125.6 + + + 2538.7 + + + 2892.3 + + + 1827.2 + + + 2527.3 + + + 2337.7 + + + 1215.1 + + + 3047.8 + + + 1928.6 + + + 2232.6 + + + 2867.9 + + + 964.7 + + + 770.1 + + + 600.0 + + + 3868.1 + + + 2795.6 + + + 1867.6 + + + 1611.4 + + + 1082.2 + + + 3943.2 + + + 2764.8 + + + 3593.0 + + + 982.8 + + + 3613.9 + + + 676.7 + + + 2952.1 + + + 1883.2 + + + 2183.4 + + + 3651.2 + + + 3579.7 + + + 1289.2 + + + 3874.8 + + + 3355.2 + + + 2514.9 + + + 2990.0 + + + 3124.6 + + + 1848.8 + + + 1869.1 + + + 2438.6 + + + 1983.8 + + + 3943.9 + + + 2887.0 + + + 1986.6 + + + 1460.9 + + + 2794.6 + + + 2913.1 + + + 3722.2 + + + 1761.6 + + + 3493.7 + + + 3293.7 + + + 3399.7 + + + 2449.6 + + + 1466.0 + + + 3115.0 + + + 2263.2 + + + 1869.3 + + + 3385.8 + + + 1987.2 + + + 2777.6 + + + 3643.8 + + + 3298.2 + + + 1090.8 + + + 1422.2 + + + 2442.7 + + + 679.9 + + + 1231.4 + + + 1461.7 + + + 1815.5 + + + 1620.7 + + + 2057.0 + + + 2690.3 + + + 707.2 + + + 2982.1 + + + 2432.7 + + + 2708.8 + + + 2267.0 + + + 2257.3 + + + 2448.7 + + + 3033.1 + + + 3146.8 + + + 716.8 + + + 3505.8 + + + 635.0 + + + 3254.8 + + + 2307.0 + + + 1919.8 + + + 765.6 + + + 2269.7 + + + 1153.3 + + + 972.9 + + + 1277.0 + + + 3237.5 + + + 3226.4 + + + 1756.2 + + + 1377.7 + + + 3699.6 + + + 3117.2 + + + 2089.5 + + + 669.8 + + + 2758.4 + + + 1548.3 + + + 821.6 + + + 1816.0 + + + 3682.5 + + + 1150.8 + + + 2116.7 + + + 1494.2 + + + 1230.7 + + + 1389.6 + + + 2985.0 + + + 3279.5 + + + 2590.9 + + + 1066.0 + + + 3324.2 + + + 2227.7 + + + 2753.5 + + + 1096.8 + + + 3528.0 + + + 3957.0 + + + 3161.6 + + + 1366.2 + + + 2068.4 + + + 694.1 + + + + + 1364.1 + + + 3827.9 + + + 3564.4 + + + 2372.3 + + + 2639.4 + + + 2995.2 + + + 735.8 + + + 2810.6 + + + 3443.5 + + + 3508.3 + + + 3175.2 + + + 2504.6 + + + 3271.8 + + + 2950.8 + + + 2695.6 + + + 3776.1 + + + 1374.7 + + + 1414.0 + + + 1252.3 + + + 1547.0 + + + 1240.4 + + + 3327.6 + + + 2932.3 + + + 1295.2 + + + 998.5 + + + 1439.9 + + + 1747.1 + + + 1875.0 + + + 3770.4 + + + 1466.1 + + + 1880.3 + + + 826.3 + + + 2283.2 + + + 3391.9 + + + 1503.4 + + + 2264.5 + + + 1664.7 + + + 3184.4 + + + 839.0 + + + 1033.2 + + + 2039.1 + + + 803.6 + + + 3046.5 + + + 1349.5 + + + 3754.7 + + + 678.0 + + + 1184.3 + + + 2441.8 + + + 2542.2 + + + 3352.5 + + + 1981.8 + + + 3632.4 + + + 610.8 + + + 3527.0 + + + 1389.4 + + + 2110.1 + + + 3492.0 + + + 3201.2 + + + 2084.6 + + + 670.4 + + + 2103.4 + + + 863.0 + + + 1172.5 + + + 1801.1 + + + 3654.7 + + + 3866.2 + + + 2794.6 + + + 674.4 + + + 669.7 + + + 2042.1 + + + 1515.0 + + + 1137.5 + + + 2358.4 + + + 723.4 + + + 1377.2 + + + 2692.5 + + + 2385.9 + + + 1081.6 + + + 1232.5 + + + 827.0 + + + 2546.9 + + + 3547.1 + + + 2198.8 + + + 658.4 + + + 862.0 + + + 3262.7 + + + 2481.7 + + + 2414.4 + + + 1706.6 + + + 3365.2 + + + 1938.9 + + + 3478.2 + + + 1848.4 + + + 2146.8 + + + 1151.1 + + + 3941.7 + + + 2398.2 + + + 2471.1 + + + 2186.5 + + + 3273.7 + + + 1977.7 + + + 1167.4 + + + 723.8 + + + 3462.6 + + + 1900.7 + + + 2256.6 + + + 3235.2 + + + 2607.5 + + + 3298.8 + + + 3661.7 + + + 1957.4 + + + 1296.0 + + + 1434.2 + + + 944.4 + + + 3691.9 + + + 865.6 + + + 2475.5 + + + 3480.4 + + + 1655.2 + + + 944.4 + + + 803.6 + + + 1862.3 + + + 2683.9 + + + 1851.1 + + + 3604.0 + + + 2347.5 + + + 3146.4 + + + 1121.3 + + + 3028.5 + + + 1089.4 + + + 2697.4 + + + 749.6 + + + 2022.5 + + + 3602.8 + + + 3967.4 + + + 1155.5 + + + 3275.8 + + + 1163.1 + + + 2876.0 + + + 3074.9 + + + 3852.2 + + + 3664.5 + + + 1710.2 + + + 3036.3 + + + 2619.1 + + + 675.6 + + + 2330.0 + + + 1976.0 + + + 3534.6 + + + 1360.3 + + + 1445.7 + + + 2971.1 + + + 1959.5 + + + 1424.5 + + + 2853.8 + + + 793.5 + + + 2008.4 + + + 1859.3 + + + 3992.5 + + + 1327.7 + + + 1427.0 + + + 2633.1 + + + 942.2 + + + 1110.2 + + + 3776.9 + + + 1116.4 + + + 711.1 + + + 2907.7 + + + 2864.6 + + + 1653.2 + + + 880.0 + + + 3736.7 + + + 3402.2 + + + 1590.9 + + + 2683.3 + + + 3498.1 + + + 2350.6 + + + 1564.6 + + + 1153.5 + + + 3835.8 + + + 3369.8 + + + 3300.5 + + + 1315.9 + + + 725.1 + + + 1102.1 + + + 3057.4 + + + 1687.8 + + + 1993.6 + + + 2883.6 + + + 3011.0 + + + 1838.3 + + + 766.3 + + + 1924.5 + + + 3886.3 + + + 2128.7 + + + 3005.1 + + + 3155.1 + + + 3555.2 + + + 916.9 + + + 742.9 + + + 2284.2 + + + 1213.7 + + + 1826.5 + + + 1746.4 + + + 3895.5 + + + 2913.2 + + + 1529.7 + + + 2607.7 + + + 3217.0 + + + 3878.1 + + + 3989.1 + + + 660.9 + + + 813.5 + + + 2176.0 + + + 3738.5 + + + 1500.0 + + + 3376.9 + + + 1942.4 + + + 1354.1 + + + 607.7 + + + 3331.2 + + + 3795.7 + + + 3091.4 + + + 3214.3 + + + 2572.6 + + + 3250.9 + + + 1922.7 + + + 2830.7 + + + 1717.8 + + + 938.2 + + + 1174.7 + + + 1150.3 + + + 1828.9 + + + 3992.8 + + + 859.2 + + + 3786.1 + + + 3219.2 + + + 1208.5 + + + 2301.6 + + + 2336.7 + + + 3270.7 + + + 1199.8 + + + 1450.9 + + + 3334.9 + + + 815.7 + + + 3371.7 + + + 3744.0 + + + 3501.9 + + + 3344.7 + + + 1169.6 + + + 2593.6 + + + 3131.8 + + + 3254.6 + + + 1695.5 + + + 723.7 + + + 658.2 + + + 1751.7 + + + 1671.6 + + + 926.3 + + + 2615.8 + + + 2475.5 + + + 3356.8 + + + 1436.5 + + + 732.4 + + + 2860.4 + + + 3373.4 + + + 3489.1 + + + 1836.9 + + + 1983.4 + + + 1228.2 + + + 2384.8 + + + + + + Running Track Chamonix + GPS-recorded track 9 + + + 1734.9 + + + 1321.9 + + + 1540.2 + + + 2440.4 + + + 2466.4 + + + 1610.5 + + + 3644.1 + + + 3724.1 + + + 1514.8 + + + 3826.5 + + + 2221.7 + + + 2442.7 + + + 2508.0 + + + 1172.5 + + + 3546.8 + + + 1342.6 + + + 3854.8 + + + 3964.8 + + + 1529.3 + + + 1237.7 + + + 1317.4 + + + 2394.5 + + + 2608.6 + + + 1446.7 + + + 2028.8 + + + 3924.6 + + + 1460.0 + + + 2778.8 + + + 1050.3 + + + 3132.6 + + + 1052.1 + + + 2553.6 + + + 3421.9 + + + 1301.5 + + + 3271.0 + + + 1997.8 + + + 2107.6 + + + 1303.1 + + + 3517.6 + + + 3827.1 + + + 2073.2 + + + 3434.5 + + + 1293.1 + + + 1563.7 + + + 2209.0 + + + 2145.1 + + + 1283.1 + + + 3875.9 + + + 2089.6 + + + 2793.7 + + + 2475.9 + + + 1510.1 + + + 936.0 + + + 1654.7 + + + 3854.3 + + + 1457.1 + + + 3766.7 + + + 3743.1 + + + 3923.3 + + + 1713.1 + + + 2531.0 + + + 968.8 + + + 1454.0 + + + 2738.1 + + + 2521.7 + + + 2723.7 + + + 3516.5 + + + 2410.5 + + + 2512.3 + + + 3646.1 + + + 1296.9 + + + 846.9 + + + 2100.4 + + + 1342.8 + + + 3721.9 + + + 2786.3 + + + 917.8 + + + 917.9 + + + 3137.8 + + + 2218.4 + + + 3174.9 + + + 2264.5 + + + 3617.4 + + + 1379.7 + + + 2967.7 + + + 2491.4 + + + 3934.4 + + + 1254.7 + + + 753.7 + + + 1272.0 + + + 2128.2 + + + 3067.5 + + + 3172.4 + + + 2068.2 + + + 1140.8 + + + 719.4 + + + 2029.2 + + + 848.9 + + + 2978.9 + + + 3026.7 + + + 3663.1 + + + 2591.1 + + + 3417.8 + + + 1333.0 + + + 3444.7 + + + 2661.4 + + + 3441.1 + + + 3226.7 + + + 2576.8 + + + 782.9 + + + 3514.4 + + + 2808.8 + + + 779.6 + + + 3316.4 + + + 3518.2 + + + 2432.6 + + + 918.0 + + + 3415.6 + + + 3448.9 + + + 2975.8 + + + 2104.0 + + + 872.5 + + + 2916.6 + + + 712.6 + + + 2860.6 + + + 703.2 + + + 3347.1 + + + 1247.4 + + + 3264.4 + + + 3277.4 + + + 3045.4 + + + 2885.4 + + + 1585.5 + + + 2455.6 + + + 2436.0 + + + 3211.2 + + + 1582.5 + + + 1890.1 + + + 689.3 + + + 1488.4 + + + 1761.1 + + + 2402.1 + + + 1401.7 + + + 1374.7 + + + 1845.1 + + + 1876.1 + + + 2725.1 + + + 1994.6 + + + 2701.5 + + + 2723.0 + + + 3743.8 + + + 1510.6 + + + 1931.7 + + + 2489.7 + + + 2727.0 + + + 1203.3 + + + 3765.1 + + + 3197.1 + + + 1510.5 + + + 2968.6 + + + 1181.0 + + + 3083.9 + + + 1425.2 + + + 1852.1 + + + 1327.2 + + + 879.4 + + + 1591.5 + + + 1805.9 + + + 3545.3 + + + 1378.9 + + + 2922.8 + + + 3698.9 + + + 1882.7 + + + 1588.0 + + + 2317.7 + + + 3084.7 + + + 2303.3 + + + 1727.3 + + + 3428.0 + + + 727.6 + + + 3500.1 + + + 1310.4 + + + 3417.0 + + + 3010.2 + + + 1080.8 + + + 2639.5 + + + 3277.1 + + + 1275.3 + + + 1273.2 + + + 1698.3 + + + 3506.4 + + + 3721.6 + + + 3178.6 + + + 2169.8 + + + 2478.7 + + + 636.3 + + + 3990.7 + + + 3247.4 + + + 2630.5 + + + 1882.8 + + + 1497.2 + + + 1007.0 + + + 610.5 + + + 1850.9 + + + 797.0 + + + 1018.8 + + + 1575.2 + + + 1949.8 + + + 2594.3 + + + 1180.0 + + + 2662.3 + + + 2134.4 + + + 913.6 + + + 3514.7 + + + 1984.6 + + + 2743.6 + + + 2274.1 + + + 3288.5 + + + 996.1 + + + 661.1 + + + 3966.9 + + + 1928.0 + + + 3562.2 + + + 2149.1 + + + 939.8 + + + 3651.5 + + + 2330.9 + + + 2250.3 + + + 2508.7 + + + 3945.3 + + + 3874.6 + + + 2485.4 + + + 3252.8 + + + 3533.6 + + + 2397.3 + + + 2793.5 + + + 948.4 + + + 661.4 + + + 3976.8 + + + 1583.8 + + + 3029.8 + + + 3344.9 + + + 2547.5 + + + 2723.2 + + + 2833.0 + + + 3182.3 + + + 3187.7 + + + 2375.5 + + + 2569.6 + + + 1099.6 + + + 3225.8 + + + 3293.8 + + + 3627.8 + + + + + 1080.4 + + + 2503.5 + + + 2978.9 + + + 1953.2 + + + 3906.0 + + + 3147.5 + + + 3063.0 + + + 2878.5 + + + 1429.5 + + + 3650.0 + + + 3935.2 + + + 3864.1 + + + 1084.4 + + + 1760.6 + + + 1100.6 + + + 2900.3 + + + 3264.2 + + + 1983.5 + + + 650.8 + + + 2884.8 + + + 1247.0 + + + 3933.6 + + + 1252.9 + + + 1418.0 + + + 2330.7 + + + 700.1 + + + 1360.7 + + + 2076.4 + + + 812.9 + + + 3195.1 + + + 2872.3 + + + 3896.3 + + + 825.2 + + + 1549.4 + + + 3516.6 + + + 3230.4 + + + 3564.3 + + + 2731.7 + + + 2767.5 + + + 2098.2 + + + 797.8 + + + 3068.5 + + + 1042.1 + + + 3315.5 + + + 2726.3 + + + 762.2 + + + 2646.1 + + + 1950.7 + + + 828.3 + + + 1389.2 + + + 1668.3 + + + 1772.1 + + + 2451.9 + + + 3488.5 + + + 945.6 + + + 982.9 + + + 2345.2 + + + 2285.5 + + + 1012.3 + + + 2870.8 + + + 767.2 + + + 1698.3 + + + 2334.9 + + + 1536.3 + + + 1296.6 + + + 2199.4 + + + 2174.9 + + + 1179.1 + + + 1149.0 + + + 3390.2 + + + 2736.1 + + + 2027.6 + + + 3421.1 + + + 2089.0 + + + 1387.5 + + + 991.0 + + + 3986.1 + + + 2546.2 + + + 2275.6 + + + 2764.6 + + + 1004.6 + + + 3527.1 + + + 2362.1 + + + 652.6 + + + 1088.3 + + + 1361.3 + + + 893.0 + + + 1844.5 + + + 2120.8 + + + 2536.1 + + + 3218.3 + + + 1071.7 + + + 3166.9 + + + 2416.5 + + + 2719.7 + + + 3551.5 + + + 3723.2 + + + 3834.8 + + + 3462.0 + + + 3037.0 + + + 2804.5 + + + 3476.9 + + + 824.5 + + + 2728.6 + + + 3154.0 + + + 911.7 + + + 2449.5 + + + 2433.5 + + + 3775.3 + + + 865.2 + + + 2856.6 + + + 2274.4 + + + 2553.6 + + + 3474.7 + + + 2354.8 + + + 729.8 + + + 2382.9 + + + 2725.7 + + + 1515.7 + + + 1825.2 + + + 2706.4 + + + 704.2 + + + 2442.0 + + + 1873.4 + + + 1936.8 + + + 1800.3 + + + 2770.2 + + + + + + Via Ferrata Dolomiten + GPS-recorded track 10 + + + 3284.4 + + + 3798.1 + + + 2546.8 + + + 1486.5 + + + 1234.4 + + + 3769.3 + + + 1922.9 + + + 3816.6 + + + 1767.3 + + + 947.6 + + + 961.1 + + + 687.2 + + + 3851.3 + + + 653.2 + + + 2912.4 + + + 1260.1 + + + 2896.5 + + + 3749.5 + + + 2246.4 + + + 2995.9 + + + 728.1 + + + 1645.8 + + + 1711.0 + + + 3078.4 + + + 3379.6 + + + 2383.4 + + + 3495.3 + + + 1503.6 + + + 2787.3 + + + 3294.3 + + + 2152.6 + + + 1014.8 + + + 804.5 + + + 2225.2 + + + 3472.0 + + + 1784.3 + + + 1725.0 + + + 2055.7 + + + 3227.9 + + + 2129.3 + + + 1169.8 + + + 1746.8 + + + 2440.6 + + + 3879.4 + + + 3023.1 + + + 1038.2 + + + 3890.0 + + + 1729.8 + + + 3600.5 + + + 1866.5 + + + 1389.1 + + + 3583.1 + + + 3317.7 + + + 618.9 + + + 1293.9 + + + 1219.3 + + + 3493.8 + + + 2593.9 + + + 2298.6 + + + 1355.1 + + + 2099.6 + + + 1109.0 + + + 2352.1 + + + 1854.0 + + + 2387.8 + + + 2447.4 + + + 1247.4 + + + 1086.9 + + + 1636.6 + + + 627.6 + + + 1492.1 + + + 2174.8 + + + 2203.6 + + + 2150.5 + + + 2232.3 + + + 1834.8 + + + 1888.3 + + + 1706.5 + + + 3236.8 + + + 3658.8 + + + 1934.5 + + + 2094.4 + + + 815.3 + + + 3276.5 + + + 2828.3 + + + 3748.7 + + + 2560.6 + + + 3119.4 + + + 3091.5 + + + 2589.6 + + + 2472.2 + + + 2821.3 + + + 2563.9 + + + 2490.2 + + + 2263.0 + + + 3600.5 + + + 1585.5 + + + 1487.5 + + + 3234.8 + + + 1213.6 + + + 3526.1 + + + 1192.7 + + + 1438.1 + + + 2391.8 + + + 2267.1 + + + 2570.6 + + + 1426.3 + + + 3159.3 + + + 1950.2 + + + 2463.1 + + + 1502.1 + + + 3795.1 + + + 2320.1 + + + 1316.0 + + + 1865.5 + + + 2767.3 + + + 3448.7 + + + 3213.1 + + + 1627.2 + + + 3369.9 + + + 972.2 + + + 1154.2 + + + 3655.0 + + + 1107.3 + + + 3081.2 + + + 681.7 + + + 2822.8 + + + 2681.1 + + + 2013.0 + + + 2617.8 + + + 1093.6 + + + 1116.1 + + + 2935.1 + + + 2186.3 + + + 2659.8 + + + 983.2 + + + 1214.4 + + + 2118.1 + + + 3746.8 + + + 1273.0 + + + 725.9 + + + 1746.9 + + + 2148.2 + + + 3240.6 + + + 1559.0 + + + 3285.4 + + + 1078.1 + + + 2860.7 + + + 1574.8 + + + 2610.8 + + + 3890.1 + + + 824.5 + + + 3338.2 + + + 2627.1 + + + 2288.3 + + + 3894.2 + + + 2041.1 + + + 1846.7 + + + 1320.8 + + + 3619.5 + + + 3966.3 + + + 1830.2 + + + 2282.5 + + + 809.2 + + + 2066.2 + + + 1375.9 + + + 2810.3 + + + 1487.6 + + + 2927.1 + + + 3015.1 + + + 1024.0 + + + 1978.8 + + + 2775.5 + + + 1648.1 + + + 2902.0 + + + 2045.6 + + + 2230.5 + + + 1453.7 + + + 2486.8 + + + 2348.2 + + + 3699.0 + + + 941.3 + + + 905.9 + + + 2450.9 + + + 2878.7 + + + 703.6 + + + 2994.6 + + + 744.3 + + + 2170.6 + + + 762.5 + + + 2404.4 + + + 3323.5 + + + 684.3 + + + 2407.8 + + + 2048.1 + + + 700.9 + + + 1276.5 + + + 1281.2 + + + 3726.2 + + + 2087.4 + + + 868.4 + + + 3424.4 + + + 709.1 + + + 2046.9 + + + 2603.7 + + + 772.0 + + + 1022.7 + + + 2827.4 + + + 3801.0 + + + 3174.8 + + + 1516.5 + + + 3991.4 + + + 743.8 + + + 3346.6 + + + 3393.8 + + + 2660.8 + + + 2803.3 + + + 2929.5 + + + 2492.2 + + + 1898.8 + + + 1574.6 + + + 1505.0 + + + 1695.9 + + + 3887.9 + + + 2581.7 + + + 2116.6 + + + 3697.6 + + + 3959.4 + + + 2188.7 + + + 2437.9 + + + 786.7 + + + 693.3 + + + 923.3 + + + 3808.6 + + + 2926.8 + + + 2624.6 + + + 2125.2 + + + 3918.4 + + + 2427.5 + + + 3754.5 + + + 3494.8 + + + 3640.2 + + + 3176.6 + + + 1810.6 + + + 2541.7 + + + 2088.1 + + + 3360.4 + + + 2212.6 + + + 3464.8 + + + 889.4 + + + 2803.0 + + + 3629.4 + + + 3291.0 + + + 3673.0 + + + 1933.2 + + + 3483.0 + + + 746.7 + + + 1479.4 + + + 2040.0 + + + 3658.6 + + + 1566.0 + + + 2497.8 + + + + + 3312.7 + + + 1953.7 + + + 2656.1 + + + 3439.3 + + + 3049.2 + + + 1503.2 + + + 2109.4 + + + 1912.2 + + + 2572.0 + + + 1535.7 + + + 3890.0 + + + 1504.1 + + + 1375.0 + + + 3949.0 + + + 3957.6 + + + 831.7 + + + 903.2 + + + 984.0 + + + 1444.2 + + + 2776.3 + + + 1295.5 + + + 2215.4 + + + 1234.4 + + + 1692.1 + + + 3251.2 + + + 1825.1 + + + 2285.0 + + + 2058.3 + + + 2969.8 + + + 2015.7 + + + 3117.1 + + + 822.1 + + + 2621.5 + + + 742.2 + + + 609.7 + + + 1094.6 + + + 2006.6 + + + 1370.0 + + + 1941.4 + + + 2035.4 + + + 3711.5 + + + 2968.0 + + + 2322.9 + + + 3546.9 + + + 3928.2 + + + 1022.0 + + + 1214.0 + + + 3981.9 + + + 3623.1 + + + 2213.2 + + + 1372.0 + + + 2487.8 + + + 1445.1 + + + 940.0 + + + 2638.3 + + + 684.2 + + + 3343.8 + + + 1450.7 + + + 3995.4 + + + 3670.3 + + + 2066.6 + + + 2081.7 + + + 3766.7 + + + 2266.1 + + + 699.2 + + + 3211.2 + + + 2321.8 + + + 1424.6 + + + 1975.1 + + + 2754.0 + + + 1583.2 + + + 2652.4 + + + 631.1 + + + 3509.0 + + + 1215.5 + + + 3878.7 + + + 1656.1 + + + 828.9 + + + 1555.2 + + + 3696.4 + + + 1454.1 + + + 3977.5 + + + 2425.3 + + + 3938.7 + + + 2500.0 + + + 2564.7 + + + 3764.5 + + + 1528.9 + + + 3294.6 + + + 2045.0 + + + 1159.7 + + + 3100.4 + + + 2062.3 + + + 3545.8 + + + 1791.2 + + + 3943.6 + + + 1034.9 + + + 1390.7 + + + 3309.5 + + + 706.9 + + + 2819.6 + + + 3115.2 + + + 860.8 + + + 3777.8 + + + 2426.7 + + + 2270.3 + + + 3943.2 + + + 1953.7 + + + 2793.2 + + + 1177.1 + + + 2432.2 + + + 3652.3 + + + 2445.9 + + + 3265.7 + + + 1466.8 + + + 1914.5 + + + 1705.0 + + + 1507.0 + + + 1380.4 + + + 3730.8 + + + 953.0 + + + 2097.8 + + + 827.7 + + + 1548.1 + + + 1822.2 + + + 2407.9 + + + 2940.7 + + + 2131.1 + + + 2782.1 + + + 3068.5 + + + 1019.3 + + + 2342.5 + + + 1912.4 + + + 3959.8 + + + 3608.3 + + + 1640.7 + + + 2990.5 + + + 3455.1 + + + 1390.7 + + + 3954.1 + + + 3015.6 + + + 1292.7 + + + 2128.4 + + + 3294.0 + + + 2796.6 + + + 3727.8 + + + 1314.3 + + + 3742.1 + + + 1321.1 + + + 997.7 + + + 607.2 + + + 2168.5 + + + 701.3 + + + 3550.6 + + + 3030.5 + + + 3380.6 + + + 916.0 + + + 3652.3 + + + 1429.5 + + + 3167.4 + + + 3262.3 + + + 1070.1 + + + 3940.3 + + + 932.0 + + + 3981.6 + + + 3714.4 + + + 1548.4 + + + 1889.2 + + + 3670.0 + + + 3649.4 + + + 851.0 + + + 1861.3 + + + 2993.9 + + + 3955.1 + + + 1701.4 + + + 3715.4 + + + 742.7 + + + 2509.8 + + + 1287.8 + + + 3067.1 + + + 1920.6 + + + 1395.9 + + + 3652.2 + + + 3983.0 + + + 2688.8 + + + + + diff --git a/server/tests/fixtures/large-test.kml b/server/tests/fixtures/large-test.kml new file mode 100644 index 00000000..8ad41955 --- /dev/null +++ b/server/tests/fixtures/large-test.kml @@ -0,0 +1,3181 @@ + + + + TREK Large Test KML + Generated fixture with 150 points and 20 paths across folders + + + + + + Summits + + Quellfassung 1 + Quellfassung 1
Category: Summits
Elevation: 1008 m]]>
+ #point-style + + 8.300302,46.415912,1007.9 + +
+ + Hochlager 2 + Hochlager 2
Category: Summits
Elevation: 2879 m]]>
+ #point-style + + 7.846314,45.332978,2878.7 + +
+ + Bergseehütte 3 + Bergseehütte 3
Category: Summits
Elevation: 3590 m]]>
+ #point-style + + 9.654517,47.537235,3590.5 + +
+ + Parkplatz 4 + Parkplatz 4
Category: Summits
Elevation: 2401 m]]>
+ #point-style + + 10.388902,45.597872,2400.6 + +
+ + Rifugio 5 + Rifugio 5
Category: Summits
Elevation: 1123 m]]>
+ #point-style + + 10.945969,48.089302,1122.8 + +
+ + Hochlager 6 + Hochlager 6
Category: Summits
Elevation: 1552 m]]>
+ #point-style + + 10.899869,47.442272,1551.9 + +
+ + Gipfelkreuz 7 + Gipfelkreuz 7
Category: Summits
Elevation: 1476 m]]>
+ #point-style + + 7.851209,47.509510,1475.7 + +
+ + Panoramapunkt 8 + Panoramapunkt 8
Category: Summits
Elevation: 1093 m]]>
+ #point-style + + 7.530479,45.877597,1092.8 + +
+ + Rifugio 9 + Rifugio 9
Category: Summits
Elevation: 1073 m]]>
+ #point-style + + 9.574620,45.123266,1073.5 + +
+ + Jochpass 10 + Jochpass 10
Category: Summits
Elevation: 1134 m]]>
+ #point-style + + 8.930100,48.706848,1133.8 + +
+ + Jochpass 11 + Jochpass 11
Category: Summits
Elevation: 3544 m]]>
+ #point-style + + 9.266858,47.790033,3544.5 + +
+ + Bergstation 12 + Bergstation 12
Category: Summits
Elevation: 526 m]]>
+ #point-style + + 10.141199,48.575642,526.0 + +
+ + Refugio 13 + Refugio 13
Category: Summits
Elevation: 1825 m]]>
+ #point-style + + 10.235459,45.484749,1824.7 + +
+ + Kapelle 14 + Kapelle 14
Category: Summits
Elevation: 2499 m]]>
+ #point-style + + 8.071646,47.328059,2499.1 + +
+ + Gipfelkreuz 15 + Gipfelkreuz 15
Category: Summits
Elevation: 1083 m]]>
+ #point-style + + 9.593608,46.118791,1082.7 + +
+ + Alpenrose 16 + Alpenrose 16
Category: Summits
Elevation: 2255 m]]>
+ #point-style + + 9.271891,47.361735,2254.6 + +
+ + Alpenrose 17 + Alpenrose 17
Category: Summits
Elevation: 3535 m]]>
+ #point-style + + 10.279996,46.215234,3535.5 + +
+ + Alpenrose 18 + Alpenrose 18
Category: Summits
Elevation: 3048 m]]>
+ #point-style + + 10.373454,47.754216,3048.4 + +
+ + Alpenrose 19 + Alpenrose 19
Category: Summits
Elevation: 728 m]]>
+ #point-style + + 9.133198,47.232138,728.0 + +
+ + Talstation 20 + Talstation 20
Category: Summits
Elevation: 1794 m]]>
+ #point-style + + 11.464486,45.983895,1793.6 + +
+ + Quellfassung 21 + Quellfassung 21
Category: Summits
Elevation: 2097 m]]>
+ #point-style + + 8.776426,48.321723,2097.1 + +
+ + Jochpass 22 + Jochpass 22
Category: Summits
Elevation: 3196 m]]>
+ #point-style + + 7.968853,48.184502,3196.0 + +
+ + Gipfelkreuz 23 + Gipfelkreuz 23
Category: Summits
Elevation: 438 m]]>
+ #point-style + + 9.504931,47.513299,437.9 + +
+ + Kapelle 24 + Kapelle 24
Category: Summits
Elevation: 1545 m]]>
+ #point-style + + 9.007897,46.149740,1545.0 + +
+ + Jochpass 25 + Jochpass 25
Category: Summits
Elevation: 2217 m]]>
+ #point-style + + 10.754754,46.343760,2216.9 + +
+ + Kapelle 26 + Kapelle 26
Category: Summits
Elevation: 2854 m]]>
+ #point-style + + 11.192145,48.318273,2853.6 + +
+ + Quellfassung 27 + Quellfassung 27
Category: Summits
Elevation: 3302 m]]>
+ #point-style + + 8.420839,47.883286,3302.2 + +
+ + Rastplatz 28 + Rastplatz 28
Category: Summits
Elevation: 3372 m]]>
+ #point-style + + 8.742626,47.413455,3372.4 + +
+ + Quellfassung 29 + Quellfassung 29
Category: Summits
Elevation: 2693 m]]>
+ #point-style + + 10.737096,45.674416,2692.6 + +
+ + Rifugio 30 + Rifugio 30
Category: Summits
Elevation: 3661 m]]>
+ #point-style + + 9.252501,46.334218,3660.9 + +
+ + Almhütte 31 + Almhütte 31
Category: Summits
Elevation: 637 m]]>
+ #point-style + + 11.130629,46.484437,636.9 + +
+ + Panoramapunkt 32 + Panoramapunkt 32
Category: Summits
Elevation: 3080 m]]>
+ #point-style + + 10.998372,48.428823,3079.9 + +
+ + Almhütte 33 + Almhütte 33
Category: Summits
Elevation: 1865 m]]>
+ #point-style + + 11.084053,45.309122,1865.1 + +
+ + Almhütte 34 + Almhütte 34
Category: Summits
Elevation: 2477 m]]>
+ #point-style + + 10.132870,46.437330,2477.4 + +
+ + Gatterl 35 + Gatterl 35
Category: Summits
Elevation: 3333 m]]>
+ #point-style + + 10.213401,46.315835,3333.2 + +
+ + Talstation 36 + Talstation 36
Category: Summits
Elevation: 1691 m]]>
+ #point-style + + 9.260214,47.467127,1691.4 + +
+ + Talstation 37 + Talstation 37
Category: Summits
Elevation: 2761 m]]>
+ #point-style + + 10.168245,46.181530,2760.7 + +
+ + Wegkreuz 38 + Wegkreuz 38
Category: Summits
Elevation: 3084 m]]>
+ #point-style + + 10.171267,48.365316,3084.3 + +
+ + Quellfassung 39 + Quellfassung 39
Category: Summits
Elevation: 3345 m]]>
+ #point-style + + 10.472065,47.428666,3345.3 + +
+ + Bergstation 40 + Bergstation 40
Category: Summits
Elevation: 2632 m]]>
+ #point-style + + 10.947010,46.432983,2632.3 + +
+
+ + + Huts + + Almhütte 41 + Almhütte 41
Category: Huts
Elevation: 404 m]]>
+ #point-style + + 9.626994,45.249329,404.4 + +
+ + Hochlager 42 + Hochlager 42
Category: Huts
Elevation: 3768 m]]>
+ #point-style + + 11.413200,48.599610,3767.5 + +
+ + Refugio 43 + Refugio 43
Category: Huts
Elevation: 1098 m]]>
+ #point-style + + 9.276467,47.939486,1098.0 + +
+ + Gatterl 44 + Gatterl 44
Category: Huts
Elevation: 2507 m]]>
+ #point-style + + 9.624124,46.672098,2507.3 + +
+ + Wanderheim 45 + Wanderheim 45
Category: Huts
Elevation: 470 m]]>
+ #point-style + + 8.301138,47.875745,470.1 + +
+ + Gipfelkreuz 46 + Gipfelkreuz 46
Category: Huts
Elevation: 509 m]]>
+ #point-style + + 7.802286,48.025507,508.7 + +
+ + Bachübergang 47 + Bachübergang 47
Category: Huts
Elevation: 2295 m]]>
+ #point-style + + 8.976906,46.467092,2294.6 + +
+ + Kapelle 48 + Kapelle 48
Category: Huts
Elevation: 740 m]]>
+ #point-style + + 8.276016,47.099650,740.4 + +
+ + Alpenrose 49 + Alpenrose 49
Category: Huts
Elevation: 1967 m]]>
+ #point-style + + 8.189012,46.393786,1966.8 + +
+ + Kapelle 50 + Kapelle 50
Category: Huts
Elevation: 3351 m]]>
+ #point-style + + 11.107070,48.311491,3350.9 + +
+ + Wegkreuz 51 + Wegkreuz 51
Category: Huts
Elevation: 1008 m]]>
+ #point-style + + 11.317326,46.472652,1008.0 + +
+ + Kapelle 52 + Kapelle 52
Category: Huts
Elevation: 3271 m]]>
+ #point-style + + 9.352648,45.630446,3270.8 + +
+ + Bachübergang 53 + Bachübergang 53
Category: Huts
Elevation: 3794 m]]>
+ #point-style + + 11.150707,45.534658,3793.7 + +
+ + Jochpass 54 + Jochpass 54
Category: Huts
Elevation: 2020 m]]>
+ #point-style + + 11.057065,48.371718,2019.6 + +
+ + Hochlager 55 + Hochlager 55
Category: Huts
Elevation: 3790 m]]>
+ #point-style + + 9.626654,48.721742,3790.4 + +
+ + Rastplatz 56 + Rastplatz 56
Category: Huts
Elevation: 2080 m]]>
+ #point-style + + 8.818986,44.861652,2080.0 + +
+ + Wegkreuz 57 + Wegkreuz 57
Category: Huts
Elevation: 2714 m]]>
+ #point-style + + 7.515762,47.420729,2714.1 + +
+ + Jochpass 58 + Jochpass 58
Category: Huts
Elevation: 1807 m]]>
+ #point-style + + 9.831777,47.334030,1807.4 + +
+ + Gipfelkreuz 59 + Gipfelkreuz 59
Category: Huts
Elevation: 1751 m]]>
+ #point-style + + 11.179977,45.344610,1750.7 + +
+ + Jochpass 60 + Jochpass 60
Category: Huts
Elevation: 2969 m]]>
+ #point-style + + 8.419823,46.169559,2968.8 + +
+ + Quellfassung 61 + Quellfassung 61
Category: Huts
Elevation: 3035 m]]>
+ #point-style + + 7.730026,48.151335,3035.1 + +
+ + Wegkreuz 62 + Wegkreuz 62
Category: Huts
Elevation: 984 m]]>
+ #point-style + + 7.968669,48.021417,984.2 + +
+ + Panoramapunkt 63 + Panoramapunkt 63
Category: Huts
Elevation: 3524 m]]>
+ #point-style + + 7.782582,47.427352,3523.9 + +
+ + Panoramapunkt 64 + Panoramapunkt 64
Category: Huts
Elevation: 3664 m]]>
+ #point-style + + 8.914730,45.494560,3663.9 + +
+ + Alpenrose 65 + Alpenrose 65
Category: Huts
Elevation: 2864 m]]>
+ #point-style + + 10.211497,45.125852,2864.1 + +
+ + Kapelle 66 + Kapelle 66
Category: Huts
Elevation: 2981 m]]>
+ #point-style + + 7.846034,45.400790,2981.2 + +
+ + Gatterl 67 + Gatterl 67
Category: Huts
Elevation: 2838 m]]>
+ #point-style + + 10.983489,45.466255,2837.6 + +
+ + Gipfelkreuz 68 + Gipfelkreuz 68
Category: Huts
Elevation: 948 m]]>
+ #point-style + + 11.374511,47.233060,947.8 + +
+ + Almhütte 69 + Almhütte 69
Category: Huts
Elevation: 1883 m]]>
+ #point-style + + 10.737712,48.372338,1882.9 + +
+ + Hochlager 70 + Hochlager 70
Category: Huts
Elevation: 2088 m]]>
+ #point-style + + 7.944543,45.518302,2088.3 + +
+ + Wanderheim 71 + Wanderheim 71
Category: Huts
Elevation: 1681 m]]>
+ #point-style + + 7.550150,47.722768,1681.2 + +
+ + Gatterl 72 + Gatterl 72
Category: Huts
Elevation: 2327 m]]>
+ #point-style + + 11.459521,47.931056,2326.9 + +
+ + Jochpass 73 + Jochpass 73
Category: Huts
Elevation: 3179 m]]>
+ #point-style + + 7.820715,48.296785,3178.7 + +
+ + Gipfelkreuz 74 + Gipfelkreuz 74
Category: Huts
Elevation: 1466 m]]>
+ #point-style + + 8.992464,47.896180,1465.5 + +
+ + Alpenrose 75 + Alpenrose 75
Category: Huts
Elevation: 3625 m]]>
+ #point-style + + 7.581521,47.137376,3625.2 + +
+
+ + + Viewpoints + + Almhütte 76 + Almhütte 76
Category: Viewpoints
Elevation: 3153 m]]>
+ #point-style + + 11.023824,48.574574,3152.9 + +
+ + Gipfelkreuz 77 + Gipfelkreuz 77
Category: Viewpoints
Elevation: 3126 m]]>
+ #point-style + + 9.469773,45.960409,3125.5 + +
+ + Rastplatz 78 + Rastplatz 78
Category: Viewpoints
Elevation: 1479 m]]>
+ #point-style + + 9.925919,46.214968,1478.9 + +
+ + Kapelle 79 + Kapelle 79
Category: Viewpoints
Elevation: 3694 m]]>
+ #point-style + + 10.721954,47.455963,3693.7 + +
+ + Bergstation 80 + Bergstation 80
Category: Viewpoints
Elevation: 2263 m]]>
+ #point-style + + 9.941091,48.055557,2263.1 + +
+ + Refugio 81 + Refugio 81
Category: Viewpoints
Elevation: 2912 m]]>
+ #point-style + + 11.255305,47.854804,2912.4 + +
+ + Hochlager 82 + Hochlager 82
Category: Viewpoints
Elevation: 1175 m]]>
+ #point-style + + 11.104967,46.849089,1174.6 + +
+ + Wegkreuz 83 + Wegkreuz 83
Category: Viewpoints
Elevation: 1222 m]]>
+ #point-style + + 7.718290,46.425119,1222.0 + +
+ + Bachübergang 84 + Bachübergang 84
Category: Viewpoints
Elevation: 1878 m]]>
+ #point-style + + 9.768725,45.455342,1878.0 + +
+ + Jochpass 85 + Jochpass 85
Category: Viewpoints
Elevation: 848 m]]>
+ #point-style + + 10.850619,48.574419,848.0 + +
+ + Jochpass 86 + Jochpass 86
Category: Viewpoints
Elevation: 3393 m]]>
+ #point-style + + 7.745087,47.317042,3392.5 + +
+ + Gipfelkreuz 87 + Gipfelkreuz 87
Category: Viewpoints
Elevation: 411 m]]>
+ #point-style + + 9.222515,47.119017,410.9 + +
+ + Aussichtspunkt 88 + Aussichtspunkt 88
Category: Viewpoints
Elevation: 3721 m]]>
+ #point-style + + 11.247777,46.811912,3720.6 + +
+ + Bergseehütte 89 + Bergseehütte 89
Category: Viewpoints
Elevation: 1878 m]]>
+ #point-style + + 8.433266,45.462545,1877.9 + +
+ + Quellfassung 90 + Quellfassung 90
Category: Viewpoints
Elevation: 2107 m]]>
+ #point-style + + 7.863976,48.467711,2106.7 + +
+ + Wanderheim 91 + Wanderheim 91
Category: Viewpoints
Elevation: 2833 m]]>
+ #point-style + + 9.438560,47.734538,2832.6 + +
+ + Alpenrose 92 + Alpenrose 92
Category: Viewpoints
Elevation: 3122 m]]>
+ #point-style + + 8.306704,46.545347,3122.2 + +
+ + Wegkreuz 93 + Wegkreuz 93
Category: Viewpoints
Elevation: 986 m]]>
+ #point-style + + 11.089653,46.701670,986.1 + +
+ + Panoramapunkt 94 + Panoramapunkt 94
Category: Viewpoints
Elevation: 3123 m]]>
+ #point-style + + 9.563304,45.471372,3122.5 + +
+ + Refugio 95 + Refugio 95
Category: Viewpoints
Elevation: 1171 m]]>
+ #point-style + + 8.462020,45.108995,1171.0 + +
+ + Rastplatz 96 + Rastplatz 96
Category: Viewpoints
Elevation: 1222 m]]>
+ #point-style + + 9.745610,46.381721,1221.5 + +
+ + Bergstation 97 + Bergstation 97
Category: Viewpoints
Elevation: 1043 m]]>
+ #point-style + + 8.836756,47.868475,1042.9 + +
+ + Gipfelkreuz 98 + Gipfelkreuz 98
Category: Viewpoints
Elevation: 1472 m]]>
+ #point-style + + 9.247031,48.573220,1471.6 + +
+ + Wegkreuz 99 + Wegkreuz 99
Category: Viewpoints
Elevation: 3461 m]]>
+ #point-style + + 8.240470,47.452163,3460.8 + +
+ + Parkplatz 100 + Parkplatz 100
Category: Viewpoints
Elevation: 1067 m]]>
+ #point-style + + 9.621258,44.840546,1067.2 + +
+ + Almhütte 101 + Almhütte 101
Category: Viewpoints
Elevation: 884 m]]>
+ #point-style + + 8.878276,45.256305,883.8 + +
+ + Bergstation 102 + Bergstation 102
Category: Viewpoints
Elevation: 590 m]]>
+ #point-style + + 8.292455,46.923425,589.6 + +
+ + Kapelle 103 + Kapelle 103
Category: Viewpoints
Elevation: 666 m]]>
+ #point-style + + 8.064545,46.219797,666.3 + +
+ + Quellfassung 104 + Quellfassung 104
Category: Viewpoints
Elevation: 3425 m]]>
+ #point-style + + 11.265451,44.872238,3425.5 + +
+ + Jochpass 105 + Jochpass 105
Category: Viewpoints
Elevation: 473 m]]>
+ #point-style + + 10.619189,46.953259,473.2 + +
+
+ + + Parking + + Aussichtspunkt 106 + Aussichtspunkt 106
Category: Parking
Elevation: 1270 m]]>
+ #point-style + + 8.874241,45.647511,1270.1 + +
+ + Aussichtspunkt 107 + Aussichtspunkt 107
Category: Parking
Elevation: 2635 m]]>
+ #point-style + + 9.236222,47.380041,2635.5 + +
+ + Refugio 108 + Refugio 108
Category: Parking
Elevation: 1657 m]]>
+ #point-style + + 11.390637,47.567501,1657.2 + +
+ + Wegkreuz 109 + Wegkreuz 109
Category: Parking
Elevation: 2301 m]]>
+ #point-style + + 9.003384,47.033396,2301.3 + +
+ + Hochlager 110 + Hochlager 110
Category: Parking
Elevation: 797 m]]>
+ #point-style + + 8.879489,46.108323,796.7 + +
+ + Rastplatz 111 + Rastplatz 111
Category: Parking
Elevation: 1147 m]]>
+ #point-style + + 11.221285,45.773900,1147.0 + +
+ + Gatterl 112 + Gatterl 112
Category: Parking
Elevation: 1443 m]]>
+ #point-style + + 8.583925,47.283676,1443.2 + +
+ + Bergstation 113 + Bergstation 113
Category: Parking
Elevation: 1912 m]]>
+ #point-style + + 9.269060,45.772417,1912.0 + +
+ + Wanderheim 114 + Wanderheim 114
Category: Parking
Elevation: 3041 m]]>
+ #point-style + + 8.547695,47.207694,3040.8 + +
+ + Rifugio 115 + Rifugio 115
Category: Parking
Elevation: 3000 m]]>
+ #point-style + + 8.629194,47.750830,2999.5 + +
+ + Quellfassung 116 + Quellfassung 116
Category: Parking
Elevation: 1491 m]]>
+ #point-style + + 11.217925,46.503276,1491.1 + +
+ + Wanderheim 117 + Wanderheim 117
Category: Parking
Elevation: 3363 m]]>
+ #point-style + + 9.638341,47.219891,3363.2 + +
+ + Bachübergang 118 + Bachübergang 118
Category: Parking
Elevation: 1109 m]]>
+ #point-style + + 9.093505,48.464544,1108.6 + +
+ + Gatterl 119 + Gatterl 119
Category: Parking
Elevation: 2066 m]]>
+ #point-style + + 9.315084,48.501129,2066.3 + +
+ + Alpenrose 120 + Alpenrose 120
Category: Parking
Elevation: 2315 m]]>
+ #point-style + + 7.579350,45.140481,2314.6 + +
+ + Bachübergang 121 + Bachübergang 121
Category: Parking
Elevation: 3403 m]]>
+ #point-style + + 8.269490,48.249804,3402.9 + +
+ + Talstation 122 + Talstation 122
Category: Parking
Elevation: 3118 m]]>
+ #point-style + + 7.578365,47.114937,3117.8 + +
+ + Refugio 123 + Refugio 123
Category: Parking
Elevation: 764 m]]>
+ #point-style + + 10.060660,45.614711,763.5 + +
+ + Alpenrose 124 + Alpenrose 124
Category: Parking
Elevation: 1023 m]]>
+ #point-style + + 11.015059,48.121423,1022.7 + +
+ + Refugio 125 + Refugio 125
Category: Parking
Elevation: 3794 m]]>
+ #point-style + + 10.262872,48.075812,3794.3 + +
+ + Kapelle 126 + Kapelle 126
Category: Parking
Elevation: 2956 m]]>
+ #point-style + + 10.867613,45.218733,2956.0 + +
+ + Almhütte 127 + Almhütte 127
Category: Parking
Elevation: 3625 m]]>
+ #point-style + + 7.810751,47.327241,3625.2 + +
+ + Wegkreuz 128 + Wegkreuz 128
Category: Parking
Elevation: 938 m]]>
+ #point-style + + 11.446770,45.800764,938.2 + +
+ + Aussichtspunkt 129 + Aussichtspunkt 129
Category: Parking
Elevation: 2457 m]]>
+ #point-style + + 7.640505,47.710440,2456.7 + +
+ + Gatterl 130 + Gatterl 130
Category: Parking
Elevation: 3421 m]]>
+ #point-style + + 9.064628,45.803250,3420.6 + +
+
+ + + Water + + Hochlager 131 + Hochlager 131
Category: Water
Elevation: 3330 m]]>
+ #point-style + + 10.884162,46.737393,3330.2 + +
+ + Jochpass 132 + Jochpass 132
Category: Water
Elevation: 2100 m]]>
+ #point-style + + 9.291507,46.026528,2100.4 + +
+ + Alpenrose 133 + Alpenrose 133
Category: Water
Elevation: 1519 m]]>
+ #point-style + + 8.096067,46.637788,1519.0 + +
+ + Hochlager 134 + Hochlager 134
Category: Water
Elevation: 1607 m]]>
+ #point-style + + 8.419814,48.007898,1607.2 + +
+ + Bachübergang 135 + Bachübergang 135
Category: Water
Elevation: 3057 m]]>
+ #point-style + + 9.723142,45.082288,3056.6 + +
+ + Bachübergang 136 + Bachübergang 136
Category: Water
Elevation: 2894 m]]>
+ #point-style + + 10.399673,44.801342,2894.3 + +
+ + Rifugio 137 + Rifugio 137
Category: Water
Elevation: 701 m]]>
+ #point-style + + 7.542175,47.973726,700.6 + +
+ + Aussichtspunkt 138 + Aussichtspunkt 138
Category: Water
Elevation: 3356 m]]>
+ #point-style + + 7.519174,45.226631,3355.8 + +
+ + Wegkreuz 139 + Wegkreuz 139
Category: Water
Elevation: 789 m]]>
+ #point-style + + 8.694876,47.906736,789.4 + +
+ + Talstation 140 + Talstation 140
Category: Water
Elevation: 2287 m]]>
+ #point-style + + 8.192932,47.632296,2286.9 + +
+ + Talstation 141 + Talstation 141
Category: Water
Elevation: 485 m]]>
+ #point-style + + 7.946238,45.155990,485.5 + +
+ + Wegkreuz 142 + Wegkreuz 142
Category: Water
Elevation: 546 m]]>
+ #point-style + + 8.839112,45.991085,545.6 + +
+ + Alpenrose 143 + Alpenrose 143
Category: Water
Elevation: 1142 m]]>
+ #point-style + + 9.966177,46.718084,1142.0 + +
+ + Quellfassung 144 + Quellfassung 144
Category: Water
Elevation: 3503 m]]>
+ #point-style + + 8.800350,44.972067,3503.1 + +
+ + Gatterl 145 + Gatterl 145
Category: Water
Elevation: 2535 m]]>
+ #point-style + + 10.743864,48.203283,2535.4 + +
+ + Parkplatz 146 + Parkplatz 146
Category: Water
Elevation: 956 m]]>
+ #point-style + + 9.404820,46.498623,956.2 + +
+ + Panoramapunkt 147 + Panoramapunkt 147
Category: Water
Elevation: 3273 m]]>
+ #point-style + + 10.413954,47.029532,3272.8 + +
+ + Alpenrose 148 + Alpenrose 148
Category: Water
Elevation: 2418 m]]>
+ #point-style + + 7.565661,46.027050,2417.9 + +
+ + Panoramapunkt 149 + Panoramapunkt 149
Category: Water
Elevation: 2215 m]]>
+ #point-style + + 7.683169,45.313250,2215.4 + +
+ + Wanderheim 150 + Wanderheim 150
Category: Water
Elevation: 2282 m]]>
+ #point-style + + 8.951694,45.971570,2282.1 + +
+
+ + + Paths + + Alpine Traverse + Hiking path with 110 coordinate points + #line-style + + 1 + 10.004663,46.187127,1057.0 + 10.005033,46.186829,1733.9 + 10.007212,46.189300,1680.6 + 10.010901,46.190866,1351.0 + 10.014828,46.193540,1620.0 + 10.017552,46.192792,3265.3 + 10.018806,46.193263,1287.0 + 10.021462,46.192760,1439.0 + 10.023094,46.195493,973.9 + 10.027020,46.197161,976.0 + 10.029666,46.200041,2826.4 + 10.031858,46.202823,2151.6 + 10.033503,46.202393,2202.0 + 10.035240,46.202453,1713.3 + 10.036623,46.204804,3199.1 + 10.039228,46.204718,2049.4 + 10.042346,46.204924,3467.6 + 10.045591,46.204431,802.7 + 10.049418,46.204851,3398.9 + 10.050840,46.207559,2747.0 + 10.051098,46.210468,3001.8 + 10.054827,46.212365,1908.0 + 10.057875,46.213938,2040.2 + 10.058741,46.215811,729.5 + 10.062682,46.215692,1851.1 + 10.066564,46.218244,3258.6 + 10.067718,46.219441,2365.5 + 10.070607,46.221522,694.6 + 10.070737,46.220748,1322.6 + 10.072223,46.221322,2803.2 + 10.073360,46.223379,1611.0 + 10.076125,46.225943,692.5 + 10.079419,46.226262,2155.2 + 10.081079,46.226065,2562.3 + 10.082095,46.225775,3038.4 + 10.083745,46.226332,3469.9 + 10.084958,46.229164,600.3 + 10.088860,46.228852,2636.8 + 10.092477,46.228779,3143.5 + 10.092548,46.230628,2132.8 + 10.093572,46.230517,1740.9 + 10.097108,46.231122,1686.2 + 10.097732,46.230785,1944.6 + 10.101089,46.230896,2426.5 + 10.101798,46.232822,2243.7 + 10.101991,46.234585,1756.9 + 10.102605,46.236257,3375.6 + 10.103076,46.236105,2594.2 + 10.106168,46.239047,2142.7 + 10.107030,46.239361,786.7 + 10.109545,46.239256,1317.4 + 10.110172,46.242210,1309.0 + 10.113987,46.242840,1589.3 + 10.116886,46.242578,849.9 + 10.117747,46.243461,3430.2 + 10.119770,46.244212,3372.7 + 10.123740,46.245822,1843.1 + 10.125478,46.248311,1403.3 + 10.127970,46.251154,1015.8 + 10.130735,46.252429,993.9 + 10.132510,46.253363,1131.5 + 10.134459,46.255109,2327.8 + 10.136918,46.256252,2859.1 + 10.140778,46.257188,3216.7 + 10.141263,46.258006,2020.5 + 10.144818,46.258530,1511.7 + 10.145599,46.257769,3145.8 + 10.149238,46.257256,1846.0 + 10.152256,46.258420,3115.7 + 10.154611,46.257872,1147.5 + 10.157697,46.260691,1336.2 + 10.161600,46.261074,3378.7 + 10.161756,46.263142,1237.2 + 10.162583,46.264556,2099.5 + 10.164428,46.263581,1981.2 + 10.165815,46.265896,3210.4 + 10.165884,46.265872,1910.4 + 10.169705,46.265541,2983.9 + 10.170559,46.268285,3137.0 + 10.171688,46.270942,2963.3 + 10.172433,46.271417,2561.8 + 10.173639,46.271754,2469.3 + 10.173697,46.273347,683.2 + 10.174058,46.274491,913.5 + 10.177866,46.277229,2664.0 + 10.180626,46.279924,3286.4 + 10.182117,46.278938,1659.6 + 10.183195,46.279294,1527.7 + 10.186993,46.281269,3274.0 + 10.189405,46.281830,1269.3 + 10.193051,46.282064,2161.7 + 10.194978,46.281416,2888.1 + 10.195424,46.283139,1727.4 + 10.196307,46.283422,1312.2 + 10.198521,46.285773,2928.7 + 10.201889,46.286360,621.0 + 10.202652,46.286725,1378.9 + 10.203367,46.288665,3146.0 + 10.206869,46.289982,666.4 + 10.207001,46.289229,3462.5 + 10.207240,46.290107,1434.1 + 10.208276,46.290522,1513.6 + 10.209269,46.290479,1851.6 + 10.211662,46.289931,2940.9 + 10.214210,46.289635,2588.9 + 10.215748,46.289902,1005.2 + 10.219661,46.291366,2852.3 + 10.221374,46.291225,2077.8 + 10.225227,46.291348,3146.6 + 10.226134,46.292133,2782.7 + + + + Ridge Walk + Hiking path with 106 coordinate points + #line-style + + 1 + 8.657816,45.412623,2736.5 + 8.660630,45.412025,710.5 + 8.661421,45.411150,3099.1 + 8.661704,45.411900,1092.7 + 8.662197,45.411895,799.7 + 8.665639,45.410971,1380.5 + 8.667124,45.413467,1686.5 + 8.669523,45.414248,1842.2 + 8.671573,45.415124,1529.9 + 8.672269,45.414429,1501.9 + 8.674664,45.416712,2449.6 + 8.674842,45.418398,2199.2 + 8.675784,45.420394,1954.8 + 8.677998,45.420473,2760.7 + 8.678652,45.419968,2929.7 + 8.679383,45.422137,978.1 + 8.682756,45.423642,2305.1 + 8.683815,45.426443,2871.6 + 8.685885,45.426278,3294.4 + 8.688353,45.425350,2567.2 + 8.689437,45.426165,2274.3 + 8.691202,45.426180,2222.9 + 8.693061,45.428175,1598.4 + 8.694974,45.431089,1310.8 + 8.698446,45.432136,3148.6 + 8.702056,45.431409,801.8 + 8.705591,45.432819,2131.6 + 8.707268,45.433912,2512.6 + 8.708396,45.436108,1138.8 + 8.712017,45.436553,751.5 + 8.715031,45.437955,2765.4 + 8.718828,45.438400,2427.5 + 8.721104,45.439329,1943.2 + 8.722594,45.439002,1303.3 + 8.723706,45.440675,2372.4 + 8.726632,45.442047,1999.1 + 8.728535,45.443336,2479.3 + 8.731989,45.444173,1101.8 + 8.733206,45.446874,684.8 + 8.733276,45.447711,1657.2 + 8.734792,45.449794,969.6 + 8.737038,45.450941,1003.2 + 8.740421,45.452986,2356.5 + 8.741200,45.454134,3000.1 + 8.741612,45.456310,903.4 + 8.742832,45.458829,2066.5 + 8.745939,45.461506,3144.2 + 8.749917,45.463474,2375.9 + 8.753361,45.464294,2330.0 + 8.754657,45.464271,2600.3 + 8.758463,45.463370,2891.6 + 8.761288,45.465357,3200.0 + 8.762204,45.465192,1983.6 + 8.763077,45.464369,2974.9 + 8.764099,45.465218,3069.7 + 8.764924,45.464446,851.4 + 8.767056,45.464973,2598.9 + 8.769332,45.464559,3186.6 + 8.769666,45.466798,798.7 + 8.771618,45.467123,1272.9 + 8.771931,45.468142,1758.3 + 8.773824,45.469192,3104.1 + 8.775145,45.471366,1852.1 + 8.776449,45.472723,1095.4 + 8.777372,45.472266,1783.3 + 8.780901,45.473194,2613.7 + 8.783384,45.473596,733.6 + 8.786096,45.474559,662.3 + 8.787803,45.476271,765.7 + 8.788176,45.476146,2311.8 + 8.790096,45.477653,1143.9 + 8.792227,45.476956,1726.4 + 8.793125,45.476518,1710.5 + 8.796113,45.478875,1635.7 + 8.799184,45.478129,2436.2 + 8.799976,45.477257,2211.6 + 8.803487,45.479881,989.0 + 8.805634,45.482465,1165.3 + 8.806621,45.482922,1457.7 + 8.809772,45.483400,3281.2 + 8.810401,45.485103,3499.0 + 8.814356,45.484399,2289.1 + 8.817985,45.483575,2755.8 + 8.819145,45.484579,704.8 + 8.819326,45.486810,1139.8 + 8.823009,45.489254,1692.2 + 8.825695,45.488400,2258.0 + 8.828057,45.489677,1749.5 + 8.831539,45.489483,2046.0 + 8.832194,45.489098,2860.9 + 8.834086,45.489419,918.9 + 8.836487,45.492415,2735.7 + 8.837069,45.493910,2778.8 + 8.837985,45.493527,1255.1 + 8.840872,45.496114,2456.2 + 8.841989,45.495199,1960.6 + 8.843759,45.494994,3093.4 + 8.845824,45.495236,3217.8 + 8.849065,45.497954,2554.1 + 8.852312,45.497862,1523.8 + 8.855069,45.496862,1852.1 + 8.857154,45.495895,2869.4 + 8.858671,45.498463,3182.9 + 8.859588,45.498948,2690.5 + 8.862430,45.499328,2389.0 + 8.862863,45.498893,711.4 + + + + Valley Loop + Hiking path with 85 coordinate points + #line-style + + 1 + 8.984522,46.505581,1029.8 + 8.987147,46.504893,1430.7 + 8.989606,46.506021,1867.4 + 8.991625,46.505443,3220.0 + 8.992245,46.506293,994.3 + 8.994992,46.509153,1544.8 + 8.998527,46.510190,1846.7 + 9.001859,46.510043,2308.5 + 9.002607,46.511006,2462.9 + 9.003685,46.512175,3498.3 + 9.006202,46.513115,2343.5 + 9.006361,46.512761,3140.6 + 9.008152,46.513139,1229.2 + 9.011133,46.514498,1712.6 + 9.011924,46.515281,2351.8 + 9.015077,46.515515,3115.0 + 9.017433,46.514518,1583.9 + 9.018393,46.514533,1482.5 + 9.018909,46.513895,749.6 + 9.022611,46.514202,1583.9 + 9.023338,46.516582,2666.9 + 9.024176,46.519396,2294.4 + 9.024189,46.521300,687.4 + 9.025224,46.522024,1676.1 + 9.028337,46.523170,1318.9 + 9.029432,46.525533,2535.2 + 9.029860,46.528090,3353.2 + 9.030188,46.527098,2366.0 + 9.033857,46.528521,2522.1 + 9.036019,46.528250,3405.2 + 9.038877,46.530537,1324.5 + 9.041201,46.531303,963.1 + 9.042994,46.531913,1710.6 + 9.044038,46.534004,2023.1 + 9.046445,46.536787,2108.2 + 9.049239,46.538711,2198.4 + 9.051555,46.539203,3282.7 + 9.054210,46.539876,1366.7 + 9.055412,46.540963,1484.2 + 9.056109,46.540776,880.2 + 9.059933,46.539976,2552.1 + 9.061727,46.539750,3041.4 + 9.062432,46.539240,879.8 + 9.064275,46.540087,755.3 + 9.066609,46.541335,2585.4 + 9.066747,46.542763,769.9 + 9.067674,46.543731,1491.3 + 9.070484,46.545474,1562.3 + 9.070964,46.547771,950.1 + 9.074522,46.547461,2207.5 + 9.074545,46.549090,2207.6 + 9.076576,46.551534,2666.4 + 9.077760,46.551914,2998.8 + 9.077863,46.553973,1582.7 + 9.078640,46.553433,1655.9 + 9.082291,46.553608,3179.1 + 9.083588,46.555109,1713.1 + 9.083934,46.557649,1769.7 + 9.086402,46.557027,2047.5 + 9.086821,46.558260,1628.6 + 9.090572,46.557999,1951.3 + 9.091313,46.557262,1970.7 + 9.093751,46.557545,3293.5 + 9.096099,46.558291,1355.9 + 9.098434,46.560726,3468.2 + 9.102103,46.561698,1090.9 + 9.104254,46.564292,940.1 + 9.105491,46.565679,600.4 + 9.105517,46.564948,1833.5 + 9.107901,46.567255,2508.7 + 9.110431,46.566749,1875.1 + 9.112240,46.566248,3025.2 + 9.115392,46.567929,602.3 + 9.119387,46.567970,741.0 + 9.120982,46.567670,790.4 + 9.124580,46.567544,2460.1 + 9.127571,46.569192,3129.4 + 9.128593,46.568675,649.0 + 9.131272,46.569019,1639.3 + 9.135263,46.570440,1895.8 + 9.137449,46.571790,1923.7 + 9.139019,46.571236,2934.6 + 9.142528,46.573218,3201.7 + 9.144095,46.575619,3455.1 + 9.145399,46.576017,1089.4 + + + + Glacier Path + Hiking path with 71 coordinate points + #line-style + + 1 + 9.170238,47.009927,2358.7 + 9.173026,47.012209,3045.8 + 9.175569,47.013697,2315.7 + 9.176182,47.016404,1475.4 + 9.177679,47.016098,893.0 + 9.179180,47.017864,1393.7 + 9.181378,47.017662,3269.4 + 9.184270,47.018600,3332.7 + 9.184888,47.018116,1478.5 + 9.186463,47.018413,2563.6 + 9.187970,47.018102,1728.0 + 9.189142,47.017561,2129.5 + 9.191889,47.018633,1306.8 + 9.191913,47.019240,1441.3 + 9.194618,47.021221,2342.2 + 9.196535,47.022640,2072.1 + 9.196611,47.025445,2915.2 + 9.197444,47.027616,3019.4 + 9.198174,47.027072,2071.6 + 9.201804,47.026103,2714.7 + 9.202563,47.026104,1227.2 + 9.205530,47.028196,1844.2 + 9.208372,47.030985,1144.4 + 9.210942,47.031370,1658.7 + 9.214245,47.032038,2598.5 + 9.217595,47.034675,988.4 + 9.218732,47.033766,2556.2 + 9.222282,47.035695,1341.8 + 9.224945,47.037033,3295.4 + 9.227146,47.039089,2707.1 + 9.228738,47.041274,2748.8 + 9.232131,47.040604,2139.8 + 9.233046,47.040181,2770.1 + 9.236809,47.042448,803.9 + 9.239069,47.042950,1633.3 + 9.242503,47.042398,1553.0 + 9.244291,47.041489,1592.2 + 9.245955,47.043407,1324.9 + 9.246400,47.044084,3042.0 + 9.246694,47.043361,2729.2 + 9.247988,47.044756,1523.9 + 9.250564,47.047621,3102.7 + 9.251939,47.050244,1046.2 + 9.251986,47.050214,1378.7 + 9.254637,47.051613,1077.0 + 9.256519,47.053228,2255.8 + 9.259099,47.053435,2471.6 + 9.259313,47.055495,887.8 + 9.262519,47.057562,3121.1 + 9.265775,47.060260,2301.1 + 9.267039,47.062842,2459.5 + 9.270350,47.064721,673.0 + 9.274148,47.067023,2648.8 + 9.275986,47.069067,1368.5 + 9.276730,47.069392,1499.4 + 9.280340,47.069884,1735.1 + 9.281995,47.072296,1996.8 + 9.284744,47.072554,3252.3 + 9.285427,47.073304,3332.3 + 9.286171,47.073765,704.7 + 9.288820,47.072899,2244.5 + 9.292108,47.075240,1770.7 + 9.292398,47.077435,1366.1 + 9.293448,47.076588,1020.3 + 9.297355,47.078958,1356.1 + 9.299680,47.078428,3040.3 + 9.301261,47.078626,629.0 + 9.304225,47.078150,2520.1 + 9.306187,47.080569,919.5 + 9.306222,47.079784,1131.7 + 9.307346,47.081532,2819.4 + + + + Forest Trail + Hiking path with 51 coordinate points + #line-style + + 1 + 10.645626,45.458383,1088.7 + 10.646495,45.460225,2597.2 + 10.648860,45.462359,1361.2 + 10.652630,45.464677,690.2 + 10.655757,45.466898,2925.4 + 10.656345,45.468003,1646.3 + 10.659023,45.469755,869.6 + 10.659485,45.468871,2872.9 + 10.659821,45.467973,876.7 + 10.660235,45.467169,2931.4 + 10.663494,45.470099,1240.5 + 10.664107,45.470488,3177.9 + 10.665009,45.470671,1111.4 + 10.667551,45.472483,3404.5 + 10.669436,45.474905,2520.1 + 10.672696,45.474010,949.6 + 10.674926,45.476286,3184.9 + 10.676307,45.478313,1852.8 + 10.677061,45.480167,610.8 + 10.677423,45.482989,2993.6 + 10.679517,45.482703,2517.1 + 10.680716,45.483171,2280.2 + 10.680739,45.484833,1242.5 + 10.683109,45.486439,2559.3 + 10.686840,45.486378,2346.2 + 10.687620,45.485781,3104.4 + 10.689640,45.486685,1566.9 + 10.690676,45.487664,3469.4 + 10.693383,45.488851,1340.3 + 10.696884,45.488705,1336.3 + 10.698609,45.490145,2021.3 + 10.702477,45.490753,625.0 + 10.703400,45.490259,2206.3 + 10.705440,45.491988,1338.3 + 10.708970,45.494549,3372.6 + 10.712430,45.497254,2665.8 + 10.713208,45.500050,1021.4 + 10.717000,45.502362,2154.7 + 10.719490,45.503553,2601.4 + 10.719958,45.504356,1101.9 + 10.720387,45.505131,776.9 + 10.724169,45.506532,1777.8 + 10.726378,45.508707,1868.8 + 10.730369,45.510004,944.7 + 10.731454,45.511265,2319.2 + 10.732760,45.513288,1336.2 + 10.732851,45.513680,2490.0 + 10.733431,45.513525,1314.0 + 10.736972,45.514837,3146.1 + 10.738251,45.515225,3376.2 + 10.738999,45.517775,961.2 + + + + Summit Route + Hiking path with 92 coordinate points + #line-style + + 1 + 10.615780,47.650951,1832.5 + 10.616865,47.650852,1175.3 + 10.618972,47.651031,2318.7 + 10.619446,47.650736,2928.3 + 10.619901,47.651960,1767.5 + 10.622634,47.651304,853.6 + 10.624501,47.650612,2811.2 + 10.627950,47.651719,2647.8 + 10.631105,47.652790,2299.0 + 10.632562,47.653461,1833.8 + 10.634407,47.652868,2137.2 + 10.635284,47.655092,2352.9 + 10.638715,47.655505,1973.6 + 10.641547,47.657126,1448.9 + 10.644631,47.659677,2000.0 + 10.644773,47.662317,1377.0 + 10.647793,47.661769,3252.6 + 10.650655,47.663281,1721.8 + 10.651480,47.662694,1779.6 + 10.655161,47.665178,1349.3 + 10.657402,47.666716,1965.9 + 10.658030,47.668883,2114.0 + 10.659088,47.668727,2446.2 + 10.660395,47.670605,2002.6 + 10.662214,47.670674,1048.9 + 10.665763,47.671664,2258.3 + 10.666843,47.674126,1857.5 + 10.670397,47.676764,2015.2 + 10.670827,47.677903,1035.6 + 10.673749,47.678870,2183.9 + 10.677652,47.679044,2396.4 + 10.679629,47.679696,1174.0 + 10.681871,47.681640,736.8 + 10.681961,47.680671,900.7 + 10.683606,47.683050,2363.6 + 10.683878,47.684659,853.9 + 10.686267,47.683913,2383.5 + 10.689815,47.684036,2845.7 + 10.691149,47.686391,2189.8 + 10.692817,47.688090,1505.6 + 10.693027,47.687250,2226.3 + 10.695319,47.688994,3306.3 + 10.697823,47.690817,3037.0 + 10.698636,47.692219,627.8 + 10.701096,47.694078,3440.0 + 10.702770,47.693971,875.2 + 10.706485,47.695260,1604.4 + 10.709120,47.694726,2798.4 + 10.709481,47.694307,1661.4 + 10.711813,47.696524,2438.1 + 10.714826,47.698330,3090.5 + 10.716943,47.698760,2345.1 + 10.720062,47.699562,3228.8 + 10.724016,47.699751,2594.5 + 10.726759,47.699314,3095.5 + 10.726981,47.698394,2184.9 + 10.728976,47.698560,2887.1 + 10.731591,47.700221,3138.0 + 10.731865,47.699604,2137.5 + 10.735677,47.701022,1143.8 + 10.736149,47.703347,3359.9 + 10.739592,47.705047,2975.6 + 10.742436,47.707322,2891.0 + 10.744694,47.708041,2794.5 + 10.746918,47.709893,2896.4 + 10.747119,47.712627,2669.9 + 10.747935,47.715082,1803.7 + 10.751143,47.714472,1502.8 + 10.754731,47.716784,1235.6 + 10.755826,47.718395,867.4 + 10.756208,47.718079,1133.7 + 10.756277,47.719396,3285.2 + 10.760177,47.718685,2913.4 + 10.764112,47.719980,2505.0 + 10.765725,47.721429,1358.2 + 10.768849,47.720512,3212.8 + 10.771294,47.722044,796.3 + 10.773173,47.722885,3402.8 + 10.776415,47.722830,3005.5 + 10.779141,47.725475,1914.5 + 10.782855,47.724728,2935.8 + 10.785588,47.726473,2795.3 + 10.788069,47.728772,1610.2 + 10.788339,47.728659,2122.1 + 10.789046,47.730103,1653.2 + 10.790211,47.732580,2501.3 + 10.792064,47.731777,2220.3 + 10.795211,47.734035,621.0 + 10.798964,47.733135,2821.4 + 10.801623,47.732774,2892.3 + 10.805442,47.734083,789.7 + 10.806017,47.735581,1107.4 + + + + Lakeside Walk + Hiking path with 106 coordinate points + #line-style + + 1 + 9.332979,47.075471,1180.9 + 9.333148,47.075415,1365.8 + 9.335787,47.077675,3103.6 + 9.338500,47.077730,3049.9 + 9.341522,47.079875,1543.7 + 9.343050,47.079512,800.4 + 9.345340,47.079042,843.8 + 9.348586,47.079352,1975.9 + 9.351903,47.078521,2800.1 + 9.354432,47.078421,2156.9 + 9.355427,47.078131,1968.7 + 9.359263,47.077909,2379.3 + 9.362401,47.077680,929.9 + 9.362654,47.080225,2449.6 + 9.364305,47.080744,640.0 + 9.364736,47.082520,2069.2 + 9.366861,47.082854,2092.5 + 9.368531,47.085853,2318.2 + 9.370542,47.087545,3073.5 + 9.373077,47.086638,3399.2 + 9.374336,47.088216,2678.1 + 9.377388,47.091008,1421.6 + 9.377463,47.093405,3324.4 + 9.381009,47.093275,2727.2 + 9.384080,47.094734,1684.4 + 9.387518,47.093902,2698.2 + 9.390657,47.092932,2178.3 + 9.391296,47.095756,626.0 + 9.394218,47.096241,3349.6 + 9.397960,47.097229,1817.2 + 9.401619,47.096277,1500.8 + 9.404699,47.096138,2912.9 + 9.405986,47.096435,944.5 + 9.409806,47.098639,2673.6 + 9.412588,47.100228,2179.5 + 9.413344,47.100034,1365.9 + 9.415252,47.100733,2105.4 + 9.416061,47.100457,2267.9 + 9.419813,47.101915,2360.9 + 9.422973,47.102831,3140.8 + 9.424889,47.101843,1172.8 + 9.425066,47.102433,2607.2 + 9.427918,47.101673,2674.9 + 9.430887,47.101608,1472.0 + 9.434137,47.102676,3114.7 + 9.437352,47.102588,1059.3 + 9.438216,47.102219,1585.0 + 9.439389,47.102730,3062.6 + 9.441312,47.103175,3468.3 + 9.444873,47.104265,660.7 + 9.446858,47.107158,1757.0 + 9.449455,47.108932,3108.0 + 9.452401,47.108195,1408.3 + 9.455377,47.108598,2329.7 + 9.457105,47.108864,1446.7 + 9.458916,47.109076,1266.5 + 9.458992,47.109552,3106.9 + 9.459223,47.109442,1752.7 + 9.461174,47.112072,1692.8 + 9.461681,47.112186,3224.9 + 9.462299,47.113096,3408.7 + 9.464752,47.116036,2854.4 + 9.468032,47.118587,965.4 + 9.468480,47.119989,2362.5 + 9.470630,47.121565,2145.0 + 9.471863,47.121768,1826.6 + 9.471912,47.120976,617.8 + 9.474837,47.121254,2337.5 + 9.477616,47.123587,3129.4 + 9.480016,47.124276,1562.1 + 9.483502,47.124648,2137.0 + 9.483940,47.126942,3467.6 + 9.487898,47.129408,1052.3 + 9.490800,47.131411,3353.9 + 9.494296,47.134188,913.2 + 9.497414,47.133597,2891.6 + 9.500192,47.132950,1070.0 + 9.502949,47.135650,1761.5 + 9.504934,47.137919,1046.1 + 9.508027,47.138554,937.6 + 9.510101,47.139620,2353.4 + 9.514066,47.140597,2280.9 + 9.515912,47.142781,3070.2 + 9.516745,47.141985,2763.8 + 9.518984,47.141168,1884.7 + 9.521196,47.142685,2860.8 + 9.524799,47.143666,2078.6 + 9.525277,47.143485,903.1 + 9.525285,47.143426,2352.0 + 9.526541,47.144734,2729.0 + 9.528589,47.144021,2165.2 + 9.529925,47.144197,1020.9 + 9.532622,47.145276,2274.5 + 9.534154,47.146237,2964.2 + 9.536192,47.145273,3008.1 + 9.538992,47.144899,2976.7 + 9.539027,47.143974,1956.4 + 9.539454,47.146815,2679.3 + 9.541233,47.147001,661.4 + 9.542771,47.149360,1094.1 + 9.545538,47.148512,2512.1 + 9.545674,47.151045,1174.1 + 9.548584,47.152902,1149.2 + 9.549860,47.153126,1393.1 + 9.552722,47.152423,3346.9 + 9.555983,47.153433,3072.6 + + + + Gorge Trail + Hiking path with 116 coordinate points + #line-style + + 1 + 9.452128,48.040532,1207.4 + 9.455980,48.040947,1093.0 + 9.457517,48.042813,1442.8 + 9.459704,48.043137,2479.1 + 9.462415,48.043008,2897.4 + 9.463831,48.044321,854.0 + 9.466721,48.043554,1054.9 + 9.467410,48.042722,1395.3 + 9.469052,48.044510,2103.4 + 9.469096,48.046872,2080.9 + 9.472320,48.047202,3320.2 + 9.475230,48.049139,3434.6 + 9.479158,48.049896,1514.1 + 9.481744,48.052441,1888.6 + 9.481947,48.051815,2920.0 + 9.482514,48.052569,2658.9 + 9.485262,48.053844,804.4 + 9.486165,48.055661,2251.8 + 9.489499,48.056668,3257.1 + 9.490938,48.057127,2730.7 + 9.493236,48.057371,2770.2 + 9.496567,48.057751,3371.6 + 9.499079,48.058880,1258.7 + 9.502464,48.059861,1065.5 + 9.506361,48.060731,754.0 + 9.509503,48.063228,1463.0 + 9.512899,48.064150,2152.0 + 9.513774,48.066023,702.5 + 9.516318,48.065511,1854.9 + 9.519118,48.066584,1920.3 + 9.522116,48.066934,2827.4 + 9.522202,48.066962,3282.0 + 9.523988,48.069356,3146.2 + 9.526746,48.070300,2123.9 + 9.526948,48.070624,2834.0 + 9.527936,48.071898,1224.5 + 9.528792,48.072477,3333.6 + 9.529386,48.072509,3050.6 + 9.533377,48.074470,936.5 + 9.536291,48.075287,2319.0 + 9.538896,48.074776,1826.3 + 9.541401,48.077611,944.3 + 9.542386,48.076866,2543.6 + 9.544421,48.077115,1293.8 + 9.546326,48.077343,1921.9 + 9.546757,48.079278,768.4 + 9.547615,48.081573,1566.1 + 9.550190,48.080891,3383.7 + 9.553795,48.082287,1550.1 + 9.555248,48.082926,2992.1 + 9.556869,48.083808,1432.6 + 9.557912,48.085183,1049.8 + 9.558998,48.086821,1771.1 + 9.562121,48.087220,907.6 + 9.566053,48.089409,2638.5 + 9.566719,48.090202,2776.1 + 9.570016,48.092074,960.7 + 9.572697,48.093127,1364.7 + 9.575315,48.094976,2785.8 + 9.577799,48.095912,2090.4 + 9.578483,48.096875,2715.3 + 9.579233,48.099584,2736.0 + 9.581665,48.102037,1021.8 + 9.584617,48.101258,2885.4 + 9.587291,48.102247,2692.0 + 9.588153,48.103328,3274.8 + 9.590618,48.105553,2995.0 + 9.594087,48.106881,1598.1 + 9.597220,48.106468,1260.6 + 9.600115,48.109352,996.4 + 9.604095,48.112222,1426.4 + 9.605923,48.111927,2107.0 + 9.609348,48.114391,2692.3 + 9.611101,48.114239,1367.7 + 9.611216,48.115635,862.2 + 9.612484,48.116185,1545.5 + 9.613980,48.118055,1495.5 + 9.614404,48.118836,899.7 + 9.615093,48.118219,1366.6 + 9.618657,48.119532,1967.1 + 9.621748,48.121612,2106.6 + 9.623014,48.123431,2286.7 + 9.625870,48.123909,2413.6 + 9.627001,48.125247,3005.7 + 9.628040,48.125307,1242.0 + 9.631552,48.124445,2233.3 + 9.632554,48.124316,1122.6 + 9.633337,48.123453,3092.8 + 9.633373,48.126340,1850.2 + 9.635462,48.129175,3340.5 + 9.635737,48.128728,1360.3 + 9.636282,48.129261,1985.0 + 9.638378,48.131183,630.0 + 9.638833,48.133843,720.6 + 9.640916,48.134239,3441.4 + 9.642556,48.136392,1872.4 + 9.643417,48.138335,3307.4 + 9.645238,48.139101,1820.1 + 9.645687,48.138154,2505.0 + 9.648050,48.140128,2395.2 + 9.648106,48.139877,3298.4 + 9.648956,48.139924,1334.9 + 9.651925,48.139857,3233.5 + 9.652838,48.141900,2185.6 + 9.652853,48.143288,1472.3 + 9.656067,48.144873,3205.9 + 9.656943,48.146951,2066.4 + 9.660651,48.148804,2802.2 + 9.664107,48.150835,1091.4 + 9.665480,48.150952,3097.7 + 9.668119,48.152909,1098.1 + 9.671976,48.153975,1816.9 + 9.675971,48.155777,2947.0 + 9.679551,48.154983,2296.7 + 9.681670,48.155546,2765.2 + 9.682505,48.154906,2830.7 + + + + High Route + Hiking path with 118 coordinate points + #line-style + + 1 + 9.820172,47.990937,2539.5 + 9.821070,47.993370,1397.5 + 9.823127,47.995513,1657.3 + 9.826170,47.995532,2653.2 + 9.827448,47.997792,1306.3 + 9.827688,48.000110,1417.6 + 9.827928,48.002346,3382.3 + 9.829958,48.004596,1310.1 + 9.832870,48.007578,1651.9 + 9.833755,48.010042,2325.1 + 9.837390,48.009446,3217.6 + 9.838044,48.010997,3453.6 + 9.840026,48.011849,2908.1 + 9.842729,48.014529,2519.6 + 9.845638,48.015664,2191.4 + 9.846299,48.017473,2313.8 + 9.848645,48.019829,2142.9 + 9.849385,48.019891,796.0 + 9.851829,48.021203,1899.0 + 9.852524,48.022782,1615.8 + 9.855974,48.022392,2004.3 + 9.858604,48.022007,3436.4 + 9.859704,48.021693,1360.4 + 9.859749,48.021575,2870.6 + 9.861692,48.023086,2224.2 + 9.864523,48.023482,2109.3 + 9.867990,48.026198,3219.6 + 9.871062,48.026716,675.7 + 9.873738,48.026097,2884.0 + 9.877249,48.026382,2413.6 + 9.880141,48.026258,1098.7 + 9.883280,48.027827,2332.7 + 9.883693,48.027503,1780.4 + 9.885257,48.027530,2703.1 + 9.886692,48.027299,2178.1 + 9.889642,48.026355,898.5 + 9.889868,48.026554,1244.6 + 9.890191,48.025768,949.5 + 9.890426,48.025162,2706.0 + 9.893688,48.024940,1656.3 + 9.895094,48.026616,802.1 + 9.896344,48.026459,3219.2 + 9.900320,48.026436,2848.3 + 9.900367,48.028777,3094.5 + 9.901116,48.028585,833.1 + 9.903221,48.029904,1761.2 + 9.906011,48.029805,1411.4 + 9.906188,48.030682,1743.3 + 9.907405,48.033013,3071.1 + 9.909289,48.035800,3371.4 + 9.911816,48.037950,1139.4 + 9.915160,48.037977,2499.9 + 9.919114,48.037402,623.5 + 9.923099,48.038534,1338.4 + 9.923382,48.039490,2311.9 + 9.926801,48.041858,2511.0 + 9.930275,48.042103,3363.4 + 9.933411,48.042669,840.0 + 9.933933,48.043470,3480.2 + 9.936726,48.045487,765.5 + 9.940462,48.047767,997.0 + 9.943139,48.047752,2768.9 + 9.946177,48.048511,797.3 + 9.948772,48.048959,665.1 + 9.952048,48.048437,1442.9 + 9.955856,48.050337,1581.2 + 9.957006,48.051654,1053.2 + 9.958590,48.052644,1115.9 + 9.962516,48.053919,1542.3 + 9.964081,48.056654,3431.7 + 9.965661,48.059200,1399.1 + 9.965723,48.058740,967.3 + 9.968629,48.059566,1252.2 + 9.969293,48.060076,3199.0 + 9.972648,48.059111,2574.6 + 9.976433,48.058711,2445.1 + 9.979717,48.058264,2607.2 + 9.981518,48.060704,896.2 + 9.984965,48.061710,2512.0 + 9.986407,48.063858,3310.1 + 9.987542,48.066804,1110.4 + 9.988536,48.066096,2526.9 + 9.992028,48.066100,1731.8 + 9.993367,48.065740,3257.7 + 9.996933,48.067312,3307.5 + 9.999802,48.067044,2662.4 + 9.999930,48.066969,3230.0 + 10.003405,48.066783,1808.1 + 10.004308,48.065947,919.8 + 10.004769,48.068355,2389.6 + 10.005274,48.069928,2491.7 + 10.005841,48.070696,3470.2 + 10.008893,48.072975,1458.0 + 10.010633,48.075966,2362.8 + 10.013577,48.077187,3105.5 + 10.017150,48.078318,1337.6 + 10.018452,48.080317,1797.6 + 10.022435,48.079860,2113.9 + 10.023140,48.082546,1598.8 + 10.023633,48.085248,1448.5 + 10.025500,48.085909,723.1 + 10.026246,48.085742,2557.4 + 10.029646,48.084780,3425.6 + 10.033478,48.087577,1054.1 + 10.034447,48.089573,769.7 + 10.035304,48.089991,1527.0 + 10.036702,48.089262,2853.2 + 10.040188,48.092220,1524.8 + 10.041658,48.095201,3133.4 + 10.043347,48.094505,2523.3 + 10.046237,48.095585,3062.6 + 10.048600,48.095882,1690.4 + 10.049368,48.095476,3245.7 + 10.051416,48.098092,1477.9 + 10.052486,48.099253,1583.7 + 10.055123,48.099706,1525.5 + 10.055650,48.099679,1702.0 + 10.058960,48.099505,1710.7 + + + + Border Path + Hiking path with 56 coordinate points + #line-style + + 1 + 10.829134,46.028759,1409.5 + 10.830628,46.027782,892.3 + 10.831442,46.027875,2309.7 + 10.835438,46.028681,2081.1 + 10.838443,46.028598,3361.8 + 10.838734,46.028513,1702.7 + 10.840161,46.030434,2350.0 + 10.843182,46.031564,3269.7 + 10.844732,46.031330,1670.5 + 10.845910,46.031018,1350.3 + 10.849786,46.031765,2268.4 + 10.851533,46.032742,1092.8 + 10.855382,46.034447,2526.2 + 10.858730,46.035205,1234.1 + 10.859823,46.035620,3147.8 + 10.860760,46.035632,2305.2 + 10.863856,46.037785,1736.1 + 10.867713,46.037457,2312.8 + 10.868787,46.036523,1605.1 + 10.871738,46.036522,2498.0 + 10.872522,46.039364,1519.6 + 10.875470,46.038878,2813.9 + 10.876880,46.039592,2961.4 + 10.876925,46.039493,1177.2 + 10.877611,46.040147,2569.1 + 10.878405,46.041599,1421.5 + 10.881820,46.041724,2191.4 + 10.884457,46.042182,3332.1 + 10.885267,46.044027,1528.7 + 10.886995,46.044669,2734.9 + 10.888511,46.043959,3181.4 + 10.892002,46.046771,2963.4 + 10.894580,46.048532,1080.3 + 10.896860,46.050865,2550.6 + 10.897214,46.051223,1664.5 + 10.898058,46.051257,3393.1 + 10.901653,46.051738,3075.0 + 10.905370,46.051294,1018.6 + 10.906279,46.054127,3362.1 + 10.907410,46.056374,2413.8 + 10.909856,46.056183,1210.1 + 10.910882,46.055797,2936.1 + 10.913128,46.057370,1395.7 + 10.917032,46.059228,1907.2 + 10.919404,46.060041,2695.0 + 10.922285,46.061806,3115.7 + 10.926111,46.061193,983.8 + 10.929415,46.060595,2042.1 + 10.929967,46.061078,2923.5 + 10.930596,46.062177,1010.6 + 10.931356,46.061471,2885.4 + 10.932373,46.060940,1137.5 + 10.933310,46.061302,929.0 + 10.937210,46.061514,653.7 + 10.939747,46.061019,1826.9 + 10.941208,46.061083,1614.9 + + + + Pilgrim Way + Hiking path with 48 coordinate points + #line-style + + 1 + 9.092436,46.381906,2415.8 + 9.092896,46.381943,1169.3 + 9.092958,46.381995,995.9 + 9.096691,46.383018,3460.7 + 9.097469,46.383365,2422.0 + 9.098127,46.382626,3256.5 + 9.101573,46.385084,2086.8 + 9.103804,46.386046,1229.2 + 9.105101,46.385684,1479.9 + 9.108457,46.385989,3050.4 + 9.111604,46.386651,1546.5 + 9.115332,46.388681,2153.5 + 9.118117,46.390496,1424.9 + 9.119273,46.392390,1438.1 + 9.120604,46.395327,1170.0 + 9.124079,46.395768,1256.7 + 9.126929,46.396114,1083.8 + 9.129612,46.395487,674.7 + 9.130326,46.397392,2566.5 + 9.132964,46.397990,2408.4 + 9.135708,46.398822,2311.5 + 9.136384,46.401582,2359.3 + 9.138660,46.401776,2985.8 + 9.141343,46.401595,2131.0 + 9.144630,46.402632,2853.8 + 9.148084,46.404735,1504.0 + 9.151840,46.405490,1891.4 + 9.155102,46.406830,2875.3 + 9.157546,46.408998,3349.1 + 9.161388,46.410969,3438.5 + 9.162223,46.411830,1818.5 + 9.163512,46.414512,3399.5 + 9.166191,46.415658,1813.7 + 9.166874,46.415249,1115.6 + 9.170169,46.415403,1395.8 + 9.170871,46.416054,972.3 + 9.172911,46.415526,672.6 + 9.175464,46.414983,3211.6 + 9.179309,46.415782,1624.5 + 9.182609,46.417876,2713.7 + 9.183213,46.417391,1852.8 + 9.186612,46.417855,1369.5 + 9.188280,46.419695,3023.2 + 9.191332,46.421928,616.5 + 9.194556,46.422332,2624.5 + 9.195926,46.423513,3115.6 + 9.197342,46.423522,2851.1 + 9.200953,46.422966,1609.5 + + + + Hunter's Trail + Hiking path with 81 coordinate points + #line-style + + 1 + 9.160059,46.402381,2738.0 + 9.163791,46.401502,1439.3 + 9.166411,46.404404,1174.2 + 9.167635,46.403405,2790.3 + 9.171006,46.405623,1860.5 + 9.172168,46.407830,2166.1 + 9.172237,46.409754,1391.0 + 9.175602,46.410362,1004.6 + 9.176754,46.410156,1699.5 + 9.180696,46.413008,1195.2 + 9.182477,46.413756,2173.6 + 9.182753,46.415685,1444.6 + 9.184839,46.415815,3148.9 + 9.188461,46.417008,3308.9 + 9.189917,46.419298,691.2 + 9.192603,46.421640,1557.4 + 9.192794,46.421272,930.8 + 9.196580,46.423458,2914.1 + 9.199770,46.426193,3378.5 + 9.203147,46.426011,2697.8 + 9.206156,46.428954,2018.0 + 9.206339,46.431524,2122.7 + 9.206678,46.433304,1342.4 + 9.210674,46.433903,3407.7 + 9.211033,46.433803,1080.7 + 9.212784,46.434060,1409.7 + 9.213356,46.434341,1887.1 + 9.216880,46.437110,2190.9 + 9.220824,46.436931,1906.2 + 9.224608,46.438979,2741.6 + 9.227208,46.441205,1505.4 + 9.229326,46.442476,1220.6 + 9.231066,46.444135,637.1 + 9.232025,46.444628,2396.9 + 9.234313,46.446628,3144.7 + 9.235189,46.446876,2548.2 + 9.236905,46.446597,1397.0 + 9.238845,46.445714,3498.3 + 9.239744,46.447561,3250.7 + 9.242600,46.449152,2111.1 + 9.245731,46.448575,1580.4 + 9.247585,46.451310,901.1 + 9.249171,46.452041,666.5 + 9.253102,46.452093,701.5 + 9.253674,46.452401,3225.2 + 9.256141,46.452524,1654.0 + 9.256540,46.454438,646.0 + 9.258004,46.453788,1535.1 + 9.258214,46.454158,2744.6 + 9.260156,46.453892,2420.2 + 9.262645,46.455783,1909.5 + 9.264803,46.457782,2932.5 + 9.266054,46.457586,1201.2 + 9.266925,46.458151,659.1 + 9.270095,46.457900,606.2 + 9.272345,46.457915,3458.9 + 9.273588,46.460634,2951.8 + 9.274818,46.461824,2068.8 + 9.275251,46.462888,2404.9 + 9.277513,46.464882,1601.4 + 9.279088,46.464890,3184.5 + 9.281549,46.466246,2121.1 + 9.284370,46.468517,2339.3 + 9.286293,46.468972,2167.6 + 9.289460,46.469776,2430.8 + 9.291008,46.471753,2777.5 + 9.293792,46.471042,1359.7 + 9.297557,46.470814,1212.1 + 9.300190,46.473191,2110.3 + 9.302820,46.473012,647.6 + 9.303936,46.475382,1642.6 + 9.306953,46.475300,3053.1 + 9.309015,46.474730,2187.8 + 9.312640,46.475107,3225.0 + 9.313702,46.476371,1887.9 + 9.315820,46.477635,1392.8 + 9.316537,46.478002,2596.3 + 9.317781,46.480082,3217.3 + 9.321113,46.479643,1961.6 + 9.321941,46.479370,1293.2 + 9.325659,46.481039,2898.2 + + + + Shepherd's Route + Hiking path with 95 coordinate points + #line-style + + 1 + 8.513514,46.349933,1240.1 + 8.513871,46.349864,706.1 + 8.514764,46.349607,1034.5 + 8.517543,46.350072,2331.3 + 8.519762,46.350280,2117.6 + 8.522459,46.350539,2165.0 + 8.523564,46.351283,3463.9 + 8.524961,46.351185,2258.7 + 8.527375,46.352705,2129.9 + 8.529710,46.355663,1587.1 + 8.531131,46.356337,1320.9 + 8.533975,46.358564,603.9 + 8.535795,46.361535,2462.7 + 8.536313,46.362312,3211.8 + 8.539675,46.363437,1660.5 + 8.541035,46.366248,1553.1 + 8.543238,46.368133,1168.6 + 8.545994,46.367254,1193.2 + 8.549138,46.367886,1815.7 + 8.550544,46.368174,998.3 + 8.554426,46.371087,3200.2 + 8.558324,46.371578,2123.0 + 8.558971,46.373858,1655.9 + 8.562019,46.376858,2869.3 + 8.566006,46.379507,1526.2 + 8.568191,46.382436,2392.8 + 8.571370,46.385086,1504.6 + 8.575126,46.386183,894.0 + 8.577929,46.386993,2375.2 + 8.580971,46.389771,2125.9 + 8.582142,46.392486,1760.2 + 8.585992,46.391811,1632.6 + 8.586711,46.392309,1994.6 + 8.588051,46.395298,2338.4 + 8.588067,46.396811,1112.8 + 8.591633,46.397244,3246.1 + 8.594844,46.396563,2627.2 + 8.596776,46.398575,2047.9 + 8.600426,46.398152,1158.4 + 8.601372,46.398965,3181.9 + 8.602729,46.401637,1016.2 + 8.605888,46.401658,2927.6 + 8.607920,46.402086,3124.6 + 8.610541,46.404416,952.7 + 8.612853,46.404796,1586.9 + 8.615392,46.407355,2516.1 + 8.617967,46.408426,811.1 + 8.621213,46.407806,1099.2 + 8.624570,46.409010,1645.9 + 8.625850,46.409847,1348.5 + 8.627734,46.412634,2184.9 + 8.630967,46.414278,2221.0 + 8.631301,46.416332,2800.8 + 8.632798,46.418462,714.7 + 8.635169,46.421074,3299.7 + 8.635796,46.423105,3447.6 + 8.636781,46.422388,3085.4 + 8.638751,46.424090,1600.7 + 8.642025,46.424253,3055.1 + 8.643035,46.426681,2866.3 + 8.645686,46.427027,1328.8 + 8.649153,46.426981,2187.9 + 8.652245,46.428872,1276.8 + 8.653493,46.431284,2584.1 + 8.655834,46.432950,2952.2 + 8.656074,46.433120,3314.6 + 8.657369,46.434865,1696.9 + 8.660716,46.436926,1447.5 + 8.661485,46.439383,2700.9 + 8.663009,46.441604,1960.0 + 8.665356,46.444516,1803.5 + 8.665966,46.447496,3142.1 + 8.669336,46.447084,2333.8 + 8.670685,46.448094,1471.8 + 8.673729,46.449137,710.7 + 8.677422,46.449880,3017.4 + 8.680584,46.451713,2911.6 + 8.682569,46.450824,3139.3 + 8.684692,46.453086,1542.1 + 8.686002,46.453828,3176.0 + 8.686054,46.456713,2464.9 + 8.687243,46.458402,1895.1 + 8.688708,46.461349,2115.3 + 8.688927,46.464124,3092.1 + 8.690969,46.466179,789.9 + 8.694675,46.467815,2480.4 + 8.695235,46.470239,2110.9 + 8.697813,46.470095,949.9 + 8.699593,46.472870,1969.3 + 8.701274,46.475432,1714.6 + 8.704962,46.477209,2945.4 + 8.706292,46.477050,1662.4 + 8.706674,46.480021,826.5 + 8.707453,46.482423,2250.0 + 8.709077,46.482446,1269.7 + + + + Royal Road + Hiking path with 53 coordinate points + #line-style + + 1 + 8.446910,46.271182,1342.3 + 8.450651,46.270406,2064.0 + 8.451533,46.272876,732.7 + 8.451933,46.272858,3469.4 + 8.454315,46.272082,1405.9 + 8.457186,46.272961,1907.2 + 8.459320,46.273163,1948.1 + 8.460506,46.275215,1877.1 + 8.461685,46.276790,2636.2 + 8.462107,46.276871,2088.8 + 8.464680,46.277193,3055.8 + 8.464792,46.277478,1131.7 + 8.465902,46.276919,969.1 + 8.467814,46.276898,2424.3 + 8.468449,46.278782,3006.5 + 8.468531,46.279317,3419.5 + 8.470047,46.282314,3153.0 + 8.471310,46.281522,3476.8 + 8.473060,46.283002,2991.9 + 8.473421,46.285565,2518.3 + 8.473454,46.288428,1748.0 + 8.475138,46.288613,1845.8 + 8.479092,46.289543,3180.7 + 8.482328,46.290602,3350.0 + 8.485314,46.289636,650.1 + 8.486779,46.290760,1364.3 + 8.489548,46.292232,2630.4 + 8.492623,46.293090,3331.3 + 8.496150,46.295290,2850.9 + 8.496433,46.297364,857.8 + 8.499841,46.297904,1059.4 + 8.500767,46.299770,2515.6 + 8.502253,46.299023,1525.6 + 8.502593,46.300559,2453.6 + 8.503941,46.300287,1885.4 + 8.504676,46.301623,2892.0 + 8.505362,46.302233,3260.2 + 8.507483,46.303040,3240.3 + 8.510724,46.304068,1244.1 + 8.514320,46.306092,1880.6 + 8.516417,46.306770,3212.3 + 8.519917,46.305924,2514.7 + 8.520364,46.306541,3111.9 + 8.522653,46.308292,3108.9 + 8.523453,46.309741,3364.6 + 8.523955,46.311642,3045.1 + 8.526056,46.313495,3084.0 + 8.526957,46.314128,1347.9 + 8.526999,46.316033,1438.6 + 8.530950,46.315132,2562.7 + 8.532087,46.317083,1884.8 + 8.532182,46.316590,2111.8 + 8.532613,46.318985,2534.6 + + + + Via Romana + Hiking path with 108 coordinate points + #line-style + + 1 + 9.764037,47.217598,2988.1 + 9.765593,47.219345,2838.4 + 9.767922,47.219932,2248.7 + 9.769033,47.220442,980.9 + 9.772113,47.219904,662.8 + 9.774246,47.220173,2566.8 + 9.776152,47.220630,3263.2 + 9.778615,47.223214,2721.4 + 9.781790,47.225292,2162.1 + 9.785370,47.226816,1557.1 + 9.788659,47.229384,3201.5 + 9.791297,47.231357,1153.7 + 9.793192,47.234176,2313.9 + 9.793452,47.233268,3015.4 + 9.796571,47.233422,1443.4 + 9.800314,47.234891,1538.5 + 9.800755,47.235165,2814.9 + 9.803472,47.236578,2371.0 + 9.806877,47.237518,1282.5 + 9.807953,47.239931,2924.8 + 9.811677,47.242412,3285.9 + 9.814351,47.244077,2328.3 + 9.817755,47.243639,644.7 + 9.818374,47.243007,2937.3 + 9.818550,47.245532,2372.1 + 9.819217,47.247835,3042.8 + 9.822435,47.248645,1752.9 + 9.824455,47.248745,1677.9 + 9.826190,47.248626,1202.6 + 9.829913,47.248927,3462.2 + 9.830234,47.250929,1663.0 + 9.830605,47.253571,1680.2 + 9.833776,47.254119,2551.7 + 9.835534,47.256831,1114.3 + 9.837487,47.256314,2037.7 + 9.838710,47.258141,3367.2 + 9.839925,47.257153,2515.5 + 9.841166,47.258462,1007.2 + 9.843749,47.257986,881.1 + 9.847014,47.258658,1260.7 + 9.849383,47.259145,1498.4 + 9.850525,47.259135,1659.0 + 9.851485,47.259573,3167.9 + 9.852138,47.258815,1363.5 + 9.853543,47.260011,3111.5 + 9.855669,47.259635,1750.5 + 9.856994,47.261082,2373.2 + 9.860948,47.263043,1615.4 + 9.863500,47.265483,1802.9 + 9.864959,47.265811,1549.4 + 9.867728,47.267324,2425.7 + 9.870950,47.269817,1752.0 + 9.873740,47.271872,3092.5 + 9.876782,47.271859,676.9 + 9.878896,47.271964,2246.5 + 9.882249,47.274613,2661.3 + 9.883521,47.274707,1295.1 + 9.884049,47.275083,1864.1 + 9.887357,47.274694,2340.7 + 9.888332,47.277572,3066.2 + 9.891467,47.278236,659.9 + 9.892494,47.281175,993.5 + 9.895936,47.281716,896.9 + 9.899537,47.280833,2195.7 + 9.902248,47.282295,1774.8 + 9.904245,47.281940,1531.9 + 9.904291,47.281188,865.9 + 9.906118,47.283597,1851.5 + 9.906997,47.284560,1610.8 + 9.909104,47.287059,2553.8 + 9.910966,47.287496,3411.0 + 9.911801,47.287071,3298.9 + 9.915725,47.286122,2605.9 + 9.919101,47.286275,1640.5 + 9.922439,47.285363,1600.4 + 9.922521,47.284975,1051.1 + 9.923282,47.287267,1863.4 + 9.924377,47.287852,1988.6 + 9.926322,47.289325,1914.9 + 9.927383,47.290177,2984.4 + 9.928940,47.291783,2519.4 + 9.930201,47.293296,1204.6 + 9.931086,47.295004,1528.1 + 9.933569,47.294497,982.0 + 9.933789,47.293869,1398.0 + 9.935853,47.296519,2340.9 + 9.936238,47.296595,2534.9 + 9.938059,47.298637,1411.8 + 9.940386,47.300003,3439.2 + 9.943319,47.300446,2571.6 + 9.944508,47.302599,2885.3 + 9.947390,47.304514,608.9 + 9.947720,47.304602,1346.5 + 9.949710,47.304953,2246.9 + 9.950599,47.307861,2499.0 + 9.952527,47.309877,2846.9 + 9.953610,47.309156,2140.0 + 9.954460,47.311194,3115.5 + 9.957215,47.310494,2465.2 + 9.961143,47.313408,1919.2 + 9.963548,47.314229,1795.1 + 9.965752,47.316524,1972.2 + 9.968414,47.319384,1390.4 + 9.969494,47.320970,3332.8 + 9.970604,47.322852,787.3 + 9.974418,47.322769,1864.2 + 9.976979,47.324500,1756.1 + 9.979169,47.324044,2545.9 + + + + East Face Descent + Hiking path with 110 coordinate points + #line-style + + 1 + 9.408229,45.755638,3181.2 + 9.411063,45.757376,3302.0 + 9.411617,45.756732,793.4 + 9.413852,45.756845,3266.8 + 9.416151,45.756098,1200.5 + 9.417173,45.757017,2042.7 + 9.420171,45.758056,3030.7 + 9.422884,45.758551,624.3 + 9.424642,45.760472,1849.9 + 9.426543,45.761548,3171.7 + 9.427315,45.760938,3364.9 + 9.429882,45.760833,2454.0 + 9.430564,45.760679,2586.3 + 9.431498,45.763027,2927.1 + 9.434424,45.765397,2390.9 + 9.434810,45.767311,2384.5 + 9.437355,45.769240,859.3 + 9.439413,45.771694,3331.5 + 9.439862,45.773554,1424.6 + 9.442508,45.773331,645.7 + 9.445093,45.773179,2126.1 + 9.448176,45.775401,3207.8 + 9.451762,45.775000,3401.0 + 9.454903,45.777098,2116.4 + 9.458380,45.777398,2085.2 + 9.460206,45.777149,3197.0 + 9.463649,45.777523,925.8 + 9.466239,45.780249,2223.3 + 9.467250,45.779445,1034.6 + 9.467260,45.780685,2162.2 + 9.469218,45.781084,3430.1 + 9.471870,45.782477,3008.8 + 9.474665,45.782221,2679.4 + 9.477354,45.782218,3298.9 + 9.480009,45.784769,1260.7 + 9.480325,45.784601,1022.5 + 9.482175,45.786880,1260.2 + 9.484253,45.787226,1228.1 + 9.487040,45.790006,2321.2 + 9.488812,45.791610,2152.2 + 9.492784,45.793031,2624.7 + 9.494545,45.793436,1838.5 + 9.496143,45.794871,3062.6 + 9.499528,45.797483,2201.7 + 9.502862,45.798167,1383.4 + 9.504422,45.799341,2544.4 + 9.506744,45.799198,863.5 + 9.508471,45.798202,2497.7 + 9.508501,45.799490,1003.2 + 9.508946,45.798853,1967.7 + 9.512685,45.799834,2846.1 + 9.513973,45.801330,1431.4 + 9.517967,45.802054,3291.8 + 9.518708,45.802484,821.4 + 9.518903,45.802041,1526.2 + 9.521789,45.801846,1937.6 + 9.525674,45.804389,808.0 + 9.527586,45.806858,1688.2 + 9.528355,45.807431,3400.8 + 9.528530,45.810256,2622.2 + 9.531697,45.811494,837.3 + 9.533800,45.813865,2519.9 + 9.534087,45.814874,1089.6 + 9.534153,45.815542,1687.8 + 9.535109,45.814948,2711.8 + 9.536372,45.816392,784.8 + 9.540152,45.818071,2246.3 + 9.541293,45.819655,1766.4 + 9.545134,45.820795,2880.3 + 9.548454,45.822037,2371.6 + 9.548652,45.822701,2792.4 + 9.549253,45.823440,891.1 + 9.552883,45.826030,2341.9 + 9.555196,45.828346,1874.0 + 9.556367,45.829092,2779.2 + 9.556805,45.829306,1981.5 + 9.560719,45.829934,1020.9 + 9.563519,45.831652,3067.3 + 9.564818,45.832136,1569.2 + 9.565018,45.832318,1517.4 + 9.568794,45.832572,1818.9 + 9.571530,45.832898,1318.0 + 9.573158,45.834739,945.8 + 9.574456,45.835584,835.5 + 9.574515,45.837286,1074.8 + 9.574592,45.837267,2643.3 + 9.576236,45.838488,1346.3 + 9.578709,45.838775,1345.3 + 9.579359,45.839567,2786.9 + 9.581418,45.840645,1915.3 + 9.582083,45.840350,2522.5 + 9.583639,45.842276,3005.7 + 9.584568,45.844713,2981.0 + 9.586841,45.845832,3127.4 + 9.589013,45.845258,1703.7 + 9.592070,45.844774,3044.1 + 9.592616,45.845585,3481.8 + 9.596191,45.848303,2109.8 + 9.598010,45.849532,1350.9 + 9.598390,45.852383,3120.2 + 9.600873,45.854437,3242.5 + 9.604034,45.856191,2351.2 + 9.606184,45.856469,1435.4 + 9.606205,45.856426,1204.0 + 9.607365,45.855896,2698.4 + 9.610631,45.856961,2313.5 + 9.610908,45.859778,1663.5 + 9.612829,45.861911,1578.0 + 9.614799,45.861668,2305.4 + 9.614867,45.862479,2110.5 + + + + North Ridge + Hiking path with 49 coordinate points + #line-style + + 1 + 10.153777,46.952741,3106.8 + 10.154306,46.952176,1744.8 + 10.156650,46.953060,1554.0 + 10.159582,46.955270,1143.1 + 10.159725,46.956388,1498.6 + 10.160136,46.957401,1900.0 + 10.162872,46.957366,2868.4 + 10.166457,46.959264,2806.3 + 10.167134,46.958564,1767.2 + 10.168212,46.960481,2196.8 + 10.170303,46.963256,3434.3 + 10.173559,46.962330,1053.2 + 10.174323,46.963486,1095.2 + 10.176939,46.962721,2322.1 + 10.176999,46.962171,2085.7 + 10.177909,46.963807,2939.8 + 10.179383,46.966432,2653.8 + 10.183222,46.968899,2931.0 + 10.185515,46.969596,1250.8 + 10.189415,46.970534,1859.0 + 10.190632,46.973499,1040.3 + 10.193504,46.973665,3436.0 + 10.195183,46.976069,3250.4 + 10.195558,46.978576,2615.7 + 10.197337,46.977817,2639.0 + 10.200831,46.979124,3297.0 + 10.202773,46.979656,2050.8 + 10.205998,46.982435,2420.7 + 10.208447,46.984500,3407.0 + 10.209377,46.986311,2702.2 + 10.211317,46.987898,1705.5 + 10.214065,46.989662,2173.8 + 10.214872,46.991911,1102.3 + 10.217302,46.992087,722.4 + 10.219205,46.991791,1823.0 + 10.220272,46.993833,2156.3 + 10.223060,46.995145,715.9 + 10.223155,46.994290,2480.8 + 10.224232,46.995135,2563.8 + 10.227901,46.997191,3136.4 + 10.228311,46.999445,1479.2 + 10.230461,46.998449,748.9 + 10.230768,46.998278,2839.9 + 10.232248,46.999741,1926.5 + 10.233564,47.000750,3380.7 + 10.236309,47.001879,3267.3 + 10.236593,47.001887,2708.8 + 10.240184,47.002384,2792.7 + 10.242284,47.004689,1006.0 + + + + South Couloir + Hiking path with 108 coordinate points + #line-style + + 1 + 9.438302,45.568636,1010.7 + 9.441644,45.567838,1850.7 + 9.444699,45.567343,2027.5 + 9.447537,45.567913,3196.3 + 9.448416,45.567896,1457.1 + 9.450585,45.569988,2665.9 + 9.454151,45.570123,1490.4 + 9.457158,45.572690,2538.6 + 9.460498,45.575038,1603.3 + 9.463883,45.575109,2238.9 + 9.465290,45.577936,1854.2 + 9.467498,45.580143,2748.3 + 9.467644,45.582708,3158.9 + 9.468537,45.582595,934.5 + 9.470699,45.585463,2749.6 + 9.471903,45.585288,2707.3 + 9.474119,45.587815,2827.0 + 9.477523,45.587481,3343.4 + 9.478357,45.588086,1589.9 + 9.481747,45.588675,1586.1 + 9.483821,45.589760,2325.6 + 9.486473,45.590419,3049.1 + 9.488358,45.591301,1957.1 + 9.489679,45.591623,2898.4 + 9.492568,45.591443,3067.7 + 9.494740,45.594049,3304.4 + 9.494943,45.595629,2427.3 + 9.498932,45.596144,2172.6 + 9.499513,45.598126,1175.3 + 9.500253,45.600191,1660.5 + 9.501539,45.600649,1178.9 + 9.504229,45.602346,1680.0 + 9.507869,45.601774,826.1 + 9.508972,45.603025,2083.1 + 9.509099,45.602501,793.7 + 9.509616,45.603906,2083.3 + 9.512216,45.606054,3444.6 + 9.513583,45.606822,2281.3 + 9.516282,45.608584,1980.1 + 9.516766,45.609289,1747.5 + 9.519481,45.611471,1221.7 + 9.522844,45.613736,1702.8 + 9.525521,45.614387,3256.6 + 9.527875,45.615891,2175.1 + 9.528726,45.617221,2744.8 + 9.529039,45.618501,728.5 + 9.532516,45.621336,2471.0 + 9.535458,45.624112,1773.6 + 9.536824,45.624657,1704.9 + 9.538714,45.626095,1925.3 + 9.541925,45.625384,1568.3 + 9.543717,45.627835,1387.2 + 9.545718,45.627202,3378.7 + 9.548125,45.628356,1488.5 + 9.550628,45.630686,1699.7 + 9.551393,45.630717,2582.2 + 9.553985,45.630005,2145.2 + 9.554070,45.632894,2125.2 + 9.554785,45.633594,3453.1 + 9.558122,45.635294,1480.4 + 9.558851,45.634975,3182.5 + 9.561429,45.637719,1843.2 + 9.561977,45.639792,1117.8 + 9.564585,45.641979,1951.1 + 9.565510,45.643923,2672.2 + 9.565871,45.646189,2338.5 + 9.569095,45.646060,3234.9 + 9.569764,45.645566,2968.1 + 9.571944,45.647991,3398.2 + 9.575183,45.650182,2281.5 + 9.575812,45.651233,3473.1 + 9.576646,45.653619,2800.3 + 9.576981,45.655038,1262.0 + 9.577013,45.656534,704.2 + 9.580492,45.656008,1318.9 + 9.580549,45.656647,2238.3 + 9.581510,45.659488,3296.3 + 9.583542,45.662205,3355.4 + 9.586814,45.662763,1013.2 + 9.586945,45.663032,1018.9 + 9.590547,45.662907,2236.0 + 9.593391,45.664608,3082.6 + 9.595106,45.664050,1020.5 + 9.595955,45.663211,3086.7 + 9.596809,45.664153,1822.9 + 9.600433,45.664931,1362.3 + 9.602430,45.665864,2175.8 + 9.606390,45.666406,3230.3 + 9.608401,45.667771,871.5 + 9.611725,45.667300,2382.1 + 9.614078,45.668560,1168.4 + 9.614123,45.669242,3398.8 + 9.616496,45.670596,3310.6 + 9.619629,45.671569,1479.1 + 9.622262,45.671405,1946.2 + 9.625177,45.673891,1368.9 + 9.628551,45.672922,3159.7 + 9.632492,45.672382,2170.2 + 9.635071,45.674409,3328.5 + 9.636626,45.675673,1261.7 + 9.640381,45.678491,1427.6 + 9.640733,45.678852,2747.9 + 9.642697,45.681242,712.0 + 9.642854,45.681846,2225.4 + 9.644904,45.681957,2892.9 + 9.648070,45.682532,2535.1 + 9.650439,45.681632,704.0 + 9.651437,45.681616,2834.7 + + + + West Flank + Hiking path with 64 coordinate points + #line-style + + 1 + 8.566710,46.553398,2014.8 + 8.567690,46.556249,2946.5 + 8.569448,46.558862,1142.2 + 8.570719,46.560939,1502.6 + 8.573659,46.563484,927.0 + 8.576222,46.564769,1630.7 + 8.579626,46.564511,2118.8 + 8.582827,46.563942,3193.8 + 8.583033,46.565721,1438.2 + 8.583869,46.566866,1387.8 + 8.584490,46.567241,2730.2 + 8.586051,46.566423,1547.7 + 8.589263,46.566627,3364.6 + 8.589974,46.567102,2053.3 + 8.590261,46.568836,1952.4 + 8.592335,46.568779,3215.4 + 8.592752,46.569430,2749.2 + 8.593613,46.570360,1257.7 + 8.595496,46.572484,629.0 + 8.597898,46.575127,934.6 + 8.598319,46.577370,1104.4 + 8.599306,46.577847,1094.4 + 8.600416,46.579911,2307.5 + 8.600436,46.580782,3207.0 + 8.600668,46.580285,773.2 + 8.601129,46.579711,1818.1 + 8.601753,46.580087,936.4 + 8.602141,46.580417,2572.9 + 8.603445,46.582000,998.0 + 8.606135,46.581681,1680.1 + 8.606729,46.582500,3097.9 + 8.607620,46.582445,2134.0 + 8.609196,46.583514,2803.0 + 8.610718,46.584607,876.3 + 8.611117,46.586194,1453.7 + 8.613350,46.585285,2293.1 + 8.616573,46.587543,1167.9 + 8.620114,46.590497,3346.5 + 8.622477,46.590748,967.8 + 8.625086,46.590733,2022.4 + 8.625912,46.589798,1324.4 + 8.628090,46.589474,979.6 + 8.630471,46.590340,3093.1 + 8.632298,46.593021,791.0 + 8.635122,46.592406,2325.3 + 8.637564,46.594193,776.7 + 8.638563,46.595390,1606.0 + 8.638988,46.597509,3351.5 + 8.639189,46.599171,1899.4 + 8.639571,46.601798,1456.1 + 8.642170,46.602877,1451.0 + 8.642294,46.603686,834.8 + 8.644379,46.605268,3264.0 + 8.648005,46.605981,3309.0 + 8.649028,46.608390,2571.5 + 8.650842,46.611326,2873.7 + 8.652075,46.611819,846.4 + 8.653770,46.612892,2285.8 + 8.655188,46.613288,2489.7 + 8.655708,46.614919,1412.1 + 8.657037,46.616832,2796.1 + 8.660309,46.616204,1076.8 + 8.660695,46.617101,2633.5 + 8.660993,46.616328,3272.1 + + + + Cirque Route + Hiking path with 76 coordinate points + #line-style + + 1 + 9.061195,47.831400,2519.8 + 9.062822,47.832430,1508.5 + 9.066062,47.832040,3169.1 + 9.066417,47.834093,786.8 + 9.067397,47.833769,3378.5 + 9.070763,47.832810,2407.2 + 9.074647,47.831950,1616.4 + 9.075345,47.831351,2660.1 + 9.077096,47.832709,2342.8 + 9.080768,47.831730,2159.0 + 9.082306,47.833497,1089.8 + 9.084655,47.832861,2518.4 + 9.085568,47.834570,1206.1 + 9.088243,47.836203,3029.9 + 9.089624,47.838879,2228.4 + 9.093251,47.841142,3493.8 + 9.093536,47.843539,1314.6 + 9.095693,47.846057,2283.8 + 9.098521,47.846879,1488.0 + 9.098985,47.848137,825.3 + 9.099927,47.849660,3132.1 + 9.100558,47.849362,1650.0 + 9.104340,47.850702,2429.8 + 9.104922,47.852661,2219.0 + 9.105452,47.853023,1111.1 + 9.106007,47.852824,2322.4 + 9.109630,47.853068,2877.0 + 9.111315,47.855715,1466.4 + 9.112737,47.855002,774.2 + 9.114698,47.856024,720.9 + 9.116929,47.857082,1706.8 + 9.117523,47.856606,1547.1 + 9.118046,47.857317,2572.7 + 9.120061,47.856986,694.8 + 9.122503,47.859263,1654.8 + 9.124274,47.858881,1826.9 + 9.128115,47.857902,1654.6 + 9.131587,47.860821,3245.2 + 9.131902,47.863363,2095.6 + 9.134348,47.865476,2578.2 + 9.137601,47.866668,1843.4 + 9.139270,47.866436,3175.3 + 9.140263,47.868936,1016.0 + 9.142419,47.871262,760.3 + 9.144239,47.871059,776.7 + 9.144485,47.870586,3173.1 + 9.146131,47.871063,862.9 + 9.146476,47.871643,1563.9 + 9.148724,47.872996,2202.2 + 9.149203,47.874802,2719.6 + 9.152453,47.876038,820.5 + 9.153644,47.878833,2922.2 + 9.156842,47.878303,3205.7 + 9.159142,47.879459,1224.5 + 9.160394,47.879143,2745.4 + 9.162616,47.880763,2236.4 + 9.163493,47.881435,1165.4 + 9.165036,47.883117,2644.6 + 9.166732,47.885927,2849.5 + 9.168690,47.885488,774.5 + 9.169892,47.886326,2095.4 + 9.170412,47.886823,1559.1 + 9.171384,47.889151,1241.0 + 9.173762,47.888682,1562.0 + 9.174710,47.888713,708.0 + 9.177716,47.889861,3351.7 + 9.179157,47.891497,2585.4 + 9.181460,47.891579,926.5 + 9.183044,47.891200,1448.0 + 9.185537,47.890535,782.6 + 9.186880,47.893420,3369.1 + 9.187171,47.893698,1681.5 + 9.189896,47.894733,3381.3 + 9.191983,47.897049,1243.4 + 9.194451,47.897603,2448.2 + 9.196668,47.899409,2331.9 + + + + + + Special + + Viewpoints + + Nested Viewpoint 1 + Nested folder placemark + + 9.250304,46.672707,2535.9 + + + + Nested Viewpoint 2 + Nested folder placemark + + 9.193707,46.772537,2451.3 + + + + Nested Viewpoint 3 + Nested folder placemark + + 9.597240,46.639362,2777.4 + + + + Nested Viewpoint 4 + Nested folder placemark + + 9.467073,46.690801,1419.3 + + + + Nested Viewpoint 5 + Nested folder placemark + + 9.266845,46.804848,1316.9 + + + + Nested Viewpoint 6 + Nested folder placemark + + 9.130465,46.956038,1694.9 + + + + Nested Viewpoint 7 + Nested folder placemark + + 9.041431,46.810917,2404.9 + + + + Nested Viewpoint 8 + Nested folder placemark + + 9.950283,46.563146,2509.2 + + + + Nested Viewpoint 9 + Nested folder placemark + + 9.793267,46.738520,2265.9 + + + + Nested Viewpoint 10 + Nested folder placemark + + 9.180004,46.648983,2014.3 + + + + + Test Polygon (unsupported) + + 9.0,46.0,0 9.1,46.0,0 9.1,46.1,0 9.0,46.1,0 9.0,46.0,0 + + + + +
+
\ No newline at end of file diff --git a/server/tests/fixtures/large-test.kmz b/server/tests/fixtures/large-test.kmz new file mode 100644 index 0000000000000000000000000000000000000000..c063684c4f27aac8aca6dc2c7c2e11617ae6b515 GIT binary patch literal 26416 zcmV)&MSe)Xc=7n=>$l&$dh_jH z{Ng|T=5PPq`o+Kh>d(IXJ*YtW=KWv%;=2zYe)#h*zWBo*{_ybjhsQVHzIy-g=JCT9 zp!lLZlwW-HXD?oS`PXm1{_*?AHy`*1{PE4p?;pSV&9DFFA71?O<-2bmU;O6r{f8I- z@PGXB%P-__l<1qs_g}wz^}`1&_Ui|LWfBW#`yT=!Qc=h4C7rCbw zKfHbQ=EM6JFW-FgqTo+2KYWKjeEsh2`}Z$?_xANS_>wQb_;jd0)6xI&!yjKizIgS` zU;IJ__;>I5=U;sF<$wJ8?Hl>KufBZ$^-KH@w&2Sz^dGz7|Ye*f~_?~i|TkpEvlKE8hayO;0Z|M=$H7y0~GZo|L) zpZ@b-{qw`a@#imc zO6$e<|K-1YI-hX&}_wD<~$M621|NFy-#~1bD9Spy_bQf#i z=`OptA4YFI&r_QZz0OkmiE}^n8|>n*@b7+j{qn~ciFw19_m_~ zPq;sHT)|mRm^?j<8*JaNU;XaKZ(qHA(J$V-PkwPR^UI~)YwLFFd+bwN4{2@kK}=pA z)=jzJ_zdnhuGqfb%e}U5?Y+2t^P#m;aPu&EzKQVoyH`K_?(y~S-#z~L-(Sp&H_-j+ z;w@}*+)IG;T$1cyPrK)I!rc8P0_0aO-@JYI^81%R{P^bgA6~30*q{5|^GOokJ|2}JVRtaU8eY$X{@#&K5D(mz5BQmms=lazqwrPYxmi`{ebJX`XcX} z8&*g?aqgSkJyC)`$QQoIms=nHXP53_FZU52kifa+yhVZOvyWCzn44zz|No5dQQ26& z-1PYLCl^27RrllGBYPM=RqYJ*Kn44;@me;W}Zy4a=8_9_M3~jVBYs~ zVePS2uiU-7T2JM~xo`4}Kh^2V<#vew*`<5f>b)3YPiRoPueEO5igTMW5(l_stTNk;JSDBC-V1##&zL(n|;dd9ykDBfiMS{9qU~)?c zWUX4D&2a9Aatj~U%l98Nl3OmfLe775`Tos&?cap%Qp6S0GR%>F;@meeLe6i~<#tH; z-Nlc2+$n@aPFQE!qDqh18f-aCUTzviua}!5pZ?_HD>%k|QV{k->7}PEQl!ETUMI|* zZX!kg^7Z#eD$C_I$oY>h-M>-qB!wYJY8qur64q*v!>3ViBZSX`x%F}*B>wPnf6Tg1 z9udAXv_77nszSb#K~`B34gK| zm)j!eKe{|DGVi8O!+cva$xE&iip+_V-$WSy-AnAlyVo!36)}-desZ}YQ|~5%3-lKg zBe^f;0pdS5(F8wn?bXZe5%;T$AMn_B6T~BxEDq^5W-jx@xo>j)KHZDU?Gg8@i(l}( zlPbd(JXWDmHy-lJoi7+>@lB#~zkU2xol^C3Yvk-VSBT&{*~HjBT)Q#FCLX;?NMyB# zdTVQ>ex@~2udqg-+ukiS*Jeqh%R`1qJZKbCDBn0Yrd@84_@7;F($%}Ar|vdWHep<# z8lE`$XK&({UmS%b`p4(WO8)-G%WFzLDl_eKOquqaDI@cHH+H&LLf3%FyrmkVv|VY? zq;?bG^D!~HeNHV&d(M1$ZtLnCcZwxvI&X^R6860Zt}P~?Hwnfcd0FlHRIc`%-SXVd z<+Se=Jb`>}L#ju1-He8E?%TwlOn+{dTP&acADx;d5=di0^+u+U#}W+&7Ui zzkT@ztn$0ZSKq&Amzyi@SC=unc^_v33+FOZ7U;H~eNtnb>E;P=?Q&zq|Lmge>*;Ql zbE(kR&&r$9aqmt&-KcQx<=2py{|t5ICG@>^xvdg^c-aFk`#xIYdf0R9B>7ulfN7T# z=Dtk=+!2Xums={I{^X*Ko9$jtmlj=?h)IH|?dgQ6Z=AK&J|}CdJ!hOeD{c3-h-D)rmwcu!a8a>L@&pImn9%Dk6Du=X9lU z#`%vfyn1&E2d{?(U4^GNf-OTslcuYg>a};$yr&;!i zbKf>2x?gTuoc-o9U1i zYPwTZ+ z|Jh}d_g;nQ`Jv9%bJj}gn!xck&g$CQ5U^uk$W9FhBDs10^#kR_g zAyDV#2FLl2E_Jfp=juV}YpplQ81H!zL6LLcI*DLj5!X2X)n%7&+)ZZmo^nruAW_O3 zxO?NqGGktDY<&8Y%kEt*cdMCcefN?z?I))`PQV~N zSzPjEkGWvt<{P0Fp8;@Qku~LhcG;_i?r<*yW0y80&3^5X7g0OMt$}mq=NK6C;+V#` z6Yd&V-pclHbA{*~C(ixZEbo%6+<$uY_y<|_XXNJ2YXGx8{ZZKVPgW70e!r8llOIy4 zt26}+Z1_LC{eBx|=j;)GCT8z*#>$`C$2$QpZrCY%PEmXING)w_W4R3n_!wiGpHs9x zpR-nedJA*AlN8zyyY!v6qK~yr)u%xU-4c3YUT(5{`jcyJ;J8;(LSjUCEBILJeUA!0 zdZVO-PisBa=aq=8=S-KM-p5kLy-*l;%ZVt0>mhagXP!9sP262a68*Z|dinGx*KFU; zcPg@ht^_ql~Ip7SbOE4@n3kSd(}CaE6h*=p-@n=BGDs-47IW^)#|Ua<_(mTJ6^Rce#yn_M2-In|ilq z4lqm7(gd136La3`E;muFKQ%Sh=UFK0It!)cyCDG-FhUW_V26p_mGX@cd47EV{?*st zeRyvTLaxB~jlaC+MneC+n~u|_dI?Fw1Q^ESMOlui_3?$pACm|J^v$=RGKV=qsb`zHQ}^Te(Fxfx&kI@6`ztDWC|=%d!8 z2{dH`1>4(TSZ)vr!C~9{WQeONOQ@2TesrA++sQV%{2n0-3bhCVFOc}Nkc3pGsM!yZK`KK zHW|+8a#O|q>Z-?lC&U2OFfxx=%J5q1jrcrc-b6wC7=X{|b0D?kSJyZd?^Id=hB#N5 zVl@{iIjNu4x=o+SvzCYHGg~F5>x`9qH5zBSQz1Y)#Pcq-m452$Hl@f<-TTtzrpnoG zuGqeDx3s4;T32YylK0&6gsE>2?FiZbdA=L@;Z=g=UcD#tLy?~M!W3sIb)7i*t^4kN zMwmr@bd7`iPMwQ$dKh`r@*j3Jb zQ{M%Q{Oeo!rss^EpSp>o-z^)w?lh_d4LeN7gsX_jZ|jB3=`(B{`O(GKucmv23)z2r zNirG!p>Af=r+gFH{xKmZU(pHP{p!*koOkOXxaMvujo3lB)YIIziN<_%gXJrF!Mk5w z{DgD6+Y>IZ6PF~O%!%imPMrLvIIj7MUhrqXxp@29-CSmU)mYii}%WAWdDTC>PfkSg%Adu`!hinj3nXP53_g-UU+o7m>qdJ_}= zsj(^YZJcIDSFC(RU-+}%T)unhUOM$CJ8x?#4MD;p^1W}O2cFx?S2Tu?KfIhD-l-D` z#YvvR5XVDLwFpDp`8GH%pT5Z}0KnYOF20QQZn^QJuG+M$JntotrQ=3IE+;Rb#P(ZiE5p9ax2Z62@9~OO@z4D7VxHL2y%cSz zh;0jub7`CPE|~i^vFT5mW#udS#rvOKyocbOce{g!yC&$$l{o>Dl zbFru4egI9dP=*>Qg8}_~nh?sjEk4gzG>mt@x>y*eJLQQnY}5FkdcAjp!%kpR$yR@h5G&C&&F2 zE#uFBbm{)>d*Q9+hsKftksvE2NUjs+zD-7s3%*^^H2%4tUi_Ndogz>3VYJk>{CVco z8XNxh+c^H8jI=BI#-IJ>a%K8Xl~tP=C6Fb05o>{gg1JAxuYQtxIQLD|pHCm+<@SsF)rI>v z?o{}v81Gq}VUXQx6EZN}Ce^_?WUlBm|I9BhWP|BWl6yZ)y1)u$Pgcy~q1`Lo|#wt4eTX*ctsL)Dt1J#L8N zGEOtyrmoy;(^s^df9|K3>NIyUs@bq+=c%oy%NSLf>-C#>6h9hwSM;1e|IvlFaNSEH zkKNXka;pu=TyYE6zKKHfscZU*Ui1EE7qY;z?$#7@mt7@!bJS9pJy+IkuxQV=;&PM3 z|Ln3IZ0lYQoeeXviXE7H-x6NAP4erNj`12oKws*bVt14sFowMjMd*`(JsWUQg zTXW=!*75FF7pl|P>3iY-jgb}GW#+{wGv~gEIdYyib43&R_`?h3Z@*hqbfqNm8uEV`0vS@p!pZz6y{DHy$?mAw1aWgB>} zfC@B~)K*dOzr7$wP7J5qzVB4NqL=(Lzr64Xm-`h|H7KVFD4PmPs@N;I+^CDlN11Si zA#(n!3*T|OUsl8DkR|FEoT)PukZ%KR<*(D_hR8F&yi}XMTdvYrH4o_qRI>yHIQLE1 z;ioB>SG1Y;KfBl-xfdqW?wN_#R0(J9lRLP|XG`PDY9l{K%Q?OH_!%!`tKR>AZ|Q5y_$PMsm-y>{{O}G_ooy@j z={S?k00M6=|gM7y#g zW~}ru98pd?3D?Oxtuaoy_8g`I$7Gz1M71}LR8l0Gz3X(l%uW73@CH56T4~rw1>c!r zW`~gq6osde27yQFjzl%>rbNAMi0v770E|>cd&W%$bKP^qaiGG>ib~ST$yJ~VIHH0>zhzH9751BXw)SyT`p3=|Pq*XB z=|~xOosRRAAC@P=jhjkTnC!>f8AqCH8lV97TXrBl6i>8f(~rx2G2N}~hwf-b8LJr0 z*aR)c@%sH95YSDDR%zJE(pT6@f+^a^?Z(?w+1kYs%``LwNYPZv3EVaeG}Ej`lV)3Z zm!(9aQtX}-{ypw4%qG79U=2eLrPMmXD@Kkle2%Q;4yE+nDwb5@vUjtN3k4fi_y~HK zjmb3!QNqEMK1#UXP`LLo+nmxQcZI9F*p|*ls?;aj*x|;lw(Vh-opC%Pr8wJK#|A9v zNIB{V7j6@!)rjU~M~+s*4(x(^DMU35iQU!`N2+}mBkC*yo4A#J-QY*bM#>o{2i`ow zUb0EZF%las@k;qSkQ3q7jA+q$*ucMfAe#2(U?K9N6nxzbMBCUvZJpCiSVG$q;aK%l zm^B4Uz0Mxjg!K+1g8Vx1PWi9PY2c%kXoG{Dm`fSgG}#1Qi7?h_Vw(cZK5#`?#7Gea z#o67a%q`HpYOL>CxDGdRqaCd9q zOc_FH>bJE#-)ea;pW+ zf~>s{o323*!O8S6UB~svHWJffrEJ+RykjAHoxoG^Ml<$3TqiuIq{o6c$JR`hZP^0R zv^5QhU-N#&k+5Lh%z@qDX6_&}!EBqUAEQFBp0c#7^+asfr9_>~31^qW)5Kq`b`Wt3 z_H#tIyvInLf_1NWGeKgP4)R<&)77AVK;gmz7NQ9avard0d@K_WSGQnFGjg0{P*R|D zkGhR$;zboos4!AJ&JKbYOab<3*d{!2lVYHTVdI2|dMfy3^%n)aq1cir#kcCt zo(Mv7lw;kMuQ%#Ml>a7Iz$z-Wh84F7ywFrIjFhk@*uwtgpnIDu4N4(wDaw_ggi>Xm z&4|FM!R2O=2BEiRD!M*MZ?y8wrErqRjQD>OrEn;L0?XZ@zte@!OZLe}ARx zBaNT2>jS0~1YlNdf3OG|*E9R!l6Np4kWxsN|M~S_cXhgsx)9;Y$4sw*%4^y ziZhzd5y6np^-j{1GHgAK+8aJU2bwm9t%pm4&}!%0Uuc%*w!!9yN(^p{Z};oLap^c< zT@(;tyl2mcG}yCcG>sf|j^y_cVy)X;n|=K7hQdBih$;Pcd9^2q0e_-kRb4L+Yz|6w z2$VomMH?Xvv==OA&FnvYf$1DHc*hWFFkPCXS+(18I^&j54yNB8NWD63oJ>2IqOqRS zY~JC6X3@_I8{Zul1mn=#453FXl1w9!Mj#4(=4cunxYsKR|en7FzFpkes{pRG-aiM|3YQhn96N(V=E3NS?ehI_fz$4+?wu0Mk#igN6++T(p2)}K>t0~0|NzI7wpM>*8mT&{edD(NA2*O88q9Z8#pQ$43$53 z6k{G-1;i8r-Fc2lv#T8!ElwKUYlJ1MqnZ!ed*SW zSA1;F5(JRu94JDwkMEjAI=N>5{IEA|*_t=@;RFusUf;rHuR0a(5F;1ag~3Qe<nYvg~H?>Nl+!bz9h;l!FZ!K!s)`f0>~( zb)&((p{U(njxIW#n~hVdIPj)lbK4B`2+d>)5SHw@C3s^skqzB_(3McCAE)=H{TV_N zIqK?@u4`QnjWQh9_G|77++S%#qC%K>DX=FWxocjAO%A(~wF} zjXRwgQkjm1#eqsQ68q=iCb-)HNiYszw@xFFtxepiZt&!0G@~%LV0+tb^U2tfccz5v z&GW-e2cj;Sv9+X8f=`!BwKDD7J6n=U5G6U9*`DSSEup*E0BKg;Pc)U+XBGeLnIgcx zOFTZ#GXfI&`fPg|H}kovp#N)ds57*|c=H&6re$@lf;$UfvUf8mjk=bheKUUt7F-~~ zyU`tFvI(W>9A_+(G|&un2Mb%`(mSxia$xW3iLxQPr4ScSV_|fh74%WoNL5c$=?+(# zDbf_0u|F+rPPg@lgPq^t*IY-+_UTIt`%_p?g|?^T)YS2Xxt8Wnl-8skSEadrVyMSW z1(Qwlh(NM)RTa~nI;?p+QL18%(Zx6pbbWn*bwG_~UH9@!X4p_at1fN2>6|&6=`p?A zIJ;1%WAdG28vXE;cLuB(@g9M(FiNIJ3NvoVg>Qa6Y&W)z28#M=trSQg`>IVv{TTNn zoYr8B#b~B@TwC^^MPXWw&uZzk=JrpWmT|Y$u!l31P{+aWl8yy~y-0|lhna@O-my!t zb78`4f8Fs)rZt_cu&5N=(@h2ab{&l=Y58ZL$Rad*7!B+=m;*iey_rv<4TFH6GM&fY z(s?vS=}@J1QW|#!VgDGzzy+yInTq>ztifI=3mO~2Uzguxh7t8Vd03QN@p^%z**KM) z6>O~gEnG~vF`AJW7@6CDJB$hA2ATsPxo&}GYwVTJ>YyCOSGaN5-mcp?;AZ8-;97Phz1eY%K487k{nR{3 z*RU`~*%E}P{;QbVW&D(ZOuoOg-0skmT8##;i~+&f{c@l&*bRdv%&=N2p1)@rZ^h=? zg-iDD5Bfu0^B{p}n=LzO1OC6)F$&wG;sgALGqc7PGYPI**rv@b7@TR^N0~>c>@W^A zIXkLF=P$O=ucp!?Wy4Uyg z8F>|@0e@|5@91S|hCrD%&Y(UA!^>akxf!|=63UGeQ*r%Niy03_!$g2uMtD67(%QyZ zOk{!*?FAEP*0OO{&IIM2-dMtq#fvU9P!brNtNz+AO@-5OsKw8+{CBM__#U$6PQjgB z;xK5oCV%LfDJW|i{wTI+l|mzm58jg(I(awoTeEbEEnjJv70(XY=D90Pp?GErhku4l zd&o-Dsf`kBD2xNvVP#sl0T4P^mp_ZW1P(!HFb(I*ZV#FGl8k0hL@`myofjkYb{3>* zG!&;>Pl4E}dKL^QN-CY#Qwwu}^(<46;ps5Lr0E`l(J^71 zp@{BiX5U8B$i?^aD!Sk<+EjH8(TRs&a$(Vo(ug-R*XC_o$iRIpXR@9HdW3(0h1;sH z9sGPTr5?Irb~IRzULUCZ3{uVJ^}fQWuU?Io22B|6n zgyd$%`F12bje`UlF@;=pH=e1psTvJ43}Ezl&cdwNZA;GVj+6WLuL8%(u&>a-G&3X6 ztp}D_(i;X*wxpv(33H91eG@Pa9Ux@}mqrnvzm3CW1My+Hxq6Z!0n$wR_-js{ixn~X zg{CJK60i)y&jp$_4w~FFWz3J$tZ5Z&MnZySCNR10>I?xYO=Y=ADcuVTJOWWwg{IM{ zYVoy0voS6PyK=LG==8&k@-!88M^vQjZ_ki)f6@#(wX{!^r@>p$aiGF8lLKe$AL-P3 zandw4${sV^p2Wlpz)`%sgP#ISllCJT~#w=~pju($l~q&qf2oQDQaj@<5Q5 zWK%5(3pMXOVK!UREu2!dlxMJo%9iIVG?SM{ToLZmp?KO+Oy>I1y_@`!VT;;Bo{J?N znYjUeoIOm{n2X8B(i-M`9Nece?9&%>VMp;wIG=wxrpl7OQ#_kv(#_IcW+AI5{&Uug>5prDSZJ{? z=7Qm3hFsji1Op93fx8qbI|<3{-~vnG&j}jSnJhIz-uj>kW<&(LJux~IXu#$yg<=p$ z4^N@{WLzc)TGVt9iP{nu&`I+hP z)t2AD3KqJ1TbypUdSowbDJpW$mP8Yl#X*#q8r0%e;^*Z^U;wLRe6WRePt6dQi~)*1 z2}UkYG>_!G1p^7!#O2*7O|!#Yg)!N}lA5rj-7U>%QMNHF2J&654@DrwOS?+tzlcR6B8R?t7K0E zy`fo_)P@MGC*p)BPb1kGO2auFI=1nB*O7d26`2F{8)=HywggTA zB5}jZNSnb97%5>*ESqjdL_=ZqD0g669Rnri)IvXv%21iR+YUhNu=1>hS2=3V$#$Sj ziD9`^G$CT0rxHn7tb`cCNR5WPj<`bwVbK^{88}RBbxQUP53* z_C-xrT_hl4Oc+LG)ENfsd;K~BAyD+DxCOYrxgA7PoFwsB7!%da#Ijj6SbBUcSV}XZ zCI<{nB|H}T9P}38DRHEfLs=P!Rxu)6aT*~t)V>c`+-)ln)er1bEL0uLiq##*Knyw6 z9SZsIS1?|Uh*~X}O5E{9m>3~ibmO09#5Nf8s~U~y;5((jcUG0uDZx(?^r<@HTY>4N zetc{x&RSTC|1?_N(*~mFyFg=$3g}+;GNEIV%dHklqsL}@FC`iniaWW(w{4ciD3N5P zus2g!_pZqf4!2Sup~^EFArnAsBuR&?QIUgmCj1MDaMLj{^o;n%H%md42)B4cZH!tB zfp?4s><-2gxL(vP%hWgpT~9bTMpC_;PeTkt(sT`cM-6-?mKxb?j5L?I1K93vXQOVWh&OH6_~ORD{tJ zHA0wrHr-}qUf?GDbhwp$8xgcBt|=h1(0V+!V6mvJJYyQtUp9}g>s&aUp+QH>U7gp^ zvWqx^YXGpBEOW54+vb!HtZ71{o>NC+qcP2;5Blz~x57X|BP>S`s1z^^`z}h<1#x3X z><(C~O)oDIXarMchl=J2wr*{P(-P36`OJCzNj3x35Q|l%UYs#f&dEUDmGHpohi6F+Q~8uME&X@5>AlftD8G(pI}hWCi$8^ zMY1+QCe!GKR8{rn(>e%hWKlT!FUx6#q%w%~BQi!s7bH%0k%R>sMpCA-fs-v`&k6p%_ z-X&}x)L?_dT^fw=b@rs;4jK(3f5Yj3LNcpGkm&GcA*&BeS6Da}^CFX^@v?nxJF5dM z{=W!*q|OI74&9WxgG=)op#k`DrWTHp2FB?k4q#Xh)A4Q1;6BhchpX2o&8!wo-#M`W z97pE22dv3XVlx38a}}TVQCCiukFaZz(zTFc!m@9R8$$c7%#vSmoY^e}2a-B5X@7_P zVV#qHyLN0Su&DjbfTFE>J=9Ak3E&#|Bi*eWQw?c9S>w$BGZ?mHLHmK$?LUi3uLaz< z=owIWl6zbY@@XG5nHeuwpS!8p*&_-L(E*XMLnOFCflEe0TZ|{_xe9D+lf9W5Fa0*t z$4r^DtpRR&WqA^H__t2RqqZ?oF!^UC8bdjc7j%J!VoEa-3&Ge}%UwwCaBoSoo9ckfFRfCS?eq3pKUg#Vju2Kj z(XgD}>$ELgUCLC7Pddk~GKVHZ&CF|Tp?Irx*haR6n}~-nVTQl*pxL%Pn*h03vlmMZ zBB$8)?4~v8K5K|Dahj6{Lm-LPy2CZ3A37UNW@NL3Y^XNqnN^xf7X+2NFc-9NT{CW> zxM#X&n2cvvqrt(=(;nfKg3T)5%`{plNr#t9j0&q;F;iZ5qCkdQNW-Wa4a6;&VHy4w z!Y;B~2)EeE@4 zPVD4qcC$|(%v}uFyrhvHeL@3bdB{H7r)@|#9S7_yXD!Tfr;hPeEHMue41qSL<4>Cz zQ%V9IXh!&n&2VD2%Um_JFHNtpbQY881*qG` zg|JcSqZ&RPxYu#qLXK1ERPg#`uTN|jvS$mxiD%!Pir%q>L{Q3X{n0#4ds+{D))2W~ zlCsdQDt~H%z32U=TT0QUZSy8fAQ`=1GR2aSR{Br3kNVdZe!PA6?c?X< zx8XJZtj&MigiBGMG9zr=y_au8fFX$RQG zRCxuLa`I~kiiOh9JfqJ$i1$jBS!f6$jdir~X6iVMrx5>X)y!#rS`fzd;ouEpk>EZ-(RmY0K2eX7xHR=3v{>ZY(^R8f_N zxVCtW{uwsbQ0O+}`4AC{Xr*4fXC9=`WZ^EmczN2G>~W04R8*qm`tMquwTeOmYqy^Vtc}yQ0|b1VN(XDeX#*6RI7e^m;MyA3%vda-MkK?snZGxyt=_c zM5CF~P#|QTV=)3IPa13V|?EET)~)|3AY`Wo`#OOeB#+ zd9Zu`xWJB=Z4bTLyoFJoGU%_%kxa|eOkU-MCQ-BQAj9*#7~xT{e)pgmtr^+h+jduEx%RwXYvlUfG@GTi&noFgyZi5n?21Z3x@huViuEp{2rU}$jF zu+1=n?Pkm@-KR?^+K0L!Xmg~I{JUDKcQ!z<)n((r_GS@W_f`8HKbv~P@DOK$0_lx2 zXcAIlvLwTOO-%uFORj4fUzs!y)cOb-fEb-i{DC-9x==hxd0O#UQZ&@USGCs4F$@7Z zG7F2HZistXY#iuol{X{%lVIVJefcJcC1wl?3D&9XU71NxC9fj3{n)M;+p zTv8F9v6FtTf!nLwycpkt>KrT!Jl?MCfbi91y46>Pu)I0zlA&eMWjF))MxSacl7@Xe zPCGb0SZ2QUu!wdhMJT~&n9$O6mx=Ys`2F&pe{j)h+-ZwBQ&K2(CS>c<&)Q>&w={cM zi%k}7mNc8JM__o}n^<|MFrk`XrzV)X^{h5j6@iTgzRfVX_Ye^7UEd#lP>e{^sp02h z>4ef$!Y~QQxSMOIq3n`X?DTG9>3$sA&$gaH>^4DCb;q^kJ}3=)(?B)$Y-Qj?vwge7 zEB5wA!$?{(y`<1x@ZCzJ>AUD>5e7%_LcE7y(C@9EMGGbC_M9IVxJ_2p!_K2s>KUfH zK7;O{p){Sy+H3eix{-^(U62G@De)dJMhDwt=sG8zf^ zwCp+aC<>}h8d~QX$M)ku>#}jWUCX^Eag2+kcV)ClJ378Ps{FdmHS>>0Gk<&6}d_?q-Jk97+%uU zX6y;;s{Ha`aSfPd)mFUu%5Y!3aARfCI>yO@ewV_Rw%NPfqV#pO#j5hc4rFeBtl9`Poxn1*e#5zs-i$#} zpD%nBfftUIRcecOgs#UR1}N93?z12oG5Y2`AIv73y5`;`V`I0#zllk0G>v`|7JmBm zU})n$kX(dQox!;_h=J9QaR>mnXmj1l%kuZHzWwfVQu*=_ z2<4&}PD&#n%y>mUNU)0NX+neZtqPf49SvIy95jp-`_v%POsk9Cd(z~|KI2qqh8btn zI|v_YQz z;(hU@&S3M^?1m)O3<4VJ)(j=zyd!vk(0?h#JWWf#6ai)qi$DyHN|c++dKTF$<)Bn^W>eCEEG?(*#pDAixtHI=L94yu2B+k z&6eifoQnuj_UWC0*WCiYWLDQOlFTZ?nZkwN_26MdJP~M?z~^?CcCNAX$R-!CXLhl95e*q((2KKf4@^E@*Dle+x&KB!InQu{3COPyG#lpRSe=0(6M z5Zq?ELUPiKlnzv$~}ERIks{(V$iX zJIJtTj8O7!sxp?j)|xcoW%ojN!KA#yq7?#{$F_uiE~v_O6pcCNrxkMTK(fvA0pnm2 zbu{2%;wn$O@2wN^dj@awX3~`fE0}|Mf_U>YHNnj_5;i`hht7RC-6s~xS#%TQF#NS> zlE17ZTLAsNEQS`G%=bq8_o)FPH&e$AH+MY|jhG1(ZZbVtMw5p|a=bnR1*03|?rgBb zT7Pd2Y@)>2I8`G5y?I@Q)#6K!s#B$>(DKL{j2@Uvzi)O7p+)4so%!&3o@@e~PV^!8 zsn#%A++kWVgQt7TV-IHg3C&1J60U;0-pn_*s0f}0OHiigrZ6d5eOeA_S_{X8L$>%G zYY^IViDWFDI${GvDLt{Q!jmw(q9nw#)ljlqGEp^kDu}+$OwOPYGV`|`;R70GbT;{i z1))`sFa#rT*g)tEvr5adt>26+5{S+eR?AfXr%Og;X?+8pRD2p2{N@D?% z_r09)W{$woraE7UN$b0C<5rk1!d?PQ-kP!FbrK|cAu3|MHrGBOOW*flI|)OU;Rxnt z`Gx1&j|j@lk_{EBH=r+Pb#5dgWk`j5Y%!h*@h(IwNo_2-nI~dbV%_j5=?MkmDfV<{ zj;4-81T1yq`h~N4(cJeEdyt}Dzwpu}4PJ=o3792x)}_j4aj%6-$()QHYJu27{vc{B z7O$;@q9Q5FvcSaTu1Y98^mK2{;=$nOQ?ZNv9jLvUy3Pv!terVSn|Xp>8IknrUa>Mo zP~LP&?8U;`$Q74#oWua4OGcQGFLe#oqv&63Un$V*oQKt&Yq(UU%k4m(} z@Y$gmEVIYPv8G0jTw?hsZvt?L!B;7cPH%k?sAWkiX4#i7* zv-P-N!B*;0+2b$W$^t(qM6*2Ok`toVv{MAl zx>XeF&`-zICnIW1=VU99@DSLj+SWv*PsX%EUAG`g+1GKShzl0iL}Npbg}`uGyN3s& z15uo&8>5#@Me|RJwuch+#4>QIBjJGe6c`K=Tb z>j%v_R1j}A(n!Zh#1sKT@N3$Mh%ib-yjgO(a4hs#7+1lwV~J_Yp=zBCx9r{ZAqgEN z2S~Tg`U~B0CV()|5+5f~`;FH#SeZ15aC)Mdj2*Jz0-SEAaL1v)O8|R_wbvx(9`}jp zzIvb7xOHe(8b2Ld#bhJO>>{+{4q%hB4MYo+tqtN{68Li3K*Wk5=%&8}Y@oh%FBur8 zewo8{B8aPP_{dFA1YJ&s5EY3V%bLVp_Y5k*W-)5Akt$xqE&=Xm*IAxun8U~rh~Fe8 zUO#;s{Q;{4T(HeHF6$uTJ;CZ9_N~;WU%h($?YmdszxeITKVGLcwV!wV3({Di9R{l= zC5+fYP(hrCPc%~PHoSwL)sM<=s)E`w*~YiF00FipN!gdpO2VE%tZj*HG>8ykG?fgn z74M_~gUH%i21)}FK#3p~no?+IY|?b%JL9ed6!uh?zUs<=$at=06%4YRY|VWv0mS!? zPuxBeNtNn{9-Sz zm*K(cg8_Iu$9kAbGb!4=>y!AN%F{t3C05*LhrZA3BcqYNgM5m9pQ&3E8;9;u);RjU z6iE^oNirpD)kD!7tBZy$S%N`@MQU!FYoE##n7&Faiq2y{&ZyeN2l|S%;;NwzGV_#@ z(J;TW?2ucR*^0Ox(^}|F~V$|BAO;mZa>aS$9mYNl_k4ACPn97vvC;hXL!*2 z@!1MZwU9=a5+P`&&*Nj5t_`J;oJc7%c2|Qn!Lo;JU_T_+Jv3Ax%&~quvv#Ew!umv#?fl*kAj;qZ7#^w zcG@9#B{TEzSWYKU^z=zqnHy-Uk@St~JHkr)i<*wrXrMMEG*hGMzX8dwCBDGzf zO!W(Rv(nmP9J@@U69#I-F0#G(Nt;PF_K{)Tg#(?Er#kH!vbQ4LVzC7gldX(K(mAEd%v~ZPBzL6|KRHn!!yA)! zmCDM9IK$2^{uV(;9Lj>|Kc>?+B%*_+XbZyzrJ+lYnZSN$AI1_P?Af@B*%1D*vcZ$l zWCGQW$flM!)`dnYpO}E^zkMk#u+R|X0Sb5QF~bP{!S3i&Xd%ANem3=B9!r{AHXjCL z3F$CCHMyS32BDrtyVT30hxj@{_X=+wJjfoI-~?vx$QGKZtq?)OK2>0Cp_|Px%eFD^ zRPZEx;ZtTdlhF*?J++1|JtbA^=Guyc$e9E0yCKkOF#fWnuP}|#7 zbfs(_t))~J4H^v#za^8;T_0#a&1m{0C}8ZGTWF!`HaBSm1rsD3J8!WbSKI83TZnS+ zz8y1YU%W=FbZ&FV|H0OF^(!=DWV13Q4zshVHVo~v*w_p2((9oef`m<}lUIxjo0K*q zVd+vWp#dNCa!Gs=nl8;{L)jer%b2oC_$6?j=Cgl@d6sM{FaoyviOo@s}cn0c?6Ltv;@M;Gd(HfP8=u~VY8|FGZrj5KC z2hqKH&7y&AB$Ij8BR3!&wGzneO><0NR9+nQ-D z`hGXFs{^3H#-I^gLXmz=S~o`;_7o;Jh<$2`IlvpS1Ivk++71Is9RErpF%!sbZ=%#R z4&9mgf&}R4K2eR}tLkHH%xslxe=lo^0buVK=g&ORQhXJIv{ke18La9Rx#dvLXl2_t zq920kdNZcd5p7$z=u9tt`*c8pATGVCAX!CIh#d`?tbIFyt5_oxAjBXyjfcTdqhFX6 zl@8X6OiZup%4|JVm@RobsQm99fB5e4-8Zm6J_jzp|BNk=C9MsVkwcPhZ9j7E(H9ys z&M9qSU^8RXle9UGVFpX7U}K!UR?^X|j&@RT6Ie%u0CDKig zA2glEPjr*yXll3hun3&$;W&@8k)~}`)Iq}vy5ft~Ni(aqxUOuF-nFTuA7@YXnpYO; zOG?7@`%`nSHrJ@y`#kw^s+6t>&Eo1u3QMkis(h-0Qkk7^i(hgbrjAvj+81?#EJw44 zO$G5(1vxtTk4E*zE`Da^#AEtuVlCe6;rIM6s) zvhBh%i^a{owPP+;<~q%fB(0gJ5+}m+r;?JNs;{a_IA~ZiA%*eD(`>u&Q!?#BKkdbu zWb4*jrvRaq<^1kTijtG9XZIwb&f(WXuy=D}i?_dc2E5o0*yOG_%!hg*-8MrE*tahU z%|XWc&zke_2Q$3Nk_iV-4w{vYPsbii>7p7wow}d-j3pZfW_sA>)%P|Uy2aU3viK#h zG1Y5PCHa^g_QGeus_I75iy*(btYoDb=64@5SI%JzcOKRJ?vp(naR~ww+ilqmW40;* z{~RE?!!YFGFke}ki*&zcC~0-t{w$_`O6=N?qop$9`598Qo&EJ0 z0<>|^h<#r?!yE(^I2_$c`ezlt&x6@*PadQ^PP==?GO`vJsxMqHRMzlIe(C`Zb6hef zd-mHr^0eqEn|orTmNUK9gVg)i!^gw~uH-;Vm$X7LWt937b7h*psVx8K2hAb^PjzX9 zq?wlD)1>S+Ldb)LzbG*WwLu+Sip4hEqF=N4{tdu z+jvo3LAKD$eK>LndRSWvhfYqa%PF*)p=i3u@sfTkW?z&hy%>gTG_hEkdw2lY1D2Hx zuA4Y%s)-6_>eG$6#&En)%5H7VQ_?=FYYae|oe#&d;QEW=>yARizd4zR8kEZ%ew<#L z*&xg^V2ST)e$5mF=8O?!ys}*)`I2(kCnpp8F%Zw$?{lBk&I@-74;iNyUd7NxaO(Kc zmNnB~!;zbL5{ThRdnb7f0m?!ZVA$a-K}`{Tm( zv>+tbPS5??;pwpD%;5&8ESby>;6rG(#?&!KE={om+&Ejpfoi^k!3y}hTh4B#^fj^u zDv!>ewWKA9y=3;_-D(N@appSAIB}Qk7N^?X9^jM`Rfe|QSIs1?uhoKW(mO6ZuE{XE zS+KJZ>0vj|#0XtwaLj=Bx)}eHm51--ao$_tt-F)Y1sdq-Z8Jvwx{gX z!b>(zvwD@Kv2*tmNnf!?C23k!Z;QCq8DC3YkD#wvg%>q4LCZ*9GlZE_i+xtjGy`{A z?v*w$jb!8WLcFLoy{Raez_y+tjoC)^S|3yg!B!9&X)ep|y35hDv1~n4qB_ZS5*HeC zps}k23&?#-WYVzPtk$Ky65WzHZZ?GBWJ!v+J zPHJ;qoR{z%4MY`|^^?hWZ@)nbS*{zsnuS2UK}VotZ5T}zr`FzH0Vlp{(e4CJPHynY9sk6!TZ@ zELeX#PNRE&PGe^DwqclTEdW;Tv$zPG3i>1S391(s?8>W&CWSc_RF8r~hb|V?z^PyH zHWSlkdzxmibWdhZv)`NB9T@v+p0MjiYKo8U%s|%q>CMo9CQhq+bCR7MY%1yDER`XF z#)KC!gV;&NBOQ>Ynwye$hbU@3FOxKFn5n4kTcl}=GszPX?UaUj8>|WlrgWNu0Y(}& zP^amdg5QkLdrHF$BP|XOK00Giy!isMvy%TV7*UqOme?#=n+*jY-9BhKL2U?k8T@9c zn*S{cUUALYzb-sXOK`3NGpFG2o~E{ghH%$RT@Ee+g7c`OD%0c6?kHZ(Vxu+HiYv7& zX4$OZ9%16JephZ;V3UGrPnxYR6auYRP2Y3tVWTqL=gc0o%$JK!x{{X7H6=loSO?xo zSFB=$4)~okMj2>^Is1stNDf8p(_3jI|Mco5^pWphzIl0VGoPP#qU{_lDbjq&Q~C?Q z4slVL35!u1ffh(pB7v*jn+MQ%5S zkIB$v(oR-|AURc&GCj@6+7*B;uGtI%Ox(p%=3GPjeUZy&ffM)PLkM^3AwtvnP)iQ@ z`EzL@0%?elr0FW2MjCSo&1RZsEg{ZLz)0=uNj#U%+}G|?@2DFnRCpmI*ih(~?P;2Q zd&Otd{gHi+#ALmBHku}=f6b#1z+9*bvQPLEDc%eTmy75-x`%Y$@MdHmriBsSrmbg) z+nHbw4t4{Kd)l{4bDK=;S3lR3_#tW77Loow53abt25lT%5{UpTpYM$o)~?^q?!64< z7N1CgV?VyD5ti*VLSDzpt17%XL%2z5Jp!^)hVrCWqbafy+oU_F5Q3uGk|)8!Y6>ve z%c_k@Be*`&bi;iXOY{#K!vAw80?o*Fof~_fGFQNdX^FmMFR+R6U0Xn4T+GzeZ)Zp2 zO~cb85VNFFo01g9Zs-ON2o*$V()N@El9KJ3d-v!GtU^EL8U*pq>>=a!87HB=Xh6eD zOQ6p%&XQOaX;xNw>fs7jJvsvvteBB^>QU|*vR#6$Ssa;c{o}`JM`K-#!-hp``GkT6 zZQfp2Uy@>6Le+r)H0e?FotuDIdcF&tvL8#{%w(d#j3`dlcNH|OKd3phAY!XBMf5L% zVoJ@}XZeHCb!}T&1X)y78&A{6uutC-RiEAGQLa9=wrnh{k&$N+0LNI*qw5(gEmc%0 z_wA!vb2xT}P}GC)EJoYLd9lk`DjYnF42^-m zf}p6k%6&To6C0VQ$Rj< zz&~3p9piM>#)eOlT=*{SCC0hfX_mm79x-awYM(W`eERVDLy#eD*f^_*edcRcz;K$~ z3>K8ITqj&Y`qze$(6_uoTHe~bn@+RRlD1sipXVlmgPu9IlD(H^fa)b)l=e|6EHGjs zY7Lk_pwZ^JT=80`6}p5GH7VOv(yb;lvD||=u^+I~v@WfYv(F3KY(Hn4GnqVHdZ^mi ztc|eUQU7*}5JZH;&Br)1D}1L%O*;Cs7HCOKh93bglgLWPID?=@Ox6w1N|0Q7zX-ij zHO}IpmCA8XS-VZR%#e7$DWr?7nMJg0vlPZ@)+!4$OS&_LE(YwEXlg3Ea#{aY592fs zw!5X@Ew;zEC}8QCJ#|=wt16bChjB{9_Ghz5h|)Fy?a=bm^8$OJb}SW-gP@Ki*ZEDGCjdSM?dYPiNx9I%u{TRi0)gP z=!!r&bziRmf;ZUU-KWw}B|E{W4Sh8$Z-$aEGrpGK253|ov(Y4J z;gP(Af*CEn!DTt@5G`fxu1O3X4K~iAH;wMdKtnIM(GW^`6mO=`Z_#wgBT3u$6`C2d z$-5n2_|k8{i=I#7!(k7|-sL7^cI5KWQLdXq+n(h#~( zvus?SoytlJr6CA46S+b=qsoj`rNQbmfyF}W)+&iJLIYu*PV1RYpu1(=9_W7LigdjD zVIY_E)2DWFY9(|zOz>;{SzSjsv%48x3(^VDg6M zoP!Z?k}Tq&GU>>{Xa@~T{iEi~FnO|X7c9JI40Bdx6q^fM&PGlpbPHW{ zs4C6ShEO9kmRgcmBs8;#KPG5@ZB1#_0GgecQ%PrK7zZZ1Ua?KXLAyQqCRSSRMH;ZL ztYxyk8CGL2ZKYvGW?sSN$FP<$C}Mi%XZ!5J04sW?TOv60)W&95$1B~$l|Qk!f$zCO zFR{^VmY+d5driEv8qU6J6M}gKzXg#NjD16OP6A@&-9w=1$!G-MHA8Lnou$}dXV^H@ zrKQt{8z)PrOdV%Qr>hn^?XbK{wh(rhU|kV3)b|3_R6)`d=G)+^hPE|XGiZc{Em2l* zV6*lt)nk~JV;-)w`4r#qYYCPvnYFE+pG53Aq}0T zER*!`-O}Y#cQAJ%jN-FAi|@5(=KqBPRzW3JE|h-J=3vS^r#2@)H4 zZd(pia{4VokBZKUJUy&Jr#Y>r(2))Ir5la3MJlnUU1wHbF&gMbtef`Ls1RJn!WQgF z%IqC_Sm8vMk&MKm9&BlB&MB>CNwe4&09QLSWb32alPHogP#}5y#pq0zGn&fuSumH- z3@b@9ETLgDy65`8G8t8A#M~j53pZ?N9UEy%uF_mNtY`G}L?ei!J%`43xKO(pO&7H= zd#NPu=wvj~jIM?PW7ceTn~OH_X}eSll0Z`HHsc+VW7N=qa5B3{NoHZ1V2VAgCfk4? zG=sgc0uFeG(o=1!)z(s*+tbjkYBZCcpW2@3@m0IgY_?&97z?dk>Co3NZ%N`KJN630 zTd-T$Qf1a_4>B7X)=<3%f|*@^pJ|=c>7TQ?nxRJa>GZStsvt=bp^ZU%EW5%Gmtc=maFwV&9pdt7Y!*-!D*(mnfK~v1b z<{OjEI*f*prYzqN&5CG|W*dh|ZW31Y-NMo~VcYkVjw3NZC79Vh zXxO+^P#*nyn5d*Q?0BoWs5gTd`9I&j`|#b1U%&e1+bb*l+Wr}PBM8se>(s2(O~h&j ztkSuUK!{q^%*0Jih&4SeKnt@Oi)OaqZ3tLGcjmMZ?XGDnn+>UnM%MzkVP@mLu0-r1 zra?Uie#c7YYHOObE7|%gOgHEYH=;^h>=l7diI3843$~!NFW0z7h^ArGZmqu~`iMsx z%N!p=sLQmeEB8YoF4B zH^Vpzn1m$S2T|6P(SU%MC$kW3x~1X#fR0WCrDbax?dX)`iNNrS?bFhB?hb%Uh67h?OVVYV1@PtO+71)4jhOD`2u2H5CNvBdF z0%zTd9^vm3uC@Z|?YTNLP)3B4B$SetO_Nj7rv|a=rz;Q?n(|5?Y!M45fP)v}A={c* znTE5A`&8(a*EKCkln}Uu>4{d;?Ag7<87#0VVY*$7VZe-}mukB1ZX{x=un|qs#b$(> zr$>M2*+J+K)ttCHe&Mt_^z63E3#g+0?D)fEM3Oo;OYx^dY%B#3VS=JSa=Fq@5^*za zpsHcf9#}ysM{+Q1c;*~T*B3)+a<|!86~75IQp*N)CK732SZ1TlARJLR-Ks^_ldaBX zXAon>8&`+mt&s@k4BwNpA(Y5+>qx}5&NGD`0faqSHXZ$@YKZyi=z%sO0#ML{fj53L zY=E~vLIe1#w?Dpq`|92QSUHi6K%ipYuyButUCO})*z!=fQ)k1Cr5#H>4fNJ+Gz4Yj z1N81Rf?-Uf*@8?L5GERIbye{>m!NCLUiUq9^*b@ShJYy}Bq9i4ji|YdbT9?Hh438f zJ=BPlX3sQ}st=+!$p39LJ@MOFukYQ%)r@|YXS03@7uT<6NM5|qNUwG2F7MsL^O%dx z%~%{5x@0lg+|FY0`LHk|&b5wxUd)0z?Ha-l>{Fu0wb;6xQ+aO?EI#w6frhO{F`xGf zv?;l4LZj7Ys{|*!y55zB%2cf(0aMZeZOKau_ErcN5f9*?8H&^tf)u!(MUQ<^vTGki z_g!l=wc7elEfXv@AaFJ&6zNfHDfoph5U$~ph7w9+ng8C}qQ__)JpkiXHy1PCC}2Ho z1Zh|TZZty>L1*yeq-Fuqq5UrbzW=2q@^;kL`_Ub;S zqHmiqRPXfO<~zVJg0jqE+?VH44jL)^tPvcL@_pIv48gZ8%V(ppu6$=H82q1BO2puGxFNrO}4a z^d;y>)8|EWNk~$fDRC!nK9Xi9yBS%+472#KO0U{%tP=r%<;SIkENP}+>H{1FEQWQX z0n4%1u{0J-C@W@>&j6g&nF?z(gJrC2!yNt0TSHuf5`c+G!}Hxvz`v<9lDbT^9Pa>? z0z#uHQn)pJT|mcy9&;_0aI>|t-=D4>QaTX{SWYi^m~I>fR;tl(WyER=4q~EL7)=sb zo93gaFzwClHt1|^iUbm9<}ps~3`DnpW(7kjYxru^E-eat*|E?pmC3Mjnf%6HDZyc# zn=y^bf=1>IgxJ)`Y=9^Mr*&)%c58^c|4Y5fWMh$y7`qFsX zAcEUgWD>vTEqHQ5LyMx8kVyubsrd(k5Wa-79zOhu+q3N{({DyWxkFTGs^JP6dfSct zc(-=95b0otBxa<|a9&_O8s8gC(AtV)5!~9_*iTp8rwpIH0&T`zyRcYkxAO_QB`*jy zTh0*Z)*5~OD|HRjvw9IDJfOpC~ zO?*LE^Vk|4CK`jZ3W;mpSo5@Tc9J!Fl*P{GmAZvPAU|0I=h+x;rY16gMoQZmqv@Vz z_hUVU>F5O0_a1b}*KOl4j)&{vQ^W~cQ>)PkI4PTixc2dhRUS&ym-JMjT0VHa3bev+z?g+JKnQJw(I(yi4LPQOVWk&h)(U6i+$EEt?0{f zONJ4nVYe}*Cx%)jYG{U^+Yu^5*q8$pxkm;GkdIE>dZRBwOv~;Q{dMnmVPZFRK_2$9A zSPi3f(6|7VqQLi}B~5RNnuT5Fu@Bb)i6r967Lv|BHoJx|->cdMo7%W4JDZ#|+k$BX zEGFz28@BIFDPTfTCMyltnSiP$e^Y5?t2-Ao>~S@xM^_|Xd$A>NBJN3Q zMfNm?eoyr0#V;RW#l_&Iskez?G0*ufX=+n+E@m1)Z13&CFc{LdBL`1iRubt~GYm9} zekwu9-4SJP4uv!5QuWCeFTSK7EFjyFFTpu21jBMPIn8Vv*a%&^X234X*&6$FhO}wI zxT0jz5R5}Y#H6!R8INIgOY$a}IwF>;0eW*5cC)uNGR z8)ALm0^rV`4FNxIcl~lcA8ugl0qw9+GgF$vN*MOr@#~>GtSL>USx+s)L*qda*-Fx- zp1lM>Bq}``|6*%8ai=EF;h^azg^*!Fym+=}_;jX<%1Pkjvx@-M9j(eACd|6Lo5D$>ZK`Wpg_M>kUlzHhu-*SLIBio-!Gni{sAe;lD zU9!$UpeceO6OjWM^0m*Jtm}g$jOcl3D?ZGi+Q_3|J^QVc!oPa;?!W%{_&Lzi^XJ?Y zcx#hIsoP>x-;^{r)pfU{yR;~c*q%`5ya~fD_T!)te4ISIX_kiK+K{G6hlS8_jt%O! zkyNUD&vZe7O+mMSPQ^+Nc42c?7Z@_#6#7|P@aRGn1D;N8$EOpAf&B~ol34>WY%V4U zmKq=t($ZM2jH5{Lq3%7cBFcz-7X^x>_xf?#=>7OErhXCV%B`72?KYPnud+>@U-Md} z+5X^(F{n{|um-b_(v)T~Ak5aBelAv}O3^5R2BAj9gsgLOF&9L)r!#VjEez?{P(f$4xfn+}&Dfy|Kij%JYz@^;Gh`&QxnHYps&%$;h+hHgBXm!Mnq^ao(Ig#L zJWVSKk+KNYoMXdO(yV-J;3V3fjN^JwF`la%2+>_wWfgq(pb@M-(o`0j7p9B(aae_> zG`&eHp6PwbxZ*>Lwqa}1ybxczZ|As+BK|CsrJ6jWT)nFcN7|(T9f?l5{0K z(8)%>tn2Z?0VpcPE(Y{-zT%mE6rJs_y3q_e(N}QpW^lM>Q!PnW+Mx=BA*oi3hG~Zo zf<9e4GsbK*o6Qq&aYJ`NMn7$?Z8-+s^#;93LZzTtA_>K@?P(NznKaB+rjZlJ!LzY( zYL?Wv*lTS_&8yJR1;84j*rIX`t$GIy9sgu1o@B3W!wgYo`6S$OW+F_I(k$_cgw{X= z9oEF6wKfw-@UCM^yE;u1k%4%7yoZy)+-yr;%$%R0hA7h~Zo9K7jm121C(7PK$_;Q> zTcUj7v#|eKJkJ$FviTe?>0+7ef^G6ULSTjEfMFZwX1nnY-6=HQ)Z#uNftJEdu%Lov ze66BwkrQmGj0){v*EY}>8k>wy)eu5w<-P(_z-VPs1P&tbCT7UlTzp#)qM_Yb-on|0 zW;fBVh->n%QRBb-;&0!+{^s%BSAV8|JpN?lnlbjnBld!|Ftt5><7P-HN5!Y82Kkm1y0UkKm9jBx2IUtRNFC`9HjpD zpW0m6%JVUwF_EtP>T>_DX33z!^3GZKW(sZnde5(}_y209@@p%qQAMkjPF?MuU)}Ei z)otLY)eEo_k%Tg_itl>X{omkSk74~6p+B!C5&Lo9cRl_M-Zj&zk7^Z7^@J&K*RP)U z|7z$&O-mCNj6f=B(tYrrU%l@C)te>x7X7MO0GZPM$>wr5`RaZDub%7{tCEX=GU-HY z=KG#?PX7iEn=luurnE(^QXqE@KW6#I z)8Nd7@tmlhksTV29Uw-k|Z@>QW`$z8SmtXw;``2Inzfem70u%!j00008 z0HL*!T+3F<$iQX*09erh00#g700000000000Du7i0001FZ(}ZNZER3W1qJ{B00031 P0RS}s008A?00000 { expect(res.body.summary.totalPlacemarks).toBe(3); expect(res.body.summary.skippedCount).toBe(1); expect(Array.isArray(res.body.summary.errors)).toBe(true); - expect(res.body.summary.errors.join(' ')).toContain('missing Point coordinates'); + expect(res.body.summary.errors.join(' ')).toContain('unsupported geometry type'); const nested = res.body.places.find((p: any) => p.name === 'Nested Place'); expect(nested).toBeDefined(); @@ -862,7 +862,7 @@ describe('GPX Import — edge cases', () => { .set('Cookie', authCookie(user.id)) .attach('file', emptyGpx, { filename: 'empty.gpx', contentType: 'application/gpx+xml' }); expect(res.status).toBe(400); - expect(res.body.error).toMatch(/no waypoints/i); + expect(res.body.error).toMatch(/no matching places/i); }); }); diff --git a/server/tests/unit/services/placeService.test.ts b/server/tests/unit/services/placeService.test.ts index 4d6a8fc8..00fb6920 100644 --- a/server/tests/unit/services/placeService.test.ts +++ b/server/tests/unit/services/placeService.test.ts @@ -277,19 +277,24 @@ describe('importGpx', () => { expect(result.places[1].name).toBe('London'); }); - it('PLACE-SVC-022 — falls back to route points when no elements exist', () => { + it('PLACE-SVC-022 — imports as a single polyline-place with routeGeometry', () => { const { user } = createUser(testDb); const trip = createTrip(testDb, user.id); const gpx = Buffer.from(` + My Route Start End `); const result = importGpx(String(trip.id), gpx) as any; - expect(result.places).toHaveLength(2); - expect(result.places[0].name).toBe('Start'); - expect(result.places[1].name).toBe('End'); + expect(result.places).toHaveLength(1); + expect(result.places[0].name).toBe('My Route'); + expect(result.places[0].lat).toBe(48.8566); + expect(result.places[0].lng).toBe(2.3522); + expect(result.places[0].route_geometry).toBeTruthy(); + const coords = JSON.parse(result.places[0].route_geometry); + expect(coords).toHaveLength(2); }); it('PLACE-SVC-023 — imports track as a single place with routeGeometry', () => {