adds route editing

This commit is contained in:
Christian Beutel
2024-04-22 22:37:13 +02:00
parent 9af7ddeffd
commit 8d7ed16904
19 changed files with 744 additions and 132 deletions

View File

@@ -0,0 +1,12 @@
export default class Bounds {
minlat: number;
minlon: number;
maxlat: number;
maxlon: number;
constructor(object: {minlat: number, minlon: number, maxlat: number, maxlon: number}) {
this.minlat = object.minlat;
this.minlon = object.minlon;
this.maxlat = object.maxlat;
this.maxlon = object.maxlon;
}
}

View File

@@ -0,0 +1,10 @@
export default class Copyright {
author: string;
year: number;
license: string;
constructor(object: { author: string, year: number, license: string }) {
this.author = object.author;
this.year = object.year;
this.license = object.license;
}
}

View File

@@ -0,0 +1,101 @@
import * as xml2js from 'isomorphic-xml2js';
import Metadata from './metadata';
import Waypoint from './waypoint';
import Route from './route';
import Track from './track';
import { removeEmpty, allDatesToISOString } from './utils';
const defaultAttributes = {
version: '1.1',
creator: 'wanderer',
xmlns: 'http://www.topografix.com/GPX/1/1',
'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
'xsi:schemaLocation':
'http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd'
}
export default class GPX {
$: {
version: string;
creator: string;
xmlns: string;
'xmlns:xsi': string;
'xsi:schemaLocation': string;
}
extensions?: string;
metadata?: Metadata;
wpt?: Waypoint[];
rte?: Route[];
trk?: Track[];
constructor(object: {
$?: {
version: string,
creator: string,
xmlns: string,
'xmlns:xsi': string,
'xsi:schemaLocation': string,
}
extensions?: string,
metadata?: Metadata,
wpt?: Waypoint[] | Waypoint,
rte?: Route[] | Route,
trk?: Track[],
}) {
this.$ = Object.assign({}, defaultAttributes, object.$ || {});
if (object.extensions) {
this.extensions = object.extensions;
}
if (object.metadata) {
this.metadata = object.metadata;
}
if (object.wpt) {
if (!Array.isArray(object.wpt)) {
object.wpt = [object.wpt];
}
this.wpt = object.wpt.map(wpt => new Waypoint(wpt))
}
if (object.rte) {
if (!Array.isArray(object.rte)) {
object.rte = [object.rte];
}
this.rte = object.rte.map(rte => new Route(rte))
}
if (object.trk) {
if (!Array.isArray(object.trk)) {
object.trk = [object.trk];
}
this.trk = object.trk.map(trk => new Track(trk))
}
removeEmpty(this);
}
static parse(gpxString: string): Promise<GPX | Error> {
return new Promise<GPX | Error>((resolve, reject) => xml2js.parseString(gpxString, {
explicitArray: false
}, (err, xml) => {
if (err) {
reject(err);
}
const gpx = new GPX({
$: xml.gpx.$,
metadata: xml.gpx.metadata,
wpt: xml.gpx.wpt,
rte: xml.gpx.rte,
trk: xml.gpx.trk
});
resolve(gpx)
}));
}
toString(options?: xml2js.BuilderOptions) {
options = options || {};
options.rootName = 'gpx';
const builder = new xml2js.Builder(options), gpx = new GPX(this);
allDatesToISOString(gpx);
return builder.buildObject(gpx);
}
}

View File

@@ -0,0 +1,13 @@
export default class Link {
$: {
href?: string;
}
text: string;
type: string;
constructor(object: any) {
this.$ = {};
this.$.href = object.$.href || object.href;
this.text = object.text;
this.type = object.type;
}
}

View File

@@ -0,0 +1,48 @@
import Copyright from './copyright';
import Link from './link';
import Person from './person';
import Bounds from './bounds';
export default class Metadata {
name: string;
desc: string;
time: Date;
keywords: string;
extensions: string;
author?: Person;
link?: Link[];
bounds?: Bounds;
copyright?: Copyright;
constructor(object: {
name: string,
desc: string,
time: string,
keywords: string,
extensions: string,
author?: Person,
link?: Link | Link[],
bounds?: Bounds,
copyright?: Copyright
}) {
this.name = object.name;
this.desc = object.desc;
this.time = object.time ? new Date(object.time) : new Date();
this.keywords = object.keywords;
this.extensions = object.extensions;
if (object.author) {
this.author = new Person(object.author);
}
if (object.link) {
if (!Array.isArray(object.link)) {
object.link = [object.link];
}
this.link = object.link.map(l => new Link(l));
}
if (object.bounds) {
this.bounds = new Bounds(object.bounds);
}
if (object.copyright) {
this.copyright = new Copyright(object.copyright);
}
}
}

View File

@@ -0,0 +1,14 @@
import Link from './link';
export default class Person {
name: string;
email: string;
link?: Link;
constructor(object: { name: string, email: string, link?: Link }) {
this.name = object.name;
this.email = object.email;
if (object.link) {
this.link = new Link(object.link);
}
}
}

View File

