@open-pioneer/editing 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/{EditingWorkflowImpl.d.ts → EditingCreateWorkflowImpl.d.ts} +11 -18
- package/{EditingWorkflowImpl.js → EditingCreateWorkflowImpl.js} +80 -42
- package/EditingCreateWorkflowImpl.js.map +1 -0
- package/EditingServiceImpl.d.ts +6 -3
- package/EditingServiceImpl.js +33 -7
- package/EditingServiceImpl.js.map +1 -1
- package/EditingUpdateWorkflowImpl.d.ts +42 -0
- package/EditingUpdateWorkflowImpl.js +257 -0
- package/EditingUpdateWorkflowImpl.js.map +1 -0
- package/README.md +76 -11
- package/SaveFeaturesHandler.d.ts +9 -0
- package/SaveFeaturesHandler.js +18 -1
- package/SaveFeaturesHandler.js.map +1 -1
- package/api.d.ts +34 -9
- package/i18n/de.yaml +5 -3
- package/i18n/en.yaml +5 -3
- package/package.json +20 -10
- package/services.d.ts +1 -0
- package/style-utils.d.ts +17 -0
- package/style-utils.js +55 -0
- package/style-utils.js.map +1 -0
- package/EditingWorkflowImpl.js.map +0 -1
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { EventEmitter, createManualPromise } from '@open-pioneer/core';
|
|
2
|
+
import { TOPMOST_LAYER_Z } from '@open-pioneer/map';
|
|
3
|
+
import { Modify } from 'ol/interaction';
|
|
4
|
+
import VectorLayer from 'ol/layer/Vector';
|
|
5
|
+
import VectorSource from 'ol/source/Vector';
|
|
6
|
+
import GeoJSON from 'ol/format/GeoJSON';
|
|
7
|
+
import { unByKey } from 'ol/Observable';
|
|
8
|
+
import { Collection, Overlay } from 'ol';
|
|
9
|
+
import { createStyles } from './style-utils.js';
|
|
10
|
+
import { saveUpdatedFeature } from './SaveFeaturesHandler.js';
|
|
11
|
+
|
|
12
|
+
class EditingUpdateWorkflowImpl extends EventEmitter {
|
|
13
|
+
#waiter;
|
|
14
|
+
_httpService;
|
|
15
|
+
_intl;
|
|
16
|
+
_map;
|
|
17
|
+
_polygonStyle;
|
|
18
|
+
_vertexStyle;
|
|
19
|
+
_state;
|
|
20
|
+
_editLayerURL;
|
|
21
|
+
_featureId;
|
|
22
|
+
_initialFeature;
|
|
23
|
+
_editFeature;
|
|
24
|
+
_editingSource;
|
|
25
|
+
_editingLayer;
|
|
26
|
+
_modifyInteraction;
|
|
27
|
+
_olMap;
|
|
28
|
+
_mapContainer;
|
|
29
|
+
_tooltip;
|
|
30
|
+
_enterHandler;
|
|
31
|
+
_escapeHandler;
|
|
32
|
+
_error;
|
|
33
|
+
_interactionListener;
|
|
34
|
+
_mapListener;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
super();
|
|
37
|
+
this._httpService = options.httpService;
|
|
38
|
+
this._intl = options.intl;
|
|
39
|
+
this._polygonStyle = options.polygonStyle;
|
|
40
|
+
this._vertexStyle = options.vertexStyle;
|
|
41
|
+
this._map = options.map;
|
|
42
|
+
this._olMap = options.map.olMap;
|
|
43
|
+
this._state = "active:initialized";
|
|
44
|
+
this._editLayerURL = options.ogcApiFeatureLayerUrl;
|
|
45
|
+
this._initialFeature = options.feature.clone();
|
|
46
|
+
this._initialFeature.setId(options.feature.getId());
|
|
47
|
+
this._editFeature = options.feature.clone();
|
|
48
|
+
this._editFeature.setId(options.feature.getId());
|
|
49
|
+
this._editFeature.setStyle(
|
|
50
|
+
createStyles({
|
|
51
|
+
polygon: this._polygonStyle,
|
|
52
|
+
vertex: this._vertexStyle
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
this._editingSource = new VectorSource({
|
|
56
|
+
features: new Collection([this._editFeature])
|
|
57
|
+
});
|
|
58
|
+
this._editingLayer = new VectorLayer({
|
|
59
|
+
source: this._editingSource,
|
|
60
|
+
zIndex: TOPMOST_LAYER_Z,
|
|
61
|
+
properties: {
|
|
62
|
+
name: "editing-layer"
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
this._modifyInteraction = new Modify({
|
|
66
|
+
source: this._editingSource
|
|
67
|
+
});
|
|
68
|
+
this._tooltip = this._createTooltip(this._olMap);
|
|
69
|
+
this._enterHandler = (e) => {
|
|
70
|
+
if ((e.code === "Enter" || e.code === "NumpadEnter") && e.target === this._olMap.getTargetElement()) {
|
|
71
|
+
const updatedFeature = this._editingSource.getFeatures()[0];
|
|
72
|
+
if (!updatedFeature) {
|
|
73
|
+
throw Error("no updated feature found");
|
|
74
|
+
}
|
|
75
|
+
this._save(updatedFeature);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
this._escapeHandler = (e) => {
|
|
79
|
+
if (e.code === "Escape" && e.target === this._olMap.getTargetElement()) {
|
|
80
|
+
this.reset();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
this._interactionListener = [];
|
|
84
|
+
this._mapListener = [];
|
|
85
|
+
this._start();
|
|
86
|
+
}
|
|
87
|
+
getModifyInteraction() {
|
|
88
|
+
return this._modifyInteraction;
|
|
89
|
+
}
|
|
90
|
+
getState() {
|
|
91
|
+
return this._state;
|
|
92
|
+
}
|
|
93
|
+
_setState(state) {
|
|
94
|
+
this._state = state;
|
|
95
|
+
this.emit(state);
|
|
96
|
+
}
|
|
97
|
+
_save(feature) {
|
|
98
|
+
this._setState("active:saving");
|
|
99
|
+
const layerUrl = this._editLayerURL;
|
|
100
|
+
this._featureId = feature.getId()?.toString();
|
|
101
|
+
if (!this._featureId) {
|
|
102
|
+
this._destroy();
|
|
103
|
+
this._error = new Error("no feature id available");
|
|
104
|
+
this.#waiter?.reject(this._error);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const geometry = feature?.getGeometry();
|
|
108
|
+
if (!geometry) {
|
|
109
|
+
this._destroy();
|
|
110
|
+
this._error = new Error("no geometry available");
|
|
111
|
+
this.#waiter?.reject(this._error);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const projection = this._olMap.getView().getProjection();
|
|
115
|
+
const geoJson = new GeoJSON({
|
|
116
|
+
dataProjection: projection
|
|
117
|
+
});
|
|
118
|
+
const geoJSONGeometry = geoJson.writeGeometryObject(geometry, {
|
|
119
|
+
rightHanded: true,
|
|
120
|
+
decimals: 10
|
|
121
|
+
});
|
|
122
|
+
saveUpdatedFeature(
|
|
123
|
+
this._httpService,
|
|
124
|
+
layerUrl,
|
|
125
|
+
this._featureId,
|
|
126
|
+
geoJSONGeometry,
|
|
127
|
+
projection
|
|
128
|
+
).then((featureId) => {
|
|
129
|
+
this._destroy();
|
|
130
|
+
this.#waiter?.resolve({ featureId });
|
|
131
|
+
}).catch((err) => {
|
|
132
|
+
this._destroy();
|
|
133
|
+
this._error = new Error("Failed to save feature", { cause: err });
|
|
134
|
+
this.#waiter?.reject(this._error);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
_start() {
|
|
138
|
+
this._olMap.addLayer(this._editingLayer);
|
|
139
|
+
this._olMap.addInteraction(this._modifyInteraction);
|
|
140
|
+
const feature = this._editingSource.getFeatures()[0];
|
|
141
|
+
if (feature && !feature.getId()?.toString()) {
|
|
142
|
+
this._destroy();
|
|
143
|
+
this._error = new Error("no feature id available");
|
|
144
|
+
this.#waiter?.reject(this._error);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this._mapContainer = this._olMap.getTargetElement() ?? void 0;
|
|
148
|
+
if (this._mapContainer) {
|
|
149
|
+
this._mapContainer.addEventListener("keydown", this._enterHandler, false);
|
|
150
|
+
this._mapContainer.addEventListener("keydown", this._escapeHandler, false);
|
|
151
|
+
}
|
|
152
|
+
this._tooltip.element.classList.remove("editing-tooltip-hidden");
|
|
153
|
+
const click = this._map.olMap.on("click", (e) => {
|
|
154
|
+
const coordinate = e.coordinate;
|
|
155
|
+
const altKeyPressed = e.originalEvent.altKey;
|
|
156
|
+
const features = this._editingSource.getFeaturesAtCoordinate(coordinate);
|
|
157
|
+
if (altKeyPressed) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (features.length === 0) {
|
|
161
|
+
this.triggerSave();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
const modify = this._modifyInteraction.on("modifystart", () => {
|
|
165
|
+
this._setState("active:drawing");
|
|
166
|
+
});
|
|
167
|
+
const changedContainer = this._map.on("changed:container", () => {
|
|
168
|
+
this._mapContainer?.removeEventListener("keydown", this._enterHandler);
|
|
169
|
+
this._mapContainer?.removeEventListener("keydown", this._escapeHandler);
|
|
170
|
+
this._mapContainer = this._olMap.getTargetElement() ?? void 0;
|
|
171
|
+
if (this._mapContainer) {
|
|
172
|
+
this._mapContainer.addEventListener("keydown", this._enterHandler, false);
|
|
173
|
+
this._mapContainer.addEventListener("keydown", this._escapeHandler, false);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
this._interactionListener.push(click, modify);
|
|
177
|
+
this._mapListener.push(changedContainer);
|
|
178
|
+
}
|
|
179
|
+
reset() {
|
|
180
|
+
const geometry = this._initialFeature.getGeometry()?.clone();
|
|
181
|
+
const resetFeature = this._editingSource.getFeatures()[0];
|
|
182
|
+
if (!resetFeature) {
|
|
183
|
+
throw Error("no updated feature found");
|
|
184
|
+
}
|
|
185
|
+
resetFeature.setGeometry(geometry);
|
|
186
|
+
this._setState("active:initialized");
|
|
187
|
+
}
|
|
188
|
+
stop() {
|
|
189
|
+
this._destroy();
|
|
190
|
+
this.#waiter?.resolve(void 0);
|
|
191
|
+
}
|
|
192
|
+
_destroy() {
|
|
193
|
+
this._editingSource.clear();
|
|
194
|
+
this._olMap.removeLayer(this._editingLayer);
|
|
195
|
+
this._olMap.removeInteraction(this._modifyInteraction);
|
|
196
|
+
this._tooltip.destroy();
|
|
197
|
+
this._interactionListener.map((listener) => {
|
|
198
|
+
unByKey(listener);
|
|
199
|
+
});
|
|
200
|
+
this._mapListener.map((listener) => {
|
|
201
|
+
listener.destroy();
|
|
202
|
+
});
|
|
203
|
+
this._mapContainer?.removeEventListener("keydown", this._enterHandler);
|
|
204
|
+
this._mapContainer?.removeEventListener("keydown", this._escapeHandler);
|
|
205
|
+
this._setState("destroyed");
|
|
206
|
+
}
|
|
207
|
+
triggerSave() {
|
|
208
|
+
const feature = this._editingSource.getFeatures()[0];
|
|
209
|
+
if (!feature) {
|
|
210
|
+
throw Error("no updated feature found");
|
|
211
|
+
}
|
|
212
|
+
this._save(feature);
|
|
213
|
+
}
|
|
214
|
+
whenComplete() {
|
|
215
|
+
if (this._state === "destroyed") {
|
|
216
|
+
if (this._error) {
|
|
217
|
+
return Promise.reject(this._error);
|
|
218
|
+
} else {
|
|
219
|
+
if (this._featureId) {
|
|
220
|
+
return Promise.resolve({ featureId: this._featureId });
|
|
221
|
+
} else {
|
|
222
|
+
return Promise.resolve(void 0);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const manualPromise = this.#waiter ??= createManualPromise();
|
|
227
|
+
return manualPromise.promise;
|
|
228
|
+
}
|
|
229
|
+
_createTooltip(olMap) {
|
|
230
|
+
const element = document.createElement("div");
|
|
231
|
+
element.className = "editing-tooltip editing-tooltip-hidden";
|
|
232
|
+
element.textContent = this._intl.formatMessage({ id: "create.tooltip.deselect" });
|
|
233
|
+
const overlay = new Overlay({
|
|
234
|
+
element,
|
|
235
|
+
offset: [15, 0],
|
|
236
|
+
positioning: "center-left"
|
|
237
|
+
});
|
|
238
|
+
const pointerMove = olMap.on("pointermove", (evt) => {
|
|
239
|
+
if (evt.dragging) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
overlay.setPosition(evt.coordinate);
|
|
243
|
+
});
|
|
244
|
+
olMap.addOverlay(overlay);
|
|
245
|
+
return {
|
|
246
|
+
overlay,
|
|
247
|
+
element,
|
|
248
|
+
destroy() {
|
|
249
|
+
unByKey(pointerMove);
|
|
250
|
+
olMap.removeOverlay(overlay);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export { EditingUpdateWorkflowImpl };
|
|
257
|
+
//# sourceMappingURL=EditingUpdateWorkflowImpl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditingUpdateWorkflowImpl.js","sources":["EditingUpdateWorkflowImpl.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { EventEmitter, ManualPromise, createManualPromise } from \"@open-pioneer/core\";\nimport { MapModel, TOPMOST_LAYER_Z } from \"@open-pioneer/map\";\nimport { Modify } from \"ol/interaction\";\nimport VectorLayer from \"ol/layer/Vector\";\nimport VectorSource from \"ol/source/Vector\";\nimport { HttpService } from \"@open-pioneer/http\";\nimport { FlatStyle } from \"ol/style/flat\";\nimport Feature from \"ol/Feature\";\nimport GeoJSON from \"ol/format/GeoJSON\";\nimport GeoJSONGeometry from \"ol/format/GeoJSON\";\nimport GeoJSONGeometryCollection from \"ol/format/GeoJSON\";\nimport OlMap from \"ol/Map\";\nimport { Resource } from \"@open-pioneer/core\";\nimport { unByKey } from \"ol/Observable\";\nimport { EventsKey } from \"ol/events\";\nimport {\n EditingWorkflowEvents,\n EditingWorkflowState,\n EditingWorkflow,\n EditingWorkflowProps\n} from \"./api\";\nimport { Collection, Overlay } from \"ol\";\nimport { createStyles } from \"./style-utils\";\nimport { PackageIntl } from \"@open-pioneer/runtime\";\nimport { saveUpdatedFeature } from \"./SaveFeaturesHandler\";\n\n// Represents a tooltip rendered on the OpenLayers map\ninterface Tooltip extends Resource {\n overlay: Overlay;\n element: HTMLDivElement;\n}\n\nexport class EditingUpdateWorkflowImpl\n extends EventEmitter<EditingWorkflowEvents>\n implements EditingWorkflow\n{\n #waiter: ManualPromise<Record<string, string> | undefined> | undefined;\n\n private _httpService: HttpService;\n private _intl: PackageIntl;\n\n private _map: MapModel;\n private _polygonStyle: FlatStyle;\n private _vertexStyle: FlatStyle;\n private _state: EditingWorkflowState;\n private _editLayerURL: URL;\n private _featureId: string | undefined;\n\n private _initialFeature: Feature;\n private _editFeature: Feature;\n private _editingSource: VectorSource;\n private _editingLayer: VectorLayer<VectorSource>;\n private _modifyInteraction: Modify;\n private _olMap: OlMap;\n private _mapContainer: HTMLElement | undefined;\n private _tooltip: Tooltip;\n private _enterHandler: (e: KeyboardEvent) => void;\n private _escapeHandler: (e: KeyboardEvent) => void;\n\n private _error: Error | undefined;\n\n private _interactionListener: Array<EventsKey>;\n private _mapListener: Array<Resource>;\n\n constructor(options: { feature: Feature } & EditingWorkflowProps) {\n super();\n this._httpService = options.httpService;\n this._intl = options.intl;\n\n this._polygonStyle = options.polygonStyle;\n this._vertexStyle = options.vertexStyle;\n\n this._map = options.map;\n this._olMap = options.map.olMap;\n this._state = \"active:initialized\";\n this._editLayerURL = options.ogcApiFeatureLayerUrl;\n\n // Save copy of initial state for reset feature\n this._initialFeature = options.feature.clone();\n this._initialFeature.setId(options.feature.getId());\n\n // Work on copied feature to avoid the style to be applied on the original feature\n this._editFeature = options.feature.clone();\n this._editFeature.setId(options.feature.getId());\n\n this._editFeature.setStyle(\n createStyles({\n polygon: this._polygonStyle,\n vertex: this._vertexStyle\n })\n );\n\n this._editingSource = new VectorSource({\n features: new Collection([this._editFeature])\n });\n this._editingLayer = new VectorLayer({\n source: this._editingSource,\n zIndex: TOPMOST_LAYER_Z,\n properties: {\n name: \"editing-layer\"\n }\n });\n\n this._modifyInteraction = new Modify({\n source: this._editingSource\n });\n\n this._tooltip = this._createTooltip(this._olMap);\n\n this._enterHandler = (e: KeyboardEvent) => {\n if (\n (e.code === \"Enter\" || e.code === \"NumpadEnter\") &&\n e.target === this._olMap.getTargetElement()\n ) {\n const updatedFeature = this._editingSource.getFeatures()[0];\n if (!updatedFeature) {\n throw Error(\"no updated feature found\");\n }\n this._save(updatedFeature);\n }\n };\n\n this._escapeHandler = (e: KeyboardEvent) => {\n if (e.code === \"Escape\" && e.target === this._olMap.getTargetElement()) {\n this.reset();\n }\n };\n\n this._interactionListener = [];\n this._mapListener = [];\n\n this._start();\n }\n\n getModifyInteraction() {\n return this._modifyInteraction;\n }\n\n getState() {\n return this._state;\n }\n\n private _setState(state: EditingWorkflowState) {\n this._state = state;\n this.emit(state);\n }\n\n private _save(feature: Feature) {\n this._setState(\"active:saving\");\n\n const layerUrl = this._editLayerURL;\n\n this._featureId = feature.getId()?.toString();\n if (!this._featureId) {\n this._destroy();\n this._error = new Error(\"no feature id available\");\n this.#waiter?.reject(this._error);\n return;\n }\n\n const geometry = feature?.getGeometry();\n if (!geometry) {\n this._destroy();\n this._error = new Error(\"no geometry available\");\n this.#waiter?.reject(this._error);\n return;\n }\n const projection = this._olMap.getView().getProjection();\n const geoJson = new GeoJSON({\n dataProjection: projection\n });\n const geoJSONGeometry: GeoJSONGeometry | GeoJSONGeometryCollection =\n geoJson.writeGeometryObject(geometry, {\n rightHanded: true,\n decimals: 10\n });\n\n saveUpdatedFeature(\n this._httpService,\n layerUrl,\n this._featureId,\n geoJSONGeometry,\n projection\n )\n .then((featureId) => {\n this._destroy();\n this.#waiter?.resolve({ featureId });\n })\n .catch((err: Error) => {\n this._destroy();\n this._error = new Error(\"Failed to save feature\", { cause: err });\n this.#waiter?.reject(this._error);\n });\n }\n\n private _start() {\n this._olMap.addLayer(this._editingLayer);\n this._olMap.addInteraction(this._modifyInteraction);\n\n const feature = this._editingSource.getFeatures()[0];\n if (feature && !feature.getId()?.toString()) {\n this._destroy();\n this._error = new Error(\"no feature id available\");\n this.#waiter?.reject(this._error);\n return;\n }\n\n // Add EventListener on focused map to abort actual interaction via `Escape`\n this._mapContainer = this._olMap.getTargetElement() ?? undefined;\n if (this._mapContainer) {\n this._mapContainer.addEventListener(\"keydown\", this._enterHandler, false);\n this._mapContainer.addEventListener(\"keydown\", this._escapeHandler, false);\n }\n\n this._tooltip.element.classList.remove(\"editing-tooltip-hidden\");\n\n const click = this._map.olMap.on(\"click\", (e) => {\n const coordinate = e.coordinate;\n const altKeyPressed = e.originalEvent.altKey;\n const features = this._editingSource.getFeaturesAtCoordinate(coordinate);\n\n if (altKeyPressed) {\n return;\n }\n\n if (features.length === 0) {\n this.triggerSave();\n }\n });\n\n const modify = this._modifyInteraction.on(\"modifystart\", () => {\n this._setState(\"active:drawing\");\n });\n\n // update event handler when container changes\n const changedContainer = this._map.on(\"changed:container\", () => {\n this._mapContainer?.removeEventListener(\"keydown\", this._enterHandler);\n this._mapContainer?.removeEventListener(\"keydown\", this._escapeHandler);\n\n this._mapContainer = this._olMap.getTargetElement() ?? undefined;\n if (this._mapContainer) {\n this._mapContainer.addEventListener(\"keydown\", this._enterHandler, false);\n this._mapContainer.addEventListener(\"keydown\", this._escapeHandler, false);\n }\n });\n\n this._interactionListener.push(click, modify);\n this._mapListener.push(changedContainer);\n }\n\n reset() {\n // Clone geometry to pass geometry, not reference\n const geometry = this._initialFeature.getGeometry()?.clone();\n\n const resetFeature = this._editingSource.getFeatures()[0];\n if (!resetFeature) {\n throw Error(\"no updated feature found\");\n }\n resetFeature.setGeometry(geometry);\n\n this._setState(\"active:initialized\");\n }\n\n stop() {\n this._destroy();\n this.#waiter?.resolve(undefined);\n }\n\n private _destroy() {\n this._editingSource.clear();\n this._olMap.removeLayer(this._editingLayer);\n this._olMap.removeInteraction(this._modifyInteraction);\n this._tooltip.destroy();\n\n // Remove event listener on interaction and on map\n this._interactionListener.map((listener) => {\n unByKey(listener);\n });\n this._mapListener.map((listener) => {\n listener.destroy();\n });\n\n // Remove event escape listener\n this._mapContainer?.removeEventListener(\"keydown\", this._enterHandler);\n this._mapContainer?.removeEventListener(\"keydown\", this._escapeHandler);\n\n this._setState(\"destroyed\");\n }\n\n triggerSave() {\n const feature = this._editingSource.getFeatures()[0];\n if (!feature) {\n throw Error(\"no updated feature found\");\n }\n this._save(feature);\n }\n\n whenComplete(): Promise<Record<string, string> | undefined> {\n if (this._state === \"destroyed\") {\n if (this._error) {\n return Promise.reject(this._error);\n } else {\n if (this._featureId) {\n return Promise.resolve({ featureId: this._featureId });\n } else {\n return Promise.resolve(undefined);\n }\n }\n }\n\n const manualPromise = (this.#waiter ??= createManualPromise());\n return manualPromise.promise;\n }\n\n private _createTooltip(olMap: OlMap): Tooltip {\n const element = document.createElement(\"div\");\n element.className = \"editing-tooltip editing-tooltip-hidden\";\n element.textContent = this._intl.formatMessage({ id: \"create.tooltip.deselect\" });\n\n const overlay = new Overlay({\n element: element,\n offset: [15, 0],\n positioning: \"center-left\"\n });\n\n const pointerMove = olMap.on(\"pointermove\", (evt) => {\n if (evt.dragging) {\n return;\n }\n\n overlay.setPosition(evt.coordinate);\n });\n\n olMap.addOverlay(overlay);\n\n return {\n overlay,\n element,\n destroy() {\n unByKey(pointerMove);\n olMap.removeOverlay(overlay);\n }\n };\n }\n}\n"],"names":[],"mappings":"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"}
|
package/README.md
CHANGED
|
@@ -2,11 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
This package provides an editing service that allows to start and handle geometry editing workflows.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **_NOTE:_** The editing only works with OGC API Feature Services. The editing was only tested using
|
|
6
|
+
> the implementation of the OGC API Features in the XtraServer by interactive instruments.
|
|
7
|
+
> The collection in that the geometry will be saved, needs to support the map's coordinate system.
|
|
8
|
+
> The saving process may not be suitable for all kinds of OGC API Feature Service set up using the XtraServer.
|
|
9
|
+
> Please note the following additional information to ensure that process is appropriate for your services.
|
|
10
|
+
>
|
|
11
|
+
> Additional information:
|
|
12
|
+
>
|
|
13
|
+
> - Create Workflow: The feature is saved in the collection as a new feature with an empty properties
|
|
14
|
+
> object and without an id (using POST).
|
|
15
|
+
> - Update Workflow: The updated geometry is saved for the feature in the collection using a PATCH request.
|
|
16
|
+
> In addition to the new geometry, the PATCH request sends an empty properties object within the body.
|
|
6
17
|
|
|
7
18
|
## Usage
|
|
8
19
|
|
|
9
|
-
To use the editing in an app, inject the editing service. Use the `
|
|
20
|
+
To use the editing in an app, inject the editing service. Use the `createFeature` method to create a new `create` editing workflow or `updateFeature` method to create a new `update` editing workflow.
|
|
10
21
|
|
|
11
22
|
Example:
|
|
12
23
|
|
|
@@ -24,10 +35,13 @@ export default defineBuildConfig({
|
|
|
24
35
|
```tsx
|
|
25
36
|
const editingService = useService<EditingService>("editing.EditingService");
|
|
26
37
|
const editingCollectionUrl = new URL("...");
|
|
27
|
-
const
|
|
38
|
+
const feature = new Feature({});
|
|
39
|
+
|
|
40
|
+
const createWorkflow = editingService.createFeature(map, editingCollectionUrl);
|
|
41
|
+
const updateWorkflow = editingService.updateFeature(map, editingCollectionUrl, feature);
|
|
28
42
|
```
|
|
29
43
|
|
|
30
|
-
An editing workflow can be stopped completely or the current drawing can be
|
|
44
|
+
An editing workflow can be stopped completely or the current drawing can be reset to the initial state without leaving the edit mode.
|
|
31
45
|
|
|
32
46
|
Example:
|
|
33
47
|
|
|
@@ -63,7 +77,7 @@ Example:
|
|
|
63
77
|
```js
|
|
64
78
|
workflow
|
|
65
79
|
.whenComplete()
|
|
66
|
-
.then((featureId: string | undefined) => {
|
|
80
|
+
.then((featureId: Record<string, string> | undefined) => {
|
|
67
81
|
// ...
|
|
68
82
|
})
|
|
69
83
|
.catch((error: Error) => {
|
|
@@ -85,7 +99,9 @@ vectorLayer.getSource()?.refresh();
|
|
|
85
99
|
|
|
86
100
|
The default style of the geometries can be overridden with a custom style.
|
|
87
101
|
|
|
88
|
-
Each geometry type has its own styling property (currently
|
|
102
|
+
Each geometry type has its own styling property (currently `polygonStyle` and `vertexStyle`). See OpenLayers [`FlatStyle`](https://openlayers.org/en/latest/apidoc/module-ol_style_flat.html#~FlatStyle) for valid styling options.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
89
105
|
|
|
90
106
|
```js
|
|
91
107
|
const element = createCustomElement({
|
|
@@ -93,14 +109,20 @@ const element = createCustomElement({
|
|
|
93
109
|
config: {
|
|
94
110
|
properties: {
|
|
95
111
|
"@open-pioneer/editing": {
|
|
96
|
-
"
|
|
112
|
+
"polygonStyle": {
|
|
113
|
+
"fill-color": "rgba(255,255,255,0.4)",
|
|
97
114
|
"stroke-color": "red",
|
|
98
115
|
"stroke-width": 2,
|
|
99
|
-
"fill-color": "rgba(0, 0, 0, 0.1)",
|
|
100
116
|
"circle-radius": 5,
|
|
101
|
-
"circle-fill-color": "
|
|
102
|
-
"circle-stroke-
|
|
103
|
-
"circle-stroke-
|
|
117
|
+
"circle-fill-color": "red",
|
|
118
|
+
"circle-stroke-width": 1.25,
|
|
119
|
+
"circle-stroke-color": "red"
|
|
120
|
+
},
|
|
121
|
+
"vertexStyle": {
|
|
122
|
+
"circle-radius": 5,
|
|
123
|
+
"circle-fill-color": "red",
|
|
124
|
+
"circle-stroke-width": 1.25,
|
|
125
|
+
"circle-stroke-color": "red"
|
|
104
126
|
}
|
|
105
127
|
}
|
|
106
128
|
}
|
|
@@ -111,6 +133,49 @@ const element = createCustomElement({
|
|
|
111
133
|
customElements.define("ol-map-app", element);
|
|
112
134
|
```
|
|
113
135
|
|
|
136
|
+
Set `vertexStyle` to `null`, if no style is needed.
|
|
137
|
+
|
|
138
|
+
Example:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
const element = createCustomElement({
|
|
142
|
+
...,
|
|
143
|
+
config: {
|
|
144
|
+
properties: {
|
|
145
|
+
"@open-pioneer/editing": {
|
|
146
|
+
"polygonStyle": {
|
|
147
|
+
"fill-color": "rgba(255,255,255,0.4)",
|
|
148
|
+
"stroke-color": "red",
|
|
149
|
+
"stroke-width": 2,
|
|
150
|
+
"circle-radius": 5,
|
|
151
|
+
"circle-fill-color": "red",
|
|
152
|
+
"circle-stroke-width": 1.25,
|
|
153
|
+
"circle-stroke-color": "red"
|
|
154
|
+
},
|
|
155
|
+
"vertexStyle": null
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
...
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
customElements.define("ol-map-app", element);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Keyboard shortcuts and operating instructions
|
|
166
|
+
|
|
167
|
+
The user can use the following keyboard shortcuts / interactions during create feature workflow:
|
|
168
|
+
|
|
169
|
+
- `Esc`: Reset the drawing
|
|
170
|
+
- `Mouse double-click`: Set last vertex and finish drawing
|
|
171
|
+
- `Enter`: Finish drawing (vertex that is currently drawn is discarded)
|
|
172
|
+
|
|
173
|
+
The user can use the following keyboard shortcuts / interactions during update feature workflow:
|
|
174
|
+
|
|
175
|
+
- `Esc`: Reset the drawing
|
|
176
|
+
- `Alt + MouseClick`: Remove vertices from feature
|
|
177
|
+
- `Mouse click (outside feature)` or `Enter`: finish editing
|
|
178
|
+
|
|
114
179
|
## License
|
|
115
180
|
|
|
116
181
|
Apache-2.0 (see `LICENSE` file)
|
package/SaveFeaturesHandler.d.ts
CHANGED
|
@@ -2,4 +2,13 @@ import { HttpService } from "@open-pioneer/http";
|
|
|
2
2
|
import GeoJSONGeometry from "ol/format/GeoJSON";
|
|
3
3
|
import GeoJSONGeometryCollection from "ol/format/GeoJSON";
|
|
4
4
|
import { Projection } from "ol/proj";
|
|
5
|
+
/**
|
|
6
|
+
* Function to save a created feature to an OGC API Features service.
|
|
7
|
+
* Resolves with feature id, or rejects if an error occurs.
|
|
8
|
+
*/
|
|
5
9
|
export declare function saveCreatedFeature(httpService: HttpService, url: URL, geometry: GeoJSONGeometry | GeoJSONGeometryCollection, projection: Projection): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Function to save an updated geometry to a feature to an OGC API Features service.
|
|
12
|
+
* Resolves with feature id, or rejects if an error occurs.
|
|
13
|
+
*/
|
|
14
|
+
export declare function saveUpdatedFeature(httpService: HttpService, url: URL, featureId: string, geometry: GeoJSONGeometry | GeoJSONGeometryCollection, projection: Projection): Promise<string>;
|
package/SaveFeaturesHandler.js
CHANGED
|
@@ -19,6 +19,23 @@ async function saveCreatedFeature(httpService, url, geometry, projection) {
|
|
|
19
19
|
const featureId = location.substring(location.lastIndexOf("/") + 1);
|
|
20
20
|
return Promise.resolve(featureId);
|
|
21
21
|
}
|
|
22
|
+
async function saveUpdatedFeature(httpService, url, featureId, geometry, projection) {
|
|
23
|
+
const epsgCode = projection.getCode();
|
|
24
|
+
const crs = epsgCode.replace("EPSG:", "http://www.opengis.net/def/crs/EPSG/0/");
|
|
25
|
+
const featureUrl = new URL(`${url.toString()}/${featureId}`);
|
|
26
|
+
const response = await httpService.fetch(featureUrl, {
|
|
27
|
+
method: "PATCH",
|
|
28
|
+
body: JSON.stringify({ type: "Feature", properties: {}, geometry }),
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/geo+json; charset=utf-8",
|
|
31
|
+
"Content-Crs": `<${crs}>`
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
if (!response || !response.ok || response.status !== 204) {
|
|
35
|
+
return Promise.reject(new Error("Request failed: " + response.status));
|
|
36
|
+
}
|
|
37
|
+
return Promise.resolve(featureId);
|
|
38
|
+
}
|
|
22
39
|
|
|
23
|
-
export { saveCreatedFeature };
|
|
40
|
+
export { saveCreatedFeature, saveUpdatedFeature };
|
|
24
41
|
//# sourceMappingURL=SaveFeaturesHandler.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SaveFeaturesHandler.js","sources":["SaveFeaturesHandler.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\
|
|
1
|
+
{"version":3,"file":"SaveFeaturesHandler.js","sources":["SaveFeaturesHandler.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { HttpService } from \"@open-pioneer/http\";\nimport GeoJSONGeometry from \"ol/format/GeoJSON\";\nimport GeoJSONGeometryCollection from \"ol/format/GeoJSON\";\nimport { Projection } from \"ol/proj\";\n\n/**\n * Function to save a created feature to an OGC API Features service.\n * Resolves with feature id, or rejects if an error occurs.\n */\nexport async function saveCreatedFeature(\n httpService: HttpService,\n url: URL,\n geometry: GeoJSONGeometry | GeoJSONGeometryCollection,\n projection: Projection\n) {\n const epsgCode = projection.getCode();\n const crs = epsgCode.replace(\"EPSG:\", \"http://www.opengis.net/def/crs/EPSG/0/\");\n const response = await httpService.fetch(url, {\n method: \"POST\",\n body: JSON.stringify({ type: \"Feature\", properties: {}, geometry: geometry }),\n headers: {\n \"Content-Type\": \"application/geo+json; charset=utf-8\",\n \"Content-Crs\": `<${crs}>`\n }\n });\n\n if (!response || !response.ok || response.status !== 201) {\n return Promise.reject(new Error(\"Request failed: \" + response.status));\n }\n\n const location = response.headers.get(\"location\");\n if (!location) {\n return Promise.reject(new Error(\"Request failed: no Location response header\"));\n }\n\n const featureId = location.substring(location.lastIndexOf(\"/\") + 1);\n\n return Promise.resolve(featureId);\n}\n\n/**\n * Function to save an updated geometry to a feature to an OGC API Features service.\n * Resolves with feature id, or rejects if an error occurs.\n */\nexport async function saveUpdatedFeature(\n httpService: HttpService,\n url: URL,\n featureId: string,\n geometry: GeoJSONGeometry | GeoJSONGeometryCollection,\n projection: Projection\n) {\n const epsgCode = projection.getCode();\n const crs = epsgCode.replace(\"EPSG:\", \"http://www.opengis.net/def/crs/EPSG/0/\");\n const featureUrl = new URL(`${url.toString()}/${featureId}`);\n const response = await httpService.fetch(featureUrl, {\n method: \"PATCH\",\n body: JSON.stringify({ type: \"Feature\", properties: {}, geometry: geometry }),\n headers: {\n \"Content-Type\": \"application/geo+json; charset=utf-8\",\n \"Content-Crs\": `<${crs}>`\n }\n });\n\n if (!response || !response.ok || response.status !== 204) {\n return Promise.reject(new Error(\"Request failed: \" + response.status));\n }\n\n return Promise.resolve(featureId);\n}\n"],"names":[],"mappings":"AAWA,eAAsB,kBAClB,CAAA,WAAA,EACA,GACA,EAAA,QAAA,EACA,UACF,EAAA;AACE,EAAM,MAAA,QAAA,GAAW,WAAW,OAAQ,EAAA,CAAA;AACpC,EAAA,MAAM,GAAM,GAAA,QAAA,CAAS,OAAQ,CAAA,OAAA,EAAS,wCAAwC,CAAA,CAAA;AAC9E,EAAA,MAAM,QAAW,GAAA,MAAM,WAAY,CAAA,KAAA,CAAM,GAAK,EAAA;AAAA,IAC1C,MAAQ,EAAA,MAAA;AAAA,IACR,IAAA,EAAM,IAAK,CAAA,SAAA,CAAU,EAAE,IAAA,EAAM,WAAW,UAAY,EAAA,EAAI,EAAA,QAAA,EAAoB,CAAA;AAAA,IAC5E,OAAS,EAAA;AAAA,MACL,cAAgB,EAAA,qCAAA;AAAA,MAChB,aAAA,EAAe,IAAI,GAAG,CAAA,CAAA,CAAA;AAAA,KAC1B;AAAA,GACH,CAAA,CAAA;AAED,EAAA,IAAI,CAAC,QAAY,IAAA,CAAC,SAAS,EAAM,IAAA,QAAA,CAAS,WAAW,GAAK,EAAA;AACtD,IAAA,OAAO,QAAQ,MAAO,CAAA,IAAI,MAAM,kBAAqB,GAAA,QAAA,CAAS,MAAM,CAAC,CAAA,CAAA;AAAA,GACzE;AAEA,EAAA,MAAM,QAAW,GAAA,QAAA,CAAS,OAAQ,CAAA,GAAA,CAAI,UAAU,CAAA,CAAA;AAChD,EAAA,IAAI,CAAC,QAAU,EAAA;AACX,IAAA,OAAO,OAAQ,CAAA,MAAA,CAAO,IAAI,KAAA,CAAM,6CAA6C,CAAC,CAAA,CAAA;AAAA,GAClF;AAEA,EAAA,MAAM,YAAY,QAAS,CAAA,SAAA,CAAU,SAAS,WAAY,CAAA,GAAG,IAAI,CAAC,CAAA,CAAA;AAElE,EAAO,OAAA,OAAA,CAAQ,QAAQ,SAAS,CAAA,CAAA;AACpC,CAAA;AAMA,eAAsB,kBAClB,CAAA,WAAA,EACA,GACA,EAAA,SAAA,EACA,UACA,UACF,EAAA;AACE,EAAM,MAAA,QAAA,GAAW,WAAW,OAAQ,EAAA,CAAA;AACpC,EAAA,MAAM,GAAM,GAAA,QAAA,CAAS,OAAQ,CAAA,OAAA,EAAS,wCAAwC,CAAA,CAAA;AAC9E,EAAM,MAAA,UAAA,GAAa,IAAI,GAAI,CAAA,CAAA,EAAG,IAAI,QAAS,EAAC,CAAI,CAAA,EAAA,SAAS,CAAE,CAAA,CAAA,CAAA;AAC3D,EAAA,MAAM,QAAW,GAAA,MAAM,WAAY,CAAA,KAAA,CAAM,UAAY,EAAA;AAAA,IACjD,MAAQ,EAAA,OAAA;AAAA,IACR,IAAA,EAAM,IAAK,CAAA,SAAA,CAAU,EAAE,IAAA,EAAM,WAAW,UAAY,EAAA,EAAI,EAAA,QAAA,EAAoB,CAAA;AAAA,IAC5E,OAAS,EAAA;AAAA,MACL,cAAgB,EAAA,qCAAA;AAAA,MAChB,aAAA,EAAe,IAAI,GAAG,CAAA,CAAA,CAAA;AAAA,KAC1B;AAAA,GACH,CAAA,CAAA;AAED,EAAA,IAAI,CAAC,QAAY,IAAA,CAAC,SAAS,EAAM,IAAA,QAAA,CAAS,WAAW,GAAK,EAAA;AACtD,IAAA,OAAO,QAAQ,MAAO,CAAA,IAAI,MAAM,kBAAqB,GAAA,QAAA,CAAS,MAAM,CAAC,CAAA,CAAA;AAAA,GACzE;AAEA,EAAO,OAAA,OAAA,CAAQ,QAAQ,SAAS,CAAA,CAAA;AACpC;;;;"}
|
package/api.d.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { EventEmitter } from "@open-pioneer/core";
|
|
2
|
+
import { HttpService } from "@open-pioneer/http";
|
|
2
3
|
import { MapModel } from "@open-pioneer/map";
|
|
3
|
-
import type { DeclaredService } from "@open-pioneer/runtime";
|
|
4
|
+
import type { DeclaredService, PackageIntl } from "@open-pioneer/runtime";
|
|
5
|
+
import { Feature } from "ol";
|
|
6
|
+
import { FlatStyle } from "ol/style/flat";
|
|
4
7
|
/**
|
|
5
8
|
* State of an editing workflow
|
|
6
9
|
*/
|
|
7
|
-
export type EditingWorkflowState = "active:initialized" | "active:drawing" | "active:saving" | "
|
|
8
|
-
/**
|
|
10
|
+
export type EditingWorkflowState = "active:initialized" | "active:drawing" | "active:saving" | "destroyed";
|
|
11
|
+
/**
|
|
12
|
+
* Events emitted by the {@link EditingWorkflow}.
|
|
13
|
+
*/
|
|
9
14
|
export interface EditingWorkflowEvents {
|
|
10
15
|
/**
|
|
11
16
|
* Initial state after editing workflow was started but user has not yet started drawing.
|
|
12
17
|
*/
|
|
13
18
|
"active:initialized": void;
|
|
14
19
|
/**
|
|
15
|
-
* State while user is drawing a feature. State is entered when user adds the first vertex of the geometry.
|
|
20
|
+
* State while user is drawing a feature. State is entered when user adds the first vertex of the geometry (`create-mode`).
|
|
21
|
+
* State while user is updating an existing feature. State is entered when user moved the first vertex of the geometry (`update-mode`).
|
|
16
22
|
*/
|
|
17
23
|
"active:drawing": void;
|
|
18
24
|
/**
|
|
@@ -22,7 +28,18 @@ export interface EditingWorkflowEvents {
|
|
|
22
28
|
/**
|
|
23
29
|
* State after editing is stopped.
|
|
24
30
|
*/
|
|
25
|
-
"
|
|
31
|
+
"destroyed": void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Props of an editing workflow
|
|
35
|
+
*/
|
|
36
|
+
export interface EditingWorkflowProps {
|
|
37
|
+
map: MapModel;
|
|
38
|
+
ogcApiFeatureLayerUrl: URL;
|
|
39
|
+
polygonStyle: FlatStyle;
|
|
40
|
+
vertexStyle: FlatStyle;
|
|
41
|
+
httpService: HttpService;
|
|
42
|
+
intl: PackageIntl;
|
|
26
43
|
}
|
|
27
44
|
/**
|
|
28
45
|
* EditingWorkflows are created by the {@link EditingService}
|
|
@@ -33,12 +50,16 @@ export interface EditingWorkflow extends EventEmitter<EditingWorkflowEvents> {
|
|
|
33
50
|
* Returns the current state of the editing workflow.
|
|
34
51
|
*/
|
|
35
52
|
getState(): EditingWorkflowState;
|
|
53
|
+
/**
|
|
54
|
+
* Trigger saving the currently drawn/updated feature.
|
|
55
|
+
*/
|
|
56
|
+
triggerSave(): void;
|
|
36
57
|
/**
|
|
37
58
|
* Wait for the editing to be finished. The returned promise resolves with the
|
|
38
59
|
* feature ID when saving was successful and rejects if saving the feature
|
|
39
60
|
* failed. It resolves with undefined when the editing was stopped.
|
|
40
61
|
*/
|
|
41
|
-
whenComplete(): Promise<string | undefined>;
|
|
62
|
+
whenComplete(): Promise<Record<string, string> | undefined>;
|
|
42
63
|
}
|
|
43
64
|
/**
|
|
44
65
|
* The editing service allows to start and handle editing workflows.
|
|
@@ -47,15 +68,19 @@ export interface EditingWorkflow extends EventEmitter<EditingWorkflowEvents> {
|
|
|
47
68
|
*/
|
|
48
69
|
export interface EditingService extends DeclaredService<"editing.EditingService"> {
|
|
49
70
|
/**
|
|
50
|
-
* Creates and initializes a new {@link EditingWorkflow}.
|
|
71
|
+
* Creates and initializes a new {@link EditingWorkflow} to create a geometry.
|
|
72
|
+
*/
|
|
73
|
+
createFeature(map: MapModel, ogcApiFeatureLayerUrl: URL): EditingWorkflow;
|
|
74
|
+
/**
|
|
75
|
+
* Creates and initializes a new {@link EditingWorkflow} to update an existing feature's geometry.
|
|
51
76
|
*/
|
|
52
|
-
|
|
77
|
+
updateFeature(map: MapModel, ogcApiFeatureLayerUrl: URL, feature: Feature): EditingWorkflow;
|
|
53
78
|
/**
|
|
54
79
|
* Stops the edit mode and removes an existing {@link EditingWorkflow}.
|
|
55
80
|
*/
|
|
56
81
|
stop(mapId: string): void;
|
|
57
82
|
/**
|
|
58
|
-
*
|
|
83
|
+
* Resets the unfinished geometry from an existing {@link EditingWorkflow} without leaving the edit mode.
|
|
59
84
|
*/
|
|
60
85
|
reset(mapId: string): void;
|
|
61
86
|
}
|
package/i18n/de.yaml
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
messages:
|
|
2
2
|
title: Editierung von Objekten
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
create:
|
|
4
|
+
tooltip:
|
|
5
|
+
begin: Klicken, um mit Erstellung der Geometrie zu beginnen
|
|
6
|
+
continue: Doppelt klicken, um Geometrie abzuschließen und Feature zu speichern
|
|
7
|
+
deselect: Außerhalb der Geometrie in die Karte klicken, um Änderungen zu speichern
|
package/i18n/en.yaml
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
messages:
|
|
2
2
|
title: Editing
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
create:
|
|
4
|
+
tooltip:
|
|
5
|
+
begin: Click to start creating the geometry
|
|
6
|
+
continue: Double click to finish the geometry and save the feature
|
|
7
|
+
deselect: Click outside the geometry in the map to save changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@open-pioneer/editing",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "This package provides an editing service that allows to start and handle geometry editing workflows.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"open-pioneer-trails"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@open-pioneer/http": "^2.1.4",
|
|
19
19
|
"@open-pioneer/runtime": "^2.1.2",
|
|
20
20
|
"ol": "^9.0.0",
|
|
21
|
-
"@open-pioneer/map": "^0.
|
|
21
|
+
"@open-pioneer/map": "^0.5.0"
|
|
22
22
|
},
|
|
23
23
|
"exports": {
|
|
24
24
|
"./package.json": "./package.json",
|
|
@@ -68,15 +68,25 @@
|
|
|
68
68
|
},
|
|
69
69
|
"properties": [
|
|
70
70
|
{
|
|
71
|
-
"propertyName": "
|
|
71
|
+
"propertyName": "polygonStyle",
|
|
72
72
|
"defaultValue": {
|
|
73
|
-
"
|
|
74
|
-
"stroke-
|
|
75
|
-
"
|
|
76
|
-
"circle-radius":
|
|
77
|
-
"circle-fill-color": "
|
|
78
|
-
"circle-stroke-
|
|
79
|
-
"circle-stroke-
|
|
73
|
+
"fill-color": "rgba(255,255,255,0.4)",
|
|
74
|
+
"stroke-color": "red",
|
|
75
|
+
"stroke-width": 1.25,
|
|
76
|
+
"circle-radius": 3,
|
|
77
|
+
"circle-fill-color": "red",
|
|
78
|
+
"circle-stroke-width": 1.25,
|
|
79
|
+
"circle-stroke-color": "red"
|
|
80
|
+
},
|
|
81
|
+
"required": false
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"propertyName": "vertexStyle",
|
|
85
|
+
"defaultValue": {
|
|
86
|
+
"circle-radius": 3,
|
|
87
|
+
"circle-fill-color": "red",
|
|
88
|
+
"circle-stroke-width": 1.25,
|
|
89
|
+
"circle-stroke-color": "red"
|
|
80
90
|
},
|
|
81
91
|
"required": false
|
|
82
92
|
}
|