From 786610e523665c329eeaad005006ac2a50d62ad7 Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Wed, 6 Sep 2023 18:44:21 +0200 Subject: [PATCH 1/3] feat: `useModify` hook --- src/hooks/useModify.ts | 176 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/hooks/useModify.ts diff --git a/src/hooks/useModify.ts b/src/hooks/useModify.ts new file mode 100644 index 00000000..117cffd7 --- /dev/null +++ b/src/hooks/useModify.ts @@ -0,0 +1,176 @@ +import OlCollection from 'ol/Collection'; +import { singleClick } from 'ol/events/condition'; +import OlFeature from 'ol/Feature'; +import OlGeometry from 'ol/geom/Geometry'; +import Modify, { ModifyEvent, Options as ModifyOptions } from 'ol/interaction/Modify'; +import Translate, { Options as TranslateOptions, TranslateEvent } from 'ol/interaction/Translate'; +import OlVectorLayer from 'ol/layer/Vector'; +import OlVectorSource from 'ol/source/Vector'; +import {useMemo, useRef} from 'react'; + +import { DigitizeUtil } from '../Util/DigitizeUtil'; +import useMap from './useMap'; +import {useOlInteraction} from './useOlInteraction'; +import {useOlListener} from './useOlListener'; +import {usePropOrDefault} from './usePropOrDefault'; +import {useSelectFeatures, UseSelectFeaturesProps} from './useSelectFeatures'; + +interface OwnProps { + /** + * Active state of interactions + */ + active: boolean; + /** + * Additional configuration object to apply to the ol.interaction.Modify. + * See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Modify-Modify.html + * for more information + * + * Note: The keys features, deleteCondition and style are handled internally + * and shouldn't be overwritten without any specific cause. + */ + modifyInteractionConfig?: Omit; + /** + * Additional configuration object to apply to the ol.interaction.Translate. + * See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-Translate.html + * for more information + * + * Note: The key feature is handled internally and shouldn't be overwritten + * without any specific cause. + */ + translateInteractionConfig?: Omit; + /** + * The vector layer which will be used for digitize features. + * The standard digitizeLayer can be retrieved via `DigitizeUtil.getDigitizeLayer(map)`. + */ + digitizeLayer?: OlVectorLayer>; + /** + * Listener function for the 'modifystart' event of an ol.interaction.Modify. + * See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Modify-ModifyEvent.html + * for more information. + */ + onModifyStart?: (event: ModifyEvent) => void; + /** + * Listener function for the 'modifyend' event of an ol.interaction.Modify. + * See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Modify-ModifyEvent.html + * for more information. + */ + onModifyEnd?: (event: ModifyEvent) => void; + /** + * Listener function for the 'translatestart' event of an ol.interaction.Translate. + * See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-TranslateEvent.html + * for more information. + */ + onTranslateStart?: (event: TranslateEvent) => void; + /** + * Listener function for the 'translateend' event of an ol.interaction.Translate. + * See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-TranslateEvent.html + * for more information. + */ + onTranslateEnd?: (event: TranslateEvent) => void; + /** + * Listener function for the 'translating' event of an ol.interaction.Translate. + * See https://openlayers.org/en/latest/apidoc/module-ol_interaction_Translate-TranslateEvent.html + * for more information. + */ + onTranslating?: (event: TranslateEvent) => void; +} + +export type UseModifyProps = OwnProps & Omit; + +export const useModify = ({ + active, + onFeatureSelect, + onModifyStart, + onModifyEnd, + onTranslateStart, + onTranslateEnd, + onTranslating, + digitizeLayer, + selectStyle, + selectInteractionConfig, + hitTolerance, + modifyInteractionConfig, + translateInteractionConfig +}: UseModifyProps) => { + const map = useMap(); + + const layer = usePropOrDefault( + digitizeLayer, + () => DigitizeUtil.getDigitizeLayer(map), + [map] + ); + + const featuresRef = useRef(new OlCollection()); + + const layers = useMemo(() => layer ? [layer] : [], [layer]); + + useSelectFeatures({ + clearAfterSelect: false, + onFeatureSelect, + active, + layers, + selectStyle, + selectInteractionConfig, + hitTolerance, + featuresCollection: featuresRef.current + }); + + const translateInteraction = useOlInteraction( + () => { + const newTranslateInteraction = new Translate({ + features: featuresRef.current, + ...translateInteractionConfig + }); + newTranslateInteraction.set('name', 'react-geo-translate-interaction'); + return newTranslateInteraction; + }, + [translateInteractionConfig], + active + ); + + const modifyInteraction = useOlInteraction( + () => { + const newModifyInteraction = new Modify({ + features: featuresRef.current, + deleteCondition: singleClick, + style: selectStyle ?? DigitizeUtil.DEFAULT_SELECT_STYLE, + ...modifyInteractionConfig + }); + newModifyInteraction.set('name', 'react-geo-modify-interaction'); + return newModifyInteraction; + }, + [modifyInteractionConfig], + active + ); + + useOlListener( + modifyInteraction, + i => i.on('modifystart', e => onModifyStart?.(e)), + [onModifyStart] + ); + + useOlListener( + modifyInteraction, + i => i.on('modifyend', e => onModifyEnd?.(e)), + [onModifyEnd] + ); + + useOlListener( + translateInteraction, + i => i.on('translatestart', e => onTranslateStart?.(e)), + [onTranslateStart] + ); + + useOlListener( + translateInteraction, + i => i.on('translateend', e => onTranslateEnd?.(e)), + [onTranslateEnd] + ); + + useOlListener( + translateInteraction, + i => i.on('translating', e => onTranslating?.(e)), + [onTranslating] + ); +}; From 605cb90fa0a40635a66df8536bc8215d755b74bc Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Wed, 6 Sep 2023 19:44:18 +0200 Subject: [PATCH 2/3] fix: clear collection on deactivate --- src/hooks/useModify.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/hooks/useModify.ts b/src/hooks/useModify.ts index 117cffd7..386f2d0a 100644 --- a/src/hooks/useModify.ts +++ b/src/hooks/useModify.ts @@ -6,7 +6,7 @@ import Modify, { ModifyEvent, Options as ModifyOptions } from 'ol/interaction/Mo import Translate, { Options as TranslateOptions, TranslateEvent } from 'ol/interaction/Translate'; import OlVectorLayer from 'ol/layer/Vector'; import OlVectorSource from 'ol/source/Vector'; -import {useMemo, useRef} from 'react'; +import {useEffect, useMemo, useRef} from 'react'; import { DigitizeUtil } from '../Util/DigitizeUtil'; import useMap from './useMap'; @@ -105,6 +105,12 @@ export const useModify = ({ const layers = useMemo(() => layer ? [layer] : [], [layer]); + useEffect(() => { + if (!active) { + featuresRef.current.clear(); + } + }, [active]); + useSelectFeatures({ clearAfterSelect: false, onFeatureSelect, From 52a6982818e25c72cad7d4c697a468a2712ea0d7 Mon Sep 17 00:00:00 2001 From: Andreas Schmitz Date: Wed, 13 Sep 2023 11:24:01 +0200 Subject: [PATCH 3/3] feat: add support vor mapbox vector tiles --- .github/workflows/on-pull-request.yml | 2 +- .github/workflows/on-push-main.yml | 2 +- package-lock.json | 14 ++++++++++ package.json | 7 ++--- .../BackgroundLayerChooser.tsx | 11 ++++++++ .../BackgroundLayerPreview.tsx | 27 ++++++++++++++----- 6 files changed, 52 insertions(+), 11 deletions(-) diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml index e04b7db0..b5336d07 100644 --- a/.github/workflows/on-pull-request.yml +++ b/.github/workflows/on-pull-request.yml @@ -29,7 +29,7 @@ jobs: ${{ runner.OS }}- - name: Install dependencies ⏬ - run: npm ci + run: npm ci --force - name: Lint code 💄 run: npm run lint diff --git a/.github/workflows/on-push-main.yml b/.github/workflows/on-push-main.yml index 637353ea..472b4ad9 100644 --- a/.github/workflows/on-push-main.yml +++ b/.github/workflows/on-push-main.yml @@ -32,7 +32,7 @@ jobs: ${{ runner.OS }}- - name: Install dependencies ⏬ - run: npm ci + run: npm ci --force - name: Lint code 💄 run: npm run lint diff --git a/package-lock.json b/package-lock.json index 1aba9840..2923f277 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "moment": "^2.29.4", "np": "^7.5.0", "ol": "^7.5.2", + "ol-mapbox-style": "^11.0.3", "prop-types": "^15.8.1", "react": "^18.0.0", "react-dom": "^18.1.0", @@ -13977,6 +13978,19 @@ } }, "node_modules/ol-mapbox-style": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-11.0.3.tgz", + "integrity": "sha512-jc3PX1kHtvq8e5jPYVh9WFAppAbLrndik9qBicIemlQKPz0oFeQ98D3ITub3wfwVXXVReqeOKa43NGOseUne1Q==", + "dev": true, + "dependencies": { + "@mapbox/mapbox-gl-style-spec": "^13.23.1", + "mapbox-to-css-font": "^2.4.1" + }, + "peerDependencies": { + "ol": ">=8.0.0 || >8.0.0-dev.0 < 8.0.0 || >=7.0.0 <=7.5.1" + } + }, + "node_modules/ol/node_modules/ol-mapbox-style": { "version": "10.7.0", "resolved": "https://registry.npmjs.org/ol-mapbox-style/-/ol-mapbox-style-10.7.0.tgz", "integrity": "sha512-S/UdYBuOjrotcR95Iq9AejGYbifKeZE85D9VtH11ryJLQPTZXZSW1J5bIXcr4AlAH6tyjPPHTK34AdkwB32Myw==", diff --git a/package.json b/package.json index f40e4c8f..c4161528 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ }, "homepage": "https://github.com/terrestris/react-util#readme", "peerDependencies": { + "ol": ">=7", "react": ">=17", - "react-dom": ">=17", - "ol": ">=7" + "react-dom": ">=17" }, "dependencies": { "@terrestris/base-util": "^1.0.1", @@ -48,7 +48,6 @@ "@babel/plugin-proposal-optional-chaining": "^7.13.12", "@babel/preset-env": "^7.14.1", "@babel/preset-react": "^7.13.13", - "eslint-plugin-simple-import-sort": "^10.0.0", "@babel/preset-typescript": "^7.13.0", "@cfaester/enzyme-adapter-react-18": "^0.7.1", "@terrestris/eslint-config-typescript": "^3.0.0", @@ -66,6 +65,7 @@ "core-js": "^3.12.0", "enzyme": "^3.11.0", "eslint": "^8.14.0", + "eslint-plugin-simple-import-sort": "^10.0.0", "jest": "^29.6.4", "jest-canvas-mock": "^2.5.2", "jest-environment-jsdom": "^29.6.4", @@ -75,6 +75,7 @@ "moment": "^2.29.4", "np": "^7.5.0", "ol": "^7.5.2", + "ol-mapbox-style": "^11.0.3", "prop-types": "^15.8.1", "react": "^18.0.0", "react-dom": "^18.1.0", diff --git a/src/BackgroundLayerChooser/BackgroundLayerChooser.tsx b/src/BackgroundLayerChooser/BackgroundLayerChooser.tsx index b09d7da1..60c3e173 100644 --- a/src/BackgroundLayerChooser/BackgroundLayerChooser.tsx +++ b/src/BackgroundLayerChooser/BackgroundLayerChooser.tsx @@ -1,11 +1,13 @@ import OlOverviewMap from 'ol/control/OverviewMap'; import OlLayerBase from 'ol/layer/Base'; +import LayerGroup from 'ol/layer/Group'; import OlLayerImage from 'ol/layer/Image'; import OlLayer from 'ol/layer/Layer'; import OlLayerTile from 'ol/layer/Tile'; import { ObjectEvent } from 'ol/Object'; import { getUid } from 'ol/util'; import OlView from 'ol/View'; +import { apply as applyMapboxStyle } from 'ol-mapbox-style'; import React, { useEffect, useRef, @@ -88,6 +90,15 @@ export const BackgroundLayerChooser: React.FC = ({ ovLayer = new OlLayerImage({ source: selectedLayer.getSource() }); + } else if (selectedLayer instanceof LayerGroup) { + if (selectedLayer.get('isVectorTile')) { + ovLayer = new LayerGroup(); + applyMapboxStyle(ovLayer, selectedLayer.get('url')); + } else { + ovLayer = new LayerGroup({ + layers: selectedLayer.getLayers() + }); + } } if (ovLayer && mapTarget.current) { const overViewControl = new OlOverviewMap({ diff --git a/src/BackgroundLayerPreview/BackgroundLayerPreview.tsx b/src/BackgroundLayerPreview/BackgroundLayerPreview.tsx index 5c642fa6..8829b49b 100644 --- a/src/BackgroundLayerPreview/BackgroundLayerPreview.tsx +++ b/src/BackgroundLayerPreview/BackgroundLayerPreview.tsx @@ -1,11 +1,15 @@ +import MapUtil from '@terrestris/ol-util/dist/MapUtil/MapUtil'; import { Coordinate } from 'ol/coordinate'; import OlLayerBase from 'ol/layer/Base'; +import LayerGroup from 'ol/layer/Group'; import OlLayerImage from 'ol/layer/Image'; import OlLayer from 'ol/layer/Layer'; import OlLayerTile from 'ol/layer/Tile'; +import OlLayerVector from 'ol/layer/Vector'; import OlMap from 'ol/Map'; import { getUid } from 'ol/util'; import OlView from 'ol/View'; +import { apply as applyMapboxStyle } from 'ol-mapbox-style'; import React, { useEffect,useState } from 'react'; import useMap from '../hooks/useMap'; @@ -51,10 +55,23 @@ export const BackgroundLayerPreview: React.FC = ({ previewLayer = new OlLayerTile({ source: layer.getSource() }); - } else if (layer instanceof OlLayerImage){ + } else if (layer instanceof OlLayerImage) { previewLayer = new OlLayerImage({ source: layer.getSource() }); + } else if (layer instanceof OlLayerVector) { + previewLayer = new OlLayerVector({ + source: layer.getSource() + }); + } else if (layer instanceof LayerGroup) { + if (layer.get('isVectorTile')) { + previewLayer = new LayerGroup(); + applyMapboxStyle(previewLayer, layer.get('url')); + } else { + previewLayer = new LayerGroup({ + layers: layer.getLayers() + }); + } } setPreviewMap(new OlMap({ @@ -98,8 +115,8 @@ export const BackgroundLayerPreview: React.FC = ({ }, [zoom, center]); const getBgLayersFromMap = (): OlLayer[] => { - return mainMap?.getLayerGroup().getLayers() - .getArray().filter(backgroundLayerFilter) as OlLayer[] || []; + return MapUtil.getAllLayers(mainMap) + .filter(backgroundLayerFilter) as OlLayer[] || []; }; const updateBgLayerVisibility = (evt: React.MouseEvent) => { @@ -110,7 +127,7 @@ export const BackgroundLayerPreview: React.FC = ({ return; } - const newBgLayer = mainMap?.getLayerGroup().getLayers().getArray() + const newBgLayer = MapUtil.getAllLayers(mainMap) .find(l => getUid(l) === layerId); if (!newBgLayer) { @@ -123,7 +140,6 @@ export const BackgroundLayerPreview: React.FC = ({ if (evt.type === 'click') { onClick(newBgLayer as OlLayer); } - }; const restoreBgLayerVisibility = () => { @@ -134,7 +150,6 @@ export const BackgroundLayerPreview: React.FC = ({ const uid = getUid(layer); const activeUid = getUid(activeLayer); const isActive = uid === activeUid; - if (!previewMap) { return <>; }