@@ -0,0 +1,45 @@
import Waypoint from './waypoint';
import Link from './link';
export default class Route {
name: string;
cmt: string;
desc: string;
src: string;
number: number;
type: string;
extensions: string;
link?: Link[];
rtept?: Waypoint[];
constructor(object: {
name: string,
cmt: string, desc: string,
src: string,
number: number,
type: string,
extensions: string,
link?: Link | Link[],
rtept?: Waypoint | Waypoint[]
}) {
this.name = object.name;
this.cmt = object.cmt;
this.desc = object.desc;
this.src = object.src;
this.number = object.number;
this.type = object.type;
this.extensions = object.extensions;
if (object.link) {
if (!Array.isArray(object.link)) {
object.link = [object.link];
}
this.link = object.link.map(l => new Link(l));
}
if (object.rtept) {
if (!Array.isArray(object.rtept)) {
this.rtept = [object.rtept];
}
this.rtept = (object.rtept as Waypoint[]).map(rtept => new Waypoint(rtept))
}
}
}

View File

@@ -0,0 +1,15 @@
import Waypoint from './waypoint';
export default class TrackSegment {
trkpt?: Waypoint[];
extensions?: string;
constructor(object: { trkpt?: Waypoint[], extensions?: string }) {
if (object.trkpt) {
if (!Array.isArray(object.trkpt)) {
object.trkpt = [object.trkpt];
}
this.trkpt = object.trkpt.map(trkpt => new Waypoint(trkpt))
}
this.extensions = object.extensions;
}
}

View File

@@ -0,0 +1,45 @@
import TrackSegment from './track-segment';
import Link from './link';
export default class Track {
name?: string;
cmt?: string;
desc?: string;
src?: string;
number?: number;
type?: string;
extensions?: string;
link?: Link[];
trkseg?: TrackSegment[];
constructor(object: {
name?: string,
cmt?: string,
desc?: string,
src?: string,
number?: number,
type?: string,
extensions?: string,
link?: Link | Link[],
trkseg?: TrackSegment | TrackSegment[]
}) {
this.name = object.name;
this.cmt = object.cmt;
this.desc = object.desc;
this.src = object.src;
this.number = object.number;
this.type = object.type;
this.extensions = object.extensions;
if (object.link) {
if (!Array.isArray(object.link)) {
object.link = [object.link];
}
this.link = object.link.map(l => new Link(l));
}
if (object.trkseg) {
if (!Array.isArray(object.trkseg)) {
object.trkseg = [object.trkseg];
}
this.trkseg = object.trkseg.map(trkseg => new TrackSegment(trkseg));;
}
}
}

View File

@@ -0,0 +1,23 @@
function removeEmpty(obj: Record<string, any>) {
Object.entries(obj).forEach(([key, val]) => {
if (val && val instanceof Object) {
removeEmpty(val);
} else if (val == null) {
delete obj[key];
}
});
}
function allDatesToISOString(obj: Record<string, any>) {
Object.entries(obj).forEach(([key, val]) => {
if (val) {
if (val instanceof Date) {
obj[key] = val.toISOString().split('.')[0] + 'Z';
} else if (val instanceof Object) {
allDatesToISOString(val);
}
}
});
}
export { removeEmpty, allDatesToISOString };

View File

@@ -0,0 +1,81 @@
import Link from './link';
export default class Waypoint {
$: {
lat?: number;
lon?: number;
}
ele?: number;
time?: Date;
magvar?: string;
geoidheight?: string;
name?: string;
cmt?: string
desc?: string;
src?: string;
sym?: string;
type?: string;
sat?: string;
hdop?: string;
vdop?: string;
pdop?: string;
ageofdgpsdata?: string;
dgpsid?: string;
extensions?: string;
link?: Link[];
constructor(object: {
lat?: number,
lon?: number,
$: {
lat?: number,
lon?: number
},
ele?: number,
time?: Date,
magvar?: string,
geoidheight?: string,
name?: string,
cmt?: string,
desc?: string,
src?: string,
sym?: string,
type?: string,
sat?: string,
hdop?: string,
vdop?: string,
pdop?: string,
ageofdgpsdata?: string,
dgpsid?: string,
extensions?: string,
link?: Link[]
}) {
this.$ = {};
this.$.lat = object.$.lat === 0 || object.lat === 0 ? 0 : object.$.lat || object.lat || -1;
this.$.lon = object.$.lon === 0 || object.lon === 0 ? 0 : object.$.lon || object.lon || -1;
this.ele = object.ele;
if (object.time) {
this.time = new Date(object.time);
}
this.magvar = object.magvar;
this.geoidheight = object.geoidheight;
this.name = object.name;
this.cmt = object.cmt;
this.desc = object.desc;
this.src = object.src;
this.sym = object.sym;
this.type = object.type;
this.sat = object.sat;
this.hdop = object.hdop;
this.vdop = object.vdop;
this.pdop = object.pdop;
this.ageofdgpsdata = object.ageofdgpsdata;
this.dgpsid = object.dgpsid;
this.extensions = object.extensions;
if (object.link) {
if (!Array.isArray(object.link)) {
object.link = [object.link];
}
this.link = object.link.map(l => new Link(l));
}
}
}

View File

@@ -1,31 +1,9 @@
interface ValhallaResponse {
trip: {
legs: {
shape: string[];
shape: string;
}[];
};
}
class ValhallaRoute {
type: "Feature";
properties: {};
geometry: {
type: "LineString";
coordinates: number[][];
};
constructor() {
this.type = "Feature"
this.properties = {}
this.geometry = {
type: "LineString",
coordinates: []
}
}
toString() {
return JSON.stringify(this);
}
}
export {type ValhallaResponse, ValhallaRoute}
export { type ValhallaResponse }