From 936b118d4775012fa023d05e6feeb3d2491cd2fa Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Fri, 19 Jul 2024 16:42:30 -0700 Subject: [PATCH 01/14] Initial work to trigger relocation of file markers --- .../common/mapFSM/MapStateMachineContext.tsx | 17 +++++++++++++ .../mapFSM/machineDefinition/mapMachine.ts | 17 +++++++++++++ .../SelectedPropertyRow.tsx | 25 +++++++++++++++---- .../src/features/mapSideBar/MapSideBar.tsx | 16 ++++++++++++ source/frontend/src/mocks/mapFSM.mock.ts | 3 +++ 5 files changed, 73 insertions(+), 5 deletions(-) diff --git a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx index a22f9c9820..e404f57f6e 100644 --- a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx +++ b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx @@ -35,6 +35,7 @@ export interface IMapStateMachineContext { pendingFitBounds: boolean; requestedFitBounds: LatLngBounds; isSelecting: boolean; + isRelocating: boolean; selectingComponentId: string | null; isFiltering: boolean; isShowingMapLayers: boolean; @@ -60,6 +61,8 @@ export interface IMapStateMachineContext { prepareForCreation: () => void; startSelection: (selectingComponentId?: string) => void; finishSelection: () => void; + startRelocation: (selectingComponentId?: string) => void; + finishRelocation: () => void; toggleMapFilter: () => void; toggleMapLayer: () => void; setFilePropertyLocations: (locations: LatLngLiteral[]) => void; @@ -255,6 +258,17 @@ export const MapStateMachineProvider: React.FC> serviceSend({ type: 'FINISH_SELECTION' }); }, [serviceSend]); + const startRelocation = useCallback( + (selectingComponentId?: string) => { + serviceSend({ type: 'START_RELOCATION', selectingComponentId }); + }, + [serviceSend], + ); + + const finishRelocation = useCallback(() => { + serviceSend({ type: 'FINISH_RELOCATION' }); + }, [serviceSend]); + const setFilePropertyLocations = useCallback( (locations: LatLngLiteral[]) => { serviceSend({ type: 'SET_FILE_PROPERTY_LOCATIONS', locations }); @@ -347,6 +361,7 @@ export const MapStateMachineProvider: React.FC> pendingFitBounds: state.matches({ mapVisible: { mapRequest: 'pendingFitBounds' } }), requestedFitBounds: state.context.requestedFitBounds, isSelecting: state.matches({ mapVisible: { featureView: 'selecting' } }), + isRelocating: state.matches({ mapVisible: { featureView: 'relocating' } }), selectingComponentId: state.context.selectingComponentId, isFiltering: isFiltering, isShowingMapLayers: isShowingMapLayers, @@ -369,6 +384,8 @@ export const MapStateMachineProvider: React.FC> prepareForCreation, startSelection, finishSelection, + startRelocation, + finishRelocation, toggleMapFilter, toggleMapLayer, toggleSidebarDisplay, diff --git a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts index bc1343157a..159e157ce7 100644 --- a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts +++ b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts @@ -18,6 +18,12 @@ const featureViewStates = { assign({ selectingComponentId: (_, event: any) => event.selectingComponentId }), ], }, + START_RELOCATION: { + target: 'relocating', + actions: [ + assign({ selectingComponentId: (_, event: any) => event.selectingComponentId }), + ], + }, TOGGLE_FILTER: { target: 'filtering', }, @@ -40,6 +46,17 @@ const featureViewStates = { }, }, }, + relocating: { + on: { + FINISH_RELOCATION: { target: 'browsing' }, + SET_FILE_PROPERTY_LOCATIONS: { + actions: [ + assign({ filePropertyLocations: (_, event: any) => event.locations }), + raise('REQUEST_FIT_BOUNDS'), + ], + }, + }, + }, layerControl: { on: { TOGGLE_FILTER: { diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx index a10fae16eb..b54d9a6a0f 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx @@ -1,9 +1,11 @@ import { getIn, useFormikContext } from 'formik'; import { useEffect } from 'react'; import { Col, Row } from 'react-bootstrap'; +import { RiDragMove2Line } from 'react-icons/ri'; -import { RemoveButton } from '@/components/common/buttons'; +import { RemoveButton, StyledIconButton } from '@/components/common/buttons'; import { InlineInput } from '@/components/common/form/styles'; +import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext'; import OverflowTip from '@/components/common/OverflowTip'; import { IMapProperty } from '@/components/propertySelector/models'; import DraftCircleNumber from '@/components/propertySelector/selectedPropertyList/DraftCircleNumber'; @@ -17,9 +19,13 @@ export interface ISelectedPropertyRowProps { property: IMapProperty; } -export const SelectedPropertyRow: React.FunctionComponent< - React.PropsWithChildren -> = ({ nameSpace, onRemove, index, property }) => { +export const SelectedPropertyRow: React.FunctionComponent = ({ + nameSpace, + onRemove, + index, + property, +}) => { + const mapMachine = useMapStateMachine(); const { setFieldTouched, touched } = useFormikContext(); useEffect(() => { if (getIn(touched, `${nameSpace}.name`) !== true) { @@ -51,7 +57,7 @@ export const SelectedPropertyRow: React.FunctionComponent< - + + + { + mapMachine.startRelocation(); + }} + > + + + diff --git a/source/frontend/src/features/mapSideBar/MapSideBar.tsx b/source/frontend/src/features/mapSideBar/MapSideBar.tsx index 751d9748a0..d90673e4bd 100644 --- a/source/frontend/src/features/mapSideBar/MapSideBar.tsx +++ b/source/frontend/src/features/mapSideBar/MapSideBar.tsx @@ -1,3 +1,4 @@ +import { FaExclamationCircle } from 'react-icons/fa'; import styled from 'styled-components'; import { SideBarType } from '@/components/common/mapFSM/machineDefinition/types'; @@ -28,6 +29,21 @@ const MapSideBar: React.FunctionComponent> = () )} + {mapMachine.isRelocating && ( + mapMachine.finishRelocation()}> + + Relocating property marker... +
+ +
+ Click on the new location within the property boundary to move the marker. +
+ Click here to exit property selection. +
+
+ )} ); diff --git a/source/frontend/src/mocks/mapFSM.mock.ts b/source/frontend/src/mocks/mapFSM.mock.ts index eec83c02fd..c2de97ff5a 100644 --- a/source/frontend/src/mocks/mapFSM.mock.ts +++ b/source/frontend/src/mocks/mapFSM.mock.ts @@ -42,6 +42,7 @@ export const mapMachineBaseMock: IMapStateMachineContext = { activePimsPropertyIds: [], activeLayers: layersTree, isSelecting: false, + isRelocating: false, isFiltering: false, isShowingMapLayers: false, showDisposed: false, @@ -61,6 +62,8 @@ export const mapMachineBaseMock: IMapStateMachineContext = { prepareForCreation: vi.fn(), startSelection: vi.fn(), finishSelection: vi.fn(), + startRelocation: vi.fn(), + finishRelocation: vi.fn(), setFilePropertyLocations: vi.fn(), setVisiblePimsProperties: vi.fn(), toggleMapFilter: vi.fn(), From 8c1558e30730ae3e8903830166220a4faf37b431 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 22 Jul 2024 11:42:56 -0700 Subject: [PATCH 02/14] Add custom cursor for marker relocation --- .../src/assets/images/pins/icon-relocate.svg | 5 ++++ .../src/features/mapSideBar/MapSideBar.tsx | 13 +++++++--- .../features/properties/map/MapContainer.tsx | 26 ++++++++++++++++--- 3 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 source/frontend/src/assets/images/pins/icon-relocate.svg diff --git a/source/frontend/src/assets/images/pins/icon-relocate.svg b/source/frontend/src/assets/images/pins/icon-relocate.svg new file mode 100644 index 0000000000..5aa45217df --- /dev/null +++ b/source/frontend/src/assets/images/pins/icon-relocate.svg @@ -0,0 +1,5 @@ + + + diff --git a/source/frontend/src/features/mapSideBar/MapSideBar.tsx b/source/frontend/src/features/mapSideBar/MapSideBar.tsx index d90673e4bd..8be07951fc 100644 --- a/source/frontend/src/features/mapSideBar/MapSideBar.tsx +++ b/source/frontend/src/features/mapSideBar/MapSideBar.tsx @@ -34,11 +34,16 @@ const MapSideBar: React.FunctionComponent> = () - Relocating property marker... +

Relocating property marker...


- -
- Click on the new location within the property boundary to move the marker. +

+ +

+

+ Click on the new location within the property +
+ boundary to move the marker. +


Click here to exit property selection.
diff --git a/source/frontend/src/features/properties/map/MapContainer.tsx b/source/frontend/src/features/properties/map/MapContainer.tsx index 1d63897011..9ded7a892c 100644 --- a/source/frontend/src/features/properties/map/MapContainer.tsx +++ b/source/frontend/src/features/properties/map/MapContainer.tsx @@ -4,6 +4,7 @@ import { MapContainerProps } from 'react-leaflet'; import styled from 'styled-components'; import DraftSvg from '@/assets/images/pins/icon-draft.svg'; +import RelocationSvg from '@/assets/images/pins/icon-relocate.svg'; import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext'; import { FilterContentContainer } from '@/components/maps/leaflet/Control/AdvancedFilter/FilterContentContainer'; import { FilterContentForm } from '@/components/maps/leaflet/Control/AdvancedFilter/FilterContentForm'; @@ -18,15 +19,26 @@ import RightSideLayout from '@/features/rightSideLayout/RightSideLayout'; enum MapCursors { DRAFT = 'draft-cursor', + RELOCATION = 'relocation-cursor', DEFAULT = 'default', } const MapContainer: React.FC> = () => { const [showActionBar, setShowActionBar] = useState(false); - const { isSelecting, isFiltering, isShowingMapLayers, toggleMapFilter, toggleMapLayer } = - useMapStateMachine(); + const { + isSelecting, + isFiltering, + isShowingMapLayers, + isRelocating, + toggleMapFilter, + toggleMapLayer, + } = useMapStateMachine(); - const cursorClass = isSelecting ? MapCursors.DRAFT : MapCursors.DEFAULT; + const cursorClass = isSelecting + ? MapCursors.DRAFT + : isRelocating + ? MapCursors.RELOCATION + : MapCursors.DEFAULT; return ( @@ -74,6 +86,14 @@ const StyleMapView = styled.div` // ref: https://vitejs.dev/guide/assets cursor: url("${DraftSvg}") 15 45, pointer; } + + &.relocation-cursor, + &.relocation-cursor .leaflet-grab, + &.relocation-cursor .leaflet-interactive { + // when passing a URL of SVG to a manually constructed url(), the variable should be wrapped within double quotes. + // ref: https://vitejs.dev/guide/assets + cursor: url("${RelocationSvg}") 20 20, pointer; + } `; export default MapContainer; From b4394af063080a3830887198232c98c99f33a1d3 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 22 Jul 2024 15:28:05 -0700 Subject: [PATCH 03/14] Style tweaks --- .../selectedPropertyList/SelectedPropertyRow.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx index b54d9a6a0f..6865dfeedf 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx @@ -67,8 +67,9 @@ export const SelectedPropertyRow: React.FunctionComponent - + { mapMachine.startRelocation(); }} @@ -77,7 +78,7 @@ export const SelectedPropertyRow: React.FunctionComponent - + ); From 0c242561a2203f43addecb5575bdef95f722a9d6 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 22 Jul 2024 17:39:45 -0700 Subject: [PATCH 04/14] Add various callbacks to reposition file markers upon map click --- .../propertySelector/MapClickMonitor.tsx | 31 ++++++++++++++++--- .../propertySelector/MapSelectorContainer.tsx | 6 ++++ .../map/PropertyMapSelectorFormView.tsx | 12 +++++-- .../SelectedPropertyRow.tsx | 8 ++--- .../src/features/mapSideBar/shared/models.ts | 2 +- .../update/properties/UpdateProperties.tsx | 21 +++++++++++-- 6 files changed, 64 insertions(+), 16 deletions(-) diff --git a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx index 0aa2c059e9..96e92f6751 100644 --- a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx +++ b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx @@ -7,14 +7,18 @@ import { featuresetToMapProperty } from '@/utils/mapPropertyUtils'; import { LocationFeatureDataset } from '../common/mapFSM/useLocationFeatureLoader'; interface IMapClickMonitorProps { - addProperty: (property: LocationFeatureDataset) => void; // TODO: This should be a featureDataset + addProperty: (property: LocationFeatureDataset) => void; + repositionProperty: (property: LocationFeatureDataset) => void; modifiedProperties: LocationFeatureDataset[]; // TODO: this should be just a list of lat longs selectedComponentId: string | null; } -export const MapClickMonitor: React.FunctionComponent< - React.PropsWithChildren -> = ({ addProperty, modifiedProperties, selectedComponentId }) => { +export const MapClickMonitor: React.FunctionComponent = ({ + addProperty, + repositionProperty, + modifiedProperties, + selectedComponentId, +}) => { const mapMachine = useMapStateMachine(); const previous = usePrevious(mapMachine.mapLocationFeatureDataset); @@ -32,7 +36,24 @@ export const MapClickMonitor: React.FunctionComponent< ) { addProperty(mapMachine.mapLocationFeatureDataset); } - }, [addProperty, mapMachine.isSelecting, mapMachine.mapLocationFeatureDataset, previous]); + + if ( + mapMachine.isRelocating && + mapMachine.mapLocationFeatureDataset && + previous !== mapMachine.mapLocationFeatureDataset && + previous !== undefined && + (!selectedComponentId || + selectedComponentId === mapMachine.mapLocationFeatureDataset.selectingComponentId) + ) { + repositionProperty(mapMachine.mapLocationFeatureDataset); + } + }, [ + addProperty, + mapMachine.isSelecting, + mapMachine.isRelocating, + mapMachine.mapLocationFeatureDataset, + previous, + ]); return <>; }; diff --git a/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx b/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx index 45bf246908..c05da37704 100644 --- a/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx +++ b/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx @@ -19,12 +19,14 @@ import PropertySelectorSearchContainer from './search/PropertySelectorSearchCont export interface IMapSelectorContainerProps { addSelectedProperties: (properties: LocationFeatureDataset[]) => void; // TODO: This component should be providing the featureDataset instead of the IMapProperty. + repositionSelectedProperty: (property: LocationFeatureDataset) => void; modifiedProperties: LocationFeatureDataset[]; // TODO: Figure out if this component really needs the entire propertyForm. It could be that only the lat long are needed. selectedComponentId?: string; } export const MapSelectorContainer: FunctionComponent = ({ addSelectedProperties, + repositionSelectedProperty, modifiedProperties, selectedComponentId, }) => { @@ -88,6 +90,10 @@ export const MapSelectorContainer: FunctionComponent setLastSelectedProperty(property); addProperties([property], modifiedMapProperties, addWithPimsFeature); }} + onRepositionedProperty={(property: LocationFeatureDataset) => { + setLastSelectedProperty(property); + repositionSelectedProperty(property); + }} selectedProperties={modifiedMapProperties} selectedComponentId={selectedComponentId} lastSelectedProperty={ diff --git a/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx b/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx index 58dca35cb8..95f9cb1284 100644 --- a/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx +++ b/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx @@ -8,14 +8,19 @@ import PropertyMapSelectorSubForm from './PropertyMapSelectorSubForm'; export interface IPropertyMapSelectorFormViewProps { onSelectedProperty: (property: LocationFeatureDataset) => void; + onRepositionedProperty: (property: LocationFeatureDataset) => void; lastSelectedProperty?: LocationFeatureDataset; selectedProperties: LocationFeatureDataset[]; selectedComponentId?: string | null; } -const PropertyMapSelectorFormView: React.FunctionComponent< - React.PropsWithChildren -> = ({ onSelectedProperty, lastSelectedProperty, selectedProperties, selectedComponentId }) => { +const PropertyMapSelectorFormView: React.FunctionComponent = ({ + onSelectedProperty, + onRepositionedProperty, + lastSelectedProperty, + selectedProperties, + selectedComponentId, +}) => { const mapMachine = useMapStateMachine(); const onClickDraftMarker = () => { @@ -32,6 +37,7 @@ const PropertyMapSelectorFormView: React.FunctionComponent< diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx index 6865dfeedf..37461d5720 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx @@ -6,17 +6,17 @@ import { RiDragMove2Line } from 'react-icons/ri'; import { RemoveButton, StyledIconButton } from '@/components/common/buttons'; import { InlineInput } from '@/components/common/form/styles'; import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext'; +import { LocationFeatureDataset } from '@/components/common/mapFSM/useLocationFeatureLoader'; import OverflowTip from '@/components/common/OverflowTip'; -import { IMapProperty } from '@/components/propertySelector/models'; import DraftCircleNumber from '@/components/propertySelector/selectedPropertyList/DraftCircleNumber'; import { withNameSpace } from '@/utils/formUtils'; -import { getPropertyName, NameSourceType } from '@/utils/mapPropertyUtils'; +import { featuresetToMapProperty, getPropertyName, NameSourceType } from '@/utils/mapPropertyUtils'; export interface ISelectedPropertyRowProps { index: number; nameSpace?: string; onRemove: () => void; - property: IMapProperty; + property: LocationFeatureDataset; } export const SelectedPropertyRow: React.FunctionComponent = ({ @@ -33,7 +33,7 @@ export const SelectedPropertyRow: React.FunctionComponent) { + public constructor(baseModel?: Partial) { Object.assign(this, baseModel); } diff --git a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx index 74fea3a30a..11f4aeed80 100644 --- a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx +++ b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx @@ -17,7 +17,7 @@ import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress'; import { getCancelModalProps, useModalContext } from '@/hooks/useModalContext'; import { ApiGen_Concepts_File } from '@/models/api/generated/ApiGen_Concepts_File'; import { UserOverrideCode } from '@/models/api/UserOverrideCode'; -import { isValidId } from '@/utils'; +import { featuresetToMapProperty, getPropertyName, isValidId } from '@/utils'; import { AddressForm, FileForm, PropertyForm } from '../../models'; import SidebarFooter from '../../SidebarFooter'; @@ -150,7 +150,7 @@ export const UpdateProperties: React.FunctionComponent = > {formikProps => ( - {({ push, remove }) => ( + {({ push, remove, replace }) => ( <> @@ -192,6 +192,21 @@ export const UpdateProperties: React.FunctionComponent = }); }, Promise.resolve()); }} + repositionSelectedProperty={(property: LocationFeatureDataset) => { + const index = formikProps.values.properties.findIndex( + p => + getPropertyName(p.toMapProperty()).value === + getPropertyName(featuresetToMapProperty(property)).value, + ); + + // Find property within formik values and reposition it based on incoming file marker position + if (index >= 0) { + const formProperty = formikProps.values.properties[index]; + const newFormProperty = new PropertyForm(formProperty); + newFormProperty.fileLocation = property.fileLocation; + replace(index, newFormProperty); + } + }} modifiedProperties={formikProps.values.properties.map(p => p.toFeatureDataset(), )} @@ -212,7 +227,7 @@ export const UpdateProperties: React.FunctionComponent = }} nameSpace={`properties.${index}`} index={index} - property={property.toMapProperty()} + property={property.toFeatureDataset()} /> ))} {formikProps.values.properties.length === 0 && ( From ebd443eaec8a0b760c8417c5d096816b743add9f Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 22 Jul 2024 22:19:06 -0700 Subject: [PATCH 05/14] Rename relocation to reposition --- .../common/mapFSM/MapStateMachineContext.tsx | 20 +++++++++---------- .../mapFSM/machineDefinition/mapMachine.ts | 8 ++++---- .../propertySelector/MapClickMonitor.tsx | 4 ++-- .../SelectedPropertyRow.tsx | 2 +- .../src/features/mapSideBar/MapSideBar.tsx | 4 ++-- .../features/properties/map/MapContainer.tsx | 14 ++++++------- source/frontend/src/mocks/mapFSM.mock.ts | 6 +++--- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx index e404f57f6e..2b0e4a9694 100644 --- a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx +++ b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx @@ -35,7 +35,7 @@ export interface IMapStateMachineContext { pendingFitBounds: boolean; requestedFitBounds: LatLngBounds; isSelecting: boolean; - isRelocating: boolean; + isRepositioning: boolean; selectingComponentId: string | null; isFiltering: boolean; isShowingMapLayers: boolean; @@ -61,8 +61,8 @@ export interface IMapStateMachineContext { prepareForCreation: () => void; startSelection: (selectingComponentId?: string) => void; finishSelection: () => void; - startRelocation: (selectingComponentId?: string) => void; - finishRelocation: () => void; + startReposition: (selectingComponentId?: string) => void; + finishReposition: () => void; toggleMapFilter: () => void; toggleMapLayer: () => void; setFilePropertyLocations: (locations: LatLngLiteral[]) => void; @@ -258,15 +258,15 @@ export const MapStateMachineProvider: React.FC> serviceSend({ type: 'FINISH_SELECTION' }); }, [serviceSend]); - const startRelocation = useCallback( + const startReposition = useCallback( (selectingComponentId?: string) => { - serviceSend({ type: 'START_RELOCATION', selectingComponentId }); + serviceSend({ type: 'START_REPOSITION', selectingComponentId }); }, [serviceSend], ); - const finishRelocation = useCallback(() => { - serviceSend({ type: 'FINISH_RELOCATION' }); + const finishReposition = useCallback(() => { + serviceSend({ type: 'FINISH_REPOSITION' }); }, [serviceSend]); const setFilePropertyLocations = useCallback( @@ -361,7 +361,7 @@ export const MapStateMachineProvider: React.FC> pendingFitBounds: state.matches({ mapVisible: { mapRequest: 'pendingFitBounds' } }), requestedFitBounds: state.context.requestedFitBounds, isSelecting: state.matches({ mapVisible: { featureView: 'selecting' } }), - isRelocating: state.matches({ mapVisible: { featureView: 'relocating' } }), + isRepositioning: state.matches({ mapVisible: { featureView: 'repositioning' } }), selectingComponentId: state.context.selectingComponentId, isFiltering: isFiltering, isShowingMapLayers: isShowingMapLayers, @@ -384,8 +384,8 @@ export const MapStateMachineProvider: React.FC> prepareForCreation, startSelection, finishSelection, - startRelocation, - finishRelocation, + startReposition, + finishReposition, toggleMapFilter, toggleMapLayer, toggleSidebarDisplay, diff --git a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts index 159e157ce7..c01b80f5d4 100644 --- a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts +++ b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts @@ -18,8 +18,8 @@ const featureViewStates = { assign({ selectingComponentId: (_, event: any) => event.selectingComponentId }), ], }, - START_RELOCATION: { - target: 'relocating', + START_REPOSITION: { + target: 'repositioning', actions: [ assign({ selectingComponentId: (_, event: any) => event.selectingComponentId }), ], @@ -46,9 +46,9 @@ const featureViewStates = { }, }, }, - relocating: { + repositioning: { on: { - FINISH_RELOCATION: { target: 'browsing' }, + FINISH_REPOSITION: { target: 'browsing' }, SET_FILE_PROPERTY_LOCATIONS: { actions: [ assign({ filePropertyLocations: (_, event: any) => event.locations }), diff --git a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx index 96e92f6751..8877e2f8b7 100644 --- a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx +++ b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx @@ -38,7 +38,7 @@ export const MapClickMonitor: React.FunctionComponent = ( } if ( - mapMachine.isRelocating && + mapMachine.isRepositioning && mapMachine.mapLocationFeatureDataset && previous !== mapMachine.mapLocationFeatureDataset && previous !== undefined && @@ -50,7 +50,7 @@ export const MapClickMonitor: React.FunctionComponent = ( }, [ addProperty, mapMachine.isSelecting, - mapMachine.isRelocating, + mapMachine.isRepositioning, mapMachine.mapLocationFeatureDataset, previous, ]); diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx index 37461d5720..1995129073 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx @@ -71,7 +71,7 @@ export const SelectedPropertyRow: React.FunctionComponent { - mapMachine.startRelocation(); + mapMachine.startReposition(); }} > diff --git a/source/frontend/src/features/mapSideBar/MapSideBar.tsx b/source/frontend/src/features/mapSideBar/MapSideBar.tsx index 8be07951fc..7534beae19 100644 --- a/source/frontend/src/features/mapSideBar/MapSideBar.tsx +++ b/source/frontend/src/features/mapSideBar/MapSideBar.tsx @@ -29,8 +29,8 @@ const MapSideBar: React.FunctionComponent> = () )} - {mapMachine.isRelocating && ( - mapMachine.finishRelocation()}> + {mapMachine.isRepositioning && ( + mapMachine.finishReposition()}> diff --git a/source/frontend/src/features/properties/map/MapContainer.tsx b/source/frontend/src/features/properties/map/MapContainer.tsx index 9ded7a892c..ef73257e43 100644 --- a/source/frontend/src/features/properties/map/MapContainer.tsx +++ b/source/frontend/src/features/properties/map/MapContainer.tsx @@ -19,7 +19,7 @@ import RightSideLayout from '@/features/rightSideLayout/RightSideLayout'; enum MapCursors { DRAFT = 'draft-cursor', - RELOCATION = 'relocation-cursor', + REPOSITION = 'reposition-cursor', DEFAULT = 'default', } @@ -29,15 +29,15 @@ const MapContainer: React.FC> = () => isSelecting, isFiltering, isShowingMapLayers, - isRelocating, + isRepositioning, toggleMapFilter, toggleMapLayer, } = useMapStateMachine(); const cursorClass = isSelecting ? MapCursors.DRAFT - : isRelocating - ? MapCursors.RELOCATION + : isRepositioning + ? MapCursors.REPOSITION : MapCursors.DEFAULT; return ( @@ -87,9 +87,9 @@ const StyleMapView = styled.div` cursor: url("${DraftSvg}") 15 45, pointer; } - &.relocation-cursor, - &.relocation-cursor .leaflet-grab, - &.relocation-cursor .leaflet-interactive { + &.reposition-cursor, + &.reposition-cursor .leaflet-grab, + &.reposition-cursor .leaflet-interactive { // when passing a URL of SVG to a manually constructed url(), the variable should be wrapped within double quotes. // ref: https://vitejs.dev/guide/assets cursor: url("${RelocationSvg}") 20 20, pointer; diff --git a/source/frontend/src/mocks/mapFSM.mock.ts b/source/frontend/src/mocks/mapFSM.mock.ts index c2de97ff5a..943fb54c14 100644 --- a/source/frontend/src/mocks/mapFSM.mock.ts +++ b/source/frontend/src/mocks/mapFSM.mock.ts @@ -42,7 +42,7 @@ export const mapMachineBaseMock: IMapStateMachineContext = { activePimsPropertyIds: [], activeLayers: layersTree, isSelecting: false, - isRelocating: false, + isRepositioning: false, isFiltering: false, isShowingMapLayers: false, showDisposed: false, @@ -62,8 +62,8 @@ export const mapMachineBaseMock: IMapStateMachineContext = { prepareForCreation: vi.fn(), startSelection: vi.fn(), finishSelection: vi.fn(), - startRelocation: vi.fn(), - finishRelocation: vi.fn(), + startReposition: vi.fn(), + finishReposition: vi.fn(), setFilePropertyLocations: vi.fn(), setVisiblePimsProperties: vi.fn(), toggleMapFilter: vi.fn(), From d999fa8f662bf5a7b5a123718ae60d399568ad1e Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Tue, 23 Jul 2024 17:32:05 -0700 Subject: [PATCH 06/14] Update map state machine to hold on to file marker being repositioned --- .../common/mapFSM/MapStateMachineContext.tsx | 11 ++++++++--- .../common/mapFSM/machineDefinition/mapMachine.ts | 11 +++++++++-- .../common/mapFSM/machineDefinition/types.ts | 1 + .../selectedPropertyList/SelectedPropertyRow.tsx | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx index 2b0e4a9694..261468dfa3 100644 --- a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx +++ b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx @@ -27,6 +27,7 @@ export interface IMapStateMachineContext { mapLocationSelected: LatLngLiteral | null; mapLocationFeatureDataset: LocationFeatureDataset | null; selectedFeatureDataset: LocationFeatureDataset | null; + repositioningFeatureDataset: LocationFeatureDataset | null; showPopup: boolean; isLoading: boolean; mapSearchCriteria: IPropertyFilter | null; @@ -61,7 +62,10 @@ export interface IMapStateMachineContext { prepareForCreation: () => void; startSelection: (selectingComponentId?: string) => void; finishSelection: () => void; - startReposition: (selectingComponentId?: string) => void; + startReposition: ( + repositioningFeatureDataset: LocationFeatureDataset | null, + selectingComponentId?: string, + ) => void; finishReposition: () => void; toggleMapFilter: () => void; toggleMapLayer: () => void; @@ -259,8 +263,8 @@ export const MapStateMachineProvider: React.FC> }, [serviceSend]); const startReposition = useCallback( - (selectingComponentId?: string) => { - serviceSend({ type: 'START_REPOSITION', selectingComponentId }); + (repositioningFeatureDataset: LocationFeatureDataset | null, selectingComponentId?: string) => { + serviceSend({ type: 'START_REPOSITION', repositioningFeatureDataset, selectingComponentId }); }, [serviceSend], ); @@ -353,6 +357,7 @@ export const MapStateMachineProvider: React.FC> mapLocationSelected: state.context.mapLocationSelected, mapLocationFeatureDataset: state.context.mapLocationFeatureDataset, selectedFeatureDataset: state.context.selectedFeatureDataset, + repositioningFeatureDataset: state.context.repositioningFeatureDataset, showPopup: showPopup, isLoading: state.context.isLoading, mapSearchCriteria: state.context.searchCriteria, diff --git a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts index c01b80f5d4..7d74006375 100644 --- a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts +++ b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts @@ -21,7 +21,10 @@ const featureViewStates = { START_REPOSITION: { target: 'repositioning', actions: [ - assign({ selectingComponentId: (_, event: any) => event.selectingComponentId }), + assign({ + selectingComponentId: (_, event: any) => event.selectingComponentId, + repositioningFeatureDataset: (_, event: any) => event.repositioningFeatureDataset, + }), ], }, TOGGLE_FILTER: { @@ -48,7 +51,10 @@ const featureViewStates = { }, repositioning: { on: { - FINISH_REPOSITION: { target: 'browsing' }, + FINISH_REPOSITION: { + target: 'browsing', + actions: [assign({ repositioningFeatureDataset: () => null })], + }, SET_FILE_PROPERTY_LOCATIONS: { actions: [ assign({ filePropertyLocations: (_, event: any) => event.locations }), @@ -399,6 +405,7 @@ export const mapMachine = createMachine({ mapFeatureSelected: null, mapLocationFeatureDataset: null, selectedFeatureDataset: null, + repositioningFeatureDataset: null, selectingComponentId: null, isLoading: false, searchCriteria: null, diff --git a/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts b/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts index c41d153f21..47b09cba47 100644 --- a/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts +++ b/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts @@ -27,6 +27,7 @@ export type MachineContext = { mapLocationSelected: LatLngLiteral | null; mapLocationFeatureDataset: LocationFeatureDataset | null; selectedFeatureDataset: LocationFeatureDataset | null; + repositioningFeatureDataset: LocationFeatureDataset | null; selectingComponentId: string | null; mapFeatureData: MapFeatureData; diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx index 1995129073..0dda6aaeff 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx @@ -71,7 +71,7 @@ export const SelectedPropertyRow: React.FunctionComponent { - mapMachine.startReposition(); + mapMachine.startReposition(property); }} > From 36468e3a9d633f00e60e0c189541b6f05413cf8e Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Wed, 24 Jul 2024 22:32:23 -0700 Subject: [PATCH 07/14] Highlight the property boundary when repositioning file marker --- .../common/mapFSM/MapStateMachineContext.tsx | 16 ++++++- .../mapFSM/machineDefinition/mapMachine.ts | 9 +++- .../common/mapFSM/machineDefinition/types.ts | 1 + .../src/components/maps/MapLeafletView.tsx | 47 ++++++++++++------- .../propertySelector/MapClickMonitor.tsx | 7 ++- .../propertySelector/MapSelectorContainer.tsx | 12 +++-- .../map/PropertyMapSelectorFormView.tsx | 2 +- .../SelectedPropertyRow.tsx | 2 +- .../src/features/mapSideBar/shared/models.ts | 3 ++ .../update/properties/UpdateProperties.tsx | 16 +++---- 10 files changed, 79 insertions(+), 36 deletions(-) diff --git a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx index 261468dfa3..219a13c5a1 100644 --- a/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx +++ b/source/frontend/src/components/common/mapFSM/MapStateMachineContext.tsx @@ -28,6 +28,7 @@ export interface IMapStateMachineContext { mapLocationFeatureDataset: LocationFeatureDataset | null; selectedFeatureDataset: LocationFeatureDataset | null; repositioningFeatureDataset: LocationFeatureDataset | null; + repositioningPropertyIndex: number | null; showPopup: boolean; isLoading: boolean; mapSearchCriteria: IPropertyFilter | null; @@ -64,6 +65,7 @@ export interface IMapStateMachineContext { finishSelection: () => void; startReposition: ( repositioningFeatureDataset: LocationFeatureDataset | null, + index: number | null, selectingComponentId?: string, ) => void; finishReposition: () => void; @@ -263,8 +265,17 @@ export const MapStateMachineProvider: React.FC> }, [serviceSend]); const startReposition = useCallback( - (repositioningFeatureDataset: LocationFeatureDataset | null, selectingComponentId?: string) => { - serviceSend({ type: 'START_REPOSITION', repositioningFeatureDataset, selectingComponentId }); + ( + repositioningFeatureDataset: LocationFeatureDataset | null, + index: number | null, + selectingComponentId?: string, + ) => { + serviceSend({ + type: 'START_REPOSITION', + repositioningFeatureDataset, + repositioningPropertyIndex: index, + selectingComponentId, + }); }, [serviceSend], ); @@ -358,6 +369,7 @@ export const MapStateMachineProvider: React.FC> mapLocationFeatureDataset: state.context.mapLocationFeatureDataset, selectedFeatureDataset: state.context.selectedFeatureDataset, repositioningFeatureDataset: state.context.repositioningFeatureDataset, + repositioningPropertyIndex: state.context.repositioningPropertyIndex, showPopup: showPopup, isLoading: state.context.isLoading, mapSearchCriteria: state.context.searchCriteria, diff --git a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts index 7d74006375..ab7e6c8a6e 100644 --- a/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts +++ b/source/frontend/src/components/common/mapFSM/machineDefinition/mapMachine.ts @@ -24,6 +24,7 @@ const featureViewStates = { assign({ selectingComponentId: (_, event: any) => event.selectingComponentId, repositioningFeatureDataset: (_, event: any) => event.repositioningFeatureDataset, + repositioningPropertyIndex: (_, event: any) => event.repositioningPropertyIndex, }), ], }, @@ -53,7 +54,12 @@ const featureViewStates = { on: { FINISH_REPOSITION: { target: 'browsing', - actions: [assign({ repositioningFeatureDataset: () => null })], + actions: [ + assign({ + repositioningFeatureDataset: () => null, + repositioningPropertyIndex: () => null, + }), + ], }, SET_FILE_PROPERTY_LOCATIONS: { actions: [ @@ -406,6 +412,7 @@ export const mapMachine = createMachine({ mapLocationFeatureDataset: null, selectedFeatureDataset: null, repositioningFeatureDataset: null, + repositioningPropertyIndex: null, selectingComponentId: null, isLoading: false, searchCriteria: null, diff --git a/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts b/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts index 47b09cba47..327297ac55 100644 --- a/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts +++ b/source/frontend/src/components/common/mapFSM/machineDefinition/types.ts @@ -28,6 +28,7 @@ export type MachineContext = { mapLocationFeatureDataset: LocationFeatureDataset | null; selectedFeatureDataset: LocationFeatureDataset | null; repositioningFeatureDataset: LocationFeatureDataset | null; + repositioningPropertyIndex: number | null; selectingComponentId: string | null; mapFeatureData: MapFeatureData; diff --git a/source/frontend/src/components/maps/MapLeafletView.tsx b/source/frontend/src/components/maps/MapLeafletView.tsx index 09eee3a0e7..1089797eda 100644 --- a/source/frontend/src/components/maps/MapLeafletView.tsx +++ b/source/frontend/src/components/maps/MapLeafletView.tsx @@ -96,28 +96,41 @@ const MapLeafletView: React.FC> = ( zoom, ]); - const mapMachineMapLocationFeatureDataset = mapMachine.mapLocationFeatureDataset; + const { mapLocationFeatureDataset, repositioningFeatureDataset, isRepositioning } = mapMachine; + useEffect(() => { activeFeatureLayer?.clearLayers(); - if (mapMachineMapLocationFeatureDataset !== null) { - const location = mapMachineMapLocationFeatureDataset.location; - - let activeFeature: Feature = { - geometry: { coordinates: [location.lng, location.lat], type: 'Point' }, - type: 'Feature', - properties: {}, - }; - if (mapMachineMapLocationFeatureDataset.parcelFeature !== null) { - activeFeature = mapMachineMapLocationFeatureDataset.parcelFeature; - activeFeatureLayer?.addData(activeFeature); - } else if (mapMachineMapLocationFeatureDataset.municipalityFeature !== null) { - activeFeature = mapMachineMapLocationFeatureDataset.municipalityFeature; - if (mapMachineMapLocationFeatureDataset.municipalityFeature?.geometry?.type === 'Polygon') { + + if (isRepositioning) { + if ( + repositioningFeatureDataset !== null && + repositioningFeatureDataset.pimsFeature !== null + ) { + // File marker repositioning is active - highlight the property and the corresponding boundary when user triggers the relocate action. + activeFeatureLayer?.addData(repositioningFeatureDataset.pimsFeature); + } + } else { + // Not repositioning - highlight parcels on map click as usual workflow + if (mapLocationFeatureDataset !== null) { + const location = mapLocationFeatureDataset.location; + + let activeFeature: Feature = { + geometry: { coordinates: [location.lng, location.lat], type: 'Point' }, + type: 'Feature', + properties: {}, + }; + if (mapLocationFeatureDataset.parcelFeature !== null) { + activeFeature = mapLocationFeatureDataset.parcelFeature; activeFeatureLayer?.addData(activeFeature); + } else if (mapLocationFeatureDataset.municipalityFeature !== null) { + activeFeature = mapLocationFeatureDataset.municipalityFeature; + if (mapLocationFeatureDataset.municipalityFeature?.geometry?.type === 'Polygon') { + activeFeatureLayer?.addData(activeFeature); + } } } } - }, [activeFeatureLayer, mapMachineMapLocationFeatureDataset]); + }, [activeFeatureLayer, isRepositioning, mapLocationFeatureDataset, repositioningFeatureDataset]); const hasPendingFlyTo = mapMachine.pendingFlyTo; const requestedFlyTo = mapMachine.requestedFlyTo; @@ -206,7 +219,7 @@ const MapLeafletView: React.FC> = ( ))} )} - {mapMachine.showPopup && ( + {mapMachine.showPopup && !mapMachine.isRepositioning && ( // Draws the popup on top of the map )} diff --git a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx index 8877e2f8b7..5a5f51dd23 100644 --- a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx +++ b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx @@ -8,7 +8,7 @@ import { LocationFeatureDataset } from '../common/mapFSM/useLocationFeatureLoade interface IMapClickMonitorProps { addProperty: (property: LocationFeatureDataset) => void; - repositionProperty: (property: LocationFeatureDataset) => void; + repositionProperty: (property: LocationFeatureDataset, propertyIndex: number | null) => void; modifiedProperties: LocationFeatureDataset[]; // TODO: this should be just a list of lat longs selectedComponentId: string | null; } @@ -45,7 +45,10 @@ export const MapClickMonitor: React.FunctionComponent = ( (!selectedComponentId || selectedComponentId === mapMachine.mapLocationFeatureDataset.selectingComponentId) ) { - repositionProperty(mapMachine.mapLocationFeatureDataset); + repositionProperty( + mapMachine.mapLocationFeatureDataset, + mapMachine.repositioningPropertyIndex, + ); } }, [ addProperty, diff --git a/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx b/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx index c05da37704..919273216c 100644 --- a/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx +++ b/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx @@ -19,7 +19,10 @@ import PropertySelectorSearchContainer from './search/PropertySelectorSearchCont export interface IMapSelectorContainerProps { addSelectedProperties: (properties: LocationFeatureDataset[]) => void; // TODO: This component should be providing the featureDataset instead of the IMapProperty. - repositionSelectedProperty: (property: LocationFeatureDataset) => void; + repositionSelectedProperty: ( + property: LocationFeatureDataset, + propertyIndex: number | null, + ) => void; modifiedProperties: LocationFeatureDataset[]; // TODO: Figure out if this component really needs the entire propertyForm. It could be that only the lat long are needed. selectedComponentId?: string; } @@ -90,9 +93,12 @@ export const MapSelectorContainer: FunctionComponent setLastSelectedProperty(property); addProperties([property], modifiedMapProperties, addWithPimsFeature); }} - onRepositionedProperty={(property: LocationFeatureDataset) => { + onRepositionedProperty={( + property: LocationFeatureDataset, + propertyIndex: number | null, + ) => { setLastSelectedProperty(property); - repositionSelectedProperty(property); + repositionSelectedProperty(property, propertyIndex); }} selectedProperties={modifiedMapProperties} selectedComponentId={selectedComponentId} diff --git a/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx b/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx index 95f9cb1284..b3c5028e02 100644 --- a/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx +++ b/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx @@ -8,7 +8,7 @@ import PropertyMapSelectorSubForm from './PropertyMapSelectorSubForm'; export interface IPropertyMapSelectorFormViewProps { onSelectedProperty: (property: LocationFeatureDataset) => void; - onRepositionedProperty: (property: LocationFeatureDataset) => void; + onRepositionedProperty: (property: LocationFeatureDataset, propertyIndex: number | null) => void; lastSelectedProperty?: LocationFeatureDataset; selectedProperties: LocationFeatureDataset[]; selectedComponentId?: string | null; diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx index 0dda6aaeff..ed209c03cc 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.tsx @@ -71,7 +71,7 @@ export const SelectedPropertyRow: React.FunctionComponent { - mapMachine.startReposition(property); + mapMachine.startReposition(property, index); }} > diff --git a/source/frontend/src/features/mapSideBar/shared/models.ts b/source/frontend/src/features/mapSideBar/shared/models.ts index 629307b49a..bf95d6d8cb 100644 --- a/source/frontend/src/features/mapSideBar/shared/models.ts +++ b/source/frontend/src/features/mapSideBar/shared/models.ts @@ -243,6 +243,9 @@ export class PropertyForm { newForm.latitude = model.property?.latitude ?? undefined; newForm.longitude = model.property?.longitude ?? undefined; newForm.fileLocation = getLatLng(model.location) ?? undefined; + newForm.polygon = exists(model.property?.boundary) + ? (model.property?.boundary as Polygon | MultiPolygon) + : undefined; newForm.planNumber = model.property?.planNumber ?? undefined; newForm.region = model.property?.region?.id ?? undefined; newForm.district = model.property?.district?.id ?? undefined; diff --git a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx index 11f4aeed80..daf8970b31 100644 --- a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx +++ b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx @@ -1,5 +1,6 @@ import axios, { AxiosError } from 'axios'; import { FieldArray, Formik, FormikProps } from 'formik'; +import isNumber from 'lodash/isNumber'; import { useContext, useRef, useState } from 'react'; import { Col, Row } from 'react-bootstrap'; import { toast } from 'react-toastify'; @@ -17,7 +18,7 @@ import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress'; import { getCancelModalProps, useModalContext } from '@/hooks/useModalContext'; import { ApiGen_Concepts_File } from '@/models/api/generated/ApiGen_Concepts_File'; import { UserOverrideCode } from '@/models/api/UserOverrideCode'; -import { featuresetToMapProperty, getPropertyName, isValidId } from '@/utils'; +import { isValidId } from '@/utils'; import { AddressForm, FileForm, PropertyForm } from '../../models'; import SidebarFooter from '../../SidebarFooter'; @@ -192,15 +193,12 @@ export const UpdateProperties: React.FunctionComponent = }); }, Promise.resolve()); }} - repositionSelectedProperty={(property: LocationFeatureDataset) => { - const index = formikProps.values.properties.findIndex( - p => - getPropertyName(p.toMapProperty()).value === - getPropertyName(featuresetToMapProperty(property)).value, - ); - + repositionSelectedProperty={( + property: LocationFeatureDataset, + index: number | null, + ) => { // Find property within formik values and reposition it based on incoming file marker position - if (index >= 0) { + if (isNumber(index) && index >= 0) { const formProperty = formikProps.values.properties[index]; const newFormProperty = new PropertyForm(formProperty); newFormProperty.fileLocation = property.fileLocation; From 379af0d88e24dfbb797a5df46eba16df7328bd1f Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Wed, 24 Jul 2024 22:46:13 -0700 Subject: [PATCH 08/14] Add geojson spatial analysis library - turf.js --- source/frontend/package-lock.json | 2448 ++++++++++++++++++++++++++++- source/frontend/package.json | 1 + 2 files changed, 2399 insertions(+), 50 deletions(-) diff --git a/source/frontend/package-lock.json b/source/frontend/package-lock.json index 7564c1016d..e6376a8b98 100644 --- a/source/frontend/package-lock.json +++ b/source/frontend/package-lock.json @@ -12,6 +12,7 @@ "@bcgov/design-tokens": "3.0.0-rc1", "@react-keycloak/web": "3.4.0", "@reduxjs/toolkit": "1.8.6", + "@turf/turf": "7.0.0", "@types/polylabel": "1.0.5", "@xstate/react": "3.2.2", "axios": "1.6.7", @@ -3756,6 +3757,2241 @@ "react-dom": ">=16.8" } }, + "node_modules/@turf/along": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/along/-/along-7.0.0.tgz", + "integrity": "sha512-OyZcvwYwsLxlqC6ksFMNAkZ1fF/0Xfg38v4jx6D9OsaQcIDgvzXnlkuUnS3w11imfulokijkLEFnvJXFZKAzRw==", + "dependencies": { + "@turf/bearing": "^7.0.0", + "@turf/destination": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/along/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/angle": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/angle/-/angle-7.0.0.tgz", + "integrity": "sha512-3+Lp/fyJdtoS+zMu4ZGgV1PhXub7fiowvwId6w5fNtUHWaQ2e0fUgWMAv8opVSC9gRnUtOq2QuJZdIGQAL+fkQ==", + "dependencies": { + "@turf/bearing": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/rhumb-bearing": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/angle/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/area": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/area/-/area-7.0.0.tgz", + "integrity": "sha512-Q/P6OGV8dJJs1BiraKFNBjtsMbz7B52mLCtgKh3syzujSREMx52RlsiOBQp8GujFMMiau+Mt25XKbVwtjHVi8Q==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/area/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/bbox": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.0.0.tgz", + "integrity": "sha512-IyXG5HAsn6IZLdAtQo7aWYccjU5WsV+uzIzhGaXrh/qTVylSYmRiWgLdiekHZVED9nv9r7D/EJUMOT4zyA6POA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-clip": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-clip/-/bbox-clip-7.0.0.tgz", + "integrity": "sha512-ZSReB14sSQpP5TE6g5SijVFijxMp8pyrM0PgEN1LR9Bm+nj7BmmGzHafV3lyteml2bmlFdQxkbTqcbvlVXS98g==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-clip/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/bbox-polygon": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/bbox-polygon/-/bbox-polygon-7.0.0.tgz", + "integrity": "sha512-RMBADOr0zOhVhTidKXCAx1TLTzgBvZwQKI6KJ1FgoCPH7GMZZnMXGMvOtdQLdsplS4Zs6+NoVtaK2x0+EXdYJQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bbox-polygon/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/bbox/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/bearing": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-7.0.0.tgz", + "integrity": "sha512-r6eBNqqiC8OtW+xIzu0ZyciAUfM85l2LVN2qpTeEyhnaNmnPw7hDsnqwZcbqoBFSLB66MO+BLH40X5OdaoRmmA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bearing/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/bezier-spline": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/bezier-spline/-/bezier-spline-7.0.0.tgz", + "integrity": "sha512-1FFVLc+oa5t9S7XWsYImfOVNLWESAJo3ztC4GRlGeAFQBi5z8kSptBaGRLkTn387lZLZ1VQXYRgAfR2mP69/VA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/bezier-spline/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-clockwise": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-7.0.0.tgz", + "integrity": "sha512-Tn+ShgMcgIll+TeIAUHW0diPpxCN1PfQ6uZrwUJXzQXfoxlns16NvJByTBWCl+E9UCA+m1QRjHpVOzyf8pSOOQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-clockwise/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-concave": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-concave/-/boolean-concave-7.0.0.tgz", + "integrity": "sha512-ybKMUriBFzqvjJMOM+YPID2h0a7ye1ur95NkXzV+GRDG16W0KOtTcSXz3LFfHIBEXtG1dqRIxPSV2uwTTKwDTQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-concave/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-contains": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-7.0.0.tgz", + "integrity": "sha512-1NILJdO5OO1YrD7hYPlpahROkzd1DFA7Lcp7SxL+hTtKTp/a2iZx+K6u2qKMLUlPO1p2zhSbMfvjl1T6s/H8XQ==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/boolean-point-on-line": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-contains/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-crosses": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-crosses/-/boolean-crosses-7.0.0.tgz", + "integrity": "sha512-T8/U3fXvEaaf7NbRf42s7hnOYUarK7K1ztXOan0hESnWVzMRFwzIv9QeSW4mARAPU/oV1oOMhSITfbF/Et6W1A==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/line-intersect": "^7.0.0", + "@turf/polygon-to-line": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-crosses/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-disjoint": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-disjoint/-/boolean-disjoint-7.0.0.tgz", + "integrity": "sha512-MAHKqMtdktgxFnz9XFrzyN+VTtmNSd+PgRyo/CT9ucsZ18hd3PV9Y3obGw/k0T6AglMivhBadlHiSMnTfed4Qg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/line-intersect": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/polygon-to-line": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-disjoint/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-equal": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-equal/-/boolean-equal-7.0.0.tgz", + "integrity": "sha512-9C7xkxOyqN0/8Ze4TrysKWWYZxWB0s6zSBaADuVhaIsFzfwvC5wbEpwOL8pRop++7YRRlJ92QM8Bq53DU8+7LQ==", + "dependencies": { + "@turf/clean-coords": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-equal/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-intersects": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-intersects/-/boolean-intersects-7.0.0.tgz", + "integrity": "sha512-+jNZw/ziZ2xyeDoasuwubVu3arRS71i6tSxvnPQmsWISki6rLJF7OQEkc9LNIllYlADCZUkUTJ1OktznrEUDkA==", + "dependencies": { + "@turf/boolean-disjoint": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-intersects/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-overlap": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-overlap/-/boolean-overlap-7.0.0.tgz", + "integrity": "sha512-mnZxjGSs8OuH/+QUuP5FGkut2bLr1FGZ63ThJ97aUuJP+lZQnYnkPdT1hQIdcyhtTQgF1yOM7EH+O//DkoROpA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/line-intersect": "^7.0.0", + "@turf/line-overlap": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-overlap/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-parallel": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-parallel/-/boolean-parallel-7.0.0.tgz", + "integrity": "sha512-OTL9XLjzRulLPQ12il3my9fLhemHsoyYe/owxNYz+kYtUyDHQ6lRz6vEaI3W7MKY9fnqwuIdJQpckOL5TH2cPQ==", + "dependencies": { + "@turf/clean-coords": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/line-segment": "^7.0.0", + "@turf/rhumb-bearing": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-parallel/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-point-in-polygon": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.0.0.tgz", + "integrity": "sha512-Z9swETfICqUJ8iVLZimvIOh8r4Wrlu9/X/c/5vIEeVvG4Lu78Ztmgu1KaobZJFC93/ntOAjMBavc9aNgw7TXgQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "point-in-polygon-hao": "^1.1.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-in-polygon/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-point-on-line": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-7.0.0.tgz", + "integrity": "sha512-9/1hj2MxcUU4fZu+MQC6rdMsdvAYNTtfxssLrZ1dGXo+NcAoWFbZSrfk62pSJBflveyKY5kXPYY+xQfLT0NeDQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-point-on-line/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-touches": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-touches/-/boolean-touches-7.0.0.tgz", + "integrity": "sha512-eZE2uvylkQLAePHTGRjnVlr+QKnwU9gPAarz2q4YF7hi2QRDQd8kc+Ai/450C3xL9iPYO6wrbAQ7qpmB1Jsq4g==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/boolean-point-on-line": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-touches/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-valid": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-valid/-/boolean-valid-7.0.0.tgz", + "integrity": "sha512-TM2UBVFu59u8fAZVHsVnnnCv6486J2uCFmhsVsSCgTgTET1+Lm0TK0XTj5cyPSYR35qR/BjcU1gO3l45bdap9A==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/boolean-crosses": "^7.0.0", + "@turf/boolean-disjoint": "^7.0.0", + "@turf/boolean-overlap": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/boolean-point-on-line": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/line-intersect": "^7.0.0", + "geojson-polygon-self-intersections": "^1.2.1", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-valid/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/boolean-within": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/boolean-within/-/boolean-within-7.0.0.tgz", + "integrity": "sha512-QmOX34T7z9wzuzUVsXx3KMCMsxLi71/SOcrgkcHhcWsThx+VPPmyIUtTa8XnBt1BBlx7IIrR2pAeORkUi0CdEg==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/boolean-point-on-line": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/boolean-within/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/buffer": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/buffer/-/buffer-7.0.0.tgz", + "integrity": "sha512-viw3XjTtYVtkq5DkRDBQjXoi5QeEMhe4JHWXIfHMHs4o5F9B+lZ8+TtXWo18X5aAXknv6ib1z2syoaQdBpb5Xw==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/center": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/jsts": "^2.7.1", + "@turf/meta": "^7.0.0", + "@turf/projection": "^7.0.0", + "d3-geo": "1.7.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/center/-/center-7.0.0.tgz", + "integrity": "sha512-5RZia9uuWxz2oCyd1vsNkBeraBNdwCsIo4UGRQdyswBeLFVbRwIUa7M7+2z2D7B1YIgovuLIRVfk6FeWUQXDtQ==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-mean": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/center-mean/-/center-mean-7.0.0.tgz", + "integrity": "sha512-pneYkH6/4a6gDDvhVL2by9OCJ4yTIANoHQ4JpYVjvB1VlQWrVI5qQd80+q3bMKCDWaACEq8UWa/5NmvKlSRT3A==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-mean/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/center-median": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/center-median/-/center-median-7.0.0.tgz", + "integrity": "sha512-ppCVjwwNe3Rz86TQp+zt9uFI9ZlFwu2miQtpFVcv2ej9IN2D5PUIRnehWgVeQ+yZ+ocMjMNLMcackUnqeKR4FA==", + "dependencies": { + "@turf/center-mean": "^7.0.0", + "@turf/centroid": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-median/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/center-of-mass": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/center-of-mass/-/center-of-mass-7.0.0.tgz", + "integrity": "sha512-vnD43bX4jl1ViDjY+nxISeQd8vfPqvLw+0N1MxpweCh5S85DNqUg6J8GXC1kbZlfOBS3mS9p2CW1Rfg4ggiGHw==", + "dependencies": { + "@turf/centroid": "^7.0.0", + "@turf/convex": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/center-of-mass/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/center/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/centroid": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-7.0.0.tgz", + "integrity": "sha512-TMKp5yadglNVRxX3xuk1qQDEy5JFHmlYVBamzXuD8DL8rYdVog2x4gQHrwn7xrUyAlKJ4fUZZPkYBWfW6TDWbw==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/centroid/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/circle": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/circle/-/circle-7.0.0.tgz", + "integrity": "sha512-rRMthTL5+mhiFXQwlk4jUuf0pkqDAhry/El03VNYNyDGOx4N6p0XMmgbIdJmG6ZooinHpHfMU4N8ZQ9Xo6vVPA==", + "dependencies": { + "@turf/destination": "^7.0.0", + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/circle/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/clean-coords": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/clean-coords/-/clean-coords-7.0.0.tgz", + "integrity": "sha512-kaKR4/NnhZpgC+hWY3MfPqV2KwzG4Vr66WH59GbT5B2tvAOJqAYUmq+rgyMsG6xA3fTBL3neDW0bY1DHFVcvHQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clean-coords/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/clone": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-7.0.0.tgz", + "integrity": "sha512-bQBx/wbQoGNtZzuHetLt44NMqOCnjSXcvTWm+LJ7YTmwrqZVAjISDhFxgawY/L+G3p+ya5WoxQwZWak80uYg3A==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clone/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/clusters": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/clusters/-/clusters-7.0.0.tgz", + "integrity": "sha512-s96uAedbT+9JX6Cg11TsjyZRZIOYABBplA7vKlFgfEKitscCRFUP+pVgiRXy8wBgyKsXqG28DJr96kXQaJKSeg==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-dbscan": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-7.0.0.tgz", + "integrity": "sha512-q8P7MjD0jGhNOzE+I6Cj8UiVaqkuDV27nGA8fLZWNxLW27+X2QJzU+NFLkx0suV9ApmCWRiaIyWUHWG9uZntxA==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "rbush": "^3.0.1", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-dbscan/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/clusters-kmeans": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-7.0.0.tgz", + "integrity": "sha512-ZwN4sLcTgeD7meryarJZunLXISHM7ZLc0S7Lgwv64fYsYU2ea64BMst5mRFCZsrrlFdYmrwUo4DlihL8k9XLKw==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "skmeans": "0.9.7", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/clusters-kmeans/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/clusters/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/collect": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/collect/-/collect-7.0.0.tgz", + "integrity": "sha512-zgKPVVmNr/5OJiHLnQAzDCMv1xDxwuNw8PfCPYkek8s2dvr9LFlEMfPxnp9hY+/oHJx8+1mC01c9qgb7hF8yPQ==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/helpers": "^7.0.0", + "rbush": "^3.0.1", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/collect/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/combine": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/combine/-/combine-7.0.0.tgz", + "integrity": "sha512-xNg6XGAwEWmpjYImJ3+vie+ASKQ7dzUrwUDJ5DOOS8XonLkVAIGus+qDoXdIzAcb9jqSaSFC4sGmQFdTM1Sj1w==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/combine/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/concave": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/concave/-/concave-7.0.0.tgz", + "integrity": "sha512-iwrLBgCUgsquvLSCnzppPVDWGLoCjVDV9xgg7jncbi9aURuGPfhHd5eaC9fyxtIlSbcfrSDXSHJLoeC6bpmlOw==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/tin": "^7.0.0", + "topojson-client": "3.x", + "topojson-server": "3.x", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/concave/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/convex": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/convex/-/convex-7.0.0.tgz", + "integrity": "sha512-iAAwC1FPB4aiA7djUU/pk7lMgWjACbQOB2oX6cH39P5M7W6GXhrTEvYAx+thBtNq+M6BPk/gk3bU1BjZFrQ4+g==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "concaveman": "^1.2.1", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/convex/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/destination": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-7.0.0.tgz", + "integrity": "sha512-wXfLd37ul7xuFvv4L7dtNQOZnmYepnrsMZrxbmxvy2SCnF+Rzf1C7D1NQ6+Nx5SInB/SbTfi6SCDgyfB8MOawQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/destination/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/difference": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/difference/-/difference-7.0.0.tgz", + "integrity": "sha512-JCPRuGcOkT+Hq5PJcEUhkCheTyJcwB5dS1SIJGhDDNOJp8m8REHFJCmvxA3K40demovbyDLk85AojYpJ3jlALQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "polygon-clipping": "^0.15.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/difference/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/dissolve": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/dissolve/-/dissolve-7.0.0.tgz", + "integrity": "sha512-WUapSrl4nTpKGQ9uacfefPNI8mEl0PYrCN6vDTs2W4k6UT9NdeJYNPaR6275nAoOUEL9t9K+adPgdMltm/EG3g==", + "dependencies": { + "@turf/flatten": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "polygon-clipping": "^0.15.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/dissolve/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/distance": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-7.0.0.tgz", + "integrity": "sha512-DBPKhHABpPZ0KRduRpEaoi8MB6r1DVuyyps68VFH2Qi5H0ZnFtJFj7nQxBPZR3bVpbUq4zzu7I+MiNAd3ujFWQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance-weight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/distance-weight/-/distance-weight-7.0.0.tgz", + "integrity": "sha512-BFftfC1QdtcJNMx4TOYxkHX/xsy4FjiG4tdEgdx99DBZ4L1RS/qqgC1O943WuqbfvJTn15E6ka0Rkep2/90IMA==", + "dependencies": { + "@turf/centroid": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/distance-weight/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/distance/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/ellipse": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/ellipse/-/ellipse-7.0.0.tgz", + "integrity": "sha512-bdHzKPfxIWVz3p7jlsyi89BAxvjIhxCgLzDXKYp4tSf2LYGpkfWpSJA2bRnj7TuEq5zYQR8L5LJhn7jg0hehlg==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/rhumb-destination": "^7.0.0", + "@turf/transform-rotate": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/ellipse/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/envelope": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/envelope/-/envelope-7.0.0.tgz", + "integrity": "sha512-110YN+9PmhRYE7co+4ewB/IY3SL9q90mw5FoXrv97VRYVlf8jQCa2mwSalMiuAGnhvfG+Yq1k+ibjjLJcS3gWA==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/bbox-polygon": "^7.0.0", + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/envelope/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/explode": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/explode/-/explode-7.0.0.tgz", + "integrity": "sha512-q7KZ/PxY9zHN4UGXaADUpsHGkj8lbexVZxdBnp0nEfTHm/ziLTAfpI15CdAknoz4Ee8k8tx7ldosVjjg7YJ3/g==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/explode/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/flatten": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/flatten/-/flatten-7.0.0.tgz", + "integrity": "sha512-QVTPgE9PdulQiQPglQ+mXpThBfp3v5RhCSupBCAoqOkqo/KfnliHqJXmVTaZotUZnMgc3wbAMJtSJvDRhTnFFA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flatten/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/flip": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/flip/-/flip-7.0.0.tgz", + "integrity": "sha512-8yqhjXhqKF9fqN4ZfLgVNKCX0AVDaOY9KzD3AGQ+UiRHgmnY/F/T8Np9hSgEVQl1E9N6GXWF/7d8fOTVKCFCbQ==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/flip/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/geojson-rbush": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/geojson-rbush/-/geojson-rbush-7.0.0.tgz", + "integrity": "sha512-h0fJPWHkpGEHkVvQ/tv4FwYkWuEbhbSF0rrQEUyXwvZtlZPRBYJ7WQ5qRsvE7QdQK81B7jWxZiM/qWeayALu1g==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "@types/geojson": "7946.0.8", + "rbush": "^3.0.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/geojson-rbush/node_modules/@types/geojson": { + "version": "7946.0.8", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz", + "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==" + }, + "node_modules/@turf/great-circle": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/great-circle/-/great-circle-7.0.0.tgz", + "integrity": "sha512-onZYQ4ZiN2sIff8j648k+eaeavC+W2wG9L+7wSZjWgeaM4t40r3ZhcJLwqmMUSHxccACNlnINukQodulsflFDg==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.0.0.tgz", + "integrity": "sha512-vwZvxRuyjGpGXvhXSbT9mX6FK92dBMLWbMbDJ/MXQUPx17ReVPFc+6N6IcxAzZfkiCnqy7vpuq0c+/TTrQxIiA==", + "dependencies": { + "deep-equal": "^2.2.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/helpers/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/hex-grid": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/hex-grid/-/hex-grid-7.0.0.tgz", + "integrity": "sha512-6vWO/eVyCvk9mUOj7wxCRUSFpZ0Rlqo4fLvAWX+WsNlOn7xnYs8TFXyiVuWcJdWiMX/LzjOF2OTI6HXF3BhEAQ==", + "dependencies": { + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/intersect": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/hex-grid/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/interpolate": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/interpolate/-/interpolate-7.0.0.tgz", + "integrity": "sha512-ERorrpqItp/qrVtkCkFhB0i4fgjb27cCObTucWdSVUMEitH2ieEQzysmghtyq2F6zq1DW3tC33Jjr79IDj+X6A==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/centroid": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/hex-grid": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/point-grid": "^7.0.0", + "@turf/square-grid": "^7.0.0", + "@turf/triangle-grid": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/intersect": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/intersect/-/intersect-7.0.0.tgz", + "integrity": "sha512-2GL9yAPwmzkec5UiuPbVdLpPVsJIRVl8obPu4oMzhOMKVqw3UGsFGCjWNOY2cKg2cN8E7ijfAEoFNztLyNPY+g==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "polygon-clipping": "^0.15.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/intersect/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/invariant": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.0.0.tgz", + "integrity": "sha512-Kayszfz3W8yJ1/cIA3/aNSzAuw7QgSp+IwsSmhLAfp4DbjV0o6sjxRZXRY2gRstZHqkNHSSEeir8V/icdO8sjA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/invariant/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/isobands": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/isobands/-/isobands-7.0.0.tgz", + "integrity": "sha512-2l+FIbywYPCsus8+H6eSyyf3Xsrbe+Zi0NbgpVDScvR9TVu55ta0KMvQdS4aMEtg9ADlQa/C81hZhmYsBtvLug==", + "dependencies": { + "@turf/area": "^7.0.0", + "@turf/bbox": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/explode": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "marchingsquares": "^1.3.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isobands/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/isolines": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/isolines/-/isolines-7.0.0.tgz", + "integrity": "sha512-3LqwbzHnZ19gca5VC08XnHK36iRw2tEKjVtrI87iq6QEN9rR4FYcZOCmkPznYKwMKr44KkZKOlfNq/s72s1t3w==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "marchingsquares": "^1.3.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/isolines/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/jsts": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@turf/jsts/-/jsts-2.7.1.tgz", + "integrity": "sha512-+nwOKme/aUprsxnLSfr2LylV6eL6T1Tuln+4Hl92uwZ8FrmjDRCH5Bi1LJNVfWCiYgk8+5K+t2zDphWNTsIFDA==", + "dependencies": { + "jsts": "2.7.1" + } + }, + "node_modules/@turf/kinks": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/kinks/-/kinks-7.0.0.tgz", + "integrity": "sha512-rUzx2G4NSb7f+SMjuBZBakrK4BrS3pfb67vYH8XQA28C9NBRQcZqJBCjXqA079q16GXiDpjCLianQMewtd6ksw==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "sweepline-intersections": "^1.5.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/kinks/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/length": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/length/-/length-7.0.0.tgz", + "integrity": "sha512-B+ABesWLtQc8W310LJ8kmUn//NfpUUhsm4Gwt9ZHNYxBVV+FNolY965F7DYm/tvLMZP9bGMomTlczFWV7O4YIA==", + "dependencies": { + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/length/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/line-arc": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-arc/-/line-arc-7.0.0.tgz", + "integrity": "sha512-kxZJlbQHR5F7toJ7QR+qF4eWX74RydGavCr2/IPOjcFK1mcSkTfyiSli3pciavD4eH3tHx0flTqH2poqTQxtyg==", + "dependencies": { + "@turf/circle": "^7.0.0", + "@turf/destination": "^7.0.0", + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-arc/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/line-chunk": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-chunk/-/line-chunk-7.0.0.tgz", + "integrity": "sha512-oA4GuUSxof3o0JOhNyS+CdzpqJ0VFro8RlGkTtymMhEMly4T7xjbMdffIrJ9o8hpnNKdwkJs4bcB98UD8sSPeA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/length": "^7.0.0", + "@turf/line-slice-along": "^7.0.0", + "@turf/meta": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-intersect": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-7.0.0.tgz", + "integrity": "sha512-vxCwzxCbTyKXO3GsEFQ8hyH1nLQShBhvFobRM2bLrbBlP2fWY9LDGixBcmWuOwV/G/wpQJxAjBJ6IYHjKIJqyA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "sweepline-intersections": "^1.5.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-intersect/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/line-offset": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-offset/-/line-offset-7.0.0.tgz", + "integrity": "sha512-ZQHAuR2y0ktG8uYAbaxGsJh931oCBwrxzOjiRtOcMifc/Hty37WFVaE9rCDrYrMLut7Ye9JQ36c6DMOljOLkMA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-overlap": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-overlap/-/line-overlap-7.0.0.tgz", + "integrity": "sha512-yYjT0Qe2WIvf3InuSzobmEkD4XguoimdzXt23eHFF/RKNubAwwdFDeLbmyA7fNZFGrMLNEl/zYvgBVXiQ7tihg==", + "dependencies": { + "@turf/boolean-point-on-line": "^7.0.0", + "@turf/geojson-rbush": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/line-segment": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/nearest-point-on-line": "^7.0.0", + "deep-equal": "^2.2.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-overlap/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/line-segment": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-7.0.0.tgz", + "integrity": "sha512-Fn8IPEMGQyDAJjjrEOrF0VUCdRosjdvd9x38gq73Qg5oSsZ4p9DdMdgydK27XL74/ivM4+CtDqQkHcj5Aec/yw==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-segment/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/line-slice": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice/-/line-slice-7.0.0.tgz", + "integrity": "sha512-De+j4QTdoHguYu4S3UqOdU8GEg7VrVqQ67SkGzgFWL/SFh3V98+KDhxFIMA1OmzOc5/ox3yUQ8a/BOYslAV4DA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/nearest-point-on-line": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-slice-along": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-slice-along/-/line-slice-along-7.0.0.tgz", + "integrity": "sha512-+HNcI8ilm7ma/oKm23f2ca+xaNe4IjDYAjtl2Peap3b39Y9UAgw6lXhIh/L+m/XQXKkdWOfmqf4J2ar0bgG9DQ==", + "dependencies": { + "@turf/bearing": "^7.0.0", + "@turf/destination": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-split": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-7.0.0.tgz", + "integrity": "sha512-LuJuO1bY++PJEQ7gqbkzdL8RP1pZRQdgoqaZTsMy5AhxfjMCSL4Bx3o8SYAtsQt6CTx17dMCqBcjmjZwqNJJ6w==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/geojson-rbush": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/line-intersect": "^7.0.0", + "@turf/line-segment": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/nearest-point-on-line": "^7.0.0", + "@turf/square": "^7.0.0", + "@turf/truncate": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-to-polygon": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/line-to-polygon/-/line-to-polygon-7.0.0.tgz", + "integrity": "sha512-NmrY1jFj4oW30TW2LNoL00/uNmF1bGeTU8W1EtCunoCUUOVD9XSCj0kdSuq+MJLBH06PzOL+2f1jKTFf0X7Kqg==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/line-to-polygon/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/mask": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/mask/-/mask-7.0.0.tgz", + "integrity": "sha512-5vSNpN4rCRhEsS69d9u8USlFGdlf5rySCD701gzALNGpvTN4ViXoUUH9ysdqOdvp/IdC8thRP72Hget9fJKcfQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "polygon-clipping": "^0.15.3" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/meta": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.0.0.tgz", + "integrity": "sha512-cEXr13uFwhXq5mFBy0IK1U/QepE5qgk3zXpBYsla3lYV7cB83Vh+NNUR+r0/w/QoJqest1TG4H20F9tGYWPi/g==", + "dependencies": { + "@turf/helpers": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/midpoint": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/midpoint/-/midpoint-7.0.0.tgz", + "integrity": "sha512-Zt0OgtByf5rTKT86WssAzNN2D7eqjay9ZlRb2rutynMyJDdLgbGPurTZIOXOP7ztGaS/WIMYlvPEveIp8ao1zQ==", + "dependencies": { + "@turf/bearing": "^7.0.0", + "@turf/destination": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/moran-index": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/moran-index/-/moran-index-7.0.0.tgz", + "integrity": "sha512-Vl3PD8wn8aBYZpQ7Kq+dWosiQP2HTeZmzEyQh7WGAUK7OunODFPIdObj6jLwKYj9ZW2XVxaGmogjVEmQ2/2FQQ==", + "dependencies": { + "@turf/distance-weight": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/moran-index/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/nearest-neighbor-analysis": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-neighbor-analysis/-/nearest-neighbor-analysis-7.0.0.tgz", + "integrity": "sha512-wz+2U271niT7l18SIctAo5Muh5kqyZl4bqFUWTQhhhzXkcP2hCYT9gYWkrZN0gfyp9sCWUe7mgoU6/wGDsiENQ==", + "dependencies": { + "@turf/area": "^7.0.0", + "@turf/bbox": "^7.0.0", + "@turf/bbox-polygon": "^7.0.0", + "@turf/centroid": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/nearest-point": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-neighbor-analysis/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/nearest-point": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point/-/nearest-point-7.0.0.tgz", + "integrity": "sha512-h3uLUoeo6JWTirpI499SRooqEoDxia2C/kDqAwAeXFqwxzGqGprtNA/C0bMgHfxE1M2rxORGzvgywKirpLu1dA==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-on-line": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-7.0.0.tgz", + "integrity": "sha512-ADf+oytqY3EVT/9JApoRr5H0f8wzkzeXhJUfTDEcWZoJqT/8lOX2HCo87b6+oEb1QIavlSogoku1+M5xMIcJLw==", + "dependencies": { + "@turf/bearing": "^7.0.0", + "@turf/destination": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/line-intersect": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-on-line/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/nearest-point-to-line": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-to-line/-/nearest-point-to-line-7.0.0.tgz", + "integrity": "sha512-DmbQ88nChkVD6pe9dbFZEBVtmcgrRFKSv1n3Y1Kka+BeDFzCfie2VJuhsrqrecRmLMIEf1tdVJL/MdpinnZZTQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/point-to-line-distance": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/nearest-point-to-line/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/nearest-point/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/planepoint": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/planepoint/-/planepoint-7.0.0.tgz", + "integrity": "sha512-+/Sqk1I8QwlKsd0rfjwOac3BAdIBeyjN8Irgk2vYbxUADn6QWcxyYLro8DHUVrTu9jZfllc8QNpy9/6iUykm0A==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-grid": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/point-grid/-/point-grid-7.0.0.tgz", + "integrity": "sha512-vXtFeWFC0i9T71AVX5VdlqD2mlKhk649OF/pEJB9wtCGDHYax9kkObNDSz4mFAz4UqQ67P5Lipg8YYaawgmDZg==", + "dependencies": { + "@turf/boolean-within": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-grid/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/point-on-feature": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/point-on-feature/-/point-on-feature-7.0.0.tgz", + "integrity": "sha512-KTvYrQJEw62GLivd5LfCRt9z/4ZFdz29+3HxW9/RHhb8nrjLU8M8j7fJTQ+/Q+pPunWPK0Fvjv8LZAh0c30/oA==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/center": "^7.0.0", + "@turf/explode": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/nearest-point": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-on-feature/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/point-to-line-distance": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/point-to-line-distance/-/point-to-line-distance-7.0.0.tgz", + "integrity": "sha512-BMNy/vbvm9NjrgJq6MA3xhruy+cp/Wj+ff3fiu8Rdl5QX/dMwCeyCZuuvzCftup6GTPUhRbpe0YRFUBInGzx/g==", + "dependencies": { + "@turf/bearing": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/projection": "^7.0.0", + "@turf/rhumb-bearing": "^7.0.0", + "@turf/rhumb-distance": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/point-to-line-distance/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/points-within-polygon": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/points-within-polygon/-/points-within-polygon-7.0.0.tgz", + "integrity": "sha512-Nz4kLSitsfZH0jwgCA5PCVcCocMWNM5+0LZo13j2JoXP980zTwL5t6jiwS2qFoofNE0Q6RfjrfQ3kVm5/g1/bQ==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/points-within-polygon/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/polygon-smooth": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-smooth/-/polygon-smooth-7.0.0.tgz", + "integrity": "sha512-1pY81xZivsToG8zFGvX/1NjrYCNOWm+fdtciWyF+tt0rGE/xvMwE4yiX1chDrKiV2X8W+Ip/ZLry3MIBcSUx0Q==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-smooth/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/polygon-tangents": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-tangents/-/polygon-tangents-7.0.0.tgz", + "integrity": "sha512-BNSKWqOTiCvVSj5CuLmkcHkagFsBSbUuSSffEGxC3cFY2tb0vP71nFE9qM+h9FpApkR6F/bWxRu5AxQ4oVHySQ==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/boolean-within": "^7.0.0", + "@turf/explode": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/nearest-point": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-tangents/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/polygon-to-line": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/polygon-to-line/-/polygon-to-line-7.0.0.tgz", + "integrity": "sha512-FkbxgABNzULN//WiSWiBqkbePbQANMmYKFgD6tUnVGldTFa1RuUjt/bgbvPjVHZfUdJieyxBszzl1dP0z8czDQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygon-to-line/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/polygonize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/polygonize/-/polygonize-7.0.0.tgz", + "integrity": "sha512-aTVANRcWHVFZIgUwH5H5BMn9OoGk8KI+mGe4H8cXQWUvarKUz86t8BkdkbWnsAfdOTrEwK4WFWtxfmeWwJUH7Q==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/envelope": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/polygonize/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/projection": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/projection/-/projection-7.0.0.tgz", + "integrity": "sha512-EoPbZPZSDv0AJMfYhqnS455CVMYwPU78kHyQHeOnMR1Tc5z+TiImvyq55umhfecgpETzuDsjFkmeQ2phDKTmbA==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/projection/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/quadrat-analysis": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/quadrat-analysis/-/quadrat-analysis-7.0.0.tgz", + "integrity": "sha512-JaCARFLS7mFIGORpRFcqrrCWVgTi+Vw5prSgQdaMVMcXo5+wsPh0fJUno4PGHt++R6AE3ZgxtDq2gS/1RZfhOA==", + "dependencies": { + "@turf/area": "^7.0.0", + "@turf/bbox": "^7.0.0", + "@turf/bbox-polygon": "^7.0.0", + "@turf/centroid": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/point-grid": "^7.0.0", + "@turf/random": "^7.0.0", + "@turf/square-grid": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/quadrat-analysis/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/random": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/random/-/random-7.0.0.tgz", + "integrity": "sha512-l3+FW0pk6MUQx2lyMvzps2YQS7ovP6YoV0tVvuNaQq0UICB1P4EHJIKLMTe5pXk73Z3p0wTgnEPk0Z2lqWaeGQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/random/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/rectangle-grid": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/rectangle-grid/-/rectangle-grid-7.0.0.tgz", + "integrity": "sha512-iLMRfaWs9+BlGaDP5ka7J9bTGFPGim1Tl38uNTPagIVxk6bNfB8156S9up+/7scpjuQGxY0ky3tlR9aVYlWNdw==", + "dependencies": { + "@turf/boolean-intersects": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rectangle-grid/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/rewind": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-7.0.0.tgz", + "integrity": "sha512-kZwqJ4enmTZPDrI0rCf+zE9HChCuKKvD0kqZJo50XvfypfKVz5CI751Dgs/cslMIENyKFiHHHM4OMgouJ/lR1Q==", + "dependencies": { + "@turf/boolean-clockwise": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rewind/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/rhumb-bearing": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-bearing/-/rhumb-bearing-7.0.0.tgz", + "integrity": "sha512-4qDggFDNBbWdD+o3H+vna5eiKCAsmqAueP3T5rSEB1ier77wVgjg7cs7eTrEBbpuCbPAho7NDNdyAjgItydgLQ==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-bearing/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/rhumb-destination": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-destination/-/rhumb-destination-7.0.0.tgz", + "integrity": "sha512-uYgqP8BGo8DBs6ZgjBo9SNCXc6BY+iA6OG7yPYSe4Lur2vu+LkbBXV6P3IodkeHyPex+X5ATea8QHutYQX6HUg==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-destination/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/rhumb-distance": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/rhumb-distance/-/rhumb-distance-7.0.0.tgz", + "integrity": "sha512-VAZnZcHbHHUU/Li0sj50/T6bBGRWvJ6eOZmw2aZFxxnC+AkHv4LTKDf0wNsxR03ZwGEh4uJM8OuirNugLIhAyA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/rhumb-distance/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/sample": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/sample/-/sample-7.0.0.tgz", + "integrity": "sha512-yCeITDVT3vWMaK/X1JF6jxEipgBCi2Foj87lVO7rcVg5pp/6c2S8BnFB5NoI0+HKXEpulG4eH57khUVND9VCGA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sample/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/sector": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/sector/-/sector-7.0.0.tgz", + "integrity": "sha512-HXT8vbCYoIbomcyT0D/0Ap4p3bSmb3EFC7C5BBv3Gr9oWiMf5cFgwwA/inbGorU5fcRap7/Yt4hWWTGLO5MDTw==", + "dependencies": { + "@turf/circle": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/line-arc": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/sector/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/shortest-path": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/shortest-path/-/shortest-path-7.0.0.tgz", + "integrity": "sha512-nEtL6qA3Po6uXZsLnfbFCD+Nng3GIRBX2Okyva3PYCJvuD27qYJFvXk423Z0qA+09zZHhi2Ct1IGuXBBYmdkaw==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/bbox-polygon": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/clean-coords": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/transform-scale": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/shortest-path/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/simplify": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/simplify/-/simplify-7.0.0.tgz", + "integrity": "sha512-EizgFBv7LiLTfqk0BlzuKXmGj7owHksI7Q0mur+yOFGFhEmP5pWm+jYxq+pYfUZA3eki4J7kyN4Mhg5c+jhLTw==", + "dependencies": { + "@turf/clean-coords": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/simplify/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/square": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-7.0.0.tgz", + "integrity": "sha512-HnY2fESbGGzMeb19qZ0HKpsGRZv4f4e8oHH+gdCr6cs03z/TO6JslJdRA65zHNkdReSVEOQWIMF5IB+Cq20jCg==", + "dependencies": { + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square-grid": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/square-grid/-/square-grid-7.0.0.tgz", + "integrity": "sha512-Dz7GyFRbcf0aek5nm7gW5gVYq/dJdn+JkVFgSIimysRl1tBtQiE0LvjZRdS97JvPs6m6hcZT+sDEXW1fLDPYFA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/rectangle-grid": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/square-grid/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/standard-deviational-ellipse": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/standard-deviational-ellipse/-/standard-deviational-ellipse-7.0.0.tgz", + "integrity": "sha512-rr4T48aAfu2E/V6fStE8Jq1VD2a/zztS1LGu1PSK3pbk7Eq9HTEE9jzjBIONhDI6ljoLBYy4Qib+L3DaNCfmUQ==", + "dependencies": { + "@turf/center-mean": "^7.0.0", + "@turf/ellipse": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/points-within-polygon": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tag": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/tag/-/tag-7.0.0.tgz", + "integrity": "sha512-/QVKwYq9C7BkHrVtwLXwKSrEZcZT+/JQeNpGsOFhCBnDgXUMtLpj2obkMt+v7wXXtUgTsFAnSh7ClNgS6eB2Sg==", + "dependencies": { + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tag/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/tesselate": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/tesselate/-/tesselate-7.0.0.tgz", + "integrity": "sha512-AWdFrBuLh4RFEBLWVaY9Z3/8jrXFv9WDvO3SyVUCQYFFcQP9aVieHyEEwebRaKb578il/o3rNHyRBuL6xSOcAA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "earcut": "^2.2.4" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tin": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/tin/-/tin-7.0.0.tgz", + "integrity": "sha512-KuzXnoetCqu5RC6wcUWOvotxZMcgZptqz3gJZAuhqa1CF3DKUKFEJ851+f/9KzZ+woalUB8anFr9dMKJjcM6RA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/tin/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/transform-rotate": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/transform-rotate/-/transform-rotate-7.0.0.tgz", + "integrity": "sha512-zU6ypVOdVtXaJvy2LEVjx4o7y/vR9kIL6Iu/VkzXIvGCYICDdHnrpeEVJOFZPqdq4GI4C1xAQ4ARPTwtGrpPBg==", + "dependencies": { + "@turf/centroid": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/rhumb-bearing": "^7.0.0", + "@turf/rhumb-destination": "^7.0.0", + "@turf/rhumb-distance": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-scale": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/transform-scale/-/transform-scale-7.0.0.tgz", + "integrity": "sha512-G94nxT5TyP8TSNRDkoevFoGlUw0H2Az5IG1JKFTT5nRqpbML17IQblV33gaA1Hm197rekQo3CDVWEbgpOV0jAw==", + "dependencies": { + "@turf/bbox": "^7.0.0", + "@turf/center": "^7.0.0", + "@turf/centroid": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/rhumb-bearing": "^7.0.0", + "@turf/rhumb-destination": "^7.0.0", + "@turf/rhumb-distance": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/transform-translate": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/transform-translate/-/transform-translate-7.0.0.tgz", + "integrity": "sha512-sdZl29CqHoBo/Mxwos6Hvb6LXtHJYYTIjlWqphnu1kislbJwWUJpYjwD8yqTljfW4QHgDzGpnRLGzjDVZ7KHQQ==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/rhumb-destination": "^7.0.0" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/triangle-grid": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/triangle-grid/-/triangle-grid-7.0.0.tgz", + "integrity": "sha512-LKBMgkGGfZxZclRzalIYHGG50eMBOHNihBZLagK/f8RAMrvG2rS/S4n/CQbqU9OXenDgYErSm3SCVXC1dTXVPA==", + "dependencies": { + "@turf/distance": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/intersect": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/triangle-grid/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/truncate": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-7.0.0.tgz", + "integrity": "sha512-G0cKlvONexzRIHRMkzhIA9UsOHiHbVW+iFBlAyDxXKK9Cr9USp/JjETAo8zqCnTQna40xdjt4bcHtsZxqyRQZw==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/truncate/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/turf": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/turf/-/turf-7.0.0.tgz", + "integrity": "sha512-oKtvIoP9nCBT7HCUn0/QP62QwTj8eDxfS810ZGJO0aDPDg2x94tSqwbvmu5WcvcxOBB/6L6rF55UJjL+OjcHxw==", + "dependencies": { + "@turf/along": "^7.0.0", + "@turf/angle": "^7.0.0", + "@turf/area": "^7.0.0", + "@turf/bbox": "^7.0.0", + "@turf/bbox-clip": "^7.0.0", + "@turf/bbox-polygon": "^7.0.0", + "@turf/bearing": "^7.0.0", + "@turf/bezier-spline": "^7.0.0", + "@turf/boolean-clockwise": "^7.0.0", + "@turf/boolean-concave": "^7.0.0", + "@turf/boolean-contains": "^7.0.0", + "@turf/boolean-crosses": "^7.0.0", + "@turf/boolean-disjoint": "^7.0.0", + "@turf/boolean-equal": "^7.0.0", + "@turf/boolean-intersects": "^7.0.0", + "@turf/boolean-overlap": "^7.0.0", + "@turf/boolean-parallel": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/boolean-point-on-line": "^7.0.0", + "@turf/boolean-touches": "^7.0.0", + "@turf/boolean-valid": "^7.0.0", + "@turf/boolean-within": "^7.0.0", + "@turf/buffer": "^7.0.0", + "@turf/center": "^7.0.0", + "@turf/center-mean": "^7.0.0", + "@turf/center-median": "^7.0.0", + "@turf/center-of-mass": "^7.0.0", + "@turf/centroid": "^7.0.0", + "@turf/circle": "^7.0.0", + "@turf/clean-coords": "^7.0.0", + "@turf/clone": "^7.0.0", + "@turf/clusters": "^7.0.0", + "@turf/clusters-dbscan": "^7.0.0", + "@turf/clusters-kmeans": "^7.0.0", + "@turf/collect": "^7.0.0", + "@turf/combine": "^7.0.0", + "@turf/concave": "^7.0.0", + "@turf/convex": "^7.0.0", + "@turf/destination": "^7.0.0", + "@turf/difference": "^7.0.0", + "@turf/dissolve": "^7.0.0", + "@turf/distance": "^7.0.0", + "@turf/distance-weight": "^7.0.0", + "@turf/ellipse": "^7.0.0", + "@turf/envelope": "^7.0.0", + "@turf/explode": "^7.0.0", + "@turf/flatten": "^7.0.0", + "@turf/flip": "^7.0.0", + "@turf/geojson-rbush": "^7.0.0", + "@turf/great-circle": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/hex-grid": "^7.0.0", + "@turf/interpolate": "^7.0.0", + "@turf/intersect": "^7.0.0", + "@turf/invariant": "^7.0.0", + "@turf/isobands": "^7.0.0", + "@turf/isolines": "^7.0.0", + "@turf/kinks": "^7.0.0", + "@turf/length": "^7.0.0", + "@turf/line-arc": "^7.0.0", + "@turf/line-chunk": "^7.0.0", + "@turf/line-intersect": "^7.0.0", + "@turf/line-offset": "^7.0.0", + "@turf/line-overlap": "^7.0.0", + "@turf/line-segment": "^7.0.0", + "@turf/line-slice": "^7.0.0", + "@turf/line-slice-along": "^7.0.0", + "@turf/line-split": "^7.0.0", + "@turf/line-to-polygon": "^7.0.0", + "@turf/mask": "^7.0.0", + "@turf/meta": "^7.0.0", + "@turf/midpoint": "^7.0.0", + "@turf/moran-index": "^7.0.0", + "@turf/nearest-neighbor-analysis": "^7.0.0", + "@turf/nearest-point": "^7.0.0", + "@turf/nearest-point-on-line": "^7.0.0", + "@turf/nearest-point-to-line": "^7.0.0", + "@turf/planepoint": "^7.0.0", + "@turf/point-grid": "^7.0.0", + "@turf/point-on-feature": "^7.0.0", + "@turf/point-to-line-distance": "^7.0.0", + "@turf/points-within-polygon": "^7.0.0", + "@turf/polygon-smooth": "^7.0.0", + "@turf/polygon-tangents": "^7.0.0", + "@turf/polygon-to-line": "^7.0.0", + "@turf/polygonize": "^7.0.0", + "@turf/projection": "^7.0.0", + "@turf/quadrat-analysis": "^7.0.0", + "@turf/random": "^7.0.0", + "@turf/rectangle-grid": "^7.0.0", + "@turf/rewind": "^7.0.0", + "@turf/rhumb-bearing": "^7.0.0", + "@turf/rhumb-destination": "^7.0.0", + "@turf/rhumb-distance": "^7.0.0", + "@turf/sample": "^7.0.0", + "@turf/sector": "^7.0.0", + "@turf/shortest-path": "^7.0.0", + "@turf/simplify": "^7.0.0", + "@turf/square": "^7.0.0", + "@turf/square-grid": "^7.0.0", + "@turf/standard-deviational-ellipse": "^7.0.0", + "@turf/tag": "^7.0.0", + "@turf/tesselate": "^7.0.0", + "@turf/tin": "^7.0.0", + "@turf/transform-rotate": "^7.0.0", + "@turf/transform-scale": "^7.0.0", + "@turf/transform-translate": "^7.0.0", + "@turf/triangle-grid": "^7.0.0", + "@turf/truncate": "^7.0.0", + "@turf/union": "^7.0.0", + "@turf/unkink-polygon": "^7.0.0", + "@turf/voronoi": "^7.0.0", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/turf/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/union": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/union/-/union-7.0.0.tgz", + "integrity": "sha512-NRnP0GJ3guzVh6D2KNZvqNLMkbZtPQ6X1U4czK9ETicLROzqq6wM/S8gHZJVoh0KxxK1RYDsKu0RGFVobVT2vA==", + "dependencies": { + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "polygon-clipping": "^0.15.3", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/union/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + }, + "node_modules/@turf/unkink-polygon": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/unkink-polygon/-/unkink-polygon-7.0.0.tgz", + "integrity": "sha512-mkwC7+KgINIAkRYSx0iRp8BjGtw8bijvtzC9Da5CRTSg13WH2IrvksFtasTo58xMdqyt6oO2NdMEDX3JUwRCeg==", + "dependencies": { + "@turf/area": "^7.0.0", + "@turf/boolean-point-in-polygon": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/meta": "^7.0.0", + "rbush": "^3.0.1" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, + "node_modules/@turf/voronoi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@turf/voronoi/-/voronoi-7.0.0.tgz", + "integrity": "sha512-AJMrtd9eV+nVA+su1GIp96E5ENzKUepPFr9wWHqqWedj92W2KbdAwfwJVMyl0vBmiMYeTSnfzC7HenIMXePfhA==", + "dependencies": { + "@turf/clone": "^7.0.0", + "@turf/helpers": "^7.0.0", + "@turf/invariant": "^7.0.0", + "d3-voronoi": "1.1.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@types/aria-query": { "version": "4.2.2", "dev": true, @@ -5338,7 +7574,6 @@ }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.5", @@ -5560,7 +7795,6 @@ }, "node_modules/available-typed-arrays": { "version": "1.0.7", - "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -6352,7 +8586,6 @@ }, "node_modules/call-bind": { "version": "1.0.7", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -6824,6 +9057,17 @@ "dev": true, "license": "MIT" }, + "node_modules/concaveman": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/concaveman/-/concaveman-1.2.1.tgz", + "integrity": "sha512-PwZYKaM/ckQSa8peP5JpVr7IMJ4Nn/MHIaWUjP4be+KoZ7Botgs8seAZGpmaOM+UZXawcdYRao/px9ycrCihHw==", + "dependencies": { + "point-in-polygon": "^1.1.0", + "rbush": "^3.0.1", + "robust-predicates": "^2.0.4", + "tinyqueue": "^2.0.3" + } + }, "node_modules/condense-newlines": { "version": "0.2.1", "dev": true, @@ -7152,6 +9396,24 @@ "version": "3.1.3", "license": "MIT" }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/d3-geo": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.7.1.tgz", + "integrity": "sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw==", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-voronoi": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", + "integrity": "sha512-RhGS1u2vavcO7ay7ZNAPo4xeDh/VYeGof3x5ZLJBQgYhLegxr3s5IykvWmJ94FTU6mcbtp4sloqZ54mP6R4Utw==" + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "dev": true, @@ -7306,7 +9568,6 @@ }, "node_modules/deep-equal": { "version": "2.2.3", - "dev": true, "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.0", @@ -7371,7 +9632,6 @@ }, "node_modules/define-data-property": { "version": "1.1.4", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -7395,7 +9655,6 @@ }, "node_modules/define-properties": { "version": "1.2.1", - "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", @@ -7749,6 +10008,11 @@ "dev": true, "license": "MIT" }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "dev": true, @@ -7972,7 +10236,6 @@ }, "node_modules/es-define-property": { "version": "1.0.0", - "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" @@ -7983,7 +10246,6 @@ }, "node_modules/es-errors": { "version": "1.3.0", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7991,7 +10253,6 @@ }, "node_modules/es-get-iterator": { "version": "1.1.3", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -9371,7 +11632,6 @@ }, "node_modules/for-each": { "version": "0.3.3", - "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.1.3" @@ -9726,7 +11986,6 @@ }, "node_modules/function-bind": { "version": "1.1.2", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9756,7 +12015,6 @@ }, "node_modules/functions-have-names": { "version": "1.2.3", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9769,6 +12027,27 @@ "node": ">=6.9.0" } }, + "node_modules/geojson-polygon-self-intersections": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/geojson-polygon-self-intersections/-/geojson-polygon-self-intersections-1.2.1.tgz", + "integrity": "sha512-/QM1b5u2d172qQVO//9CGRa49jEmclKEsYOQmWP9ooEjj63tBM51m2805xsbxkzlEELQ2REgTf700gUhhlegxA==", + "dependencies": { + "rbush": "^2.0.1" + } + }, + "node_modules/geojson-polygon-self-intersections/node_modules/quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" + }, + "node_modules/geojson-polygon-self-intersections/node_modules/rbush": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.2.tgz", + "integrity": "sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA==", + "dependencies": { + "quickselect": "^1.0.1" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "dev": true, @@ -9787,7 +12066,6 @@ }, "node_modules/get-intrinsic": { "version": "1.2.4", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -10020,7 +12298,6 @@ }, "node_modules/gopd": { "version": "1.0.1", - "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" @@ -10125,7 +12402,6 @@ }, "node_modules/has-bigints": { "version": "1.0.2", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10140,7 +12416,6 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -10151,7 +12426,6 @@ }, "node_modules/has-proto": { "version": "1.0.3", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10162,7 +12436,6 @@ }, "node_modules/has-symbols": { "version": "1.0.3", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10173,7 +12446,6 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -10213,7 +12485,6 @@ }, "node_modules/hasown": { "version": "2.0.2", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -10550,7 +12821,6 @@ }, "node_modules/internal-slot": { "version": "1.0.7", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -10615,7 +12885,6 @@ }, "node_modules/is-arguments": { "version": "1.1.1", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -10630,7 +12899,6 @@ }, "node_modules/is-array-buffer": { "version": "3.0.4", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -10664,7 +12932,6 @@ }, "node_modules/is-bigint": { "version": "1.0.4", - "dev": true, "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" @@ -10686,7 +12953,6 @@ }, "node_modules/is-boolean-object": { "version": "1.1.2", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -10723,7 +12989,6 @@ }, "node_modules/is-callable": { "version": "1.2.7", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10759,7 +13024,6 @@ }, "node_modules/is-date-object": { "version": "1.0.5", - "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -10897,7 +13161,6 @@ }, "node_modules/is-map": { "version": "2.0.3", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10947,7 +13210,6 @@ }, "node_modules/is-number-object": { "version": "1.0.7", - "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -11031,7 +13293,6 @@ }, "node_modules/is-regex": { "version": "1.1.4", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -11062,7 +13323,6 @@ }, "node_modules/is-set": { "version": "2.0.3", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -11073,7 +13333,6 @@ }, "node_modules/is-shared-array-buffer": { "version": "1.0.3", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7" @@ -11098,7 +13357,6 @@ }, "node_modules/is-string": { "version": "1.0.7", - "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -11112,7 +13370,6 @@ }, "node_modules/is-symbol": { "version": "1.0.4", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" @@ -11151,7 +13408,6 @@ }, "node_modules/is-weakmap": { "version": "2.0.2", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -11173,7 +13429,6 @@ }, "node_modules/is-weakset": { "version": "2.0.3", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -11207,7 +13462,6 @@ }, "node_modules/isarray": { "version": "2.0.5", - "dev": true, "license": "MIT" }, "node_modules/isexe": { @@ -12041,6 +14295,14 @@ "tiny-warning": "^1.0.2" } }, + "node_modules/jsts": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/jsts/-/jsts-2.7.1.tgz", + "integrity": "sha512-x2wSZHEBK20CY+Wy+BPE7MrFQHW6sIsdaGUMEqmGAio+3gFzQaBYPwLRonUfQf9Ak8pBieqj9tUofX1+WtAEIg==", + "engines": { + "node": ">= 12" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "dev": true, @@ -12351,6 +14613,11 @@ "dev": true, "license": "ISC" }, + "node_modules/marchingsquares": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/marchingsquares/-/marchingsquares-1.3.3.tgz", + "integrity": "sha512-gz6nNQoVK7Lkh2pZulrT4qd4347S/toG9RXH2pyzhLgkL5mLkBoqgv4EvAGXcV0ikDW72n/OQb3Xe8bGagQZCg==" + }, "node_modules/markdown-to-jsx": { "version": "6.11.4", "dev": true, @@ -12996,7 +15263,6 @@ }, "node_modules/object-inspect": { "version": "1.13.1", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13004,7 +15270,6 @@ }, "node_modules/object-is": { "version": "1.1.6", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -13019,7 +15284,6 @@ }, "node_modules/object-keys": { "version": "1.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -13027,7 +15291,6 @@ }, "node_modules/object.assign": { "version": "4.1.5", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.5", @@ -13628,6 +15891,30 @@ "node": ">=4" } }, + "node_modules/point-in-polygon": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", + "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==" + }, + "node_modules/point-in-polygon-hao": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.1.0.tgz", + "integrity": "sha512-3hTIM2j/v9Lio+wOyur3kckD4NxruZhpowUbEgmyikW+a2Kppjtu1eN+AhnMQtoHW46zld88JiYWv6fxpsDrTQ==" + }, + "node_modules/polygon-clipping": { + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/polygon-clipping/-/polygon-clipping-0.15.7.tgz", + "integrity": "sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==", + "dependencies": { + "robust-predicates": "^3.0.2", + "splaytree": "^3.1.0" + } + }, + "node_modules/polygon-clipping/node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/polylabel": { "version": "1.1.0", "license": "ISC", @@ -13645,7 +15932,6 @@ }, "node_modules/possible-typed-array-names": { "version": "1.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -14189,6 +16475,11 @@ ], "license": "MIT" }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "node_modules/randombytes": { "version": "2.1.0", "dev": true, @@ -14236,6 +16527,14 @@ "node": ">= 0.8" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/react": { "version": "18.2.0", "license": "MIT", @@ -15375,7 +17674,6 @@ }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.6", @@ -15609,6 +17907,11 @@ "inherits": "^2.0.1" } }, + "node_modules/robust-predicates": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", + "integrity": "sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg==" + }, "node_modules/rollup": { "version": "4.13.0", "dev": true, @@ -16054,7 +18357,6 @@ }, "node_modules/set-function-length": { "version": "1.2.2", - "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -16070,7 +18372,6 @@ }, "node_modules/set-function-name": { "version": "2.0.2", - "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -16137,7 +18438,6 @@ }, "node_modules/side-channel": { "version": "1.0.6", - "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.7", @@ -16173,6 +18473,11 @@ "dev": true, "license": "MIT" }, + "node_modules/skmeans": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", + "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==" + }, "node_modules/slash": { "version": "3.0.0", "dev": true, @@ -16259,6 +18564,11 @@ "wbuf": "^1.7.3" } }, + "node_modules/splaytree": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/splaytree/-/splaytree-3.1.2.tgz", + "integrity": "sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==" + }, "node_modules/split-on-first": { "version": "1.1.0", "license": "MIT", @@ -16305,7 +18615,6 @@ }, "node_modules/stop-iteration-iterator": { "version": "1.0.0", - "dev": true, "license": "MIT", "dependencies": { "internal-slot": "^1.0.4" @@ -16636,6 +18945,14 @@ "dev": true, "license": "MIT" }, + "node_modules/sweepline-intersections": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sweepline-intersections/-/sweepline-intersections-1.5.0.tgz", + "integrity": "sha512-AoVmx72QHpKtItPu72TzFL+kcYjd67BPLDoR0LarIk+xyaRg+pDTMFXndIEvZf9xEKnJv6JdhgRMnocoG0D3AQ==", + "dependencies": { + "tinyqueue": "^2.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "dev": true, @@ -16926,6 +19243,40 @@ "node": ">=0.6" } }, + "node_modules/topojson-client": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", + "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "topo2geo": "bin/topo2geo", + "topomerge": "bin/topomerge", + "topoquantize": "bin/topoquantize" + } + }, + "node_modules/topojson-client/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/topojson-server": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/topojson-server/-/topojson-server-3.0.1.tgz", + "integrity": "sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw==", + "dependencies": { + "commander": "2" + }, + "bin": { + "geo2topo": "bin/geo2topo" + } + }, + "node_modules/topojson-server/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, "node_modules/toposort": { "version": "2.0.2", "license": "MIT" @@ -18722,7 +21073,6 @@ }, "node_modules/which-boxed-primitive": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", @@ -18762,7 +21112,6 @@ }, "node_modules/which-collection": { "version": "1.0.2", - "dev": true, "license": "MIT", "dependencies": { "is-map": "^2.0.3", @@ -18779,7 +21128,6 @@ }, "node_modules/which-typed-array": { "version": "1.1.15", - "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", diff --git a/source/frontend/package.json b/source/frontend/package.json index f9f3124760..aec529bef5 100644 --- a/source/frontend/package.json +++ b/source/frontend/package.json @@ -7,6 +7,7 @@ "@bcgov/design-tokens": "3.0.0-rc1", "@react-keycloak/web": "3.4.0", "@reduxjs/toolkit": "1.8.6", + "@turf/turf": "7.0.0", "@types/polylabel": "1.0.5", "@xstate/react": "3.2.2", "axios": "1.6.7", From b1b7f444f5585c56a6b2a27f29be5af90d49bc53 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Thu, 25 Jul 2024 23:31:14 -0700 Subject: [PATCH 09/14] Reposition file marker only if it falls within original property boundary --- .../propertySelector/MapClickMonitor.tsx | 13 +++++++-- .../propertySelector/MapSelectorContainer.tsx | 5 +++- .../map/PropertyMapSelectorFormView.tsx | 8 ++++- .../update/properties/UpdateProperties.tsx | 29 ++++++++++++++----- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx index 5a5f51dd23..14685094c1 100644 --- a/source/frontend/src/components/propertySelector/MapClickMonitor.tsx +++ b/source/frontend/src/components/propertySelector/MapClickMonitor.tsx @@ -1,3 +1,5 @@ +import { LatLngLiteral } from 'leaflet'; + import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext'; import useDraftMarkerSynchronizer from '@/hooks/useDraftMarkerSynchronizer'; import { usePrevious } from '@/hooks/usePrevious'; @@ -8,7 +10,11 @@ import { LocationFeatureDataset } from '../common/mapFSM/useLocationFeatureLoade interface IMapClickMonitorProps { addProperty: (property: LocationFeatureDataset) => void; - repositionProperty: (property: LocationFeatureDataset, propertyIndex: number | null) => void; + repositionProperty: ( + property: LocationFeatureDataset, + latLng: LatLngLiteral, + propertyIndex: number | null, + ) => void; modifiedProperties: LocationFeatureDataset[]; // TODO: this should be just a list of lat longs selectedComponentId: string | null; } @@ -39,6 +45,7 @@ export const MapClickMonitor: React.FunctionComponent = ( if ( mapMachine.isRepositioning && + mapMachine.repositioningFeatureDataset && mapMachine.mapLocationFeatureDataset && previous !== mapMachine.mapLocationFeatureDataset && previous !== undefined && @@ -46,7 +53,8 @@ export const MapClickMonitor: React.FunctionComponent = ( selectedComponentId === mapMachine.mapLocationFeatureDataset.selectingComponentId) ) { repositionProperty( - mapMachine.mapLocationFeatureDataset, + mapMachine.repositioningFeatureDataset, + mapMachine.mapLocationFeatureDataset.location, mapMachine.repositioningPropertyIndex, ); } @@ -55,6 +63,7 @@ export const MapClickMonitor: React.FunctionComponent = ( mapMachine.isSelecting, mapMachine.isRepositioning, mapMachine.mapLocationFeatureDataset, + mapMachine.repositioningFeatureDataset, previous, ]); return <>; diff --git a/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx b/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx index 919273216c..056ec4d264 100644 --- a/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx +++ b/source/frontend/src/components/propertySelector/MapSelectorContainer.tsx @@ -1,3 +1,4 @@ +import { LatLngLiteral } from 'leaflet'; import { FunctionComponent, useCallback, useState } from 'react'; import { toast } from 'react-toastify'; @@ -21,6 +22,7 @@ export interface IMapSelectorContainerProps { addSelectedProperties: (properties: LocationFeatureDataset[]) => void; // TODO: This component should be providing the featureDataset instead of the IMapProperty. repositionSelectedProperty: ( property: LocationFeatureDataset, + latLng: LatLngLiteral, propertyIndex: number | null, ) => void; modifiedProperties: LocationFeatureDataset[]; // TODO: Figure out if this component really needs the entire propertyForm. It could be that only the lat long are needed. @@ -95,10 +97,11 @@ export const MapSelectorContainer: FunctionComponent }} onRepositionedProperty={( property: LocationFeatureDataset, + latLng: LatLngLiteral, propertyIndex: number | null, ) => { setLastSelectedProperty(property); - repositionSelectedProperty(property, propertyIndex); + repositionSelectedProperty(property, latLng, propertyIndex); }} selectedProperties={modifiedMapProperties} selectedComponentId={selectedComponentId} diff --git a/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx b/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx index b3c5028e02..bed15f2a21 100644 --- a/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx +++ b/source/frontend/src/components/propertySelector/map/PropertyMapSelectorFormView.tsx @@ -1,3 +1,5 @@ +import { LatLngLiteral } from 'leaflet'; + import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext'; import { LocationFeatureDataset } from '@/components/common/mapFSM/useLocationFeatureLoader'; import { Section } from '@/components/common/Section/Section'; @@ -8,7 +10,11 @@ import PropertyMapSelectorSubForm from './PropertyMapSelectorSubForm'; export interface IPropertyMapSelectorFormViewProps { onSelectedProperty: (property: LocationFeatureDataset) => void; - onRepositionedProperty: (property: LocationFeatureDataset, propertyIndex: number | null) => void; + onRepositionedProperty: ( + property: LocationFeatureDataset, + latLng: LatLngLiteral, + propertyIndex: number | null, + ) => void; lastSelectedProperty?: LocationFeatureDataset; selectedProperties: LocationFeatureDataset[]; selectedComponentId?: string | null; diff --git a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx index daf8970b31..9e416127e5 100644 --- a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx +++ b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx @@ -1,5 +1,8 @@ +import { booleanPointInPolygon, point } from '@turf/turf'; import axios, { AxiosError } from 'axios'; import { FieldArray, Formik, FormikProps } from 'formik'; +import { MultiPolygon, Polygon } from 'geojson'; +import { LatLngLiteral } from 'leaflet'; import isNumber from 'lodash/isNumber'; import { useContext, useRef, useState } from 'react'; import { Col, Row } from 'react-bootstrap'; @@ -18,7 +21,7 @@ import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress'; import { getCancelModalProps, useModalContext } from '@/hooks/useModalContext'; import { ApiGen_Concepts_File } from '@/models/api/generated/ApiGen_Concepts_File'; import { UserOverrideCode } from '@/models/api/UserOverrideCode'; -import { isValidId } from '@/utils'; +import { exists, isValidId } from '@/utils'; import { AddressForm, FileForm, PropertyForm } from '../../models'; import SidebarFooter from '../../SidebarFooter'; @@ -195,14 +198,26 @@ export const UpdateProperties: React.FunctionComponent = }} repositionSelectedProperty={( property: LocationFeatureDataset, + latLng: LatLngLiteral, index: number | null, ) => { - // Find property within formik values and reposition it based on incoming file marker position - if (isNumber(index) && index >= 0) { - const formProperty = formikProps.values.properties[index]; - const newFormProperty = new PropertyForm(formProperty); - newFormProperty.fileLocation = property.fileLocation; - replace(index, newFormProperty); + if (exists(property?.pimsFeature?.geometry)) { + const location = point([latLng.lng, latLng.lat]); + const boundary = property?.pimsFeature?.geometry as + | Polygon + | MultiPolygon; + + // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. + if (booleanPointInPolygon(location, boundary)) { + if (isNumber(index) && index >= 0) { + const formProperty = formikProps.values.properties[index]; + const updatedFormProperty = new PropertyForm(formProperty); + updatedFormProperty.fileLocation = latLng; + + // Find property within formik values and reposition it based on incoming file marker position + replace(index, updatedFormProperty); + } + } } }} modifiedProperties={formikProps.values.properties.map(p => From bead572cd928941ac7dcee8edc28745f96375e20 Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Fri, 26 Jul 2024 16:13:41 -0700 Subject: [PATCH 10/14] Code cleanup and fixes --- source/frontend/src/features/leases/models.ts | 5 +++ .../propertyPicker/LeasePropertySelector.tsx | 31 ++++++++++++++++++- .../SelectedPropertyRow.tsx | 27 +++++++++++----- .../add/AcquisitionPropertiesSubForm.tsx | 2 +- .../consolidation/AddConsolidationView.tsx | 2 ++ .../subdivision/AddSubdivisionView.tsx | 2 ++ source/frontend/src/mocks/mapFSM.mock.ts | 2 ++ 7 files changed, 62 insertions(+), 9 deletions(-) diff --git a/source/frontend/src/features/leases/models.ts b/source/frontend/src/features/leases/models.ts index 49b28319f6..cffaabfb08 100644 --- a/source/frontend/src/features/leases/models.ts +++ b/source/frontend/src/features/leases/models.ts @@ -270,6 +270,11 @@ export class FormLeaseProperty { this.areaUnitTypeCode = AreaUnitTypes.SquareMeters; } + public static fromFormLeaseProperty(baseModel?: Partial): FormLeaseProperty { + const model = Object.assign(new FormLeaseProperty(), baseModel); + return model; + } + public static fromApi(apiPropertyLease: ApiGen_Concepts_PropertyLease): FormLeaseProperty { const model = new FormLeaseProperty(apiPropertyLease.file?.id); model.property = PropertyForm.fromApi(apiPropertyLease); diff --git a/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx b/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx index ab8e9b661a..966d2abe06 100644 --- a/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx +++ b/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx @@ -1,4 +1,8 @@ +import { booleanPointInPolygon, point } from '@turf/turf'; import { FieldArray, FieldArrayRenderProps, FormikProps } from 'formik'; +import { MultiPolygon, Polygon } from 'geojson'; +import { LatLngLiteral } from 'leaflet'; +import isNumber from 'lodash/isNumber'; import { useCallback, useContext, useRef, useState } from 'react'; import { Col, Row } from 'react-bootstrap'; @@ -202,6 +206,31 @@ export const LeasePropertySelector: React.FunctionComponent { + if (exists(property?.pimsFeature?.geometry)) { + const location = point([latLng.lng, latLng.lat]); + const boundary = property?.pimsFeature?.geometry as + | Polygon + | MultiPolygon; + + // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. + if (booleanPointInPolygon(location, boundary)) { + if (isNumber(index) && index >= 0) { + const formProperty = formikProps.values.properties[index]; + const updatedFormProperty = + FormLeaseProperty.fromFormLeaseProperty(formProperty); + updatedFormProperty.property.fileLocation = latLng; + + // Find property within formik values and reposition it based on incoming file marker position + arrayHelpers.replace(index, updatedFormProperty); + } + } + } + }} modifiedProperties={LeaseFormModel.getPropertiesAsForm(values).map(p => p.toFeatureDataset(), )} @@ -220,7 +249,7 @@ export const LeasePropertySelector: React.FunctionComponent onRemoveClick(index)} nameSpace={`properties.${index}`} index={index} - property={property} + property={property.toFeatureDataset()} showSeparator={index < formikProps.values.properties.length - 1} /> ); diff --git a/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx b/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx index cf106c3133..28c9e27b28 100644 --- a/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx +++ b/source/frontend/src/features/leases/shared/propertyPicker/selectedPropertyList/SelectedPropertyRow.tsx @@ -1,21 +1,23 @@ import { FormikProps, getIn } from 'formik'; import { Col, Row } from 'react-bootstrap'; +import { RiDragMove2Line } from 'react-icons/ri'; -import { RemoveButton } from '@/components/common/buttons'; +import { RemoveButton, StyledIconButton } from '@/components/common/buttons'; import { InlineInput } from '@/components/common/form/styles'; +import { useMapStateMachine } from '@/components/common/mapFSM/MapStateMachineContext'; +import { LocationFeatureDataset } from '@/components/common/mapFSM/useLocationFeatureLoader'; import OverflowTip from '@/components/common/OverflowTip'; import AreaContainer from '@/components/measurements/AreaContainer'; import DraftCircleNumber from '@/components/propertySelector/selectedPropertyList/DraftCircleNumber'; import { FormLeaseProperty, LeaseFormModel } from '@/features/leases/models'; -import { PropertyForm } from '@/features/mapSideBar/shared/models'; import { withNameSpace } from '@/utils/formUtils'; -import { getPropertyName, NameSourceType } from '@/utils/mapPropertyUtils'; +import { featuresetToMapProperty, getPropertyName, NameSourceType } from '@/utils/mapPropertyUtils'; export interface ISelectedPropertyRowProps { index: number; nameSpace?: string; onRemove: () => void; - property: PropertyForm; + property: LocationFeatureDataset; formikProps: FormikProps; showSeparator?: boolean; } @@ -28,7 +30,8 @@ export const SelectedPropertyRow: React.FunctionComponent { - const propertyName = getPropertyName(property.toMapProperty()); + const mapMachine = useMapStateMachine(); + const propertyName = getPropertyName(featuresetToMapProperty(property)); let propertyIdentifier = ''; switch (propertyName.label) { case NameSourceType.PID: @@ -57,7 +60,7 @@ export const SelectedPropertyRow: React.FunctionComponent - + + + { + mapMachine.startReposition(property, index); + }} + > + + + - + diff --git a/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx b/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx index 52bdf268ab..e0432fb846 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx @@ -107,7 +107,7 @@ export const AcquisitionPropertiesSubForm: React.FunctionComponent remove(index)} nameSpace={`properties.${index}`} index={index} - property={property.toMapProperty()} + property={property.toFeatureDataset()} /> ))} {formikProps.values.properties.length === 0 && No Properties selected} diff --git a/source/frontend/src/features/mapSideBar/consolidation/AddConsolidationView.tsx b/source/frontend/src/features/mapSideBar/consolidation/AddConsolidationView.tsx index 310b7ab156..23b785d5d8 100644 --- a/source/frontend/src/features/mapSideBar/consolidation/AddConsolidationView.tsx +++ b/source/frontend/src/features/mapSideBar/consolidation/AddConsolidationView.tsx @@ -1,4 +1,5 @@ import { FieldArray, Formik, FormikHelpers, FormikProps } from 'formik'; +import noop from 'lodash/noop'; import { Tab } from 'react-bootstrap'; import { FaInfoCircle } from 'react-icons/fa'; import { toast } from 'react-toastify'; @@ -171,6 +172,7 @@ const AddConsolidationView: React.FunctionComponent< }} selectedComponentId="destination-property-selector" modifiedProperties={[]} + repositionSelectedProperty={noop} />
diff --git a/source/frontend/src/features/mapSideBar/subdivision/AddSubdivisionView.tsx b/source/frontend/src/features/mapSideBar/subdivision/AddSubdivisionView.tsx index 7acbc4cc60..c7cad7ab38 100644 --- a/source/frontend/src/features/mapSideBar/subdivision/AddSubdivisionView.tsx +++ b/source/frontend/src/features/mapSideBar/subdivision/AddSubdivisionView.tsx @@ -1,4 +1,5 @@ import { FieldArray, Formik, FormikHelpers, FormikProps } from 'formik'; +import noop from 'lodash/noop'; import { Tab } from 'react-bootstrap'; import { FaInfoCircle } from 'react-icons/fa'; import { toast } from 'react-toastify'; @@ -167,6 +168,7 @@ const AddSubdivisionView: React.FunctionComponent< modifiedProperties={values.destinationProperties.map(dp => PropertyForm.fromPropertyApi(dp).toFeatureDataset(), )} + repositionSelectedProperty={noop} /> {({ remove }) => ( diff --git a/source/frontend/src/mocks/mapFSM.mock.ts b/source/frontend/src/mocks/mapFSM.mock.ts index 943fb54c14..7e716ab8e6 100644 --- a/source/frontend/src/mocks/mapFSM.mock.ts +++ b/source/frontend/src/mocks/mapFSM.mock.ts @@ -32,6 +32,8 @@ export const mapMachineBaseMock: IMapStateMachineContext = { mapFeatureSelected: null, mapLocationSelected: null, mapLocationFeatureDataset: null, + repositioningFeatureDataset: null, + repositioningPropertyIndex: null, selectingComponentId: null, selectedFeatureDataset: null, showPopup: false, From 24ab0565be5029f99871e6972e219e00331dc82d Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Sun, 28 Jul 2024 16:03:01 -0700 Subject: [PATCH 11/14] Code cleanup --- .../propertyPicker/LeasePropertySelector.tsx | 37 ++++++++----------- .../add/AcquisitionPropertiesSubForm.tsx | 24 +++++++++++- .../form/DispositionPropertiesSubForm.tsx | 26 ++++++++++++- .../research/add/ResearchProperties.tsx | 26 ++++++++++++- .../update/properties/UpdateProperties.tsx | 33 +++++++---------- source/frontend/src/utils/mapPropertyUtils.ts | 21 +++++++++++ 6 files changed, 120 insertions(+), 47 deletions(-) diff --git a/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx b/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx index 966d2abe06..89eba7fb79 100644 --- a/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx +++ b/source/frontend/src/features/leases/shared/propertyPicker/LeasePropertySelector.tsx @@ -1,6 +1,4 @@ -import { booleanPointInPolygon, point } from '@turf/turf'; import { FieldArray, FieldArrayRenderProps, FormikProps } from 'formik'; -import { MultiPolygon, Polygon } from 'geojson'; import { LatLngLiteral } from 'leaflet'; import isNumber from 'lodash/isNumber'; import { useCallback, useContext, useRef, useState } from 'react'; @@ -20,7 +18,7 @@ import { useProperties } from '@/hooks/repositories/useProperties'; import useDeepCompareEffect from '@/hooks/util/useDeepCompareEffect'; import useDeepCompareMemo from '@/hooks/util/useDeepCompareMemo'; import { ApiGen_Concepts_PropertyView } from '@/models/api/generated/ApiGen_Concepts_PropertyView'; -import { exists, isValidId, isValidString } from '@/utils'; +import { exists, isLatLngInFeatureSetBoundary, isValidId, isValidString } from '@/utils'; import { FormLeaseProperty, LeaseFormModel } from '../../models'; import SelectedPropertyHeaderRow from './selectedPropertyList/SelectedPropertyHeaderRow'; @@ -207,28 +205,23 @@ export const LeasePropertySelector: React.FunctionComponent { - if (exists(property?.pimsFeature?.geometry)) { - const location = point([latLng.lng, latLng.lat]); - const boundary = property?.pimsFeature?.geometry as - | Polygon - | MultiPolygon; - - // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. - if (booleanPointInPolygon(location, boundary)) { - if (isNumber(index) && index >= 0) { - const formProperty = formikProps.values.properties[index]; - const updatedFormProperty = - FormLeaseProperty.fromFormLeaseProperty(formProperty); - updatedFormProperty.property.fileLocation = latLng; - - // Find property within formik values and reposition it based on incoming file marker position - arrayHelpers.replace(index, updatedFormProperty); - } - } + // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. + if ( + isNumber(index) && + index >= 0 && + isLatLngInFeatureSetBoundary(latLng, featureset) + ) { + const formProperty = formikProps.values.properties[index]; + const updatedFormProperty = + FormLeaseProperty.fromFormLeaseProperty(formProperty); + updatedFormProperty.property.fileLocation = latLng; + + // Find property within formik values and reposition it based on incoming file marker position + arrayHelpers.replace(index, updatedFormProperty); } }} modifiedProperties={LeaseFormModel.getPropertiesAsForm(values).map(p => diff --git a/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx b/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx index e0432fb846..1ef8579683 100644 --- a/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx +++ b/source/frontend/src/features/mapSideBar/acquisition/add/AcquisitionPropertiesSubForm.tsx @@ -1,4 +1,6 @@ import { FieldArray, FormikProps } from 'formik'; +import { LatLngLiteral } from 'leaflet'; +import isNumber from 'lodash/isNumber'; import { Col, Row } from 'react-bootstrap'; import LoadingBackdrop from '@/components/common/LoadingBackdrop'; @@ -9,6 +11,7 @@ import SelectedPropertyHeaderRow from '@/components/propertySelector/selectedPro import SelectedPropertyRow from '@/components/propertySelector/selectedPropertyList/SelectedPropertyRow'; import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress'; import { useModalContext } from '@/hooks/useModalContext'; +import { isLatLngInFeatureSetBoundary } from '@/utils'; import { AddressForm, PropertyForm } from '../../shared/models'; import { AcquisitionForm } from './models'; @@ -34,7 +37,7 @@ export const AcquisitionPropertiesSubForm: React.FunctionComponent - {({ push, remove }) => ( + {({ push, remove, replace }) => ( <> @@ -95,6 +98,25 @@ export const AcquisitionPropertiesSubForm: React.FunctionComponent { + // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. + if ( + isNumber(index) && + index >= 0 && + isLatLngInFeatureSetBoundary(latLng, featureset) + ) { + const formProperty = formikProps.values.properties[index]; + const updatedFormProperty = new PropertyForm(formProperty); + updatedFormProperty.fileLocation = latLng; + + // Find property within formik values and reposition it based on incoming file marker position + replace(index, updatedFormProperty); + } + }} modifiedProperties={values.properties.map(p => p.toFeatureDataset())} /> diff --git a/source/frontend/src/features/mapSideBar/disposition/form/DispositionPropertiesSubForm.tsx b/source/frontend/src/features/mapSideBar/disposition/form/DispositionPropertiesSubForm.tsx index 227f6010f6..05d3a873b2 100644 --- a/source/frontend/src/features/mapSideBar/disposition/form/DispositionPropertiesSubForm.tsx +++ b/source/frontend/src/features/mapSideBar/disposition/form/DispositionPropertiesSubForm.tsx @@ -1,4 +1,6 @@ import { FieldArray, FormikProps } from 'formik'; +import { LatLngLiteral } from 'leaflet'; +import isNumber from 'lodash/isNumber'; import { Col, Row } from 'react-bootstrap'; import LoadingBackdrop from '@/components/common/LoadingBackdrop'; @@ -9,6 +11,7 @@ import SelectedPropertyHeaderRow from '@/components/propertySelector/selectedPro import SelectedPropertyRow from '@/components/propertySelector/selectedPropertyList/SelectedPropertyRow'; import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress'; import { useModalContext } from '@/hooks/useModalContext'; +import { isLatLngInFeatureSetBoundary } from '@/utils'; import { AddressForm, PropertyForm } from '../../shared/models'; import { DispositionFormModel } from '../models/DispositionFormModel'; @@ -34,7 +37,7 @@ const DispositionPropertiesSubForm: React.FunctionComponent - {({ push, remove }) => ( + {({ push, remove, replace }) => ( <> @@ -89,6 +92,25 @@ const DispositionPropertiesSubForm: React.FunctionComponent { + // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. + if ( + isNumber(index) && + index >= 0 && + isLatLngInFeatureSetBoundary(latLng, featureset) + ) { + const formProperty = formikProps.values.fileProperties[index]; + const updatedFormProperty = new PropertyForm(formProperty); + updatedFormProperty.fileLocation = latLng; + + // Find property within formik values and reposition it based on incoming file marker position + replace(index, updatedFormProperty); + } + }} modifiedProperties={values.fileProperties.map(p => p.toFeatureDataset())} /> @@ -101,7 +123,7 @@ const DispositionPropertiesSubForm: React.FunctionComponent remove(index)} nameSpace={`fileProperties.${index}`} index={index} - property={property.toMapProperty()} + property={property.toFeatureDataset()} /> ))} {formikProps.values.fileProperties.length === 0 && ( diff --git a/source/frontend/src/features/mapSideBar/research/add/ResearchProperties.tsx b/source/frontend/src/features/mapSideBar/research/add/ResearchProperties.tsx index 5082d24b78..aa659ad0e6 100644 --- a/source/frontend/src/features/mapSideBar/research/add/ResearchProperties.tsx +++ b/source/frontend/src/features/mapSideBar/research/add/ResearchProperties.tsx @@ -1,4 +1,6 @@ import { FieldArray, useFormikContext } from 'formik'; +import { LatLngLiteral } from 'leaflet'; +import isNumber from 'lodash/isNumber'; import { Col, Row } from 'react-bootstrap'; import LoadingBackdrop from '@/components/common/LoadingBackdrop'; @@ -9,6 +11,7 @@ import SelectedPropertyHeaderRow from '@/components/propertySelector/selectedPro import SelectedPropertyRow from '@/components/propertySelector/selectedPropertyList/SelectedPropertyRow'; import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress'; import { useModalContext } from '@/hooks/useModalContext'; +import { isLatLngInFeatureSetBoundary } from '@/utils'; import { AddressForm, PropertyForm } from '../../shared/models'; import { ResearchForm } from './models'; @@ -30,7 +33,7 @@ const ResearchProperties: React.FC = ({ confirmBeforeA - {({ push, remove }) => ( + {({ push, remove, replace }) => ( <> @@ -77,6 +80,25 @@ const ResearchProperties: React.FC = ({ confirmBeforeA }); }, Promise.resolve()); }} + repositionSelectedProperty={( + featureset: LocationFeatureDataset, + latLng: LatLngLiteral, + index: number | null, + ) => { + // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. + if ( + isNumber(index) && + index >= 0 && + isLatLngInFeatureSetBoundary(latLng, featureset) + ) { + const formProperty = values.properties[index]; + const updatedFormProperty = new PropertyForm(formProperty); + updatedFormProperty.fileLocation = latLng; + + // Find property within formik values and reposition it based on incoming file marker position + replace(index, updatedFormProperty); + } + }} modifiedProperties={values.properties.map(p => p.toFeatureDataset())} /> @@ -89,7 +111,7 @@ const ResearchProperties: React.FC = ({ confirmBeforeA onRemove={() => remove(index)} nameSpace={`properties.${index}`} index={index} - property={property.toMapProperty()} + property={property.toFeatureDataset()} /> ))} {values.properties.length === 0 && No Properties selected} diff --git a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx index 9e416127e5..34c9fa5c00 100644 --- a/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx +++ b/source/frontend/src/features/mapSideBar/shared/update/properties/UpdateProperties.tsx @@ -1,7 +1,5 @@ -import { booleanPointInPolygon, point } from '@turf/turf'; import axios, { AxiosError } from 'axios'; import { FieldArray, Formik, FormikProps } from 'formik'; -import { MultiPolygon, Polygon } from 'geojson'; import { LatLngLiteral } from 'leaflet'; import isNumber from 'lodash/isNumber'; import { useContext, useRef, useState } from 'react'; @@ -21,7 +19,7 @@ import { useBcaAddress } from '@/features/properties/map/hooks/useBcaAddress'; import { getCancelModalProps, useModalContext } from '@/hooks/useModalContext'; import { ApiGen_Concepts_File } from '@/models/api/generated/ApiGen_Concepts_File'; import { UserOverrideCode } from '@/models/api/UserOverrideCode'; -import { exists, isValidId } from '@/utils'; +import { isLatLngInFeatureSetBoundary, isValidId } from '@/utils'; import { AddressForm, FileForm, PropertyForm } from '../../models'; import SidebarFooter from '../../SidebarFooter'; @@ -197,27 +195,22 @@ export const UpdateProperties: React.FunctionComponent = }, Promise.resolve()); }} repositionSelectedProperty={( - property: LocationFeatureDataset, + featureset: LocationFeatureDataset, latLng: LatLngLiteral, index: number | null, ) => { - if (exists(property?.pimsFeature?.geometry)) { - const location = point([latLng.lng, latLng.lat]); - const boundary = property?.pimsFeature?.geometry as - | Polygon - | MultiPolygon; + // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. + if ( + isNumber(index) && + index >= 0 && + isLatLngInFeatureSetBoundary(latLng, featureset) + ) { + const formProperty = formikProps.values.properties[index]; + const updatedFormProperty = new PropertyForm(formProperty); + updatedFormProperty.fileLocation = latLng; - // As long as the marker is repositioned within the boundary of the originally selected property simply reposition the marker without further notification. - if (booleanPointInPolygon(location, boundary)) { - if (isNumber(index) && index >= 0) { - const formProperty = formikProps.values.properties[index]; - const updatedFormProperty = new PropertyForm(formProperty); - updatedFormProperty.fileLocation = latLng; - - // Find property within formik values and reposition it based on incoming file marker position - replace(index, updatedFormProperty); - } - } + // Find property within formik values and reposition it based on incoming file marker position + replace(index, updatedFormProperty); } }} modifiedProperties={formikProps.values.properties.map(p => diff --git a/source/frontend/src/utils/mapPropertyUtils.ts b/source/frontend/src/utils/mapPropertyUtils.ts index 15b964e19e..24bc710910 100644 --- a/source/frontend/src/utils/mapPropertyUtils.ts +++ b/source/frontend/src/utils/mapPropertyUtils.ts @@ -1,3 +1,4 @@ +import { booleanPointInPolygon, point } from '@turf/turf'; import { Feature, FeatureCollection, @@ -279,3 +280,23 @@ export function latLngFromMapProperty( lng: Number(mapProperty?.fileLocation?.lng ?? mapProperty?.longitude ?? 0), }; } + +/** + * Takes a (Lat, Long) value and a FeatureSet and determines if the point resides inside the polygon. + * The polygon can be convex or concave. The function accounts for holes. + * + * @param latLng The input lat/long + * @param featureset The input featureset + * @returns true if the Point is inside the FeatureSet boundary; false if the Point is not inside the boundary + */ +export function isLatLngInFeatureSetBoundary( + latLng: LatLngLiteral, + featureset: LocationFeatureDataset, +): boolean { + const location = point([latLng.lng, latLng.lat]); + const boundary = (featureset?.pimsFeature?.geometry ?? featureset?.parcelFeature?.geometry) as + | Polygon + | MultiPolygon; + + return exists(boundary) && booleanPointInPolygon(location, boundary); +} From cc099aeb4cf00aada1a65f0565686dff8272d87e Mon Sep 17 00:00:00 2001 From: Alejandro Sanchez Date: Mon, 29 Jul 2024 17:30:38 -0700 Subject: [PATCH 12/14] Test updates --- .../MapSelectorContainer.test.tsx | 42 +++- .../SelectedPropertyRow.test.tsx | 96 +++++--- .../SelectedPropertyRow.test.tsx.snap | 69 +++++- .../SelectedPropertyRow.test.tsx | 68 +++--- .../SelectedPropertyRow.test.tsx.snap | 69 +++++- source/frontend/src/mocks/geometries.mock.ts | 49 ++++ .../src/utils/mapPropertyUtils.test.tsx | 218 +++++++++++++++++- source/frontend/src/utils/mapPropertyUtils.ts | 2 + 8 files changed, 517 insertions(+), 96 deletions(-) create mode 100644 source/frontend/src/mocks/geometries.mock.ts diff --git a/source/frontend/src/components/propertySelector/MapSelectorContainer.test.tsx b/source/frontend/src/components/propertySelector/MapSelectorContainer.test.tsx index 104c4febf2..fc493eb1ef 100644 --- a/source/frontend/src/components/propertySelector/MapSelectorContainer.test.tsx +++ b/source/frontend/src/components/propertySelector/MapSelectorContainer.test.tsx @@ -1,4 +1,3 @@ -import { act, screen } from '@testing-library/react'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { Formik } from 'formik'; @@ -6,14 +5,15 @@ import noop from 'lodash/noop'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; +import { useMapProperties } from '@/hooks/repositories/useMapProperties'; import { mockFAParcelLayerResponse, mockGeocoderOptions } from '@/mocks/index.mock'; -import { fillInput, render, RenderOptions, userEvent } from '@/utils/test-utils'; +import { mapMachineBaseMock } from '@/mocks/mapFSM.mock'; +import { act, fillInput, render, RenderOptions, screen, userEvent } from '@/utils/test-utils'; import { PropertyForm } from '../../features/mapSideBar/shared/models'; import MapSelectorContainer, { IMapSelectorContainerProps } from './MapSelectorContainer'; import { IMapProperty } from './models'; -import { getMockLocationFeatureDataset } from '@/mocks/featureset.mock'; -import { useMapProperties } from '@/hooks/repositories/useMapProperties'; +import { IMapStateMachineContext } from '../common/mapFSM/MapStateMachineContext'; const mockStore = configureMockStore([thunk]); @@ -22,6 +22,7 @@ const mockAxios = new MockAdapter(axios); const store = mockStore({}); const onSelectedProperties = vi.fn(); +const onRepositionSelectedProperty = vi.fn(); const testProperty: IMapProperty = { propertyId: 123, @@ -55,12 +56,14 @@ describe('MapSelectorContainer component', () => { , { ...renderOptions, store: store, + mockMapMachine: renderOptions.mockMapMachine ?? mapMachineBaseMock, }, ); @@ -295,4 +298,35 @@ describe('MapSelectorContainer component', () => { ); expect(toast[0]).toBeVisible(); }); + + it(`calls "repositionSelectedProperty" callback when file marker has been repositioned`, async () => { + const testMapMock: IMapStateMachineContext = { ...mapMachineBaseMock }; + const mapProperties = [ + PropertyForm.fromMapProperty({ ...testProperty, pid: '009-727-493' }).toFeatureDataset(), + ]; + + const { rerender } = setup({ + modifiedProperties: mapProperties, + mockMapMachine: testMapMock, + }); + + // simulate file marker repositioning via the map state machine + await act(async () => { + testMapMock.isRepositioning = true; + testMapMock.repositioningFeatureDataset = {} as any; + testMapMock.mapLocationFeatureDataset = {} as any; + }); + + rerender( + + + , + ); + + expect(onRepositionSelectedProperty).toHaveBeenCalled(); + }); }); diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.test.tsx b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.test.tsx index b5864eeec4..9f052e8383 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.test.tsx +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/SelectedPropertyRow.test.tsx @@ -2,10 +2,13 @@ import { Formik } from 'formik'; import { createMemoryHistory } from 'history'; import noop from 'lodash/noop'; +import { LocationFeatureDataset } from '@/components/common/mapFSM/useLocationFeatureLoader'; import { IMapProperty } from '@/components/propertySelector/models'; +import { PropertyForm } from '@/features/mapSideBar/shared/models'; import { mockLookups } from '@/mocks/lookups.mock'; +import { mapMachineBaseMock } from '@/mocks/mapFSM.mock'; import { lookupCodesSlice } from '@/store/slices/lookupCodes'; -import { act, renderAsync, RenderOptions, userEvent } from '@/utils/test-utils'; +import { act, render, RenderOptions, userEvent } from '@/utils/test-utils'; import SelectedPropertyRow, { ISelectedPropertyRowProps } from './SelectedPropertyRow'; @@ -17,16 +20,24 @@ const storeState = { const onRemove = vi.fn(); describe('SelectedPropertyRow component', () => { - const setup = async ( + const setup = ( renderOptions: RenderOptions & - Partial & { values?: { properties: IMapProperty[] } } = {}, + Partial & { + values?: { properties: IMapProperty[] }; + } = {}, ) => { // render component under test - const component = await renderAsync( + const utils = render( {() => ( @@ -36,52 +47,65 @@ describe('SelectedPropertyRow component', () => { ...renderOptions, store: storeState, history, + mockMapMachine: mapMachineBaseMock, }, ); - return { - component, - }; + return { ...utils }; }; + it('renders as expected', async () => { - const { component } = await setup({}); + const { asFragment } = setup({}); await act(async () => {}); - expect(component.asFragment()).toMatchSnapshot(); + expect(asFragment()).toMatchSnapshot(); }); - it('fires onRemove when remove button clicked', async () => { - const { - component: { getByTitle }, - } = await setup({}); + it('fires onRemove when remove button is clicked', async () => { + const { getByTitle } = setup({}); await act(async () => {}); const removeButton = getByTitle('remove'); userEvent.click(removeButton); expect(onRemove).toHaveBeenCalled(); }); + it('calls map machine when reposition button is clicked', async () => { + const { getByTitle } = setup({}); + await act(async () => {}); + const moveButton = getByTitle(/Move pin location/i); + userEvent.click(moveButton); + expect(mapMachineBaseMock.startReposition).toHaveBeenCalled(); + }); + it('displays pid', async () => { const mapProperties: IMapProperty[] = [ - { pid: '111111111', pin: '1234', planNumber: 'plan', latitude: 4, longitude: 5 }, + { + pid: '111111111', + pin: '1234', + planNumber: 'plan', + latitude: 4, + longitude: 5, + }, ]; - const { - component: { getByText }, - } = await setup({ + const { getByText } = setup({ values: { properties: mapProperties, }, - } as any); + }); await act(async () => {}); expect(getByText('PID: 111-111-111')).toBeVisible(); }); + it('falls back to pin', async () => { const mapProperties: IMapProperty[] = [ - { pin: '1234', planNumber: 'plan', latitude: 4, longitude: 5 }, + { + pin: '1234', + latitude: 4, + longitude: 5, + }, ]; - const { - component: { getByText }, - } = await setup({ + const { getByText } = setup({ values: { properties: mapProperties }, }); await act(async () => {}); @@ -89,34 +113,34 @@ describe('SelectedPropertyRow component', () => { }); it('falls back to plan number', async () => { - const mapProperties: IMapProperty[] = [{ planNumber: 'plan', latitude: 4, longitude: 5 }]; - const { - component: { getByText }, - } = await setup({ + const mapProperties: IMapProperty[] = [ + { + planNumber: 'plan', + latitude: 4, + longitude: 5, + }, + ]; + const { getByText } = setup({ values: { properties: mapProperties }, - } as any); + }); await act(async () => {}); expect(getByText('Plan #: plan')).toBeVisible(); }); it('falls back to lat/lng', async () => { const mapProperties: IMapProperty[] = [{ latitude: 4, longitude: 5 }]; - const { - component: { getByText }, - } = await setup({ + const { getByText } = setup({ values: { properties: mapProperties }, - } as any); + }); await act(async () => {}); expect(getByText('5.000000, 4.000000')).toBeVisible(); }); it('falls back to address', async () => { const mapProperties: IMapProperty[] = [{ address: 'a test address' }]; - const { - component: { getByText }, - } = await setup({ + const { getByText } = setup({ values: { properties: mapProperties }, - } as any); + }); await act(async () => {}); expect(getByText('Address: a test address')).toBeVisible(); }); diff --git a/source/frontend/src/components/propertySelector/selectedPropertyList/__snapshots__/SelectedPropertyRow.test.tsx.snap b/source/frontend/src/components/propertySelector/selectedPropertyList/__snapshots__/SelectedPropertyRow.test.tsx.snap index 4ec4d41c9c..55633728d8 100644 --- a/source/frontend/src/components/propertySelector/selectedPropertyList/__snapshots__/SelectedPropertyRow.test.tsx.snap +++ b/source/frontend/src/components/propertySelector/selectedPropertyList/__snapshots__/SelectedPropertyRow.test.tsx.snap @@ -148,19 +148,43 @@ exports[`SelectedPropertyRow component > renders as expected 1`] = ` } .c5.c5.btn { + background-color: unset; + border: none; +} + +.c5.c5.btn:hover, +.c5.c5.btn:focus, +.c5.c5.btn:active { + background-color: unset; + outline: none; + box-shadow: none; +} + +.c5.c5.btn svg { + -webkit-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.c5.c5.btn svg:hover { + -webkit-transition: all 0.3s ease-in; + transition: all 0.3s ease-in; +} + +.c6.c6.btn { + font-size: 1.4rem; color: #aaaaaa; -webkit-text-decoration: none; text-decoration: none; line-height: unset; } -.c5.c5.btn .text { +.c6.c6.btn .text { display: none; } -.c5.c5.btn:hover, -.c5.c5.btn:active, -.c5.c5.btn:focus { +.c6.c6.btn:hover, +.c6.c6.btn:active, +.c6.c6.btn:focus { color: #d8292f; -webkit-text-decoration: none; text-decoration: none; @@ -174,9 +198,9 @@ exports[`SelectedPropertyRow component > renders as expected 1`] = ` flex-direction: row; } -.c5.c5.btn:hover .text, -.c5.c5.btn:active .text, -.c5.c5.btn:focus .text { +.c6.c6.btn:hover .text, +.c6.c6.btn:active .text, +.c6.c6.btn:focus .text { display: inline; line-height: 2rem; } @@ -299,7 +323,7 @@ exports[`SelectedPropertyRow component > renders as expected 1`] = `
renders as expected 1`] = ` />
+
+ +
+
renders as expected 1`] = ` />
+
+ +
renders as expected 1`] = ` />
+
+ +
renders as expected 1`] = ` />
+
+ +
renders as expected 1`] = ` />
+
+ +
renders as expected when provided no pro />
+
+ +
renders as expected when provided no pro />
+
+ +
renders as expected 1`] = ` />
+
+ +