Starting to refactor to store track points independently from their tracks (WIP) (#3)

This commit is contained in:
Eric van der Vlist 2022-09-26 01:46:19 +02:00
parent aea83fac5d
commit fafde7b831
10 changed files with 172 additions and 69 deletions

View File

@ -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(),

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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 />

View File

@ -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;

View File

@ -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;

View File

@ -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]);
}
}
}
}; };
const CURRENT_TRACK = '---current track---'; 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_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,12 +94,12 @@ 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)}`);

58
src/db/index.ts Normal file
View File

@ -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)}`);
};

View File

@ -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'