summaryrefslogtreecommitdiff
path: root/node_modules/pigeon-maps/src/overlays/GeoJson.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/pigeon-maps/src/overlays/GeoJson.tsx')
-rw-r--r--node_modules/pigeon-maps/src/overlays/GeoJson.tsx244
1 files changed, 244 insertions, 0 deletions
diff --git a/node_modules/pigeon-maps/src/overlays/GeoJson.tsx b/node_modules/pigeon-maps/src/overlays/GeoJson.tsx
new file mode 100644
index 0000000..d25cfe6
--- /dev/null
+++ b/node_modules/pigeon-maps/src/overlays/GeoJson.tsx
@@ -0,0 +1,244 @@
+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<[number, number]>>
+ | Array<Array<Array<[number, number]>>>
+ geometries?: Array<GeoJsonGeometry>
+}
+
+interface GeometryProps {
+ coordinates?:
+ | [number, number]
+ | Array<[number, number]>
+ | Array<Array<[number, number]>>
+ | Array<Array<Array<[number, number]>>>
+ latLngToPixel?: (latLng: Point, center?: Point, zoom?: number) => Point
+ svgAttributes?: SVGProps<SVGElement>
+ 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 <path d={path} {...(props.svgAttributes as SVGProps<SVGCircleElement>)} />
+ }
+ return <circle cx={cx} cy={cy} {...(props.svgAttributes as SVGProps<SVGCircleElement>)} />
+}
+
+export function MultiPoint(props: GeometryProps): JSX.Element {
+ return (
+ <>
+ {props.coordinates.map((point, i) => (
+ <PointComponent {...props} coordinates={point} key={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 <path d={p} {...(props.svgAttributes as SVGProps<SVGPathElement>)} />
+}
+
+export function MultiLineString(props: GeometryProps): JSX.Element {
+ return (
+ <>
+ {props.coordinates.map((line, i) => (
+ <LineString {...props} coordinates={line} key={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<Array<[number, number]>>).reduce(
+ (a, part) =>
+ a +
+ ' M' +
+ part.reduce((a, [y, x]) => {
+ const [v, w] = latLngToPixel([x, y])
+ return a + ' ' + v + ' ' + w
+ }, '') +
+ 'Z',
+ ''
+ )
+ return <path d={p} {...(props.svgAttributes as SVGProps<SVGPathElement>)} />
+}
+
+export function MultiPolygon(props: GeometryProps): JSX.Element {
+ return (
+ <>
+ {props.coordinates.map((polygon, i) => (
+ <Polygon {...props} coordinates={polygon} key={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) => (
+ <GeometryCollection key={i} {...props} geometry={geometry} />
+ ))}
+ </>
+ )
+ }
+
+ const Component = renderer[type]
+
+ if (Component === undefined) {
+ console.warn(`The GeoJson Type ${type} is not known`)
+ return null
+ }
+ return (
+ <Component
+ latLngToPixel={props.latLngToPixel}
+ geometry={props.geometry}
+ coordinates={coordinates}
+ svgAttributes={props.svgAttributes}
+ />
+ )
+}
+
+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<SVGElement>) => ({
+ event,
+ anchor: props.anchor,
+ payload: props.feature,
+ })
+
+ return (
+ <g
+ clipRule="evenodd"
+ style={{ pointerEvents: 'auto' }}
+ onClick={props.onClick ? (event) => 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)
+ }}
+ >
+ <GeometryCollection {...props} {...props.feature} svgAttributes={svgAttributes} />
+ </g>
+ )
+}
+
+export function GeoJson(props: GeoJsonProps): JSX.Element {
+ const { width, height } = props.mapState
+
+ return (
+ <div
+ style={{
+ position: 'absolute',
+ left: '0',
+ top: '0',
+ pointerEvents: 'none',
+ cursor: 'pointer',
+ ...(props.style || {}),
+ }}
+ className={props.className ? `${props.className} pigeon-click-block` : 'pigeon-click-block'}
+ >
+ <svg
+ width={width}
+ height={height}
+ viewBox={`0 0 ${width} ${height}`}
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ {props.data && props.data.features.map((feature, i) => <GeoJsonFeature key={i} {...props} feature={feature} />)}
+
+ {React.Children.map(props.children, (child) => {
+ if (!child) {
+ return null
+ }
+
+ if (!React.isValidElement(child)) {
+ return child
+ }
+
+ return React.cloneElement(child, props)
+ })}
+ </svg>
+ </div>
+ )
+}
+
+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 ? <GeoJson data={data} {...props} /> : null
+}