Starting to implement GPX visualization.
This commit is contained in:
parent
cd9ee67fed
commit
d86d63ca61
|
@ -41,6 +41,7 @@ import Back from './components/buttons/Back';
|
||||||
import Forward from './components/buttons/Forward';
|
import Forward from './components/buttons/Forward';
|
||||||
import CurrentLocation from './components/map/CurrentLocation';
|
import CurrentLocation from './components/map/CurrentLocation';
|
||||||
import GpxImport from './components/dialogs/GpxImport';
|
import GpxImport from './components/dialogs/GpxImport';
|
||||||
|
import Gpxes from './components/map/Gpxes';
|
||||||
// import { initDb } from './db';
|
// import { initDb } from './db';
|
||||||
// import PouchDB from 'pouchdb';
|
// import PouchDB from 'pouchdb';
|
||||||
// import PouchDBFind from 'pouchdb-find';
|
// import PouchDBFind from 'pouchdb-find';
|
||||||
|
@ -96,7 +97,7 @@ export const setCenterAtom = atom(null, (get, set, center: geoPoint) => {
|
||||||
// initDb(db);
|
// initDb(db);
|
||||||
|
|
||||||
dispatch({ action: 'initDb' });
|
dispatch({ action: 'initDb' });
|
||||||
|
//dispatch({ action: 'getGpxesForViewport', params: { type: 'tech' } });
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @returns The root app component
|
* @returns The root app component
|
||||||
|
@ -113,7 +114,10 @@ const App: React.FC = () => {
|
||||||
scope={scope}
|
scope={scope}
|
||||||
setScope={debounce(setScope, 1000)}
|
setScope={debounce(setScope, 1000)}
|
||||||
numberOfTiledLayers={5}
|
numberOfTiledLayers={5}
|
||||||
slippyGraphics={[<CurrentLocation key='currentLocation' />]}
|
slippyGraphics={[
|
||||||
|
<CurrentLocation key='currentLocation' />,
|
||||||
|
<Gpxes key='gpxes' />,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</IonApp>
|
</IonApp>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import react, { useEffect, useState } from 'react';
|
||||||
|
import dispatch from '../../workers/dispatcher-main';
|
||||||
|
import { TileKeyObject, Rectangle } from './types';
|
||||||
|
|
||||||
|
export interface GpxesProperties {
|
||||||
|
keyObject?: TileKeyObject;
|
||||||
|
zoom?: number;
|
||||||
|
viewPort?: Rectangle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Gpxes: react.FC<GpxesProperties> = (props: GpxesProperties) => {
|
||||||
|
const [visibleGpxes, setVisibleGpxes] = useState<any>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
const getVisibleGpxes = async () => {
|
||||||
|
const gpxes = await dispatch({
|
||||||
|
action: 'getGpxesForViewport',
|
||||||
|
params: {
|
||||||
|
viewport: props.viewPort,
|
||||||
|
zoomLevel: props.keyObject?.zoomLevel,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log(`Gpxes, visibles: ${JSON.stringify(gpxes)}`);
|
||||||
|
setVisibleGpxes(gpxes);
|
||||||
|
};
|
||||||
|
getVisibleGpxes();
|
||||||
|
}, [
|
||||||
|
props.viewPort?.bottomRight.x,
|
||||||
|
props.viewPort?.bottomRight.y,
|
||||||
|
props.viewPort?.topLeft.x,
|
||||||
|
props.viewPort?.topLeft.y,
|
||||||
|
props.keyObject?.zoomLevel,
|
||||||
|
]);
|
||||||
|
return <></>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Gpxes;
|
|
@ -1,8 +1,8 @@
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
import { Point } from '../components/map/types';
|
import { Point, Rectangle } from '../components/map/types';
|
||||||
import { lat2tile, lon2tile } from '../lib/geo';
|
import { lat2tile, lon2tile, rectanglesIntersect } from '../lib/geo';
|
||||||
import getUri, { intToGpxId } from '../lib/ids';
|
import getUri, { intToGpxId } from '../lib/ids';
|
||||||
import { get, put, putAll } from './lib';
|
import { get, getDocsByType, put, putAll } from './lib';
|
||||||
|
|
||||||
const emptyGpx: Gpx = {
|
const emptyGpx: Gpx = {
|
||||||
$: {
|
$: {
|
||||||
|
@ -166,3 +166,24 @@ export const pruneAndSaveImportedGpx = async (params: any) => {
|
||||||
console.error(`error: ${err}`);
|
console.error(`error: ${err}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getGpxesForViewport = async (params: any) => {
|
||||||
|
const { viewport, zoomLevel } = params;
|
||||||
|
const zoomedViewport: Rectangle = {
|
||||||
|
topLeft: {
|
||||||
|
x: viewport.topLeft.x / 2 ** zoomLevel,
|
||||||
|
y: viewport.topLeft.y / 2 ** zoomLevel,
|
||||||
|
},
|
||||||
|
bottomRight: {
|
||||||
|
x: (viewport.bottomRight.x + 1) / 2 ** zoomLevel,
|
||||||
|
y: (viewport.bottomRight.y + 1) / 2 ** zoomLevel,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const techs = await getDocsByType('tech');
|
||||||
|
console.log(`getGpxesForViewport, techs: ${JSON.stringify(techs)}`);
|
||||||
|
return techs
|
||||||
|
.filter((tech: any) => {
|
||||||
|
return rectanglesIntersect(zoomedViewport, tech.doc.viewport);
|
||||||
|
})
|
||||||
|
.map((tech: any) => getUri('tech', tech._id));
|
||||||
|
};
|
||||||
|
|
|
@ -53,7 +53,7 @@ export const initDb = async (params: any) => {
|
||||||
// WARNING: defs must use the canonical form and be identical to what will be returned by db.getIndexes
|
// WARNING: defs must use the canonical form and be identical to what will be returned by db.getIndexes
|
||||||
const requiredIndexes: any = [
|
const requiredIndexes: any = [
|
||||||
{
|
{
|
||||||
name: 'type-subtype',
|
name: 'type',
|
||||||
def: {
|
def: {
|
||||||
fields: [{ type: 'asc' }],
|
fields: [{ type: 'asc' }],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { initDb } from '.';
|
import { initDb } from '.';
|
||||||
import uri from '../lib/ids';
|
import uri from '../lib/ids';
|
||||||
import { getFamily } from './lib';
|
import { getDocsByType, getFamily } from './lib';
|
||||||
import { putNewRte } from './rte';
|
import { putNewRte } from './rte';
|
||||||
import { putNewRtept } from './rtept';
|
import { putNewRtept } from './rtept';
|
||||||
import { putNewTrk } from './trk';
|
import { putNewTrk } from './trk';
|
||||||
|
@ -269,3 +269,31 @@ Object {
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getDocsByType', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await initDb({});
|
||||||
|
globalThis.Date.now = () => 0;
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await db.destroy();
|
||||||
|
db = undefined;
|
||||||
|
globalThis.Date.now = originalDateNow;
|
||||||
|
});
|
||||||
|
test('gets the rte amongst other docs', async () => {
|
||||||
|
await putNewRtept();
|
||||||
|
const rtes = await getDocsByType('rte');
|
||||||
|
expect(rtes).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"_id": "gpx/4320000000000000/2rte/000000",
|
||||||
|
"_rev": "1-2ca14f512a9c83f5a239389e580befce",
|
||||||
|
"doc": Object {
|
||||||
|
"number": 0,
|
||||||
|
},
|
||||||
|
"type": "rte",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -46,3 +46,7 @@ export const get = async (id: string) => {
|
||||||
export const putAll = async (docs: any[]) => {
|
export const putAll = async (docs: any[]) => {
|
||||||
return await db.bulkDocs(docs);
|
return await db.bulkDocs(docs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDocsByType = async (type: string) => {
|
||||||
|
return (await db.find({ selector: { type: type } })).docs;
|
||||||
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
import { Rectangle } from '../components/map/types';
|
||||||
|
|
||||||
// cf https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#ECMAScript_(JavaScript/ActionScript,_etc.)
|
// cf https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#ECMAScript_(JavaScript/ActionScript,_etc.)
|
||||||
export const lon2tile = (lon: number, zoom: number) => {
|
export const lon2tile = (lon: number, zoom: number) => {
|
||||||
|
@ -25,3 +25,11 @@ export function tile2lat(y: number, z: number) {
|
||||||
var n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z);
|
var n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z);
|
||||||
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const rectanglesIntersect = (r1: Rectangle, r2: Rectangle) =>
|
||||||
|
!(
|
||||||
|
r2.topLeft.x > r1.bottomRight.x ||
|
||||||
|
r2.bottomRight.x < r1.topLeft.x ||
|
||||||
|
r2.topLeft.y > r1.bottomRight.y ||
|
||||||
|
r2.bottomRight.y < r1.topLeft.y
|
||||||
|
);
|
||||||
|
|
|
@ -1,10 +1,22 @@
|
||||||
import { initDb } from '../db';
|
import { initDb } from '../db';
|
||||||
import { putNewGpx, existsGpx, pruneAndSaveImportedGpx } from '../db/gpx';
|
import {
|
||||||
|
putNewGpx,
|
||||||
|
existsGpx,
|
||||||
|
pruneAndSaveImportedGpx,
|
||||||
|
getGpxesForViewport,
|
||||||
|
} from '../db/gpx';
|
||||||
import { putNewTrk } from '../db/trk';
|
import { putNewTrk } from '../db/trk';
|
||||||
|
|
||||||
const self = globalThis as unknown as SharedWorkerGlobalScope;
|
const self = globalThis as unknown as SharedWorkerGlobalScope;
|
||||||
|
|
||||||
const actions = { initDb, putNewGpx, putNewTrk, existsGpx, pruneAndSaveImportedGpx };
|
const actions = {
|
||||||
|
initDb,
|
||||||
|
putNewGpx,
|
||||||
|
putNewTrk,
|
||||||
|
existsGpx,
|
||||||
|
pruneAndSaveImportedGpx,
|
||||||
|
getGpxesForViewport,
|
||||||
|
};
|
||||||
|
|
||||||
self.onconnect = function (e) {
|
self.onconnect = function (e) {
|
||||||
var port = e.ports[0];
|
var port = e.ports[0];
|
||||||
|
|
Loading…
Reference in New Issue