Skip to content

Commit

Permalink
More course parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
OlliV committed Oct 27, 2023
1 parent 4e2e385 commit 9fd19e1
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 48 deletions.
37 changes: 8 additions & 29 deletions components/map/Course.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Polyline } from 'react-leaflet';
import {CourseData} from '../../lib/gpx_parser';

const mintrackpointdelta = 0.0001;

export default function MapCourse({ map, course }) {
const { trackpoints } = course?.tracks[0]?.segments[0] || [];
const [polyline, setPolyline] = useState([]);
export default function MapCourse({ map, course }: {map: any, course: CourseData}) {
const { trackpoints } = course?.tracks[0]?.segments[0] || {trackpoints: []};
const polyline = useMemo(() => trackpoints.map(({lat, lon}) => [lat, lon]), [trackpoints]);

useEffect(() => {
const pointArray = [];

if (trackpoints.length > 0) {
let lastlon = parseFloat(trackpoints[0].lon);
let lastlat = parseFloat(trackpoints[0].lat);

pointArray.push([lastlon, lastlat]);

for (let i = 1; i < trackpoints.length; i++) {
const lon = parseFloat(trackpoints[i].lon);
const lat = parseFloat(trackpoints[i].lat);

const latdiff = lat - lastlat;
const londiff = lon - lastlon;
if (Math.sqrt(latdiff * latdiff + londiff * londiff) > mintrackpointdelta) {
lastlon = lon;
lastlat = lat;
pointArray.push([lat, lon]);
}
}
if (map && polyline.length > 0) {
map.flyTo(polyline[0], map.getZoom());
}

setPolyline(pointArray);
}, [trackpoints]);
}, [,map, polyline]);

return <Polyline pathOptions={{ color: 'black' }} positions={polyline} />;
}
1 change: 0 additions & 1 deletion components/map/Marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import MarkerShadow from '../../node_modules/leaflet/dist/images/marker-shadow.p
export default function MapMarker({ map, position }) {
useEffect(() => {
if (map) {
console.log('fly to ', position);
map.flyTo(position, map.getZoom());
}
}, [map, position]);
Expand Down
59 changes: 49 additions & 10 deletions lib/gpx_parser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
type Coord = {
lat: number;
lon: number;
};
type Trackpoint = Coord & {
ele: number;
};
type Segment = {
trackpoints: Trackpoint[];
};
type Track = {
name?: string;
segments: Segment[];
}
type Routepoint = Coord;
type Waypoint = Coord;
export type CourseData = {
tracks: Track[];
routePoints: Routepoint[],
waypoints: Waypoint[];
};

export async function parseGpxFile2Document(file: File): Promise<Document> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
Expand All @@ -15,7 +37,7 @@ export async function parseGpxFile2Document(file: File): Promise<Document> {
});
}

