diff --git a/components/map/Course.tsx b/components/map/Course.tsx index 0b6180b..fbb2ed6 100644 --- a/components/map/Course.tsx +++ b/components/map/Course.tsx @@ -1,10 +1,12 @@ import { useEffect, useMemo } from 'react'; import { CircleMarker } from 'react-leaflet'; import AntPath from '../../components/map/AntPath'; +import MapWaypoint from './Waypoint'; import { CourseData } from '../../lib/gpx_parser'; export default function MapCourse({ map, course }: { map: any; course: CourseData }) { const { trackpoints } = course?.tracks[0]?.segments[0] || { trackpoints: [] }; + const waypoints = course?.waypoints || []; const polyline = useMemo<[number, number][]>(() => trackpoints.map(({ lat, lon }) => [lat, lon]), [trackpoints]); const first = polyline[0]; const last = polyline[polyline.length - 1]; @@ -22,6 +24,9 @@ export default function MapCourse({ map, course }: { map: any; course: CourseDat {/* @ts-ignore */} {last ? : null} + <> + {waypoints.map(({lat, lon, name}, i: number) => {name})} + ); } diff --git a/components/map/Waypoint.tsx b/components/map/Waypoint.tsx new file mode 100644 index 0000000..45c33f2 --- /dev/null +++ b/components/map/Waypoint.tsx @@ -0,0 +1,28 @@ +'use client'; +import L from 'leaflet'; +import { Marker, Popup } from 'react-leaflet'; +import MarkerIcon from '../../node_modules/leaflet/dist/images/marker-icon.png'; +import MarkerShadow from '../../node_modules/leaflet/dist/images/marker-shadow.png'; +import { ReactNode } from 'react'; + +export default function MapWaypoint({ position, children }: { position: [number, number]; children?: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/lib/gpx_parser.ts b/lib/gpx_parser.ts index b2b739c..edcd028 100644 --- a/lib/gpx_parser.ts +++ b/lib/gpx_parser.ts @@ -3,7 +3,7 @@ type Coord = { lon: number; }; type Trackpoint = Coord & { - ele: number; + ele?: number; }; type Segment = { trackpoints: Trackpoint[]; @@ -13,10 +13,17 @@ type Track = { segments: Segment[]; }; type Routepoint = Coord; -type Waypoint = Coord; +type Route = { + name?: string; + routepoints: Routepoint[]; +}; +type Waypoint = Coord & { + name?: string; + ele?: number; +}; export type CourseData = { tracks: Track[]; - routePoints: Routepoint[]; + routes: Route[]; waypoints: Waypoint[]; }; @@ -47,13 +54,22 @@ function getElValue(el: HTMLCollectionOf) { return el[0].childNodes[0].nodeValue; } +function parseTrackpoint(el: Element): Trackpoint { + const trackpoint: Trackpoint = { + lat: parseFloat(el.getAttribute('lat')), + lon: parseFloat(el.getAttribute('lon')), + }; + const ele = parseFloat(getElValue(el.getElementsByTagName('ele'))); + if (!Number.isNaN(ele)) { + trackpoint.ele = ele; + } + + return trackpoint; +} + function parseTrackpoints(trackpoints: HTMLCollectionOf): Trackpoint[] { return [ - ...elIter(trackpoints, (trackpoint) => ({ - lat: parseFloat(trackpoint.getAttribute('lat')), - lon: parseFloat(trackpoint.getAttribute('lon')), - ele: parseFloat(getElValue(trackpoint.getElementsByTagName('ele'))), - })), + ...elIter(trackpoints, parseTrackpoint), ]; } @@ -74,17 +90,45 @@ function parseTracks(tracks: HTMLCollectionOf): Track[] { ]; } +function parseRoutepoints(routepoints: HTMLCollectionOf): Routepoint[] { + return [ + ...elIter(routepoints, (routepoint) => ({ + lat: parseFloat(routepoint.getAttribute('lat')), + lon: parseFloat(routepoint.getAttribute('lon')), + })), + ]; +} + +function parseRoutes(routes: HTMLCollectionOf): Route[] { + return [ + ...elIter(routes, (route) => ({ + name: getElValue(route.getElementsByTagName('name')), + routepoints: parseRoutepoints(route.getElementsByTagName('rtept')), + })), + ]; +} + +function parseWaypoints(waypoints: HTMLCollectionOf): Waypoint[] { + return [ + ...elIter(waypoints, (waypoint) => ({ + name: getElValue(waypoint.getElementsByTagName('name')), + lat: parseFloat(waypoint.getAttribute('lat')), + lon: parseFloat(waypoint.getAttribute('lon')), + })), + ]; +} + export function gpxDocument2obj(doc: Document): CourseData { return { tracks: parseTracks(doc.documentElement.getElementsByTagName('trk')), - routePoints: [], - waypoints: [], + routes: parseRoutes(doc.documentElement.getElementsByTagName('rte')), + waypoints: parseWaypoints(doc.documentElement.getElementsByTagName('wpt')), }; } export function getMapBounds(obj: CourseData) { // TODO support all tracks and segments - const points = [...obj.tracks[0].segments[0].trackpoints, ...obj.routePoints, ...obj.waypoints]; + const points = [...obj.tracks[0].segments[0].trackpoints, ...obj.routes[0].routepoints, ...obj.waypoints]; const lats = points.map(({ lat }) => lat); const lons = points.map(({ lon }) => lon); return {