diff --git a/package-lock.json b/package-lock.json index 3c19c70..0070cc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,9 @@ "@types/react-dom": "^18.0.6", "@types/react-router": "^5.1.11", "@types/react-router-dom": "^5.1.7", + "docuri": "^4.2.2", "ionicons": "^6.0.3", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router": "^5.2.0", @@ -47,7 +49,8 @@ "workbox-streams": "^5.1.4" }, "devDependencies": { - "@capacitor/cli": "4.3.0" + "@capacitor/cli": "4.3.0", + "@types/lodash": "^4.14.186" } }, "node_modules/@adobe/css-tools": { @@ -3775,6 +3778,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "node_modules/@types/lodash": { + "version": "4.14.186", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", + "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==", + "dev": true + }, "node_modules/@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -6356,6 +6365,11 @@ "node": ">=6.0.0" } }, + "node_modules/docuri": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/docuri/-/docuri-4.2.2.tgz", + "integrity": "sha512-eEtIB7SQAEVO1HC286DlnPnSiddAl+wFv+EeBBQ7VdTEMRplzG4qIhrNdyIecXfVVD9cIghikVVkls1O78EQqQ==" + }, "node_modules/dom-accessibility-api": { "version": "0.5.14", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", @@ -18609,6 +18623,12 @@ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" }, + "@types/lodash": { + "version": "4.14.186", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.186.tgz", + "integrity": "sha512-eHcVlLXP0c2FlMPm56ITode2AgLMSa6aJ05JTTbYbI+7EMkCEE5qk2E41d5g2lCVTqRe0GnnRFurmlCsDODrPw==", + "dev": true + }, "@types/mime": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", @@ -20512,6 +20532,11 @@ "esutils": "^2.0.2" } }, + "docuri": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/docuri/-/docuri-4.2.2.tgz", + "integrity": "sha512-eEtIB7SQAEVO1HC286DlnPnSiddAl+wFv+EeBBQ7VdTEMRplzG4qIhrNdyIecXfVVD9cIghikVVkls1O78EQqQ==" + }, "dom-accessibility-api": { "version": "0.5.14", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz", diff --git a/package.json b/package.json index 0d9ff00..c732651 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,9 @@ "@types/react-dom": "^18.0.6", "@types/react-router": "^5.1.11", "@types/react-router-dom": "^5.1.7", + "docuri": "^4.2.2", "ionicons": "^6.0.3", + "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router": "^5.2.0", @@ -66,7 +68,8 @@ ] }, "devDependencies": { - "@capacitor/cli": "4.3.0" + "@capacitor/cli": "4.3.0", + "@types/lodash": "^4.14.186" }, "description": "An Ionic project" } diff --git a/src/components/map/TiledLayer.test.tsx b/src/components/map/TiledLayer.test.tsx new file mode 100644 index 0000000..68d6ef6 --- /dev/null +++ b/src/components/map/TiledLayer.test.tsx @@ -0,0 +1,42 @@ +import { render, screen } from '@testing-library/react'; +import TiledLayer from './TiledLayer'; + +describe('The TiledLayer component ', () => { + test('', () => { + // const tiledLayer = renderer.create( + // + // ); + // const tree = tiledLayer.toJSON(); + // console.log(JSON.stringify(tree)); + + const { baseElement } = render( + + + + ); + screen.debug(); + expect(baseElement).toMatchInlineSnapshot(` + +
+ + + + + +
+ +`); + }); +}); diff --git a/src/components/map/TiledLayer.tsx b/src/components/map/TiledLayer.tsx index a7ace2e..44f5497 100644 --- a/src/components/map/TiledLayer.tsx +++ b/src/components/map/TiledLayer.tsx @@ -1,12 +1,12 @@ -import react from 'react'; +import react, { useRef } from 'react'; +import { range } from 'lodash'; import { Rectangle, TileKeyObject } from './types'; +import tileUri from './uris'; export interface TiledLayerProperties { /** The key of the first (ie top/left) tile */ keyObject: TileKeyObject; - /** Number of tiles (in each direction since TiledLayers are square)*/ - nbTiles: number; /** The current viewport expressed in tiles coordinates */ viewPort: Rectangle; } @@ -22,7 +22,28 @@ export interface TiledLayerProperties { export const TiledLayer: react.FC = ( props: TiledLayerProperties ) => { - return <>; + console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`); + const tiles = useRef({}); + + range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).map( + (row) => { + range(props.viewPort.topLeft.x, props.viewPort.bottomRight.x + 1).map( + (col) => { + const key = tileUri({ + provider: props.keyObject.provider, + zoomLevel: props.keyObject.zoomLevel, + x: props.keyObject.x + row, + y: props.keyObject.x + col, + }); + if (!Object.hasOwn(tiles.current, key)) { + tiles.current[key] = ; + } + } + ); + } + ); + + return <>{Object.values(tiles.current)}; }; export default TiledLayer; diff --git a/src/components/map/uris.test.ts b/src/components/map/uris.test.ts new file mode 100644 index 0000000..1dab357 --- /dev/null +++ b/src/components/map/uris.test.ts @@ -0,0 +1,16 @@ +import tileUri from './uris'; +describe('Test that', () => { + test('uri generation is working', () => { + expect(tileUri({ provider: 'osm', zoomLevel: 16, x: 25, y: 52 })).toEqual( + 'tile/osm/16/25/52' + ); + }); + test('uri parsing works', () => { + expect(tileUri('tile/otm/5/28/3')).toEqual({ + provider: 'otm', + zoomLevel: 5, + x: 28, + y: 3, + }); + }); +}); diff --git a/src/components/map/uris.ts b/src/components/map/uris.ts new file mode 100644 index 0000000..940c119 --- /dev/null +++ b/src/components/map/uris.ts @@ -0,0 +1,21 @@ +import { route } from 'docuri'; + +/** + * A [docuri](https://github.com/jo/docuri) route for {@link components/map/types!TileKeyObject} + * + * TODO: update docuri (or write a wrapper) to support datatyping (and formats). + */ +export const tileUri = (rte: any) => { + const r = route('tile/:provider/:zoomLevel/:x/:y')(rte); + if (typeof r === 'object') { + return { + provider: r.provider, + zoomLevel: parseInt(r.zoomLevel), + x: parseInt(r.x), + y: parseInt(r.y), + }; + } + return r; +}; + +export default tileUri; diff --git a/src/missing-typedefs/docuri.d.ts b/src/missing-typedefs/docuri.d.ts new file mode 100644 index 0000000..d3cf061 --- /dev/null +++ b/src/missing-typedefs/docuri.d.ts @@ -0,0 +1 @@ +declare module 'docuri'; \ No newline at end of file