From d86d63ca611fbe72777ec83b7ab110cd15f6b3a2 Mon Sep 17 00:00:00 2001 From: evlist Date: Tue, 15 Nov 2022 20:19:14 +0100 Subject: [PATCH] Starting to implement GPX visualization. --- src/App.tsx | 8 +++++-- src/components/map/Gpxes.tsx | 36 ++++++++++++++++++++++++++++++++ src/db/gpx.ts | 27 +++++++++++++++++++++--- src/db/index.ts | 2 +- src/db/lib.test.ts | 30 +++++++++++++++++++++++++- src/db/lib.ts | 4 ++++ src/lib/geo.ts | 10 ++++++++- src/workers/dispatcher-worker.ts | 16 ++++++++++++-- 8 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 src/components/map/Gpxes.tsx diff --git a/src/App.tsx b/src/App.tsx index b2b1331..a6f5749 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,6 +41,7 @@ import Back from './components/buttons/Back'; import Forward from './components/buttons/Forward'; import CurrentLocation from './components/map/CurrentLocation'; import GpxImport from './components/dialogs/GpxImport'; +import Gpxes from './components/map/Gpxes'; // import { initDb } from './db'; // import PouchDB from 'pouchdb'; // import PouchDBFind from 'pouchdb-find'; @@ -96,7 +97,7 @@ export const setCenterAtom = atom(null, (get, set, center: geoPoint) => { // initDb(db); dispatch({ action: 'initDb' }); - +//dispatch({ action: 'getGpxesForViewport', params: { type: 'tech' } }); /** * * @returns The root app component @@ -113,7 +114,10 @@ const App: React.FC = () => { scope={scope} setScope={debounce(setScope, 1000)} numberOfTiledLayers={5} - slippyGraphics={[]} + slippyGraphics={[ + , + , + ]} /> diff --git a/src/components/map/Gpxes.tsx b/src/components/map/Gpxes.tsx new file mode 100644 index 0000000..15883f3 --- /dev/null +++ b/src/components/map/Gpxes.tsx @@ -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 = (props: GpxesProperties) => { + const [visibleGpxes, setVisibleGpxes] = useState([]); + 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; diff --git a/src/db/gpx.ts b/src/db/gpx.ts index 92d8611..e581d3f 100644 --- a/src/db/gpx.ts +++ b/src/db/gpx.ts @@ -1,8 +1,8 @@ import { PureComponent } from 'react'; -import { Point } from '../components/map/types'; -import { lat2tile, lon2tile } from '../lib/geo'; +import { Point, Rectangle } from '../components/map/types'; +import { lat2tile, lon2tile, rectanglesIntersect } from '../lib/geo'; import getUri, { intToGpxId } from '../lib/ids'; -import { get, put, putAll } from './lib'; +import { get, getDocsByType, put, putAll } from './lib'; const emptyGpx: Gpx = { $: { @@ -166,3 +166,24 @@ export const pruneAndSaveImportedGpx = async (params: any) => { 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)); +}; diff --git a/src/db/index.ts b/src/db/index.ts index 5a6c935..49536bf 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -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 const requiredIndexes: any = [ { - name: 'type-subtype', + name: 'type', def: { fields: [{ type: 'asc' }], }, diff --git a/src/db/lib.test.ts b/src/db/lib.test.ts index fb68891..2d8d58f 100644 --- a/src/db/lib.test.ts +++ b/src/db/lib.test.ts @@ -1,6 +1,6 @@ import { initDb } from '.'; import uri from '../lib/ids'; -import { getFamily } from './lib'; +import { getDocsByType, getFamily } from './lib'; import { putNewRte } from './rte'; import { putNewRtept } from './rtept'; 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", + }, +] +`); + }); +}); diff --git a/src/db/lib.ts b/src/db/lib.ts index 3309276..0b98445 100644 --- a/src/db/lib.ts +++ b/src/db/lib.ts @@ -46,3 +46,7 @@ export const get = async (id: string) => { export const putAll = async (docs: any[]) => { return await db.bulkDocs(docs); }; + +export const getDocsByType = async (type: string) => { + return (await db.find({ selector: { type: type } })).docs; +}; diff --git a/src/lib/geo.ts b/src/lib/geo.ts index 01116a2..f4b920b 100644 --- a/src/lib/geo.ts +++ b/src/lib/geo.ts @@ -1,4 +1,4 @@ - +import { Rectangle } from '../components/map/types'; // cf https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#ECMAScript_(JavaScript/ActionScript,_etc.) 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); 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 + ); diff --git a/src/workers/dispatcher-worker.ts b/src/workers/dispatcher-worker.ts index 71b335d..979ddd2 100644 --- a/src/workers/dispatcher-worker.ts +++ b/src/workers/dispatcher-worker.ts @@ -1,10 +1,22 @@ import { initDb } from '../db'; -import { putNewGpx, existsGpx, pruneAndSaveImportedGpx } from '../db/gpx'; +import { + putNewGpx, + existsGpx, + pruneAndSaveImportedGpx, + getGpxesForViewport, +} from '../db/gpx'; import { putNewTrk } from '../db/trk'; 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) { var port = e.ports[0];