Implementing a Marker component.
This commit is contained in:
parent
925174db79
commit
1ed4875e5e
20
src/App.tsx
20
src/App.tsx
|
@ -36,6 +36,7 @@ import GetLocation from './components/buttons/GetLocation';
|
||||||
import { geoPoint } from './components/map/types';
|
import { geoPoint } from './components/map/types';
|
||||||
import Back from './components/buttons/Back';
|
import Back from './components/buttons/Back';
|
||||||
import Forward from './components/buttons/Forward';
|
import Forward from './components/buttons/Forward';
|
||||||
|
import Marker from './components/map/Marker';
|
||||||
|
|
||||||
setupIonicReact();
|
setupIonicReact();
|
||||||
|
|
||||||
|
@ -62,6 +63,24 @@ const App: React.FC = () => {
|
||||||
const [scope, setScope] = useAtom(scopeAtom);
|
const [scope, setScope] = useAtom(scopeAtom);
|
||||||
|
|
||||||
console.log(`App, scope: ${JSON.stringify(scope)}`);
|
console.log(`App, scope: ${JSON.stringify(scope)}`);
|
||||||
|
const marker = (
|
||||||
|
<Marker
|
||||||
|
coordinates={{ lat: -37.8403508, lon: 77.5539501 }}
|
||||||
|
icon={
|
||||||
|
<circle
|
||||||
|
cx={0}
|
||||||
|
cy={0}
|
||||||
|
r={6 / 256}
|
||||||
|
fill='blue'
|
||||||
|
opacity='90%'
|
||||||
|
stroke='white'
|
||||||
|
strokeWidth={3 / 256}
|
||||||
|
strokeOpacity='100%'
|
||||||
|
></circle>
|
||||||
|
}
|
||||||
|
key='current'
|
||||||
|
/>
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<IonApp>
|
<IonApp>
|
||||||
<IonContent fullscreen={true}>
|
<IonContent fullscreen={true}>
|
||||||
|
@ -70,6 +89,7 @@ const App: React.FC = () => {
|
||||||
scope={scope}
|
scope={scope}
|
||||||
setScope={debounce(setScope, 1000)}
|
setScope={debounce(setScope, 1000)}
|
||||||
numberOfTiledLayers={5}
|
numberOfTiledLayers={5}
|
||||||
|
markers={[marker]}
|
||||||
/>
|
/>
|
||||||
</IonApp>
|
</IonApp>
|
||||||
</IonContent>
|
</IonContent>
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import react from 'react';
|
import react, {
|
||||||
|
cloneElement,
|
||||||
|
JSXElementConstructor,
|
||||||
|
ReactComponentElement,
|
||||||
|
ReactElement,
|
||||||
|
ReactNode,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import { TileKeyObject } from './types';
|
import { TileKeyObject } from './types';
|
||||||
|
|
||||||
|
@ -6,6 +12,8 @@ import { CoordinateSystem } from './LiveMap';
|
||||||
import { range } from 'lodash';
|
import { range } from 'lodash';
|
||||||
import tileUri from './uris';
|
import tileUri from './uris';
|
||||||
import TiledLayer from './TiledLayer';
|
import TiledLayer from './TiledLayer';
|
||||||
|
import { ReactComponentOrElement } from '@ionic/react';
|
||||||
|
import { KeyObject } from 'crypto';
|
||||||
|
|
||||||
export interface LayerStackProperties {
|
export interface LayerStackProperties {
|
||||||
/**
|
/**
|
||||||
|
@ -20,6 +28,8 @@ export interface LayerStackProperties {
|
||||||
* The coordinates system
|
* The coordinates system
|
||||||
*/
|
*/
|
||||||
coordinateSystem: CoordinateSystem;
|
coordinateSystem: CoordinateSystem;
|
||||||
|
/** Markers are non scalable SVG snippets tied to geo location */
|
||||||
|
markers?: ReactElement<any, string | JSXElementConstructor<any>>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,6 +92,7 @@ export const LayerStack: react.FC<LayerStackProperties> = (
|
||||||
<svg width='100%' height='100%' data-testid='layer-stack'>
|
<svg width='100%' height='100%' data-testid='layer-stack'>
|
||||||
<g
|
<g
|
||||||
transform={`translate(${props.coordinateSystem.shift.x}, ${props.coordinateSystem.shift.y}) scale(${props.coordinateSystem.zoom})`}
|
transform={`translate(${props.coordinateSystem.shift.x}, ${props.coordinateSystem.shift.y}) scale(${props.coordinateSystem.zoom})`}
|
||||||
|
key='tiles'
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
// Tiled layers with less detail
|
// Tiled layers with less detail
|
||||||
|
@ -98,6 +109,23 @@ export const LayerStack: react.FC<LayerStackProperties> = (
|
||||||
getTiledLayer(activeTiledLayer)
|
getTiledLayer(activeTiledLayer)
|
||||||
}
|
}
|
||||||
</g>
|
</g>
|
||||||
|
{props.markers !== undefined ? (
|
||||||
|
<g
|
||||||
|
key='markers'
|
||||||
|
transform={`translate(${props.coordinateSystem.shift.x}, ${props.coordinateSystem.shift.y}) scale(${props.coordinateSystem.zoom})`}
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
transform={`scale(256) translate(-${props.keyObject.x}, -${props.keyObject.y})`}
|
||||||
|
>
|
||||||
|
{props.markers.map((marker) =>
|
||||||
|
cloneElement(marker, {
|
||||||
|
keyObject: props.keyObject,
|
||||||
|
zoom: props.coordinateSystem.zoom,
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
) : null}
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import react, { useEffect, useState } from 'react';
|
import react, { JSXElementConstructor, ReactComponentElement, ReactElement, ReactNode, useEffect, useState } from 'react';
|
||||||
import useDimensions from 'react-cool-dimensions';
|
import useDimensions from 'react-cool-dimensions';
|
||||||
|
|
||||||
import { MapScope, Point } from './types';
|
import { MapScope, Point } from './types';
|
||||||
import Map from './Map';
|
import Map from './Map';
|
||||||
import Handlers from './Handlers';
|
import Handlers from './Handlers';
|
||||||
|
import Marker from './Marker';
|
||||||
import { tileProviders } from './tile-providers';
|
import { tileProviders } from './tile-providers';
|
||||||
import { lon2tile, lat2tile, tile2lat, tile2long } from '../../lib/geo';
|
import { lon2tile, lat2tile, tile2lat, tile2long } from '../../lib/geo';
|
||||||
|
|
||||||
|
@ -39,6 +40,8 @@ export interface LiveMapProperties {
|
||||||
numberOfTiledLayers?: number;
|
numberOfTiledLayers?: number;
|
||||||
/** If provided, a function to call when the scope is updated. */
|
/** If provided, a function to call when the scope is updated. */
|
||||||
setScope?: (scope: MapScope) => void;
|
setScope?: (scope: MapScope) => void;
|
||||||
|
/** Markers are non scalable SVG snippets tied to geo location */
|
||||||
|
markers?: ReactElement<any, string | JSXElementConstructor<any>>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -132,7 +135,11 @@ export const LiveMap: react.FC<LiveMapProperties> = (
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%', height: '100%' }} ref={observe}>
|
<div style={{ width: '100%', height: '100%' }} ref={observe}>
|
||||||
<Handlers transformMap={transform} />
|
<Handlers transformMap={transform} />
|
||||||
<Map scope={scope} numberOfTiledLayers={props.numberOfTiledLayers} />
|
<Map
|
||||||
|
scope={scope}
|
||||||
|
numberOfTiledLayers={props.numberOfTiledLayers}
|
||||||
|
markers={props.markers}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import react from 'react';
|
import react, { JSXElementConstructor, ReactElement, ReactNode } from 'react';
|
||||||
import useDimensions from 'react-cool-dimensions';
|
import useDimensions from 'react-cool-dimensions';
|
||||||
|
|
||||||
import { Point, MapScope } from './types';
|
import { Point, MapScope } from './types';
|
||||||
|
import Marker from './Marker';
|
||||||
|
|
||||||
import LayerStack from './LayerStack';
|
import LayerStack from './LayerStack';
|
||||||
import { tileProviders } from './tile-providers';
|
import { tileProviders } from './tile-providers';
|
||||||
import { lon2tile, lat2tile } from '../../lib/geo';
|
import { lon2tile, lat2tile } from '../../lib/geo';
|
||||||
|
@ -9,6 +11,8 @@ import { lon2tile, lat2tile } from '../../lib/geo';
|
||||||
export interface MapProperties {
|
export interface MapProperties {
|
||||||
scope: MapScope;
|
scope: MapScope;
|
||||||
numberOfTiledLayers?: number;
|
numberOfTiledLayers?: number;
|
||||||
|
/** Markers are non scalable SVG snippets tied to geo location */
|
||||||
|
markers?: ReactElement<any, string | JSXElementConstructor<any>>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -58,6 +62,7 @@ export const Map: react.FC<MapProperties> = (props: MapProperties) => {
|
||||||
},
|
},
|
||||||
zoom: relativeScale,
|
zoom: relativeScale,
|
||||||
}}
|
}}
|
||||||
|
markers={props.markers}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { KeyObject } from 'crypto';
|
||||||
|
import react, { ReactNode } from 'react';
|
||||||
|
import { lat2tile, lon2tile } from '../../lib/geo';
|
||||||
|
import { CoordinateSystem } from './LiveMap';
|
||||||
|
import { geoPoint, TileKeyObject } from './types';
|
||||||
|
|
||||||
|
export interface MarkerProperties {
|
||||||
|
coordinates: geoPoint;
|
||||||
|
icon: ReactNode;
|
||||||
|
keyObject?: TileKeyObject;
|
||||||
|
zoom?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Marker: react.FC<MarkerProperties> = (props: MarkerProperties) => {
|
||||||
|
if (props.keyObject === undefined || props.zoom === undefined) return null;
|
||||||
|
|
||||||
|
const x = lon2tile(props.coordinates.lon, props.keyObject.zoomLevel);
|
||||||
|
const y = lat2tile(props.coordinates.lat, props.keyObject.zoomLevel);
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${x}, ${y}) scale(${1 / props.zoom})`}>
|
||||||
|
{props.icon}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Marker;
|
|
@ -1,4 +1,4 @@
|
||||||
import react, { useRef } from 'react';
|
import react, { ReactNode, useRef } from 'react';
|
||||||
import TileSet from './TileSet';
|
import TileSet from './TileSet';
|
||||||
import { Point, TileKeyObject } from './types';
|
import { Point, TileKeyObject } from './types';
|
||||||
import { CoordinateSystem } from './LiveMap';
|
import { CoordinateSystem } from './LiveMap';
|
||||||
|
@ -75,20 +75,22 @@ export const TiledLayer: react.FC<TiledLayerProperties> = (
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<g
|
<>
|
||||||
transform={`scale(${props.zoom}) translate(${props.shift.x}, ${props.shift.y})`}
|
<g
|
||||||
ref={g}
|
transform={`scale(${props.zoom}) translate(${props.shift.x}, ${props.shift.y})`}
|
||||||
>
|
ref={g}
|
||||||
<TileSet
|
>
|
||||||
keyObject={{
|
<TileSet
|
||||||
provider: props.keyObject.provider,
|
keyObject={{
|
||||||
zoomLevel: props.keyObject.zoomLevel,
|
provider: props.keyObject.provider,
|
||||||
x: 0,
|
zoomLevel: props.keyObject.zoomLevel,
|
||||||
y: 0,
|
x: 0,
|
||||||
}}
|
y: 0,
|
||||||
viewPort={viewPort}
|
}}
|
||||||
/>
|
viewPort={viewPort}
|
||||||
</g>
|
/>
|
||||||
|
</g>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue