Skip to content

Commit f1ee66f

Browse files
committed
Add functionality for replacing geometries
1 parent 1dfa083 commit f1ee66f

5 files changed

Lines changed: 305 additions & 111 deletions

File tree

l10n/custom-data-type-nfis-geometry.csv

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ custom.data.type.nfis.geometry.uploadNewGeometry,Neue Geometrie hochladen,Upload
1010
custom.data.type.nfis.geometry.linkExistingGeometry,Existierende Geometrie verknüpfen,Link existing geometry
1111
custom.data.type.nfis.geometry.editGeometry,Bearbeiten,Edit
1212
custom.data.type.nfis.geometry.deleteGeometry,Geometrie löschen,Delete geometry
13+
custom.data.type.nfis.geometry.replaceGeometry,Geometrie ersetzen,Replace geometry
1314
custom.data.type.nfis.geometry.viewGeometry,Im Masterportal anzeigen,View in Masterportal
1415
custom.data.type.nfis.geometry.modal.cancel,Abbrechen,cancel
1516
custom.data.type.nfis.geometry.modal.ok,Ok,Ok
@@ -39,7 +40,11 @@ server.config.parameter.system.nfisGeoservices.show_upload_button.label,Hochlade
3940
server.config.parameter.system.nfisGeoservices.show_upload_button.checkbox,Button anzeigen,Show button
4041
server.config.parameter.system.nfisGeoservices.show_delete_button.label,Löschen von Geometrien,Delete geometries
4142
server.config.parameter.system.nfisGeoservices.show_delete_button.checkbox,Button anzeigen,Show button
42-
server.config.parameter.system.nfisGeoservices.wfs_temporary_geometry_field_name.label,Name des Feldes zur Kennzeichnung provisorischer Geometrien im WFS,Name of field for marking geometries as temporary in WFS
43+
server.config.parameter.system.nfisGeoservices.show_replace_button.label,Ersetzen von Geometrien,Replace geometries
44+
server.config.parameter.system.nfisGeoservices.show_replace_button.checkbox,Button anzeigen,Show button
45+
server.config.parameter.system.nfisGeoservices.wfs_marked_for_deletion_field_name.label,Name des WFS-Feldes 'Zu löschende Geometrie',Name of WFS field 'Geometry to be deleted'
46+
server.config.parameter.system.nfisGeoservices.wfs_replaced_by_field_name.label,Name des WFS-Feldes 'Ersetzt von',Name of WFS field 'Replaced by'
47+
server.config.parameter.system.nfisGeoservices.wfs_temporary_geometry_field_name.label,Name des WFS-Feldes 'Provisorische Geometrie',Name of WFS field 'Temporary geometry'
4348
server.config.parameter.system.nfisGeoservices.temporary_geometry_tag_id.label,ID des Tags zur Kennzeichnung von Objekten mit provisorischen Geometrien,ID of tag for marking objects with temporary geometries
4449
server.config.parameter.system.nfisGeoservices.wfs_configuration.label,Objekttypen,Object types
4550
server.config.parameter.system.nfisGeoservices.wfs_configuration.object_type.label,Name des Objekttyps,Object type name

manifest.master.yml

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,21 @@ base_config:
6262
show_delete_button:
6363
type: bool
6464
position: 8
65+
show_replace_button:
66+
type: bool
67+
position: 9
68+
wfs_marked_for_deletion_field_name:
69+
type: text
70+
position: 10
71+
wfs_replaced_by_field_name:
72+
type: text
73+
position: 11
6574
wfs_temporary_geometry_field_name:
6675
type: text
67-
position: 9
76+
position: 12
6877
temporary_geometry_tag_id:
6978
type: int
70-
position: 10
79+
position: 13
7180
wfs_configuration:
7281
type: table
7382
fields:
@@ -166,7 +175,7 @@ base_config:
166175
position: 0
167176
position: 14
168177
position: 1
169-
position: 11
178+
position: 14
170179
linked_objects:
171180
type: table
172181
fields:
@@ -176,7 +185,7 @@ base_config:
176185
- name: link_field_name
177186
type: text
178187
position: 1
179-
position: 12
188+
position: 15
180189
masterportal_configurations:
181190
type: table
182191
fields:
@@ -189,7 +198,7 @@ base_config:
189198
- name: file_name
190199
type: text
191200
position: 2
192-
position: 13
201+
position: 16
193202

