Implementing a Marker component.

This commit is contained in:
Eric van der Vlist 2022-11-02 21:27:29 +01:00
parent 925174db79
commit 1ed4875e5e
6 changed files with 107 additions and 19 deletions

View File

@ -36,6 +36,7 @@ import GetLocation from './components/buttons/GetLocation';
import { geoPoint } from './components/map/types';
import Back from './components/buttons/Back';
import Forward from './components/buttons/Forward';
import Marker from './components/map/Marker';
setupIonicReact();
@ -62,6 +63,24 @@ const App: React.FC = () => {
const [scope, setScope] = useAtom(scopeAtom);
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 (
<IonApp>
<IonContent fullscreen={true}>
@ -70,6 +89,7 @@ const App: React.FC = () => {
scope={scope}
setScope={debounce(setScope, 1000)}
numberOfTiledLayers={5}
markers={[marker]}
/>
</IonApp>
</IonContent>

View File

@ -1,4 +1,10 @@
import react from 'react';
import react, {
cloneElement,
JSXElementConstructor,
ReactComponentElement,
ReactElement,
ReactNode,
} from 'react';
import { TileKeyObject } from './types';
@ -6,6 +12,8 @@ import { CoordinateSystem } from './LiveMap';
import { range } from 'lodash';
import tileUri from './uris';
import TiledLayer from './TiledLayer';
import { ReactComponentOrElement } from '@ionic/react';
import { KeyObject } from 'crypto';
export interface LayerStackProperties {
/**
@ -20,6 +28,8 @@ export interface LayerStackProperties {
* The coordinates system
*/
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'>
<g
transform={`translate(${props.coordinateSystem.shift.x}, ${props.coordinateSystem.shift.y}) scale(${props.coordinateSystem.zoom})`}
key='tiles'
>
{
// Tiled layers with less detail
@ -98,6 +109,23 @@ export const LayerStack: react.FC<LayerStackProperties> = (
getTiledLayer(activeTiledLayer)
}
</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>
);
};

View File

@ -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 { MapScope, Point } from './types';
import Map from './Map';
import Handlers from './Handlers';
import Marker from './Marker';
import { tileProviders } from './tile-providers';
import { lon2tile, lat2tile, tile2lat, tile2long } from '../../lib/geo';
@ -39,6 +40,8 @@ export interface LiveMapProperties {
numberOfTiledLayers?: number;
/** If provided, a function to call when the scope is updated. */
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 (
<div style={{ width: '100%', height: '100%' }} ref={observe}>
<Handlers transformMap={transform} />
<Map scope={scope} numberOfTiledLayers={props.numberOfTiledLayers} />
<Map
scope={scope}
numberOfTiledLayers={props.numberOfTiledLayers}
markers={props.markers}
/>
</div>
);
};

View File

@ -1,7 +1,9 @@
import react from 'react';
import react, { JSXElementConstructor, ReactElement, ReactNode } from 'react';
import useDimensions from 'react-cool-dimensions';
import { Point, MapScope } from './types';
import Marker from './Marker';
import LayerStack from './LayerStack';
import { tileProviders } from './tile-providers';
import { lon2tile, lat2tile } from '../../lib/geo';
@ -9,6 +11,8 @@ import { lon2tile, lat2tile } from '../../lib/geo';
export interface MapProperties {
scope: MapScope;
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,
}}
markers={props.markers}
/>
</div>
);

View File

@ -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;

View File

@ -1,4 +1,4 @@
import react, { useRef } from 'react';
import react, { ReactNode, useRef } from 'react';
import TileSet from './TileSet';
import { Point, TileKeyObject } from './types';
import { CoordinateSystem } from './LiveMap';
@ -75,20 +75,22 @@ export const TiledLayer: react.FC<TiledLayerProperties> = (
};
}
return (
<g
transform={`scale(${props.zoom}) translate(${props.shift.x}, ${props.shift.y})`}
ref={g}
>
<TileSet
keyObject={{
provider: props.keyObject.provider,
zoomLevel: props.keyObject.zoomLevel,
x: 0,
y: 0,
}}
viewPort={viewPort}
/>
</g>
<>
<g
transform={`scale(${props.zoom}) translate(${props.shift.x}, ${props.shift.y})`}
ref={g}
>
<TileSet
keyObject={{
provider: props.keyObject.provider,
zoomLevel: props.keyObject.zoomLevel,
x: 0,
y: 0,
}}
viewPort={viewPort}
/>
</g>
</>
);
};