Skip to content

Commit 1788fb9

Browse files
committed
Added ability to parse the GPX data using xml parser
1 parent 2fb1ca4 commit 1788fb9

File tree

2 files changed

+141
-1
lines changed

2 files changed

+141
-1
lines changed

src/App.jsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {useState, useEffect, useRef} from 'react';
22

33
import './App.css';
4+
import { parseXml } from './XmlParsing';
45

56
import mapboxgl, {
67
NavigationControl,
@@ -24,6 +25,7 @@ function importAll(r) {
2425
}
2526

2627
const gLoadedImages = importAll(require.context('./data/20220507-CT-North-Chick-Hike/pictures/', false, /\.(png|jpe?g|svg)$/));
28+
const gGpxData = importAll(require.context('./data/20220507-CT-North-Chick-Hike/gpx-data', false, /\.gpx$/));
2729

2830
function mapStyleUrl(name) {
2931
if (name === 'outdoors') {
@@ -42,11 +44,47 @@ async function loadImage(dir, image) {
4244
return require(`${dir}/${image}`);
4345
}
4446

47+
function deconstructFilename(file) {
48+
const match = file.match(/(.+)\.([a-zA-Z0-9]+)\.([a-zA-Z]+)$/);
49+
const filePathParts = match[1].split('/');
50+
return { file: file, name: filePathParts[filePathParts.length - 1], hash: match[2], ext: match[3] };
51+
}
52+
53+
4554
function MapComponent(props = {}) {
55+
const {
56+
mapStyleName = 'outdoors-v11',
57+
} = props;
58+
4659
const mapRef = useRef(null);
4760
const mapObjRef = useRef(null);
4861

49-
const mapStyleName = useState('outdoors-v11');
62+
const [tracks, setTracks] = useState([]);
63+
const [waypoints, setWaypoints] = useState([]);
64+
65+
const loadGpxData = async () => {
66+
const currentWp = {};
67+
const currentTracks = {};
68+
69+
console.debug('App: Load GPX Data Started');
70+
71+
const dataProm = (
72+
gGpxData.map(async (file, i) => {
73+
const fileObj = deconstructFilename(file);
74+
fileObj.data = await parseXml(fileObj.file);
75+
if (!!fileObj.data.waypoint) {
76+
currentWp[i] = fileObj;
77+
}
78+
if (!!fileObj.data.track) {
79+
currentTracks[i] = fileObj;
80+
}
81+
})
82+
)
83+
await Promise.all(dataProm).catch((err) => console.error('App: Failed to parse data:', err.message));
84+
console.debug('App: Tracks', currentTracks);
85+
setTracks(Object.values(currentTracks));
86+
setWaypoints(Object.values(currentWp));
87+
};
5088

5189
useEffect(() => {
5290
if (mapObjRef.current === null) {
@@ -62,6 +100,8 @@ function MapComponent(props = {}) {
62100

63101
mapObjRef.current = map;
64102
}
103+
104+
loadGpxData();
65105
}, []);
66106

67107
const mapStyle = {};

src/XmlParsing.jsx

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
function getName(xml) {
2+
return xml.querySelector('name')?.textContent
3+
}
4+
5+
function getDescription(xml) {
6+
return xml.querySelector('desc')?.textContent
7+
}
8+
9+
function getColor(xml) {
10+
return xml.querySelector('color')?.textContent
11+
}
12+
13+
function getGpsTrack(xml) {
14+
const getChild = (obj, name) => {
15+
return obj.querySelector(name);
16+
}
17+
const queryFunc = (trkpt) => {
18+
return {
19+
timestamp: getChild(trkpt, 'time')?.textContent,
20+
value: [
21+
trkpt.getAttribute('lat'),
22+
trkpt.getAttribute('lon'),
23+
getChild(trkpt, 'ele')?.textContent,
24+
],
25+
}
26+
};
27+
let track = xml.querySelector('trkseg');
28+
console.debug('trkseg', track);
29+
if (!track) { return null }
30+
const trackArr = Object.values(track.children).map(queryFunc);
31+
console.debug('trackArr', trackArr);
32+
return trackArr;
33+
}
34+
35+
function getWaypoint(xml) {
36+
const waypoint = xml.querySelector('wpt');
37+
const timestamp = xml.querySelector('time');
38+
if (!waypoint) {
39+
return null;
40+
}
41+
return {
42+
timestamp: timestamp.textContent,
43+
value: [waypoint.getAttribute('lat'), waypoint.getAttribute('lon')],
44+
}
45+
}
46+
47+
export async function parseXml(file) {
48+
return new Promise((resolve, reject) => {
49+
const rawFile = new XMLHttpRequest();
50+
rawFile.onreadystatechange = () => {
51+
if (rawFile.readyState === 4 && (rawFile.status === 200 || rawFile.status === 0)) {
52+
const parser = new DOMParser();
53+
const xml = parser.parseFromString(rawFile.response, 'text/xml');
54+
55+
const data = {};
56+
data.name = getName(xml);
57+
data.description = getDescription(xml);
58+
data.color = getColor(xml);
59+
data.track = getGpsTrack(xml);
60+
data.waypoint = getWaypoint(xml);
61+
62+
resolve(Object.fromEntries(
63+
Object.entries(data).filter(([_, v]) => !!v)
64+
));
65+
}
66+
};
67+
68+
rawFile.open('GET', file, false);
69+
rawFile.send();
70+
});
71+
}
72+
73+
74+
function toGeoJSONBase(geometry, props) {
75+
const feature = {};
76+
feature.type = 'Feature';
77+
feature.geometry = geometry;
78+
feature.properties = props;
79+
return {
80+
type: 'FeatureCollection',
81+
features: [feature],
82+
}
83+
}
84+
85+
export function toGeoJSONWaypoint(item, props = {}) {
86+
const geo = {
87+
type: 'Point',
88+
coordinates: [item.value[1], item.value[0]],
89+
};
90+
return toGeoJSONBase(geo, props)
91+
}
92+
93+
export function toGeoJSONTrack(item, props = {}) {
94+
const lineCoords = item.map((v) => [v.value[1], v.value[0]]);
95+
const geo = {
96+
type: 'LineString',
97+
coordinates: lineCoords,
98+
};
99+
return toGeoJSONBase(geo, props)
100+
}

0 commit comments

Comments
 (0)