import React, { CSSProperties, SVGProps, useMemo, useEffect, useState } from 'react' import { PigeonProps, Point } from '../types' interface GeoJsonProps extends PigeonProps { className?: string data?: any svgAttributes?: any styleCallback?: any hover?: any feature?: any style?: CSSProperties children?: React.ReactNode // callbacks onClick?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void onContextMenu?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void onMouseOver?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void onMouseOut?: ({ event: HTMLMouseEvent, anchor: Point, payload: any }) => void } interface GeoJsonLoaderProps extends GeoJsonProps { link?: string } interface GeoJsonGeometry { type: string coordinates?: | [number, number] | Array<[number, number]> | Array> | Array>> geometries?: Array } interface GeometryProps { coordinates?: | [number, number] | Array<[number, number]> | Array> | Array>> latLngToPixel?: (latLng: Point, center?: Point, zoom?: number) => Point svgAttributes?: SVGProps geometry?: GeoJsonGeometry } const defaultSvgAttributes = { fill: '#93c0d099', strokeWidth: '2', stroke: 'white', r: '30' } export function PointComponent(props: GeometryProps): JSX.Element { const { latLngToPixel } = props const [y, x] = props.coordinates as [number, number] const [cx, cy] = latLngToPixel([x, y]) if (props.svgAttributes?.path) { const path = `M${cx},${cy}c${props.svgAttributes.path.split(/[c|C|L|l|v|V|h|H](.*)/s)[1]}` return )} /> } return )} /> } export function MultiPoint(props: GeometryProps): JSX.Element { return ( <> {props.coordinates.map((point, i) => ( ))} ) } export function LineString(props: GeometryProps): JSX.Element { const { latLngToPixel } = props const p = 'M' + (props.coordinates as Array<[number, number]>).reduce((a, [y, x]) => { const [v, w] = latLngToPixel([x, y]) return a + ' ' + v + ' ' + w }, '') return )} /> } export function MultiLineString(props: GeometryProps): JSX.Element { return ( <> {props.coordinates.map((line, i) => ( ))} ) } export function Polygon(props: GeometryProps): JSX.Element { const { latLngToPixel } = props // GeoJson polygons is a collection of linear rings const p = (props.coordinates as Array>).reduce( (a, part) => a + ' M' + part.reduce((a, [y, x]) => { const [v, w] = latLngToPixel([x, y]) return a + ' ' + v + ' ' + w }, '') + 'Z', '' ) return )} /> } export function MultiPolygon(props: GeometryProps): JSX.Element { return ( <> {props.coordinates.map((polygon, i) => ( ))} ) } export function GeometryCollection(props: GeometryProps): JSX.Element { const renderer = { Point: PointComponent, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, } const { type, coordinates, geometries } = props.geometry if (type === 'GeometryCollection') { return ( <> {geometries.map((geometry, i) => ( ))} ) } const Component = renderer[type] if (Component === undefined) { console.warn(`The GeoJson Type ${type} is not known`) return null } return ( ) } export function GeoJsonFeature(props: GeoJsonProps): JSX.Element { const [internalHover, setInternalHover] = useState(props.hover || false) const hover = props.hover !== undefined ? props.hover : internalHover const callbackSvgAttributes = props.styleCallback && props.styleCallback(props.feature, hover) const svgAttributes = callbackSvgAttributes ? props.svgAttributes ? { ...props.svgAttributes, ...callbackSvgAttributes } : callbackSvgAttributes : props.svgAttributes ? props.svgAttributes : defaultSvgAttributes const eventParameters = (event: React.MouseEvent) => ({ event, anchor: props.anchor, payload: props.feature, }) return ( props.onClick(eventParameters(event)) : null} onContextMenu={props.onContextMenu ? (event) => props.onContextMenu(eventParameters(event)) : null} onMouseOver={(event) => { props.onMouseOver && props.onMouseOver(eventParameters(event)) setInternalHover(true) }} onMouseOut={(event) => { props.onMouseOut && props.onMouseOut(eventParameters(event)) setInternalHover(false) }} > ) } export function GeoJson(props: GeoJsonProps): JSX.Element { const { width, height } = props.mapState return (
{props.data && props.data.features.map((feature, i) => )} {React.Children.map(props.children, (child) => { if (!child) { return null } if (!React.isValidElement(child)) { return child } return React.cloneElement(child, props) })}
) } export function GeoJsonLoader(props: GeoJsonLoaderProps): JSX.Element { const [data, setData] = useState(props.data ? props.data : null) useEffect(() => { fetch(props.link) .then((response) => response.json()) .then((data) => setData(data)) }, [props.link]) return data ? : null }