Starting to refactor to store track points independently from their tracks (WIP) (#3)
This commit is contained in:
parent
aea83fac5d
commit
fafde7b831
|
@ -7,7 +7,7 @@ import GPX from '../../lib/gpx-parser-builder';
|
||||||
import '../../theme/get-location.css';
|
import '../../theme/get-location.css';
|
||||||
import { IonIcon, IonItem } from '@ionic/react';
|
import { IonIcon, IonItem } from '@ionic/react';
|
||||||
import { downloadSharp } from 'ionicons/icons';
|
import { downloadSharp } from 'ionicons/icons';
|
||||||
import { pushTrack } from '../../db/tracks';
|
import { pushGpx } from '../../db/gpx';
|
||||||
|
|
||||||
const GpxImport: React.FC<{}> = () => {
|
const GpxImport: React.FC<{}> = () => {
|
||||||
const db = useDB();
|
const db = useDB();
|
||||||
|
@ -23,9 +23,9 @@ const GpxImport: React.FC<{}> = () => {
|
||||||
() => {
|
() => {
|
||||||
// this will then display a text file
|
// this will then display a text file
|
||||||
console.log(fileReader.result);
|
console.log(fileReader.result);
|
||||||
const track = GPX.parse(fileReader.result);
|
const gpx = GPX.parse(fileReader.result);
|
||||||
pushTrack(db, {
|
pushGpx(db, {
|
||||||
track: JSON.parse(JSON.stringify(track)),
|
gpx,
|
||||||
metadata: {
|
metadata: {
|
||||||
lastModified: new Date(file.lastModified).toISOString(),
|
lastModified: new Date(file.lastModified).toISOString(),
|
||||||
importDate: new Date().toISOString(),
|
importDate: new Date().toISOString(),
|
||||||
|
|
|
@ -2,12 +2,10 @@ import React, { useState } from 'react';
|
||||||
|
|
||||||
import { useDB } from 'react-pouchdb';
|
import { useDB } from 'react-pouchdb';
|
||||||
|
|
||||||
import GPX from '../../lib/gpx-parser-builder';
|
|
||||||
|
|
||||||
import '../../theme/get-location.css';
|
import '../../theme/get-location.css';
|
||||||
import { IonButton, IonIcon, IonItem } from '@ionic/react';
|
import { IonButton, IonIcon } from '@ionic/react';
|
||||||
import { recordingOutline, recording } from 'ionicons/icons';
|
import { recordingOutline, recording } from 'ionicons/icons';
|
||||||
import { pushTrack } from '../../db/tracks';
|
|
||||||
import {
|
import {
|
||||||
startBackgroundGeolocation,
|
startBackgroundGeolocation,
|
||||||
stopBackgroundGeolocation,
|
stopBackgroundGeolocation,
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { useFind } from 'react-pouchdb';
|
||||||
|
|
||||||
|
import { lat2tile, lon2tile } from '../../lib/geo';
|
||||||
|
import { zoom0 } from '../../store/map';
|
||||||
|
|
||||||
|
const Gpx: React.FC<{ gpx: any }> = (props) => {
|
||||||
|
console.log('gpx');
|
||||||
|
|
||||||
|
var gpxes: any[] = [];
|
||||||
|
|
||||||
|
gpxes = useFind({
|
||||||
|
selector: {
|
||||||
|
type: 'trkpt',
|
||||||
|
gpx: props.gpx._id,
|
||||||
|
},
|
||||||
|
// sort: ['trkpt.time'],
|
||||||
|
// use_index: 'type-trkpt-gpx-time3',
|
||||||
|
});
|
||||||
|
|
||||||
|
gpxes.sort((first: any, second: any) =>
|
||||||
|
first.trkpt.time.localeCompare(second.trkpt.time)
|
||||||
|
);
|
||||||
|
|
||||||
|
const d = gpxes.reduce((previous: string, current: any, index: number) => {
|
||||||
|
const action = index === 0 ? 'M' : index === 1 ? 'L' : '';
|
||||||
|
const trkpt = current.trkpt;
|
||||||
|
return `${previous} ${action} ${lon2tile(trkpt.$.lon, zoom0)}, ${lat2tile(
|
||||||
|
trkpt.$.lat,
|
||||||
|
zoom0
|
||||||
|
)}`;
|
||||||
|
}, '');
|
||||||
|
|
||||||
|
return <path d={d} className='track' />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Gpx;
|
|
@ -0,0 +1,20 @@
|
||||||
|
import React, { Suspense } from 'react';
|
||||||
|
|
||||||
|
import { useFind } from 'react-pouchdb';
|
||||||
|
|
||||||
|
import Gpx from './gpx';
|
||||||
|
|
||||||
|
const Gpxes: React.FC<{}> = () => {
|
||||||
|
const gpxes = useFind({
|
||||||
|
selector: {
|
||||||
|
type: 'gpx',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return gpxes.map((gpx: any) => {
|
||||||
|
console.log('doc');
|
||||||
|
return <Gpx key={gpx._id} gpx={gpx} />;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Gpxes;
|
|
@ -2,6 +2,7 @@ import react, { useMemo, useEffect } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { mapActions } from '../../store/map';
|
import { mapActions } from '../../store/map';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { useDB } from 'react-pouchdb';
|
||||||
|
|
||||||
import Layer from '../slippy/layer';
|
import Layer from '../slippy/layer';
|
||||||
import Slippy from '../slippy/slippy';
|
import Slippy from '../slippy/slippy';
|
||||||
|
@ -19,8 +20,9 @@ import {
|
||||||
} from '@ionic/react';
|
} from '@ionic/react';
|
||||||
|
|
||||||
import GpxImport from './gpx-import';
|
import GpxImport from './gpx-import';
|
||||||
import Tracks from './tracks';
|
import Gpxes from './gpxes';
|
||||||
import GpxRecord from './gpx-record';
|
import GpxRecord from './gpx-record';
|
||||||
|
import { initDb } from '../../db';
|
||||||
|
|
||||||
const Map: react.FC<{}> = (props: {}) => {
|
const Map: react.FC<{}> = (props: {}) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
@ -33,9 +35,10 @@ const Map: react.FC<{}> = (props: {}) => {
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const db = useDB();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.addEventListener('resize', debouncedResizeHandler);
|
window.addEventListener('resize', debouncedResizeHandler);
|
||||||
// dispatch(mapActions.shift({ x: -50, y: 0 }));
|
initDb(db);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -45,7 +48,7 @@ const Map: react.FC<{}> = (props: {}) => {
|
||||||
<Slippy>
|
<Slippy>
|
||||||
<Whiteboard>
|
<Whiteboard>
|
||||||
<CurrentLocation />
|
<CurrentLocation />
|
||||||
<Tracks />
|
<Gpxes />
|
||||||
</Whiteboard>
|
</Whiteboard>
|
||||||
<Layer>
|
<Layer>
|
||||||
<TiledMap />
|
<TiledMap />
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { lat2tile, lon2tile } from '../../lib/geo';
|
|
||||||
import { zoom0 } from '../../store/map';
|
|
||||||
|
|
||||||
const Track: React.FC<{ track: any }> = (props) => {
|
|
||||||
console.log('track');
|
|
||||||
const trkseg = props.track.track.trk[0].trkseg[0];
|
|
||||||
const d = trkseg.trkpt.reduce(
|
|
||||||
(previous: string, current: any, index: number) => {
|
|
||||||
const action = index === 0 ? 'M' : index === 1 ? 'L' : '';
|
|
||||||
return `${previous} ${action} ${lon2tile(
|
|
||||||
current.$.lon,
|
|
||||||
zoom0
|
|
||||||
)}, ${lat2tile(current.$.lat, zoom0)}`;
|
|
||||||
},
|
|
||||||
''
|
|
||||||
);
|
|
||||||
|
|
||||||
return <path d={d} className='track' />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Track;
|
|
|
@ -1,20 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { useFind } from 'react-pouchdb';
|
|
||||||
|
|
||||||
import Track from './track';
|
|
||||||
|
|
||||||
const Tracks: React.FC<{}> = () => {
|
|
||||||
const tracks = useFind({
|
|
||||||
selector: {
|
|
||||||
type: 'Track',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return tracks.map((track: any) => {
|
|
||||||
console.log('doc');
|
|
||||||
return <Track key={track._id} track={track} />;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Tracks;
|
|
|
@ -1,16 +1,13 @@
|
||||||
import {
|
import {
|
||||||
Sha256,
|
Sha256,
|
||||||
Sha512,
|
|
||||||
string_to_bytes,
|
string_to_bytes,
|
||||||
bytes_to_base64,
|
bytes_to_base64,
|
||||||
} from '@openpgp/asmcrypto.js';
|
} from '@openpgp/asmcrypto.js';
|
||||||
import { DBWrapper } from 'workbox-core/_private';
|
|
||||||
|
|
||||||
export const pushTrack = (db: any, payload: any) => {
|
export const pushGpx = async (db: any, payload: any) => {
|
||||||
|
const gpxString = JSON.stringify(payload.gpx);
|
||||||
const sha = new Sha256();
|
const sha = new Sha256();
|
||||||
const result = sha
|
const result = sha.process(string_to_bytes(gpxString)).finish().result;
|
||||||
.process(string_to_bytes(JSON.stringify(payload.track)))
|
|
||||||
.finish().result;
|
|
||||||
var _id;
|
var _id;
|
||||||
if (result === null) {
|
if (result === null) {
|
||||||
console.log(`Can't hash`);
|
console.log(`Can't hash`);
|
||||||
|
@ -19,16 +16,48 @@ export const pushTrack = (db: any, payload: any) => {
|
||||||
_id = bytes_to_base64(result);
|
_id = bytes_to_base64(result);
|
||||||
console.log(`Digest: ${_id}`);
|
console.log(`Digest: ${_id}`);
|
||||||
}
|
}
|
||||||
const doc = { _id, type: 'Track', ...payload };
|
|
||||||
// console.log(JSON.stringify(doc));
|
try {
|
||||||
db.put(doc);
|
await db.get(_id);
|
||||||
|
alert('This file has alerady been imported.');
|
||||||
|
return;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
var gpx = JSON.parse(gpxString);
|
||||||
|
var points: any[] = [];
|
||||||
|
|
||||||
|
const prune = (object: any) => {
|
||||||
|
for (var key in object) {
|
||||||
|
if (key === 'trkpt') {
|
||||||
|
points.push(...object[key]);
|
||||||
|
object[key] = [];
|
||||||
|
} else {
|
||||||
|
if (typeof object[key] === 'object') {
|
||||||
|
prune(object[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
prune(gpx);
|
||||||
|
|
||||||
|
const doc = { ...payload, _id, type: 'gpx', gpx };
|
||||||
|
console.log(JSON.stringify(doc));
|
||||||
|
await db.put(doc);
|
||||||
|
|
||||||
|
for (var point in points) {
|
||||||
|
const docPoint = { type: 'trkpt', gpx: _id, trkpt: points[point] };
|
||||||
|
console.log(JSON.stringify(docPoint));
|
||||||
|
const response = await db.post(docPoint);
|
||||||
|
console.log(JSON.stringify(docPoint));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const CURRENT_TRACK = '---current track---';
|
const CURRENT_GPX = '---current gpx---';
|
||||||
|
|
||||||
const initialTrack = {
|
const initialTrack = {
|
||||||
_id: CURRENT_TRACK,
|
_id: CURRENT_GPX,
|
||||||
type: 'Track',
|
type: 'GPX',
|
||||||
track: {
|
track: {
|
||||||
$: {
|
$: {
|
||||||
version: '1.1',
|
version: '1.1',
|
||||||
|
@ -65,18 +94,18 @@ const initialTrack = {
|
||||||
export const appendTrkpt = async (db: any, trkpt: any) => {
|
export const appendTrkpt = async (db: any, trkpt: any) => {
|
||||||
var track: any;
|
var track: any;
|
||||||
await db
|
await db
|
||||||
.get(CURRENT_TRACK)
|
.get(CURRENT_GPX)
|
||||||
.then((result: any) => {
|
.then((result: any) => {
|
||||||
track = result;
|
track = result;
|
||||||
})
|
})
|
||||||
.catch((error: any) => {
|
.catch((error: any) => {
|
||||||
if (error.status='404') {
|
if ((error.status = '404')) {
|
||||||
track = initialTrack;
|
track = initialTrack;
|
||||||
} else {
|
} else {
|
||||||
console.log(`db.get(CURRENT_TRACK), ERROR: ${JSON.stringify(error)}`);
|
console.log(`db.get(CURRENT_TRACK), ERROR: ${JSON.stringify(error)}`);
|
||||||
alert(`db.get(CURRENT_TRACK), ERROR: ${JSON.stringify(error)}`);
|
alert(`db.get(CURRENT_TRACK), ERROR: ${JSON.stringify(error)}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
track.metadata.lastModified = trkpt.time;
|
track.metadata.lastModified = trkpt.time;
|
||||||
const currentTrkseg = track.track.trk.at(-1).trkseg.at(-1);
|
const currentTrkseg = track.track.trk.at(-1).trkseg.at(-1);
|
||||||
currentTrkseg.trkpt.push(trkpt);
|
currentTrkseg.trkpt.push(trkpt);
|
|
@ -0,0 +1,58 @@
|
||||||
|
const DBDEFINITION = '--db-definition--';
|
||||||
|
|
||||||
|
const dbDefinition = {
|
||||||
|
_id: DBDEFINITION,
|
||||||
|
type: DBDEFINITION,
|
||||||
|
version: '0.000001',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initDb = async (db: any) => {
|
||||||
|
try {
|
||||||
|
await db.get(DBDEFINITION);
|
||||||
|
// TODO: support migrations
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.status !== 404) {
|
||||||
|
console.log(
|
||||||
|
`Unexpected error fetching db definition: ${JSON.stringify(error)}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.createIndex({
|
||||||
|
index: {
|
||||||
|
name: 'type',
|
||||||
|
fields: ['type'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.createIndex({
|
||||||
|
index: {
|
||||||
|
name: 'trkpt-time',
|
||||||
|
fields: ['trkpt.time'],
|
||||||
|
partial_filter_selector: {
|
||||||
|
type: 'trkpt',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.createIndex({
|
||||||
|
index: {
|
||||||
|
name: 'type-trkpt-gpx-time3',
|
||||||
|
fields: ['type', 'gpx', 'trkpt.time'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const indexes = await db.getIndexes();
|
||||||
|
console.log(`indexes: ${JSON.stringify(indexes)}`);
|
||||||
|
|
||||||
|
const explain = await db.explain({
|
||||||
|
selector: {
|
||||||
|
type: 'trkpt',
|
||||||
|
gpx: 'xxxx',
|
||||||
|
},
|
||||||
|
// sort: ['trkpt.time'],
|
||||||
|
// use_index: 'type-trkpt-gpx-time',
|
||||||
|
});
|
||||||
|
console.log(`explain: ${JSON.stringify(explain)}`);
|
||||||
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
import { BackgroundGeolocationPlugin } from '@capacitor-community/background-geolocation';
|
import { BackgroundGeolocationPlugin } from '@capacitor-community/background-geolocation';
|
||||||
import { registerPlugin } from '@capacitor/core';
|
import { registerPlugin } from '@capacitor/core';
|
||||||
import { appendTrkpt } from '../db/tracks';
|
import { appendTrkpt } from '../db/gpx';
|
||||||
|
|
||||||
const BackgroundGeolocation = registerPlugin<BackgroundGeolocationPlugin>(
|
const BackgroundGeolocation = registerPlugin<BackgroundGeolocationPlugin>(
|
||||||
'BackgroundGeolocation'
|
'BackgroundGeolocation'
|
||||||
|
|
Loading…
Reference in New Issue