Skip to content
Open
2 changes: 1 addition & 1 deletion src/components/ControlPanel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import React, { useEffect, useMemo, useState } from 'react'
import { useDocContext } from '../../state/DocContext'
import { TimeSupport } from '../../helpers/time-support'
import { formatInTimeZone } from 'date-fns-tz'
import { SampleDataLoader } from '../SampleDataLoader'
import { SampleDataLoader } from './SampleDataLoader'
import { sampleItems } from '../../data/sampleItems'

export interface TimeProps {
Expand Down
15 changes: 12 additions & 3 deletions src/components/Document/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@ import { AppDispatch } from '../../state/store'
import { NewTrackProps } from '../../types'
import { timeBoundsFor } from '../../helpers/timeBounds'
import { loadJson } from '../../helpers/loaders/loadJson'
import { loadOpRep } from '../../helpers/loaders/loadOpRep'
import Layers from '../Layers'
import Properties from '../Properties'
import Map from '../spatial/Map'
import GraphModal from '../GraphModal'
import { LoadTrackModel } from '../LoadTrackModal'
import './index.css'
import ControlPanel from '../ControlPanel'
import { checkRepSuffix, loadRepPolygon } from '../../helpers/loaders/loadRepPolygon'
import { loadOpRep } from '../../helpers/loaders/loadOpRep'
import { GraphsPanel } from '../GraphsPanel'

interface FileHandler {
blobType: string
handle: (text: string, features: Feature<Geometry, GeoJsonProperties>[], dispatch: AppDispatch, values?: NewTrackProps) => void
checkName?: (name: string) => boolean
}

export interface TimeState {
Expand All @@ -32,7 +34,9 @@ export interface TimeState {

const FileHandlers: FileHandler[] = [
{ blobType: 'application/json', handle: loadJson },
{ blobType: 'text/plain', handle: loadOpRep }
{ blobType: 'text/plain', handle: loadOpRep },
// TODO: this next type is a dummy, for testing polygon performance
{ blobType: 'unknown', handle: loadRepPolygon, checkName: checkRepSuffix }
]

function Document({ filePath }: { filePath?: string }) {
Expand Down Expand Up @@ -114,7 +118,12 @@ function Document({ filePath }: { filePath?: string }) {

for (let i = 0; i < files.length; i++) {
const file = files[i]
const handler = file && FileHandlers.find(handler => handler.blobType === file.type)
let handler = file && FileHandlers.find(handler => handler.blobType === file.type)
console.log('handler 1:',handler , '-', file.type)
if (!handler) {
handler = file && FileHandlers.find(handler => handler.checkName && handler.checkName(file.name))
}
console.log('handler 2:',handler , '-', file.type)
if (handler) {
try {
if (file.type === 'text/plain') {
Expand Down
3 changes: 3 additions & 0 deletions src/components/Layers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Feature, Geometry, MultiPoint, Point } from 'geojson'
import {
BUOY_FIELD_TYPE,
GROUP_TYPE,
MULTI_ZONE_TYPE,
REFERENCE_POINT_TYPE,
TRACK_TYPE,
ZONE_TYPE,
Expand Down Expand Up @@ -332,6 +333,8 @@ const Layers: React.FC<LayerProps> = ({ openGraph }) => {
items.push(
mapFunc(features, 'Buoy Fields', NODE_FIELDS, BUOY_FIELD_TYPE, handleAdd)
)
items.push(mapFunc(features, 'Multi Zones', 'node-multi-zones', MULTI_ZONE_TYPE, handleAdd,
<AddZoneShape addZone={addZone} />))
items.push(mapFunc(features, 'Zones', 'node-zones', ZONE_TYPE, handleAdd,
<AddZoneShape addZone={addZone} />))
items.push(
Expand Down
7 changes: 5 additions & 2 deletions src/components/spatial/Map/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Feature, MultiPoint, Point, Polygon } from 'geojson'
import { Feature, MultiPoint, MultiPolygon, Point, Polygon } from 'geojson'
import { MapContainer, useMap } from 'react-leaflet'
import ScaleNautic from 'react-leaflet-nauticsale'
import { BUOY_FIELD_TYPE, GROUP_TYPE, REFERENCE_POINT_TYPE, TRACK_TYPE, ZONE_TYPE } from '../../../constants'
import { BUOY_FIELD_TYPE, GROUP_TYPE, MULTI_ZONE_TYPE, REFERENCE_POINT_TYPE, TRACK_TYPE, ZONE_TYPE } from '../../../constants'
import Track from '../Track'
import Zone from '../Zone'
import { useCallback, useEffect, useMemo } from 'react'
Expand All @@ -16,6 +16,7 @@ import { BuoyField } from '../BuoyField'
import { BuoyFieldProps } from '../../../types'
import { EditFeature } from '../EditFeature'
import TimePeriod from '../TimePeriod'
import MultiZone from '../MultiZone'

const isVisible = (feature: Feature): boolean => {
return feature.properties?.visible
Expand All @@ -31,6 +32,8 @@ const featureFor = (feature: Feature, onClickHandler: (id: string, modifier: boo
return <Track key={feature.id} feature={feature} onClickHandler={onClickHandler} />
case ZONE_TYPE:
return <Zone key={feature.id} feature={feature as Feature<Polygon>} onClickHandler={onClickHandler}/>
case MULTI_ZONE_TYPE:
return <MultiZone key={feature.id} feature={feature as Feature<MultiPolygon>} onClickHandler={onClickHandler}/>
case REFERENCE_POINT_TYPE:
return <DataPoint key={feature.id} feature={feature as Feature<Point>} onClickHandler={onClickHandler} />
case BUOY_FIELD_TYPE:
Expand Down
3 changes: 3 additions & 0 deletions src/components/spatial/MultiZone/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.zone-label {
text-shadow: 1px 1px 2px white;
}
141 changes: 141 additions & 0 deletions src/components/spatial/MultiZone/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import * as turf from '@turf/turf'
import { Feature, MultiPolygon, Position } from 'geojson'
import { LatLngExpression, LeafletMouseEvent } from 'leaflet'
import { Polygon as ReactPolygon, Tooltip } from 'react-leaflet'
import { useMemo } from 'react'
import { useDocContext } from '../../../state/DocContext'
import { featureIsVisibleInPeriod } from '../../../helpers/featureIsVisibleAtTime'
import './index.css'
import { mouseOut, mouseOver } from '../commonHandlers'

export interface MultiZoneProps {
feature: Feature<MultiPolygon>
onClickHandler: { (id: string, modifier: boolean): void }
}

const MultiZone: React.FC<MultiZoneProps> = ({ feature, onClickHandler }) => {
const { selection, time } = useDocContext()
const { start: timeStart, end: timeEnd, filterApplied } = time
const isSelected = selection.includes(feature.id as string)

const isVisible = useMemo(() => {
return filterApplied
? featureIsVisibleInPeriod(feature, timeStart, timeEnd)
: true
}, [feature, timeStart, timeEnd, filterApplied])

const color = useMemo(() => {
return feature.properties?.stroke || '#F0F'
}, [feature])

const fill = useMemo(() => {
return feature.properties?.fill || feature.properties?.stroke || '#F0F'
}, [feature])

const lineWeight = useMemo(() => {
return feature.properties?.['stroke-width'] || 2
}, [feature])

const opacity = useMemo(() => {
return feature.properties?.['fill-opacity'] || 0.15
}, [feature])

const polygons = useMemo((): React.ReactElement[] => {
const onclick = (evt: LeafletMouseEvent) => {
onClickHandler(
feature.id as string,
evt.originalEvent.altKey || evt.originalEvent.ctrlKey
)
}

const eventHandlers = {
click: onclick,
mouseover: mouseOver,
mouseout: (evt: LeafletMouseEvent) => mouseOut(evt, isSelected),
}
const polys = feature.geometry.coordinates
return polys.map((poly, index) => {
const points = turf.featureCollection([
turf.polygon(poly),
])
const centre = turf
.center(points)
.geometry.coordinates.reverse() as LatLngExpression

const trackCoords = (poly as unknown as Position[][]).map((line) => line.map((item): LatLngExpression => [item[1], item[0]]))
return <ReactPolygon
key={feature.id + '-polygon-' + isSelected + lineWeight + color + fill + index}
fill={true}
positions={trackCoords}
interactive={false}
weight={lineWeight}
color={color}
fillOpacity={opacity}
eventHandlers={eventHandlers}
>
<Tooltip
className={'zone-label'}
position={centre}
direction='center'
opacity={1}
permanent
>
{feature.properties?.names[index]}
</Tooltip>
</ReactPolygon>
})
}, [feature, isSelected, lineWeight, color, fill, opacity, onClickHandler])


// if (coords.length === 0 || coords[0].length === 0) return null
// const points = turf.featureCollection([
// turf.polygon((feature.geometry as MultiPolygon).coordinates),
// ])
// const centre = turf
// .center(points)
// .geometry.coordinates.reverse() as LatLngExpression
// const polyCoords = (feature.geometry as MultiPolygon).coordinates
// // our coordinates may be a single line (polygon, circle) or multiple lines (shape with a hole).
// const trackCoords = (polyCoords as unknown as Position[][]).map((line) => line.map((item): LatLngExpression => [item[1], item[0]]))
// return (
// <>
// <ReactPolygon
// key={feature.id + '-polygon-' + isSelected + lineWeight + color + fill}
// fill={true}
// positions={trackCoords}
// interactive={false}
// weight={lineWeight}
// color={color}
// fillColor={fill}
// eventHandlers={eventHandlers}
// fillOpacity={ isSelected ? 0.65 : opacity}
// >
// <Tooltip
// className={'zone-label'}
// position={centre}
// direction='center'
// opacity={1}
// permanent
// >
// {feature.properties?.name}
// </Tooltip>
// </ReactPolygon>
// {/* we only allow clicking on the edge, by preventing the polygon from being clicked,
// but allowing this polyline to be clicked */}
// <Polyline
// key={feature.id + '-edge-' + isSelected}
// positions={trackCoords}
// interactive={true}
// weight={lineWeight}
// color={color}
// eventHandlers={eventHandlers}
// >
// </Polyline>
// </>
// )
// }, [feature, isSelected, lineWeight, onClickHandler, opacity, color, fill])

return <>{isVisible && polygons}</>
}

export default MultiZone
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// feature types
export const TRACK_TYPE = 'track'
export const ZONE_TYPE = 'zone'
export const MULTI_ZONE_TYPE = 'multi-zone'
export const REFERENCE_POINT_TYPE = 'reference-point'
export const GROUP_TYPE = 'group'
export const BUOY_FIELD_TYPE = 'buoyfield'

export const RECTANGLE_SHAPE = 'rectangle'
export const POLYGON_SHAPE = 'polygon'
export const MULTI_POLYGON_SHAPE = 'multi-polygon'
export const CIRCULAR_RING_SHAPE = 'circular-ring'
export const SECTION_CIRCULAR_RING_SHAPE = 'section-circular-ring'
export const CIRCULAR_SECTOR_SHAPE = 'circular-sector'
Expand Down
Loading
Loading