function* elIter(el: HTMLCollectionOf<Element>, callback: (el: Element) => any) {
function* elIter<T>(el: HTMLCollectionOf<Element>, callback: (el: Element) => T) {
for (let i = 0; i < el.length; i++) {
yield callback(el[i]);
}
Expand All @@ -25,33 +47,50 @@ function getElValue(el: HTMLCollectionOf<Element>) {
return el[0].childNodes[0].nodeValue;
}

function parseTrackpoints(trackpoints: HTMLCollectionOf<Element>) {
function parseTrackpoints(trackpoints: HTMLCollectionOf<Element>): Trackpoint[] {
return [
...elIter(trackpoints, (trackpoint) => ({
lon: parseFloat(trackpoint.getAttribute('lon')),
...elIter<Trackpoint>(trackpoints, (trackpoint) => ({
lat: parseFloat(trackpoint.getAttribute('lat')),
ele: getElValue(trackpoint.getElementsByTagName('ele')),
lon: parseFloat(trackpoint.getAttribute('lon')),
ele: parseFloat(getElValue(trackpoint.getElementsByTagName('ele'))),
})),
];
}

function parseSegments(segments: HTMLCollectionOf<Element>) {
function parseSegments(segments: HTMLCollectionOf<Element>): Segment[] {
return [
...elIter(segments, (segment) => ({
trackpoints: parseTrackpoints(segment.getElementsByTagName('trkpt')),
})),
];
}

function parseTracks(tracks: HTMLCollectionOf<Element>) {
function parseTracks(tracks: HTMLCollectionOf<Element>): Track[] {
return [
...elIter(tracks, (track) => ({
...elIter<Track>(tracks, (track) => ({
name: getElValue(track.getElementsByTagName('name')),
segments: parseSegments(track.getElementsByTagName('trkseg')),
})),
];
}

export function gpxDocument2obj(doc: Document) {
return { tracks: parseTracks(doc.documentElement.getElementsByTagName('trk')) };
export function gpxDocument2obj(doc: Document): CourseData {
return {
tracks: parseTracks(doc.documentElement.getElementsByTagName('trk')),
routePoints: [],
waypoints: [],
};
}

export function getMapBounds(obj: CourseData) {
// TODO support all tracks and segments
const points = [...obj.tracks[0].segments[0].trackpoints, ...obj.routePoints, ...obj.waypoints];
const lats = points.map(({lat}) => lat);
const lons = points.map(({lon}) => lon);
return {
minlat: Math.min(...lats),
maxlat: Math.max(...lats),
minlon: Math.min(...lons),
maxlon: Math.max(...lons),
};
}
22 changes: 14 additions & 8 deletions pages/ride/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import Container from '@mui/material/Container';
import Grid from '@mui/material/Grid';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
import { useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import MyHead from '../../../components/MyHead';
import Title from '../../../components/Title';
import OpenStreetMap from '../../../components/map/OpenStreetMap';
import MapMarker from '../../../components/map/Marker';
import Course from '../../../components/map/Course';
import ImportFileButton from '../../../components/ImportFileButton';
import { gpxDocument2obj, parseGpxFile2Document } from '../../../lib/gpx_parser';
import { CourseData, getMapBounds, gpxDocument2obj, parseGpxFile2Document } from '../../../lib/gpx_parser';

type OpenStreetMapArg = Parameters<typeof OpenStreetMap>[0];
type MapMarkerArg = Parameters<typeof MapMarker>[0];
Expand Down Expand Up @@ -45,11 +45,10 @@ function MyLocationButton({ setPosition }) {
);
}

function Courses({ map, courses }) {
function Courses({ map, courses }: { map: any, courses: CourseData[] }) {
return (
<>
{courses.map((course, i: number) => {
return <DynamicCourse key={i} map={map} course={course} />;
{courses.map((course: CourseData, i: number) => {
})}
</>
);
Expand All @@ -58,12 +57,19 @@ function Courses({ map, courses }) {
export default function RideMap() {
const [map, setMap] = useState(null);
const [coord, setCoord] = useState([51.505, -0.09]);
const [courses, setCourses] = useState([]); // TODO reducer?
const [course, setCourse] = useState<CourseData>();
const bounds = useMemo(() => course && getMapBounds(course), [course]);

useEffect(() => {
if (bounds) {
map.fitBounds([[bounds.minlat, bounds.minlon], [bounds.maxlat, bounds.maxlon]]);
}
}, [bounds]);

const importGpx = (file: File) => {
parseGpxFile2Document(file)
.then((xmlDoc: Document) => {
setCourses([...courses, gpxDocument2obj(xmlDoc)]);
setCourse(gpxDocument2obj(xmlDoc));
})
.catch((err) => {
console.error('Would be nice to show this:', err);
Expand All @@ -90,7 +96,7 @@ export default function RideMap() {

<DynamicMap center={coord} setMap={setMap}>
<DynamicMapMarker map={map} position={coord} />
<Courses map={map} courses={courses} />
{ course ? <DynamicCourse map={map} course={course} /> : <></> }
</DynamicMap>

<Grid container direction="row" alignItems="center" spacing={2}></Grid>
Expand Down

1 comment on commit 9fd19e1

@vercel
Copy link

@vercel vercel bot commented on 9fd19e1 Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

bfree – ./

bfree.vercel.app
bfree-git-master-olliv.vercel.app
bfree-olliv.vercel.app

Please sign in to comment.