diff --git a/packages/clients/diplan/example/dev/index.html b/packages/clients/diplan/example/dev/index.html
index 4e229ff4e..e96d69b4f 100644
--- a/packages/clients/diplan/example/dev/index.html
+++ b/packages/clients/diplan/example/dev/index.html
@@ -17,17 +17,26 @@
diff --git a/packages/clients/diplan/example/dev/setup.js b/packages/clients/diplan/example/dev/setup.js
index 01a388f13..394a36256 100644
--- a/packages/clients/diplan/example/dev/setup.js
+++ b/packages/clients/diplan/example/dev/setup.js
@@ -35,11 +35,6 @@ export default (client, layerConf, config) => {
* However, we're working with a loaded object here, making the
* masterportalapi's `initializeLayerList` synchronous:
*/
- client.rawLayerList.initializeLayerList(layerConf)
-
- // TODO can we encapsulate do the upper statement in the core? people
- // don't really need to do that manually, do they?
-
client
.createMap({
// id of div to render in
@@ -60,8 +55,6 @@ export default (client, layerConf, config) => {
* https://dataport.github.io/polar/docs/diplan/client-diplan.html
*/
- // TODO expand example bindings
-
const actionPlus = document.getElementById('action-plus')
const actionMinus = document.getElementById('action-minus')
const actionToast = document.getElementById('action-toast')
@@ -71,6 +64,12 @@ export default (client, layerConf, config) => {
'action-address-search-filler'
)
const actionLasso = document.getElementById('action-lasso')
+ const actionCut = document.getElementById('action-cut-polygons')
+ const actionDuplicate = document.getElementById(
+ 'action-duplicate-polygons'
+ )
+ const actionMerge = document.getElementById('action-merge-polygons')
+ const activeExtendedDrawMode = document.getElementById('active-draw-mode')
actionPlus.onclick = () =>
mapInstance.$store.dispatch('plugin/zoom/increaseZoomLevel')
@@ -99,6 +98,17 @@ export default (client, layerConf, config) => {
)
actionLasso.onclick = () =>
mapInstance.$store.dispatch('plugin/draw/setMode', 'lasso')
+ actionCut.onclick = () =>
+ mapInstance.$store.dispatch('diplan/cutPolygons')
+ actionDuplicate.onclick = () =>
+ mapInstance.$store.dispatch('diplan/duplicatePolygons')
+ actionMerge.onclick = () =>
+ mapInstance.$store.dispatch('diplan/mergePolygons')
+ mapInstance.$store.watch(
+ (_, getters) => getters['diplan/activeDrawMode'],
+ (activeDrawMode) => (activeExtendedDrawMode.innerHTML = activeDrawMode),
+ { immediate: true }
+ )
const htmlRevisedDrawExport = document.getElementById(
'subscribed-revised-draw-export'
diff --git a/packages/clients/diplan/example/diplan-ui/config.js b/packages/clients/diplan/example/diplan-ui/config.js
new file mode 100644
index 000000000..61ef42d3c
--- /dev/null
+++ b/packages/clients/diplan/example/diplan-ui/config.js
@@ -0,0 +1,193 @@
+// service id map to avoid typos, ease renames
+const basemap = 'basemapde_farbe'
+const xplanwms = 'xplanwms'
+const xplanwfs = 'xplanwfs'
+const flurstuecke = 'flurstuecke'
+const bstgasleitung = 'bst_gasleitung'
+
+export default {
+ // masterportalAPI parameters
+ startResolution: 264.583190458,
+ startCenter: [561210, 5932600],
+ extent: [
+ 248651.73157077, 5227198.20287631, 928366.12236557, 6118661.62507136,
+ ],
+ // diplan-specific configuration example (see API.md)
+ diplan: {
+ mergeToMultiGeometries: true,
+ validateGeoJson: true,
+ metaServices: [
+ {
+ id: flurstuecke,
+ propertyNames: ['land', 'gemarkung', 'regbezirk', 'kreis', 'gemeinde'],
+ aggregationMode: 'unequal',
+ },
+ ],
+ },
+ // general POLAR parameters
+ locales: [
+ {
+ type: 'de',
+ resources: {
+ diplan: {
+ layers: {
+ [basemap]: 'BasemapDE',
+ [xplanwms]: 'XPlanWMS',
+ [xplanwfs]: 'XPlanSynWFS',
+ [flurstuecke]: 'Flurstücke',
+ [bstgasleitung]: 'BST Gasleitung',
+ },
+ attributions: {
+ [basemap]: `$t(diplan.layers.${basemap}) © GeoBasis-DE / BKG
CC BY 4.0`,
+ [xplanwms]: `$t(diplan.layers.${xplanwms}) © ???`,
+ [xplanwfs]: `$t(diplan.layers.${xplanwfs}) © ???`,
+ [flurstuecke]: `$t(diplan.layers.${flurstuecke}) © ???`,
+ [bstgasleitung]: `$t(diplan.layers.${bstgasleitung}) © ???`,
+ },
+ },
+ },
+ },
+ // TODO should we provide english locales?
+ ],
+ addressSearch: {
+ displayComponent: true,
+ searchMethods: [
+ {
+ queryParameters: {
+ searchAddress: true,
+ searchStreets: true,
+ searchHouseNumbers: true,
+ },
+ type: 'mpapi',
+ url: 'https://geodienste.hamburg.de/HH_WFS_GAGES?service=WFS&request=GetFeature&version=2.0.0',
+ },
+ ],
+ minLength: 3,
+ waitMs: 300,
+ },
+ layers: [
+ {
+ id: basemap,
+ visibility: true,
+ type: 'background',
+ name: `diplan.layers.${basemap}`,
+ },
+ {
+ id: xplanwms,
+ visibility: true,
+ type: 'mask',
+ name: `diplan.layers.${xplanwms}`,
+ },
+ {
+ id: xplanwfs,
+ visibility: false,
+ type: 'mask',
+ name: `diplan.layers.${xplanwfs}`,
+ },
+ {
+ id: flurstuecke,
+ visibility: false,
+ // TODO available from 7, but only starts loading from 8 - bug or skill issue?
+ minZoom: 7,
+ type: 'mask',
+ name: `diplan.layers.${flurstuecke}`,
+ },
+ {
+ id: bstgasleitung,
+ visibility: false,
+ type: 'mask',
+ name: `diplan.layers.${bstgasleitung}`,
+ },
+ ],
+ attributions: {
+ layerAttributions: [
+ {
+ id: basemap,
+ title: `diplan.attributions.${basemap}`,
+ },
+ {
+ id: xplanwms,
+ title: `diplan.attributions.${xplanwms}`,
+ },
+ {
+ id: xplanwfs,
+ title: `diplan.attributions.${xplanwfs}`,
+ },
+ {
+ id: flurstuecke,
+ title: `diplan.attributions.${flurstuecke}`,
+ },
+ {
+ id: bstgasleitung,
+ title: `diplan.attributions.${bstgasleitung}`,
+ },
+ ],
+ },
+ draw: {
+ enableOptions: true,
+ lassos: [
+ {
+ id: flurstuecke,
+ },
+ {
+ id: xplanwfs,
+ },
+ ],
+ measureOptions: {
+ metres: true,
+ kilometres: true,
+ hectares: true,
+ },
+ selectableDrawModes: ['Point', 'LineString', 'Circle', 'Text', 'Polygon'],
+ snapTo: [xplanwfs, flurstuecke],
+ textStyle: {
+ font: {
+ size: [10, 20, 30],
+ family: 'Arial',
+ },
+ },
+ style: {
+ fill: { color: 'rgb(51 117 212 / 50%)' },
+ stroke: {
+ color: '#3375d4',
+ width: 2,
+ },
+ circle: {
+ radius: 7,
+ fillColor: 'rgb(51 117 212 / 50%)',
+ },
+ },
+ },
+ export: {
+ displayComponent: true,
+ showJpg: false,
+ showPdf: false,
+ },
+ gfi: {
+ mode: 'bboxDot',
+ layers: {
+ [xplanwms]: {
+ geometry: true,
+ window: true,
+ properties: ['xpPlanName'],
+ },
+ [xplanwfs]: {
+ geometry: true,
+ window: true,
+ properties: ['name'],
+ },
+ },
+ coordinateSources: [
+ 'plugin/pins/transformedCoordinate',
+ 'plugin/pins/coordinatesAfterDrag',
+ ],
+ },
+ pins: {
+ toZoomLevel: 9,
+ movable: 'drag',
+ appearOnClick: {
+ show: true,
+ atZoomLevel: 0,
+ },
+ },
+}
diff --git a/packages/clients/diplan/example/diplan-ui/index.html b/packages/clients/diplan/example/diplan-ui/index.html
new file mode 100644
index 000000000..a5f9f34de
--- /dev/null
+++ b/packages/clients/diplan/example/diplan-ui/index.html
@@ -0,0 +1,116 @@
+
+
+
+
+
+ @polar/client-diplan
+
+
+
+
+
+ Testseite @polar/client-diplan
(POLAR UI)
+
+
+
+
+
+ Informationen aus Kartenklient
+ Subscriptions werden i.d.R. mit JSON-fähigen Objekten beantwortet. Die untere Tabelle stellt einige ausgewählte Karteninhalte als Text dar.
+
+
+
+ Schlüssel |
+ Wert (als Text) |
+ Kommentar |
+
+
+
+
+ diplan/revisedDrawExport |
+ uninitialized |
+ Der überarbeitete Export des Draw-Tools mit diplanspezifischen Anpassungen. |
+
+
+ diplan/revisionInProgress |
+ uninitialized |
+ Indikator, ob ein Ladevorgang für die weiteren Anpassungen der Draw-Ausagbe gerade läuft. |
+
+
+ diplan/simpleGeometryValidity |
+ uninitialized |
+ Simple-Geometry-Konformität |
+
+
+ plugin/export/exportedMap |
+ ![]() |
+ Screenshot des aktuellen Kartenausschnitts. Produzierbar durch Klick auf den unteren linken "Export"-Button. |
+
+
+ plugin/zoom/zoomLevel |
+ uninitialized |
+ Zoomstufe der OpenLayers/View als Number |
+
+
+ plugin/gfi/featureInformation |
+ uninitialized |
+ Wert der aktuellen GetFeatureInformation -Antwort(en) als Record<LayerId, Feature[]> |
+
+
+ plugin/draw/featureCollection |
+ uninitialized |
+ Die hergestellte Zeichnung als GeoJSON (ohne diplanspezifische Metainformationen und Modifikationen). |
+
+
+ plugin/... |
+ uninitialized |
+ Platz für weitere Beispiele |
+
+
+ plugin/... |
+ uninitialized |
+ Platz für weitere Beispiele |
+
+
+ plugin/... |
+ uninitialized |
+ Platz für weitere Beispiele |
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/clients/diplan/example/diplan-ui/setup.js b/packages/clients/diplan/example/diplan-ui/setup.js
new file mode 100644
index 000000000..d2e50a1fe
--- /dev/null
+++ b/packages/clients/diplan/example/diplan-ui/setup.js
@@ -0,0 +1,136 @@
+/* eslint-disable max-lines-per-function */
+
+import 'diplanung-style/src/scss/main'
+
+const geoJSON = {
+ type: 'FeatureCollection',
+ features: [
+ {
+ type: 'Feature',
+ properties: {},
+ geometry: {
+ type: 'Polygon',
+ coordinates: [
+ [
+ [553702.4519707427, 5926504.665153537],
+ [549799.849911481, 5938873.929307467],
+ [582674.3113259386, 5942313.510783426],
+ [572421.7126956752, 5930142.68402234],
+ [553702.4519707427, 5926504.665153537],
+ ],
+ ],
+ },
+ },
+ ],
+}
+
+/* this is an example setup function displaying how POLAR is instantiated
+ * you may do this in any other format as long as all required contents arrive
+ * in `initializeLayerList` and `createMap` */
+export default (client, layerConf, config) => {
+ /* The parameter may be a URL; in that case, a second parameter is a callback
+ * function that provides the `layerConf` object as first parameter.
+ * The code for that would look like this:
+ * client.rawLayerList.initializeLayerList("url", (layerConf) => {
+ * client.createMap({...})
+ * })
+ * However, we're working with a loaded object here, making the
+ * masterportalapi's `initializeLayerList` synchronous:
+ */
+ client.rawLayerList.initializeLayerList(layerConf)
+
+ // TODO can we encapsulate do the upper statement in the core? people
+ // don't really need to do that manually, do they?
+
+ client
+ .createMap({
+ // id of div to render in
+ containerId: 'polarstern',
+ /* see API.md for mapConfiguration options, especially the compiled
+ * version at https://dataport.github.io/polar/docs/diplan/client-diplan.html */
+ mapConfiguration: {
+ stylePath: '../../dist/polar-client.css',
+ layerConf,
+ ...config,
+ },
+ })
+ .then((mapInstance) => {
+ /*
+ * This is a binding example for the various data POLAR makes accessible.
+ * The example is not exhaustive and additional fields may be found in
+ * the API.md and nested documents in the compiled docs:
+ * https://dataport.github.io/polar/docs/diplan/client-diplan.html
+ */
+
+ // TODO expand example bindings
+
+ const actionPlus = document.getElementById('action-plus')
+ const actionMinus = document.getElementById('action-minus')
+ const actionToast = document.getElementById('action-toast')
+ const actionLoadGeoJson = document.getElementById('action-load-geojson')
+ const actionZoomToAll = document.getElementById('action-zoom-to-all')
+ const actionFillAddressSearch = document.getElementById(
+ 'action-address-search-filler'
+ )
+ const actionLasso = document.getElementById('action-lasso')
+
+ actionPlus.onclick = () =>
+ mapInstance.$store.dispatch('plugin/zoom/increaseZoomLevel')
+ actionMinus.onclick = () =>
+ mapInstance.$store.dispatch('plugin/zoom/decreaseZoomLevel')
+ actionToast.onclick = () =>
+ mapInstance.$store.dispatch('plugin/toast/addToast', {
+ type: 'success',
+ text: 'Dies ist eine Beispielnachricht.',
+ timeout: 3000,
+ })
+ actionLoadGeoJson.onclick = () => {
+ mapInstance.$store.dispatch('plugin/draw/addFeatures', {
+ geoJSON,
+ })
+ }
+ actionZoomToAll.onclick = () =>
+ mapInstance.$store.dispatch('plugin/draw/zoomToAllFeatures', {
+ margin: 10, // defaults to 20
+ })
+ actionFillAddressSearch.addEventListener('input', (e) =>
+ mapInstance.$store.dispatch('plugin/addressSearch/search', {
+ input: e.target.value,
+ autoselect: 'first',
+ })
+ )
+ actionLasso.onclick = () =>
+ mapInstance.$store.dispatch('plugin/draw/setMode', 'lasso')
+
+ const htmlZoom = document.getElementById('subscribed-zoom')
+ const htmlGfi = document.getElementById('subscribed-gfi')
+ const htmlExportA = document.getElementById('subscribed-export-a')
+ const htmlExportImg = document.getElementById('subscribed-export-img')
+ const htmlDraw = document.getElementById('subscribed-draw')
+
+ mapInstance.subscribe(
+ 'plugin/zoom/zoomLevel',
+ (zoomLevel) => (htmlZoom.innerHTML = zoomLevel)
+ )
+ mapInstance.subscribe(
+ 'plugin/gfi/featureInformation',
+ (v) => (htmlGfi.innerHTML = JSON.stringify(v, null, 2))
+ )
+ mapInstance.subscribe('plugin/export/exportedMap', (screenshot) => {
+ htmlExportImg.setAttribute('src', screenshot)
+ if (navigator.userAgent.toLowerCase().includes('firefox')) {
+ htmlExportImg.onclick = function () {
+ window.open(this.src, '_blank')
+ }
+ htmlExportA.onclick = () => false
+ } else {
+ htmlExportA.setAttribute('href', screenshot)
+ }
+ })
+ mapInstance.subscribe('plugin/draw/featureCollection', (geojson) => {
+ htmlDraw.innerHTML = JSON.stringify(geojson, null, 2)
+ })
+
+ window.mapInstance = mapInstance
+ })
+}
diff --git a/packages/clients/diplan/src/plugins/GeoEditing/components/GeoEditing.vue b/packages/clients/diplan/src/plugins/GeoEditing/components/GeoEditing.vue
new file mode 100644
index 000000000..02e3174f2
--- /dev/null
+++ b/packages/clients/diplan/src/plugins/GeoEditing/components/GeoEditing.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
diff --git a/packages/clients/diplan/src/plugins/GeoEditing/components/index.ts b/packages/clients/diplan/src/plugins/GeoEditing/components/index.ts
new file mode 100644
index 000000000..d087c4841
--- /dev/null
+++ b/packages/clients/diplan/src/plugins/GeoEditing/components/index.ts
@@ -0,0 +1 @@
+export { default as GeoEditing } from './GeoEditing.vue'
diff --git a/packages/clients/diplan/src/plugins/GeoEditing/index.ts b/packages/clients/diplan/src/plugins/GeoEditing/index.ts
new file mode 100644
index 000000000..178332135
--- /dev/null
+++ b/packages/clients/diplan/src/plugins/GeoEditing/index.ts
@@ -0,0 +1,16 @@
+import Vue from 'vue'
+import { PluginOptions } from '@polar/lib-custom-types'
+import { GeoEditing } from './components'
+import locales from './locales'
+// import { makeStoreModule } from './store'
+
+// TODO: Are additional configuration parameters needed?
+export default (options: PluginOptions) => (instance: Vue) =>
+ instance.$store.dispatch('addComponent', {
+ name: 'geoEditing',
+ plugin: GeoEditing,
+ // locales,
+ // TODO: Use storeModule when it is merged. Move it from the general diplan-store here.
+ // storeModule: makeStoreModule(),
+ options,
+ })
diff --git a/packages/clients/diplan/src/plugins/GeoEditing/locales.ts b/packages/clients/diplan/src/plugins/GeoEditing/locales.ts
new file mode 100644
index 000000000..b1c6ea436
--- /dev/null
+++ b/packages/clients/diplan/src/plugins/GeoEditing/locales.ts
@@ -0,0 +1 @@
+export default {}
diff --git a/packages/clients/diplan/src/polar-client.ts b/packages/clients/diplan/src/polar-client.ts
index 79955464b..930a16ff7 100644
--- a/packages/clients/diplan/src/polar-client.ts
+++ b/packages/clients/diplan/src/polar-client.ts
@@ -21,6 +21,7 @@ import iconMap from '../assets/dist/iconMap'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore | intentional, file is created precompilation (not versioned)
import cssVariables from '../assets/dist/cssVariables'
+import GeoEditing from './plugins/GeoEditing'
import locales from './locales'
// TODO use when implemented
@@ -35,6 +36,10 @@ console.log(`DiPlanKarten-POLAR-Client v${packageInfo.version}.`)
setLayout(NineLayout)
polarCore.addPlugins([
+ GeoEditing({
+ displayComponent: true,
+ layoutTag: NineLayoutTag.BOTTOM_MIDDLE,
+ }),
IconMenu({
displayComponent: true,
menus: [
diff --git a/packages/clients/diplan/src/store/geoEditing/cutPolygons.ts b/packages/clients/diplan/src/store/geoEditing/cutPolygons.ts
new file mode 100644
index 000000000..bdcd74a58
--- /dev/null
+++ b/packages/clients/diplan/src/store/geoEditing/cutPolygons.ts
@@ -0,0 +1,51 @@
+import { PolarActionContext } from '@polar/lib-custom-types'
+import Draw from 'ol/interaction/Draw'
+import { getSnaps } from '@polar/plugin-draw'
+import Snap from 'ol/interaction/Snap'
+import VectorSource from 'ol/source/Vector'
+import Feature from 'ol/Feature'
+import { DiplanGetters, DiplanState } from '../../types'
+
+export const cutPolygons = ({
+ commit,
+ dispatch,
+ rootGetters,
+}: PolarActionContext) => {
+ dispatch('plugin/draw/setMode', 'none', { root: true })
+ commit('setDrawMode', 'cut')
+
+ const drawSource: VectorSource = rootGetters['plugin/draw/drawSource']
+
+ const draw = new Draw({ type: 'LineString' })
+
+ draw.on('drawend', (e) => {
+ const cutLine = e.feature
+ const features = drawSource.getFeatures()
+ const nextFeatures = features.reduce((accumulator, feature) => {
+ // TODO cut feature with line if it's a polygon, add multiple features thereafter
+ // turf.polygonToLine(poly)
+ // cut lines with cutLine, then union with all affected
+ // turf.polygonize(unionedLines)
+ console.error(cutLine)
+ accumulator.push(feature)
+ return accumulator
+ }, [] as Feature[])
+ drawSource.clear()
+ drawSource.addFeatures(nextFeatures)
+
+ commit('setDrawMode', null)
+ })
+
+ dispatch(
+ 'plugin/draw/setInteractions',
+ [
+ draw,
+ ...getSnaps(
+ rootGetters.map,
+ rootGetters.configuration?.draw?.snapTo || []
+ ),
+ new Snap({ source: drawSource }),
+ ],
+ { root: true }
+ )
+}
diff --git a/packages/clients/diplan/src/store/geoEditing/duplicatePolygons.ts b/packages/clients/diplan/src/store/geoEditing/duplicatePolygons.ts
new file mode 100644
index 000000000..4e6817f26
--- /dev/null
+++ b/packages/clients/diplan/src/store/geoEditing/duplicatePolygons.ts
@@ -0,0 +1,12 @@
+import { PolarActionContext } from '@polar/lib-custom-types'
+import { DiplanGetters, DiplanState } from '../../types'
+
+export const duplicatePolygons = ({
+ commit,
+ dispatch,
+}: PolarActionContext) => {
+ dispatch('plugin/draw/setMode', 'none', { root: true })
+ commit('setDrawMode', 'duplicate')
+
+ // TODO on end: commit('setDrawMode', null)
+}
diff --git a/packages/clients/diplan/src/store/geoEditing/mergePolygons.ts b/packages/clients/diplan/src/store/geoEditing/mergePolygons.ts
new file mode 100644
index 000000000..3f0377052
--- /dev/null
+++ b/packages/clients/diplan/src/store/geoEditing/mergePolygons.ts
@@ -0,0 +1,12 @@
+import { PolarActionContext } from '@polar/lib-custom-types'
+import { DiplanGetters, DiplanState } from '../../types'
+
+export const mergePolygons = ({
+ commit,
+ dispatch,
+}: PolarActionContext) => {
+ dispatch('plugin/draw/setMode', 'none', { root: true })
+ commit('setDrawMode', 'merge')
+
+ // TODO on end: commit('setDrawMode', null)
+}
diff --git a/packages/clients/diplan/src/store/module.ts b/packages/clients/diplan/src/store/module.ts
index d1a1dd0c7..9d3d581a7 100644
--- a/packages/clients/diplan/src/store/module.ts
+++ b/packages/clients/diplan/src/store/module.ts
@@ -3,24 +3,16 @@ import {
generateSimpleGetters,
generateSimpleMutations,
} from '@repositoryname/vuex-generators'
-import { FeatureCollection } from 'geojson'
import debounce from 'lodash.debounce'
-import { DiplanGetters, DiplanState, GeometryType } from '../types'
-import { mergeToMultiGeometries } from './utils/mergeToMultiGeometries'
-import { validateGeoJson } from './utils/validateGeoJson'
-import { enrichWithMetaServices } from './utils/enrichWithMetaServices'
-
-let abortController: AbortController | null = null
-const drawFeatureCollection = 'plugin/draw/featureCollection'
-
-// FeatureCollection is compatible to stupid clone
-const cloneFeatureCollection = (
- // No GeometryCollection from Draw, hence the
- featureCollection: FeatureCollection
-): FeatureCollection =>
- JSON.parse(JSON.stringify(featureCollection))
+import { Mode } from '@polar/plugin-draw'
+import { DiplanGetters, DiplanState, ExtendedDrawMode } from '../types'
+import { drawFeatureCollectionSource, updateState } from './updateState'
+import { cutPolygons } from './geoEditing/cutPolygons'
+import { duplicatePolygons } from './geoEditing/duplicatePolygons'
+import { mergePolygons } from './geoEditing/mergePolygons'
const getInitialState = (): DiplanState => ({
+ drawMode: null,
revisionInProgress: false,
simpleGeometryValidity: true,
revisedDrawExport: { type: 'FeatureCollection', features: [] },
@@ -36,70 +28,15 @@ const diplanModule: PolarModule = {
actions: {
setupModule({ dispatch, rootGetters }) {
const debouncedUpdate = debounce(() => dispatch('updateState'), 50)
- this.watch(() => rootGetters[drawFeatureCollection], debouncedUpdate)
- },
- // complexity deemed acceptable, it's mostly chaining
- // eslint-disable-next-line max-lines-per-function
- updateState: async ({ commit, dispatch, rootGetters, getters }) => {
- commit('setRevisionInProgress', true)
-
- if (abortController) {
- abortController.abort()
- }
- const thisController = (abortController = new AbortController())
-
- // clone to prevent accidentally messing with the draw tool's data
- let revisedFeatureCollection = cloneFeatureCollection(
- rootGetters[drawFeatureCollection]
+ this.watch(
+ () => rootGetters[drawFeatureCollectionSource],
+ debouncedUpdate
)
-
- // merge first; relevant for both follow-up steps
- if (getters.configuration.mergeToMultiGeometries) {
- revisedFeatureCollection = mergeToMultiGeometries(
- revisedFeatureCollection
- )
- }
-
- if (getters.configuration.validateGeoJson) {
- commit(
- 'setSimpleGeometryValidity',
- validateGeoJson(revisedFeatureCollection)
- )
- }
-
- if (getters.configuration.metaServices.length) {
- try {
- await enrichWithMetaServices(
- revisedFeatureCollection,
- rootGetters.map,
- getters.configuration.metaServices,
- abortController.signal
- )
- } catch (e) {
- if (thisController.signal.aborted) {
- return
- }
- console.error(
- '@polar/client-diplan: An error occurred when trying to fetch meta service data for the given feature collection.',
- e
- )
- dispatch(
- 'plugin/toast/addToast',
- {
- type: 'warning',
- text: 'diplan.error.metaInformationRetrieval',
- timeout: 10000,
- },
- { root: true }
- )
- }
- }
-
- if (!thisController.signal.aborted) {
- commit('setRevisedDrawExport', revisedFeatureCollection)
- commit('setRevisionInProgress', false)
- }
},
+ cutPolygons,
+ duplicatePolygons,
+ mergePolygons,
+ updateState,
},
mutations: {
...generateSimpleMutations(getInitialState()),
@@ -113,6 +50,9 @@ const diplanModule: PolarModule = {
// @ts-expect-error | local override for client
...(rootGetters.configuration?.diplan || {}),
}),
+ // meant for internal usage in UI
+ activeDrawMode: (_, getters, ___, rootGetters): ExtendedDrawMode =>
+ getters.drawMode ?? (rootGetters['plugin/draw/mode'] as Mode),
},
}
diff --git a/packages/clients/diplan/src/store/utils/enrichWithMetaServices.ts b/packages/clients/diplan/src/store/updateState/enrichWithMetaServices.ts
similarity index 100%
rename from packages/clients/diplan/src/store/utils/enrichWithMetaServices.ts
rename to packages/clients/diplan/src/store/updateState/enrichWithMetaServices.ts
diff --git a/packages/clients/diplan/src/store/updateState/index.ts b/packages/clients/diplan/src/store/updateState/index.ts
new file mode 100644
index 000000000..abcbed825
--- /dev/null
+++ b/packages/clients/diplan/src/store/updateState/index.ts
@@ -0,0 +1,87 @@
+import { FeatureCollection } from 'geojson'
+import { PolarActionContext } from '@polar/lib-custom-types'
+import { DiplanGetters, DiplanState, GeometryType } from '../../types'
+import { mergeToMultiGeometries } from './mergeToMultiGeometries'
+import { validateGeoJson } from './validateGeoJson'
+import { enrichWithMetaServices } from './enrichWithMetaServices'
+
+let abortController: AbortController | null = null
+export const drawFeatureCollectionSource = 'plugin/draw/featureCollection'
+
+// FeatureCollection is compatible to stupid clone
+export const cloneFeatureCollection = (
+ // No GeometryCollection from Draw, hence the
+ featureCollection: FeatureCollection
+): FeatureCollection =>
+ JSON.parse(JSON.stringify(featureCollection))
+
+// complexity deemed acceptable, it's mostly chaining
+// eslint-disable-next-line max-lines-per-function
+export const updateState = async ({
+ commit,
+ dispatch,
+ rootGetters,
+ getters,
+}: PolarActionContext) => {
+ commit('setRevisionInProgress', true)
+
+ if (abortController) {
+ abortController.abort()
+ }
+ const thisController = (abortController = new AbortController())
+
+ // clone to prevent accidentally messing with the draw tool's data
+ let revisedFeatureCollection = cloneFeatureCollection(
+ rootGetters[drawFeatureCollectionSource]
+ )
+
+ // merge first; relevant for both follow-up steps
+ if (getters.configuration.mergeToMultiGeometries) {
+ revisedFeatureCollection = mergeToMultiGeometries(revisedFeatureCollection)
+ }
+
+ // TODO consider from turf:
+ // * cleanCoords
+ // * union? or even better, combine?
+ // * unkinkPolygon?
+
+ if (getters.configuration.validateGeoJson) {
+ commit(
+ 'setSimpleGeometryValidity',
+ validateGeoJson(revisedFeatureCollection)
+ )
+ }
+
+ if (getters.configuration.metaServices.length) {
+ try {
+ await enrichWithMetaServices(
+ revisedFeatureCollection,
+ rootGetters.map,
+ getters.configuration.metaServices,
+ abortController.signal
+ )
+ } catch (e) {
+ if (thisController.signal.aborted) {
+ return
+ }
+ console.error(
+ '@polar/client-diplan: An error occurred when trying to fetch meta service data for the given feature collection.',
+ e
+ )
+ dispatch(
+ 'plugin/toast/addToast',
+ {
+ type: 'warning',
+ text: 'diplan.error.metaInformationRetrieval',
+ timeout: 10000,
+ },
+ { root: true }
+ )
+ }
+ }
+
+ if (!thisController.signal.aborted) {
+ commit('setRevisedDrawExport', revisedFeatureCollection)
+ commit('setRevisionInProgress', false)
+ }
+}
diff --git a/packages/clients/diplan/src/store/utils/mergeToMultiGeometries.ts b/packages/clients/diplan/src/store/updateState/mergeToMultiGeometries.ts
similarity index 100%
rename from packages/clients/diplan/src/store/utils/mergeToMultiGeometries.ts
rename to packages/clients/diplan/src/store/updateState/mergeToMultiGeometries.ts
diff --git a/packages/clients/diplan/src/store/utils/validateGeoJson.ts b/packages/clients/diplan/src/store/updateState/validateGeoJson.ts
similarity index 100%
rename from packages/clients/diplan/src/store/utils/validateGeoJson.ts
rename to packages/clients/diplan/src/store/updateState/validateGeoJson.ts
diff --git a/packages/clients/diplan/src/types.ts b/packages/clients/diplan/src/types.ts
index 0a3edb7f1..1d3133a6e 100644
--- a/packages/clients/diplan/src/types.ts
+++ b/packages/clients/diplan/src/types.ts
@@ -1,3 +1,4 @@
+import { Mode } from '@polar/plugin-draw'
import { FeatureCollection, Geometry, GeometryCollection } from 'geojson'
export interface MetaService {
@@ -6,6 +7,9 @@ export interface MetaService {
aggregationMode?: 'unequal' | 'all'
}
+export type DrawExtension = 'cut' | 'duplicate' | 'merge'
+export type ExtendedDrawMode = Mode | DrawExtension
+
export interface DiplanConfiguration {
mergeToMultiGeometries?: boolean
validateGeoJson?: boolean
@@ -13,6 +17,7 @@ export interface DiplanConfiguration {
}
export interface DiplanState {
+ drawMode: DrawExtension | null
revisionInProgress: boolean
simpleGeometryValidity: true
revisedDrawExport: FeatureCollection
@@ -20,6 +25,7 @@ export interface DiplanState {
export interface DiplanGetters extends DiplanState {
configuration: Required
+ activeDrawMode: ExtendedDrawMode
}
export type GeometryType = Exclude
diff --git a/packages/clients/diplan/vite.config.js b/packages/clients/diplan/vite.config.js
index a6845325f..878e58e14 100644
--- a/packages/clients/diplan/vite.config.js
+++ b/packages/clients/diplan/vite.config.js
@@ -2,6 +2,11 @@ import { getClientConfig } from '../../../viteConfigs'
export default getClientConfig({
root: 'example',
+ server: {
+ fs: {
+ strict: false,
+ },
+ },
build: {
outDir: '../dist',
lib: {
diff --git a/packages/core/src/utils/createMap/pullPolarStyleToShadow.ts b/packages/core/src/utils/createMap/pullPolarStyleToShadow.ts
index ee300a476..345e20d74 100644
--- a/packages/core/src/utils/createMap/pullPolarStyleToShadow.ts
+++ b/packages/core/src/utils/createMap/pullPolarStyleToShadow.ts
@@ -10,7 +10,18 @@ export const pullPolarStyleToShadow = (
const polarStylesheets = stylesheets.filter((el) =>
el.getAttribute('data-vite-dev-id')
)
- polarStylesheets.forEach((style) => shadowRoot.appendChild(style))
+ // TODO: This is a temporary workaround until diplanung-style provides the rule this way; this should not find it's way onto main
+ // TODO: For prod mode, add the :host to the polar.css by hand
+ polarStylesheets.forEach((style) => {
+ if (
+ style.attributes['data-vite-dev-id'].textContent.endsWith(
+ 'diplanung-style/src/scss/main.scss'
+ )
+ ) {
+ style.textContent = `:host,${style.textContent}`
+ }
+ shadowRoot.appendChild(style)
+ })
} else {
const link = document.createElement('link')
link.href = stylePath
diff --git a/packages/plugins/Draw/CHANGELOG.md b/packages/plugins/Draw/CHANGELOG.md
index 81733adaf..fbcf48f25 100644
--- a/packages/plugins/Draw/CHANGELOG.md
+++ b/packages/plugins/Draw/CHANGELOG.md
@@ -6,6 +6,9 @@
- Feature: Add a `"snapTo"` key to the configuration that allows defining vector layers to snap to while drawing, editing, and translating.
- Feature: Add a lasso mode that allows copying up features from a vector layer that are contained within the user's hand drawn polygon. This also adds the fields `addLoading`, `removeLoading`, and `toastAction` for usage in the `lassos`.
- Feature: Interactions requiring dragging are now marked with the POLAR flag `_isPolarDragLikeInteraction`.
+- Feature: An action `setInteractions` has been added to allow clients to bring their own geometry operations.
+- Feature: Expose the `getSnaps` function. Intended to be used together with the `setInteractions` action only.
+- Feature: Expose `Mode` type of Draw plugin for client-side extensions.
## 3.0.0
diff --git a/packages/plugins/Draw/README.md b/packages/plugins/Draw/README.md
index a0e53a605..009e69c1d 100644
--- a/packages/plugins/Draw/README.md
+++ b/packages/plugins/Draw/README.md
@@ -171,6 +171,8 @@ The returned featureCollection is a [GeoJSON](https://geojson.org/) FeatureColle
### Actions
+#### addFeatures
+
```js
map.$store.dispatch('plugin/draw/addFeatures', {
geoJSON: {
@@ -196,6 +198,23 @@ It adds the given features from the FeatureCollection to the drawn source. It is
It's important to note that the GeoJSON Standard [RFC7946](https://www.rfc-editor.org/rfc/rfc7946) does not support circles.
To add a circle to the map, it is assumed, that a feature being a circle has a property `radius` together with a point geometry.
+#### setInteractions
+
+>⚠️ This is a complex action and can not be used without further implementation in a client.
+
+```js
+// `yourInteractions` is of type `ol/interaction[]`
+map.$store.dispatch('plugin/draw/setInteractions', yourInteractions)
+```
+
+Allows interactions of other sources to take precedence without overlapping with the Draw interactions. By using this action, it is ensured both the draw interactions are cleared and the outside interactions are clearable by the draw tool. With this, you may write client-specific geometry operations of arbitrarily specific nature. Please mind that the Draw tool will display that no draw mode is active during this time, requiring you to provide a different UI.
+
+When setting drag-like interactions, add `_isPolarDragLikeInteraction` to the interaction. Regarding this, refer to the chapter "Special Flags" in the core documentation.
+
+If you need additional code executed on removal, you may add a method to `yourInteraction._onRemove`. It will be called on clean-up without parameters.
+
+#### zoomToFeature
+
```js
map.$store.dispatch('plugin/draw/zoomToFeature', {
index: 42, // defaults to 0
@@ -205,6 +224,8 @@ map.$store.dispatch('plugin/draw/zoomToFeature', {
Calling the action `zoomToFeature` zooms to the feature with position `index`, if given, and fits the map view around it with a padding of size `margin` in every direction of the feature.
+#### zoomToAllFeatures
+
```js
map.$store.dispatch('plugin/draw/zoomToAllFeatures', {
margin: 420, // defaults to 20
diff --git a/packages/plugins/Draw/src/index.ts b/packages/plugins/Draw/src/index.ts
index dc48c4238..e56504332 100644
--- a/packages/plugins/Draw/src/index.ts
+++ b/packages/plugins/Draw/src/index.ts
@@ -5,6 +5,9 @@ import { Draw } from './components'
import locales from './locales'
import { makeStoreModule } from './store'
+export { getSnaps } from './store/createInteractions/getSnaps'
+export type { Mode } from './types'
+
// NOTE: Currently no options are specified here, variable is kept for integrity until options are needed
export default (options: DrawConfiguration) => (instance: Vue) =>
instance.$store.dispatch('addComponent', {
diff --git a/packages/plugins/Draw/src/store/actions.ts b/packages/plugins/Draw/src/store/actions.ts
index c058fd48b..a7c1f4706 100644
--- a/packages/plugins/Draw/src/store/actions.ts
+++ b/packages/plugins/Draw/src/store/actions.ts
@@ -59,6 +59,18 @@ export const makeActions = () => {
commit('setDrawMode', drawMode)
dispatch('updateInteractions')
},
+ /** Please consult the README.md before usage. */
+ async setInteractions(
+ { dispatch, rootGetters },
+ newInteractions: Interaction[]
+ ) {
+ dispatch('setMode', 'none')
+ await dispatch('updateInteractions')
+ interactions = newInteractions
+ interactions.forEach((interaction) =>
+ rootGetters.map.addInteraction(interaction)
+ )
+ },
setMeasureMode({ commit, dispatch }, measureMode: MeasureMode) {
commit('setMeasureMode', measureMode)
dispatch('updateInteractions')
diff --git a/packages/plugins/Draw/src/store/createInteractions/createDrawInteractions.ts b/packages/plugins/Draw/src/store/createInteractions/createDrawInteractions.ts
index c23d13b18..35b46c0d7 100644
--- a/packages/plugins/Draw/src/store/createInteractions/createDrawInteractions.ts
+++ b/packages/plugins/Draw/src/store/createInteractions/createDrawInteractions.ts
@@ -3,7 +3,7 @@ import Interaction from 'ol/interaction/Interaction'
import { PolarActionContext } from '@polar/lib-custom-types'
import { CreateInteractionsPayload, DrawGetters, DrawState } from '../../types'
import createDrawStyle from '../../utils/createDrawStyle'
-import { getSchnaps } from './getSnaps'
+import { getSnaps } from './getSnaps'
export default function (
{
@@ -45,10 +45,7 @@ export default function (
draw.on('drawend', (e) => e.feature.setStyle(style))
return [
draw,
- ...getSchnaps(
- rootGetters.map,
- rootGetters.configuration?.draw?.snapTo || []
- ),
+ ...getSnaps(rootGetters.map, rootGetters.configuration?.draw?.snapTo || []),
new Snap({ source: drawSource }),
]
}
diff --git a/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts b/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts
index b81776158..02913301c 100644
--- a/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts
+++ b/packages/plugins/Draw/src/store/createInteractions/createModifyInteractions.ts
@@ -4,7 +4,7 @@ import { PolarActionContext } from '@polar/lib-custom-types'
import { Collection, Feature, Map } from 'ol'
import { CreateInteractionsPayload, DrawGetters, DrawState } from '../../types'
import { makeLocalSelector } from './localSelector'
-import { getSchnaps } from './getSnaps'
+import { getSnaps } from './getSnaps'
const createModify = (
map: Map,
@@ -76,10 +76,7 @@ export default function (
})
return [
createModify(rootGetters.map, drawLayer),
- ...getSchnaps(
- rootGetters.map,
- rootGetters.configuration?.draw?.snapTo || []
- ),
+ ...getSnaps(rootGetters.map, rootGetters.configuration?.draw?.snapTo || []),
new Snap({ source: drawSource }),
select,
]
diff --git a/packages/plugins/Draw/src/store/createInteractions/createTextInteractions.ts b/packages/plugins/Draw/src/store/createInteractions/createTextInteractions.ts
index 3c81a6cba..acbf2f703 100644
--- a/packages/plugins/Draw/src/store/createInteractions/createTextInteractions.ts
+++ b/packages/plugins/Draw/src/store/createInteractions/createTextInteractions.ts
@@ -4,7 +4,7 @@ import VectorSource from 'ol/source/Vector'
import { DrawConfiguration, PolarActionContext } from '@polar/lib-custom-types'
import { DrawGetters, DrawState } from '../../types'
import { createTextStyle } from '../../utils/createTextStyle'
-import { getSchnaps } from './getSnaps'
+import { getSnaps } from './getSnaps'
interface CreateTextInteractionsPayload {
textInput: DrawState['textInput']
@@ -50,10 +50,7 @@ export default function (
})
return [
draw,
- ...getSchnaps(
- rootGetters.map,
- rootGetters.configuration?.draw?.snapTo || []
- ),
+ ...getSnaps(rootGetters.map, rootGetters.configuration?.draw?.snapTo || []),
new Snap({ source: drawSource }),
]
}
diff --git a/packages/plugins/Draw/src/store/createInteractions/createTranslateInteractions.ts b/packages/plugins/Draw/src/store/createInteractions/createTranslateInteractions.ts
index b88459cbf..de143cdfa 100644
--- a/packages/plugins/Draw/src/store/createInteractions/createTranslateInteractions.ts
+++ b/packages/plugins/Draw/src/store/createInteractions/createTranslateInteractions.ts
@@ -4,7 +4,7 @@ import { PolarActionContext } from '@polar/lib-custom-types'
import { Collection, Feature, Map } from 'ol'
import { CreateInteractionsPayload, DrawGetters, DrawState } from '../../types'
import { makeLocalSelector } from './localSelector'
-import { getSchnaps } from './getSnaps'
+import { getSnaps } from './getSnaps'
const createTranslate = (
map: Map,
@@ -40,10 +40,7 @@ export default function (
): Interaction[] {
return [
createTranslate(rootGetters.map, drawLayer),
- ...getSchnaps(
- rootGetters.map,
- rootGetters.configuration?.draw?.snapTo || []
- ),
+ ...getSnaps(rootGetters.map, rootGetters.configuration?.draw?.snapTo || []),
new Snap({ source: drawSource }),
]
}
diff --git a/packages/plugins/Draw/src/store/createInteractions/getSnaps.ts b/packages/plugins/Draw/src/store/createInteractions/getSnaps.ts
index df82aaa3e..6e0edbf32 100644
--- a/packages/plugins/Draw/src/store/createInteractions/getSnaps.ts
+++ b/packages/plugins/Draw/src/store/createInteractions/getSnaps.ts
@@ -3,7 +3,7 @@ import { Snap } from 'ol/interaction'
import VectorLayer from 'ol/layer/Vector'
import VectorSource from 'ol/source/Vector'
-export const getSchnaps = (map: Map, snapIds: string[]): Snap[] =>
+export const getSnaps = (map: Map, snapIds: string[]): Snap[] =>
snapIds.reduce((accumulator, layerId) => {
const source = (
map