194203
callbacks:
195204
db_pre_save:

src/server/sendDataToGeoserver.js

Lines changed: 96 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ process.stdin.on('end', async () => {
2020
const changedObjects = [];
2121

2222
for (let object of data.objects) {
23-
await updateObject(
23+
const changed = await updateObject(
2424
getObjectData(object),
25-
object._current ? getObjectData(object._current) : undefined
25+
object,
26+
object._current ? getObjectData(object._current) : undefined,
27+
tagGroups
2628
);
27-
if (await handleNewlyDrawnGeometries(object, tagGroups)) changedObjects.push(object);
29+
if (changed) changedObjects.push(object);
2830
}
2931

3032
console.log(JSON.stringify({ objects: changedObjects }));
@@ -52,7 +54,9 @@ function getWFSConfiguration(configuration, objectType) {
5254
return wfsConfiguration?.find(configuration => configuration.object_type === objectType);
5355
}
5456

55-
async function updateObject(object, currentObject) {
57+
async function updateObject(object, rootObject, currentObject, tagGroups) {
58+
let changed = false;
59+
5660
const configuration = getPluginConfiguration();
5761

5862
if (currentObject) addDataFromCurrentObject(object, currentObject);
@@ -65,14 +69,19 @@ async function updateObject(object, currentObject) {
6569
if (!wfsConfiguration) return;
6670

6771
for (let fieldConfiguration of wfsConfiguration.geometry_fields) {
68-
const geometryIds = await getGeometryIds(object, fieldConfiguration.field_path.split('.'));
69-
if (geometryIds.length && await hasUsedGeometryIds(configuration, geometryIds, object._uuid)) {
72+
const geometryFieldValues = await getFieldValues(object, fieldConfiguration.field_path.split('.'));
73+
const geometryIds = await getGeometryIds(geometryFieldValues);
74+
if (geometryIds.all.length && await hasUsedGeometryIds(configuration, geometryIds, object._uuid)) {
7075
return throwErrorToFrontend('Eine oder mehrere Geometrien sind bereits mit anderen Objekten verknüpft.', undefined, 'multipleGeometryLinking');
7176
}
7277

7378
await editGeometries(object, fieldConfiguration, geometryIds);
7479
if (currentObject) await deleteGeometries(fieldConfiguration, geometryIds, currentObject);
80+
if (await handleNewlyDrawnGeometries(rootObject, tagGroups, geometryIds, fieldConfiguration)) changed = true;
81+
if (cleanUpGeometryIds(rootObject, fieldConfiguration)) changed = true;
7582
}
83+
84+
return changed;
7685
}
7786

7887
function getLinkedObjectConfiguration(objectType, configuration) {
@@ -87,24 +96,54 @@ async function updateLinkedObjects(object, linkedObjectConfiguration) {
8796
}
8897
}
8998

90-
async function getGeometryIds(object, pathSegments) {
91-
let geometryIds = [];
99+
function getGeometryIds(fieldValues) {
100+
const result = {
101+
all: [],
102+
newlyDrawn: [],
103+
replaced: {}
104+
};
92105

93-
for (let fieldValue of await getFieldValues(object, pathSegments)) {
94-
if (!fieldValue?.geometry_ids?.length) continue;
95-
geometryIds = geometryIds.concat(
96-
fieldValue.geometry_ids.filter(value => value !== undefined)
97-
);
106+
for (let fieldValue of fieldValues) {
107+
if (fieldValue?.geometry_ids?.length) {
108+
result.all = result.all.concat(fieldValue.geometry_ids.filter(value => value !== undefined));
109+
}
110+
if (fieldValue?.newly_drawn_geometry_ids?.length) {
111+
result.newlyDrawn = result.newlyDrawn.concat(fieldValue.newly_drawn_geometry_ids.filter(value => value !== undefined));
112+
}
113+
if (fieldValue?.replaced_geometry_ids) {
114+
Object.keys(fieldValue.replaced_geometry_ids).forEach(geometryId => {
115+
result.replaced[geometryId] = fieldValue.replaced_geometry_ids[geometryId];
116+
});
117+
}
98118
}
99119

100-
return geometryIds;
120+
return result;
121+
}
122+
123+
async function cleanUpGeometryIds(rootObject, fieldConfiguration) {
124+
let changed = false;
125+
126+
const fieldValues = await getFieldValues(rootObject[rootObject._objecttype], fieldConfiguration.field_path.split('.'));
127+
128+
for (let fieldValue of fieldValues) {
129+
if (fieldValue?.newly_drawn_geometry_ids) {
130+
delete fieldValue.newly_drawn_geometry_ids;
131+
changed = true;
132+
}
133+
if (fieldValue?.replaced_geometry_ids) {
134+
delete fieldValue.replaced_geometry_ids;
135+
changed = true;
136+
}
137+
}
138+
139+
return changed;
101140
}
102141

103142
async function hasUsedGeometryIds(configuration, geometryIds, uuid) {
104143
const geometryFieldPaths = getGeometryFieldPaths(configuration);
105144
const url = info.api_url + '/api/v1/search?access_token=' + info.api_user_access_token;
106145
const searchRequest = {
107-
search: geometryIds.map(geometryId => {
146+
search: geometryIds.all.map(geometryId => {
108147
return {
109148
type: 'match',
110149
bool: 'should',
@@ -147,27 +186,42 @@ async function editGeometries(object, fieldConfiguration, geometryIds) {
147186
if (isSendingDataToGeoserverActivated(fieldConfiguration, geometryIds)) {
148187
const changeMap = await getChangeMap(object, fieldConfiguration);
149188
if (Object.keys(changeMap).length) {
150-
const requestXml = getEditRequestXml(geometryIds, changeMap, fieldConfiguration.edit_wfs_feature_type);
151-
await performEditTransaction(geometryIds, requestXml, fieldConfiguration);
189+
const requestXml = getEditRequestXml(geometryIds.all, changeMap, fieldConfiguration.edit_wfs_feature_type);
190+
await performEditTransaction(geometryIds.all, requestXml, fieldConfiguration);
152191
}
153192
}
154193
}
155194

156195
async function deleteGeometries(fieldConfiguration, geometryIds, currentObject) {
157196
if (!currentObject) return;
197+
await markGeometriesAsReplaced(geometryIds, fieldConfiguration);
158198
const deletedGeometryIds = await getDeletedGeometryIds(geometryIds, currentObject, fieldConfiguration);
159199
if (deletedGeometryIds.length) await performDeleteTransaction(deletedGeometryIds, fieldConfiguration);
160200
}
161201

162202
async function getDeletedGeometryIds(geometryIds, currentObject, fieldConfiguration) {
163-
const currentGeometryIds = await getGeometryIds(currentObject, fieldConfiguration.field_path.split('.'));
164-
return currentGeometryIds.filter(geometryId => !geometryIds.includes(geometryId));
203+
const currentGeometryFieldValues = await getFieldValues(currentObject, fieldConfiguration.field_path.split('.'));
204+
const currentGeometryIds = await getGeometryIds(currentGeometryFieldValues);
205+
const replacedGeometryIds = Object.keys(geometryIds.replaced);
206+
return currentGeometryIds.all.filter(geometryId => {
207+
return !geometryIds.all.includes(geometryId) && !replacedGeometryIds.includes(geometryId);
208+
}).concat(replacedGeometryIds);
209+
}
210+
211+
async function markGeometriesAsReplaced(geometryIds, fieldConfiguration) {
212+
const wfsReplacedByFieldName = getPluginConfiguration().wfs_replaced_by_field_name;
213+
const featureType = fieldConfiguration.edit_wfs_feature_type;
214+
215+
for (let [replacedGeometryId, newGeometryId] of Object.entries(geometryIds.replaced)) {
216+
const requestXml = getMarkAsReplacedRequestXml(replacedGeometryId, newGeometryId, wfsReplacedByFieldName, featureType);
217+
await performEditTransaction([replacedGeometryId], requestXml, fieldConfiguration);
218+
}
165219
}
166220

167221
function isSendingDataToGeoserverActivated(fieldConfiguration, geometryIds) {
168222
return fieldConfiguration.send_data_to_geoserver
169223
&& fieldConfiguration.edit_wfs_url
170-
&& geometryIds?.length;
224+
&& geometryIds.all.length;
171225
}
172226

173227
function addDataFromCurrentObject(object, currentObject) {
@@ -385,7 +439,7 @@ async function performDeleteTransaction(geometryIds, fieldConfiguration) {
385439
}
386440

387441
async function performTransaction(requestXml, wfsUrl) {
388-
const transactionUrl = wfsUrl + '?service=WFS&version=1.1.0&request=Transaction';;
442+
const transactionUrl = wfsUrl + '?service=WFS&version=1.1.0&request=Transaction';
389443

390444
try {
391445
const response = await fetch(transactionUrl, {
@@ -423,6 +477,18 @@ function getMarkAsTemporaryRequestXml(geometryIds, propertyName, featureType) {
423477
);
424478
}
425479

480+
function getMarkAsReplacedRequestXml(replacedGeometryId, newGeometryId, propertyName, featureType) {
481+
return getTransactionXml(
482+
'<wfs:Update typeName="' + featureType + '">'
483+
+ '<wfs:Property>'
484+
+ '<wfs:Name>' + propertyName + '</wfs:Name>'
485+
+ '<wfs:Value>' + newGeometryId + '</wfs:Value>'
486+
+ '</wfs:Property>'
487+
+ getFilterXml([replacedGeometryId])
488+
+ '</wfs:Update>'
489+
);
490+
}
491+
426492
function getDeleteRequestXml(geometryIds, featureType) {
427493
return getTransactionXml(
428494
'<wfs:Delete typeName="' + featureType + '">'
@@ -471,32 +537,19 @@ function getGeometryFilterXml(geometryId) {
471537
+ '</ogc:PropertyIsEqualTo>';
472538
}
473539

474-
async function handleNewlyDrawnGeometries(object, tagGroups) {
475-
const configuration = getPluginConfiguration();
476-
const wfsConfiguration = getWFSConfiguration(configuration, object._objecttype);
477-
if (!wfsConfiguration) return false;
540+
async function handleNewlyDrawnGeometries(rootObject, tagGroups, geometryIds, fieldConfiguration) {
541+
if (!geometryIds.newlyDrawn?.length) return false;
478542

543+
const configuration = getPluginConfiguration();
479544
const wfsTemporaryGeometryFieldName = configuration.wfs_temporary_geometry_field_name;
480545
const temporaryGeometryTagId = configuration.temporary_geometry_tag_id;
481546

482-
let changed = false;
483-
for (let fieldConfiguration of wfsConfiguration.geometry_fields) {
484-
for (let fieldValue of await getFieldValues(object[object._objecttype], fieldConfiguration.field_path.split('.'))) {
485-
const newlyDrawnGeometryIds = fieldValue.newly_drawn_geometry_ids;
486-
if (newlyDrawnGeometryIds) {
487-
delete fieldValue.newly_drawn_geometry_ids;
488-
changed = true;
489-
}
490-
if (!newlyDrawnGeometryIds?.length) continue;
491-
492-
if (wfsTemporaryGeometryFieldName) {
493-
await markGeometriesAsTemporary(newlyDrawnGeometryIds, fieldConfiguration, wfsTemporaryGeometryFieldName);
494-
}
495-
if (temporaryGeometryTagId) setTag(object, temporaryGeometryTagId, tagGroups);
496-
}
547+
if (wfsTemporaryGeometryFieldName) {
548+
await markGeometriesAsTemporary(geometryIds.newlyDrawn, fieldConfiguration, wfsTemporaryGeometryFieldName);
497549
}
550+
if (temporaryGeometryTagId) setTag(rootObject, temporaryGeometryTagId, tagGroups);
498551

499-
return changed;
552+
return true;
500553
}
501554

502555
async function markGeometriesAsTemporary(geometryIds, fieldConfiguration, wfsTemporaryGeometryFieldName) {
@@ -511,12 +564,12 @@ function getAuthorizationString(configuration) {
511564
return btoa(username + ':' + password);
512565
}
513566

514-
function setTag(object, tagId, tagGroups) {
567+
function setTag(rootObject, tagId, tagGroups) {
515568
const tagGroup = tagGroups.find(group => group._tags.find(entry => entry.tag._id === tagId));
516569
const tagsIdsToRemove = tagGroup.taggroup.type === 'choice'
517570
? tagGroup._tags.map(entry => entry.tag._id)
518571
: [tagId];
519-
object._tags = object._tags.filter(tag => !tagsIdsToRemove.includes(tag._id))
572+
rootObject._tags = rootObject._tags.filter(tag => !tagsIdsToRemove.includes(tag._id))
520573
.concat([{ _id: tagId }]);
521574
}
522575

0 commit comments

Comments
 (0)