function xml_parse(xml) { let result; let path = []; let tag_begin; let text_begin; for (let i = 0; i < xml.length; i++) { let c = xml.charAt(i); if (!tag_begin && c == '<') { if (i > text_begin && path.length) { let value = xml.substring(text_begin, i); if (!/^\s*$/.test(value)) { path[path.length - 1].value = value; } } tag_begin = i + 1; } else if (tag_begin && c == '>') { let tag = xml.substring(tag_begin, i).trim(); if (tag.startsWith('?') && tag.endsWith('?')) { /* Ignore directives. */ } else if (tag.startsWith('/')) { path.pop(); } else { let parts = tag.split(' '); let attributes = {}; for (let j = 1; j < parts.length; j++) { let eq = parts[j].indexOf('='); let value = parts[j].substring(eq + 1); if (value.startsWith('"') && value.endsWith('"')) { value = value.substring(1, value.length - 1); } attributes[parts[j].substring(0, eq)] = value; } let next = {name: parts[0], children: [], attributes: attributes}; if (path.length) { path[path.length - 1].children.push(next); } else { result = next; } if (!tag.endsWith('/')) { path.push(next); } } tag_begin = undefined; text_begin = i + 1; } } return result; } function* xml_each(node, name) { for (let child of node.children) { if (child.name == name) { yield child; } } } export function gpx_parse(xml) { let result = {segments: []}; let tree = xml_parse(xml); if (tree?.name == 'gpx') { for (let trk of xml_each(tree, 'trk')) { for (let trkseg of xml_each(trk, 'trkseg')) { let segment = []; for (let trkpt of xml_each(trkseg, 'trkpt')) { segment.push({ lat: parseFloat(trkpt.attributes.lat), lon: parseFloat(trkpt.attributes.lon), }); } result.segments.push(segment); } } } for (let metadata of xml_each(tree, 'metadata')) { for (let link of xml_each(metadata, 'link')) { result.link = link.attributes.href; } for (let time of xml_each(metadata, 'time')) { result.time = time.value; } } return result; }