Starting to implement a LayerStack component
This commit is contained in:
parent
4238ce0938
commit
3dee9e90d7
|
@ -0,0 +1,264 @@
|
||||||
|
import { renderHook, act, render, screen } from '@testing-library/react';
|
||||||
|
import { useAtom } from 'jotai';
|
||||||
|
import LayerStack from './LayerStack';
|
||||||
|
|
||||||
|
describe('The LayerStack component', () => {
|
||||||
|
test('generates something', () => {
|
||||||
|
// const { result } = renderHook(() => useAtom(tiledLayersAtom));
|
||||||
|
render(
|
||||||
|
<LayerStack
|
||||||
|
numberOfTiledLayers={5}
|
||||||
|
keyObject={{ provider: 'xxx', zoomLevel: 9, x: 777, y: 333 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const svg = screen.getByTestId('layer-stack');
|
||||||
|
expect(svg).toMatchInlineSnapshot(`
|
||||||
|
<svg
|
||||||
|
data-testid="layer-stack"
|
||||||
|
height="768"
|
||||||
|
width="1024"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
transform="translate(0, 0) scale(1)"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
transform="scale(64) translate(0.25, 0.25)"
|
||||||
|
/>
|
||||||
|
<g
|
||||||
|
transform="scale(128) translate(0.5, 0.5)"
|
||||||
|
/>
|
||||||
|
<g
|
||||||
|
transform="scale(2048) translate(0, 0)"
|
||||||
|
/>
|
||||||
|
<g
|
||||||
|
transform="scale(1024) translate(0, 0)"
|
||||||
|
/>
|
||||||
|
<g
|
||||||
|
transform="scale(256) translate(0, 0)"
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
transform="translate(0, 0)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/777/333.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(1, 0)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/778/333.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(2, 0)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/779/333.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(3, 0)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/780/333.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(4, 0)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/781/333.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(0, 1)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/777/334.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(1, 1)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/778/334.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(2, 1)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/779/334.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(3, 1)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/780/334.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(4, 1)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/781/334.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(0, 2)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/777/335.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(1, 2)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/778/335.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(2, 2)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/779/335.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(3, 2)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/780/335.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(4, 2)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/781/335.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(0, 3)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/777/336.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(1, 3)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/778/336.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(2, 3)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/779/336.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(3, 3)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/780/336.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="translate(4, 3)"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<image
|
||||||
|
height="1"
|
||||||
|
href="https://tile.openstreetmap.org/9/781/336.png"
|
||||||
|
width="1"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,139 @@
|
||||||
|
import react, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { atom, useAtom } from 'jotai';
|
||||||
|
|
||||||
|
import { TileFactory, TileKeyObject } from './types';
|
||||||
|
|
||||||
|
import { coordinateSystemAtom } from './Map';
|
||||||
|
import TiledLayer from './TiledLayer';
|
||||||
|
import Tile from './Tile';
|
||||||
|
import _, { floor, range } from 'lodash';
|
||||||
|
import tileUri from './uris';
|
||||||
|
|
||||||
|
export interface LayerStackProperties {
|
||||||
|
/**
|
||||||
|
* A key identifying the initial top left tile
|
||||||
|
*/
|
||||||
|
keyObject: TileKeyObject;
|
||||||
|
/**
|
||||||
|
* Number of {@link components/map/TiledLayer!TiledLayer}.
|
||||||
|
*/
|
||||||
|
numberOfTiledLayers?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param props
|
||||||
|
* @returns A stack of layers embedded in an SVG element
|
||||||
|
*
|
||||||
|
* This component does the conversion between the {@link components/map/Map!CoordinateSystem} stored
|
||||||
|
* in the {@link components/map/Map!coordinateSystemAtom} atom and the {@link components/map/TiledLayer!TiledLayer}
|
||||||
|
* components which units are in tiles.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export const LayerStack: react.FC<LayerStackProperties> = (
|
||||||
|
props: LayerStackProperties
|
||||||
|
) => {
|
||||||
|
const [coordinateSystem] = useAtom(coordinateSystemAtom);
|
||||||
|
|
||||||
|
const simpleTileFactory: TileFactory = useCallback(
|
||||||
|
(keyObject) => (
|
||||||
|
<Tile
|
||||||
|
href={`https://tile.openstreetmap.org/${keyObject.zoomLevel}/${keyObject.x}/${keyObject.y}.png`}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const numberOfTiledLayers =
|
||||||
|
props.numberOfTiledLayers === undefined ? 1 : props.numberOfTiledLayers;
|
||||||
|
|
||||||
|
const [activeTiledLayer, setActiveTiledLayer] = useState(
|
||||||
|
Math.floor(numberOfTiledLayers / 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
const viewPort = {
|
||||||
|
topLeft: {
|
||||||
|
x: Math.floor(-coordinateSystem.shift.x / coordinateSystem.zoom / 256),
|
||||||
|
y: Math.floor(-coordinateSystem.shift.y / coordinateSystem.zoom / 256),
|
||||||
|
},
|
||||||
|
bottomRight: {
|
||||||
|
x: Math.ceil(
|
||||||
|
(-coordinateSystem.shift.x + window.innerWidth) /
|
||||||
|
coordinateSystem.zoom /
|
||||||
|
256
|
||||||
|
),
|
||||||
|
y: Math.ceil(
|
||||||
|
(-coordinateSystem.shift.y + window.innerHeight) /
|
||||||
|
coordinateSystem.zoom /
|
||||||
|
256
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTiledLayer = (i: number) => {
|
||||||
|
const relativeZoomLevel = i - activeTiledLayer;
|
||||||
|
const zoom = 2 ** relativeZoomLevel;
|
||||||
|
const origin = {
|
||||||
|
x: props.keyObject.x * zoom,
|
||||||
|
y: props.keyObject.y * zoom,
|
||||||
|
};
|
||||||
|
const keyObject = {
|
||||||
|
provider: props.keyObject.provider,
|
||||||
|
zoomLevel: props.keyObject.zoomLevel + relativeZoomLevel,
|
||||||
|
x: Math.floor(origin.x),
|
||||||
|
y: Math.floor(origin.y),
|
||||||
|
};
|
||||||
|
const shift = {
|
||||||
|
x: origin.x - floor(origin.x),
|
||||||
|
y: origin.y - floor(origin.y),
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<g
|
||||||
|
transform={`scale(${256 * zoom}) translate(${shift.x}, ${shift.y})`}
|
||||||
|
key={tileUri({
|
||||||
|
provider: keyObject.provider,
|
||||||
|
zoomLevel: keyObject.zoomLevel,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<TiledLayer
|
||||||
|
keyObject={keyObject}
|
||||||
|
viewPort={i === activeTiledLayer ? viewPort : undefined}
|
||||||
|
tileFactory={simpleTileFactory}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tileLayers = range(0, numberOfTiledLayers).map((i) => {});
|
||||||
|
|
||||||
|
// console.log(`tiledLayers: ${JSON.stringify(tiledLayers)}`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={window.innerWidth}
|
||||||
|
height={window.innerHeight}
|
||||||
|
data-testid='layer-stack'
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
transform={`translate(${coordinateSystem.shift.x}, ${coordinateSystem.shift.y}) scale(${coordinateSystem.zoom})`}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
// Tiled layers with less detail
|
||||||
|
range(0, activeTiledLayer).map(getTiledLayer)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Tiled layers with more details
|
||||||
|
range(numberOfTiledLayers, activeTiledLayer + 1, -1).map(
|
||||||
|
getTiledLayer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// And the active one
|
||||||
|
getTiledLayer(activeTiledLayer)
|
||||||
|
}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LayerStack;
|
|
@ -1,10 +1,9 @@
|
||||||
import react, { useCallback } from 'react';
|
import react from 'react';
|
||||||
import { atom, useAtom } from 'jotai';
|
import { atom, useAtom } from 'jotai';
|
||||||
|
|
||||||
import Handlers from './Handlers';
|
import Handlers from './Handlers';
|
||||||
import Tile from './Tile';
|
import { Point } from './types';
|
||||||
import TiledLayer from './TiledLayer';
|
import LayerStack from './LayerStack';
|
||||||
import { Point, TileFactory } from './types';
|
|
||||||
|
|
||||||
export interface MapProperties {}
|
export interface MapProperties {}
|
||||||
|
|
||||||
|
@ -75,54 +74,20 @@ export const relativeCoordinateSystemAtom = atom(
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @returns A Map component
|
* @returns A Map component
|
||||||
|
*
|
||||||
|
* TODO: Is this component really useful ?
|
||||||
|
* TODO: does the coordinate system belong to this component or to `<LayerStack>` ?
|
||||||
*/
|
*/
|
||||||
export const Map: react.FC<MapProperties> = (props: MapProperties) => {
|
export const Map: react.FC<MapProperties> = (props: MapProperties) => {
|
||||||
const [coordinateSystem, setCoordinateSystem] = useAtom(coordinateSystemAtom);
|
const [coordinateSystem, setCoordinateSystem] = useAtom(coordinateSystemAtom);
|
||||||
|
|
||||||
const simpleTileFactory: TileFactory = useCallback(
|
|
||||||
(keyObject) => (
|
|
||||||
<Tile
|
|
||||||
href={`https://tile.openstreetmap.org/${keyObject.zoomLevel}/${keyObject.x}/${keyObject.y}.png`}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const viewPort = {
|
|
||||||
topLeft: {
|
|
||||||
x: Math.floor(-coordinateSystem.shift.x / coordinateSystem.zoom / 256),
|
|
||||||
y: Math.floor(-coordinateSystem.shift.y / coordinateSystem.zoom / 256),
|
|
||||||
},
|
|
||||||
bottomRight: {
|
|
||||||
x: Math.ceil(
|
|
||||||
(-coordinateSystem.shift.x + window.innerWidth) /
|
|
||||||
coordinateSystem.zoom /
|
|
||||||
256
|
|
||||||
),
|
|
||||||
y: Math.ceil(
|
|
||||||
(-coordinateSystem.shift.y + window.innerHeight) /
|
|
||||||
coordinateSystem.zoom /
|
|
||||||
256
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Handlers />
|
<Handlers />
|
||||||
<svg width={window.innerWidth} height={window.innerHeight}>
|
<LayerStack
|
||||||
<g
|
numberOfTiledLayers={3}
|
||||||
transform={`translate(${coordinateSystem.shift.x}, ${coordinateSystem.shift.y}) scale(${coordinateSystem.zoom})`}
|
keyObject={{ provider: 'osm', zoomLevel: 16, x: 33485, y: 23936 }}
|
||||||
>
|
/>
|
||||||
<g transform='scale(256)'>
|
|
||||||
<TiledLayer
|
|
||||||
keyObject={{ provider: 'osm', zoomLevel: 16, x: 33485, y: 23936 }}
|
|
||||||
viewPort={viewPort}
|
|
||||||
tileFactory={simpleTileFactory}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ export interface TiledLayerProperties {
|
||||||
/** The key of the first (ie top/left) tile */
|
/** The key of the first (ie top/left) tile */
|
||||||
keyObject: TileKeyObject;
|
keyObject: TileKeyObject;
|
||||||
/** The current viewport expressed in tiles coordinates */
|
/** The current viewport expressed in tiles coordinates */
|
||||||
viewPort: Rectangle;
|
viewPort?: Rectangle;
|
||||||
/** The factory to create tiles */
|
/** The factory to create tiles */
|
||||||
tileFactory: TileFactory;
|
tileFactory: TileFactory;
|
||||||
}
|
}
|
||||||
|
@ -19,12 +19,14 @@ export interface TiledLayerProperties {
|
||||||
* This component is rather dumb and is mainly a sparse array of tiles.
|
* This component is rather dumb and is mainly a sparse array of tiles.
|
||||||
*
|
*
|
||||||
* New tiles are added to the array when the viewport is updated and they stay in the array until
|
* New tiles are added to the array when the viewport is updated and they stay in the array until
|
||||||
* the component is destroyed or its number of tiles is updated.
|
* the component is destroyed.
|
||||||
*
|
*
|
||||||
* This component has no need to know the number nor the size of its tiles: tiles can be added when needed and
|
* This component has no need to know the number nor the size of its tiles: tiles can be added when needed and
|
||||||
* its unit is the tile size (the parent component needs to transform its enclosing SVG group to adapt its units)
|
* its unit is the tile size (the parent component needs to transform its enclosing SVG group to adapt its units)
|
||||||
*
|
*
|
||||||
* TODO: test tiles'X and Y boundaries.
|
* TODO: test tiles'X and Y boundaries.
|
||||||
|
* TODO: housekeeping
|
||||||
|
* TODO: remove tileFactory to facilitate memoisation.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export const TiledLayer: react.FC<TiledLayerProperties> = memo(
|
export const TiledLayer: react.FC<TiledLayerProperties> = memo(
|
||||||
|
@ -32,10 +34,13 @@ export const TiledLayer: react.FC<TiledLayerProperties> = memo(
|
||||||
console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`);
|
console.log(`Rendering TiledLayer: ${JSON.stringify(props)}`);
|
||||||
const tiles = useRef<any>(new Map());
|
const tiles = useRef<any>(new Map());
|
||||||
|
|
||||||
range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).map(
|
if (props.viewPort !== undefined) {
|
||||||
(row) => {
|
range(props.viewPort.topLeft.y, props.viewPort.bottomRight.y + 1).forEach(
|
||||||
range(props.viewPort.topLeft.x, props.viewPort.bottomRight.x + 1).map(
|
(row) => {
|
||||||
(col) => {
|
range(
|
||||||
|
props.viewPort!.topLeft.x,
|
||||||
|
props.viewPort!.bottomRight.x + 1
|
||||||
|
).forEach((col) => {
|
||||||
const keyObject = {
|
const keyObject = {
|
||||||
provider: props.keyObject.provider,
|
provider: props.keyObject.provider,
|
||||||
zoomLevel: props.keyObject.zoomLevel,
|
zoomLevel: props.keyObject.zoomLevel,
|
||||||
|
@ -51,14 +56,18 @@ export const TiledLayer: react.FC<TiledLayerProperties> = memo(
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
}
|
||||||
}
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
return <>{Array.from(tiles.current.values())}</>;
|
return <>{Array.from(tiles.current.values())}</>;
|
||||||
},
|
},
|
||||||
isEqual
|
(prevProps, nextProps) =>
|
||||||
|
isEqual(
|
||||||
|
[prevProps.keyObject, prevProps.viewPort],
|
||||||
|
[nextProps.keyObject, nextProps.viewPort]
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export default TiledLayer;
|
export default TiledLayer;
|
||||||
|
|
|
@ -13,4 +13,13 @@ describe('Test that', () => {
|
||||||
y: 3,
|
y: 3,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
test('x and y are optional', () => {
|
||||||
|
expect(tileUri({ provider: 'osm', zoomLevel: 16 })).toEqual('tile/osm/16//');
|
||||||
|
});
|
||||||
|
test('uri parsing works', () => {
|
||||||
|
expect(tileUri('tile/otm/5')).toEqual({
|
||||||
|
provider: 'otm',
|
||||||
|
zoomLevel: 5,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,14 +6,19 @@ import { route } from 'docuri';
|
||||||
* TODO: update docuri (or write a wrapper) to support datatyping (and formats).
|
* TODO: update docuri (or write a wrapper) to support datatyping (and formats).
|
||||||
*/
|
*/
|
||||||
export const tileUri = (rte: any) => {
|
export const tileUri = (rte: any) => {
|
||||||
const r = route('tile/:provider/:zoomLevel/:x/:y')(rte);
|
const r = route('tile/:provider/:zoomLevel(/:x/:y)')(rte);
|
||||||
if (typeof r === 'object') {
|
if (typeof r === 'object') {
|
||||||
return {
|
return r.x === undefined
|
||||||
provider: r.provider,
|
? {
|
||||||
zoomLevel: parseInt(r.zoomLevel),
|
provider: r.provider,
|
||||||
x: parseInt(r.x),
|
zoomLevel: parseInt(r.zoomLevel),
|
||||||
y: parseInt(r.y),
|
}
|
||||||
};
|
: {
|
||||||
|
provider: r.provider,
|
||||||
|
zoomLevel: parseInt(r.zoomLevel),
|
||||||
|
x: parseInt(r.x),
|
||||||
|
y: parseInt(r.y),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue