Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #9620 better error handling of GeoProcessing #9621

Merged
merged 4 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions web/client/actions/__tests__/geoProcessing-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,16 @@ describe('Test Geo Processing Tools related actions', () => {
const source = "";
const data = {};
const nextPage = 2;
const geometryProperty = {};

const action = setFeatures(layerId, source, data, nextPage);
const action = setFeatures(layerId, source, data, nextPage, geometryProperty);
expect(action).toEqual({
type: SET_FEATURES,
layerId,
source,
data,
nextPage
nextPage,
geometryProperty
});
});
it('setFeatureSourceLoading', () => {
Expand Down
5 changes: 3 additions & 2 deletions web/client/actions/geoProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,13 @@ export const setBufferCapStyle = (capStyle) => ({
* @param {string} source can be "source" or "intersection"
* @param {object[]|object} data list of features or error
*/
export const setFeatures = (layerId, source, data, nextPage) => ({
export const setFeatures = (layerId, source, data, nextPage, geometryProperty) => ({
type: SET_FEATURES,
layerId,
source,
data,
nextPage
nextPage,
geometryProperty
});
/**
* action for the loading flag of the features
Expand Down
121 changes: 100 additions & 21 deletions web/client/epics/geoProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,10 @@ import {
calculateDistance
} from "../utils/CoordinatesUtils";
import {buildIdentifyRequest} from "../utils/MapInfoUtils";
import {logError} from "../utils/DebugUtils";
import {getFeatureInfo} from "../api/identify";
import {getFeatureSimple} from '../api/WFS';
import {findNonGeometryProperty} from '../utils/ogc/WFS/base';
import {findNonGeometryProperty, findGeometryProperty} from '../utils/ogc/WFS/base';
import toWKT from '../utils/ogc/WKT/toWKT';

const OFFSET = 550;
Expand Down Expand Up @@ -211,7 +212,7 @@ export const checkWPSAvailabilityGPTEpic = (action$, store) => action$
);
})
.catch((e) => {
console.error(e);
logError(e);
return Rx.Observable.of(
setWPSAvailability(layerId, false, source),
checkingWPS(false)
Expand Down Expand Up @@ -260,6 +261,7 @@ export const getFeaturesGPTEpic = (action$, store) => action$
startIndex: page * maxFeatures,
maxFeatures
};
const geometryProperty = findGeometryProperty(layer.describeFeatureType);
return Rx.Observable.merge(
getLayerJSONFeature({
...layer,
Expand All @@ -269,9 +271,19 @@ export const getFeaturesGPTEpic = (action$, store) => action$
url: layer.url
}
}, filterObj, options)
.map(data => setFeatures(layerId, source, data, page))
.catch(error => {
return Rx.Observable.of(setFeatures(layerId, source, error, page));
.map(data => setFeatures(layerId, source, data, page, geometryProperty))
.catch(e => {
logError(e);
return Rx.Observable.of(
setFeatures(layerId, source, e, page, geometryProperty),
showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorGettingFeaturesList",
autoDismiss: 6,
position: "tc",
values: {layerName: layer.name + " - " + layer.title}
})
);
})
.startWith(setFeatureLoading(true))
.concat(Rx.Observable.of(setFeatureLoading(false))));
Expand Down Expand Up @@ -316,7 +328,8 @@ export const getFeatureDataGPTEpic = (action$, store) => action$
]);

})
.catch(() => {
.catch((e) => {
logError(e);
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorGetFeature",
Expand Down Expand Up @@ -364,7 +377,8 @@ export const getIntersectionFeatureDataGPTEpic = (action$, store) => action$
]);

})
.catch(() => {
.catch((e) => {
logError(e);
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorGetFeature",
Expand Down Expand Up @@ -486,7 +500,8 @@ export const runBufferProcessGPTEpic = (action$, store) => action$
)
);
})
.catch(() => {
.catch((e) => {
logError(e);
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorBuffer",
Expand All @@ -504,19 +519,34 @@ export const runBufferProcessGPTEpic = (action$, store) => action$
executeOptions,
{
headers: {'Content-Type': 'application/xml', 'Accept': `application/xml, application/json`}
}).catch(() => {
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorBuffer",
autoDismiss: 6,
position: "tc"
}));
});
})
.catch((e) => {
logError(e);
return Rx.Observable.of({error: e, layerName: layer.name, layerTitle: layer.title});
});
return executeCollectProcess$
.switchMap((geom) => {
.switchMap((result) => {
if (result.error) {
if (result.error.message.includes("Failed to retrieve value for input features")) {
console.error("layerName " + result.layerName + " - " + result.layerTitle);
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorGettingFC",
autoDismiss: 6,
position: "tc",
values: {layerName: result.layerName + " - " + result.layerTitle}
}));
}
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorBuffer",
autoDismiss: 6,
position: "tc"
}));
}
const ft = {
type: "Feature",
geometry: geom
geometry: result
};
const featureReprojected = reprojectGeoJson(ft, "EPSG:4326", "EPSG:3857");
const geometry3857 = toWKT(featureReprojected.geometry);
Expand Down Expand Up @@ -574,6 +604,10 @@ export const runIntersectProcessGPTEpic = (action$, store) => action$
executeOptions,
{
headers: {'Content-Type': 'application/xml', 'Accept': `application/xml, application/json`}
})
.catch((e) => {
logError(e);
return Rx.Observable.of({error: e, layerName: layer.name, layerTitle: layer.title});
});
} else {
sourceFC$ = Rx.Observable.of(sourceFeature.geometry);
Expand All @@ -588,12 +622,46 @@ export const runIntersectProcessGPTEpic = (action$, store) => action$
executeOptions,
{
headers: {'Content-Type': 'application/xml', 'Accept': `application/xml, application/json`}
})
.catch((e) => {
logError(e);
return Rx.Observable.of({error: e, layerName: intersectionLayer.name, layerTitle: intersectionLayer.title});
});
} else {
intersectionFC$ = Rx.Observable.of(intersectionFeature.geometry);
}
return Rx.Observable.forkJoin(sourceFC$, intersectionFC$)
.switchMap(([firstGeom, secondGeom]) => {
if (firstGeom?.error || secondGeom?.error) {
const errorActions = [];
if (firstGeom?.error?.message?.includes("Failed to retrieve value for input features")) {
logError(firstGeom.error);
errorActions.push(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorGettingFC",
autoDismiss: 6,
position: "tc",
values: {layerName: firstGeom.layerName + " - " + firstGeom.layerTitle}
}));
}
if (secondGeom?.error?.message?.includes("Failed to retrieve value for input features")) {
logError(secondGeom.error);
errorActions.push(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorGettingFC",
autoDismiss: 6,
position: "tc",
values: {layerName: secondGeom.layerName + " - " + secondGeom.layerTitle}
}));
}
errorActions.push(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorIntersectGFI",
autoDismiss: 6,
position: "tc"
}));
return Rx.Observable.from(errorActions);
}

const executeProcess$ = executeProcess(
layerUrl,
Expand All @@ -616,7 +684,16 @@ export const runIntersectProcessGPTEpic = (action$, store) => action$
.switchMap((featureCollection) => {
const groups = groupsSelector(state);
const groupExist = find(groups, (g) => g.id === GPT_INTERSECTION_GROUP_ID);
const extent = getGeoJSONExtent(featureCollection);
let extent = getGeoJSONExtent(featureCollection);
if (extent.some(coord => coord === Infinity )) {
// intersection is empty, return a message
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.emptyIntersection",
autoDismiss: 6,
position: "tc"
}));
}
return (!groupExist ? Rx.Observable.of( addGroup("Intersected Layers", null, {id: GPT_INTERSECTION_GROUP_ID}, true)) : Rx.Observable.empty())
.concat(
Rx.Observable.of(
Expand Down Expand Up @@ -665,7 +742,8 @@ export const runIntersectProcessGPTEpic = (action$, store) => action$
})
));
})
.catch(() => {
.catch((e) => {
logError(e);
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorIntersectGFI",
Expand Down Expand Up @@ -804,7 +882,8 @@ export const clickToSelectFeatureGPTEpic = (action$, {getState}) =>
position: "tc"
}));
})
.catch(() => {
.catch((e) => {
logError(e);
return Rx.Observable.of(showErrorNotification({
title: "errorTitleDefault",
message: "GeoProcessing.notifications.errorGFI",
Expand Down
4 changes: 2 additions & 2 deletions web/client/observables/wfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import urlUtil from 'url';

import { castArray, isNil, isObject } from 'lodash';
import { isArray, castArray, isNil, isObject } from 'lodash';
import Rx from 'rxjs';
import { parseString } from 'xml2js';
import { stripPrefix } from 'xml2js/lib/processors';
Expand Down Expand Up @@ -259,7 +259,7 @@ export const getLayerJSONFeature = ({ search = {}, url, name } = {}, filter, {so
} : getFeature(
query(name,
[
sortBy(pn[0]),
sortBy(isArray(pn) ? pn[0] : pn),
...(pn ? [propertyName(pn)] : []),
...(filter ? castArray(filter) : [])
]),
Expand Down
7 changes: 4 additions & 3 deletions web/client/plugins/GeoProcessing/processes.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,11 @@ export const processes = [
{props.runningProcess ? <Loader size={14} style={{margin: '0 auto'}}/> : null}
</Button>
<InfoPopover
bsStyle={props.isIntersectionLayerInvalid || props.isSourceLayerInvalid ? "danger" : "info"}
bsStyle={!props.isIntersectionEnabled || props.isIntersectionLayerInvalid || props.isSourceLayerInvalid ? "danger" : "info"}
text={
props.isIntersectionLayerInvalid || props.isSourceLayerInvalid ?
getMessageById(props.messages, "GeoProcessing.tooltip.invalidLayers") : getMessageById(props.messages, "GeoProcessing.tooltip.fillRequiredDataIntersection")
!props.isIntersectionEnabled ? getMessageById(props.messages, "GeoProcessing.tooltip.pointAndPolygon") :
props.isIntersectionLayerInvalid || props.isSourceLayerInvalid ?
getMessageById(props.messages, "GeoProcessing.tooltip.invalidLayers") : getMessageById(props.messages, "GeoProcessing.tooltip.fillRequiredDataIntersection")
}
/>
</div>);
Expand Down
2 changes: 0 additions & 2 deletions web/client/reducers/__tests__/geoProcessing-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,6 @@ describe('Test Geo Processing Tools reducer', () => {
}, action);
expect(state.source.feature).toEqual(feature);
expect(state.source.features).toEqual([feature]);
expect(state.flags.isIntersectionEnabled).toEqual(true);
});
it('SET_INTERSECTION_LAYER_ID', () => {
const layerId = "id";
Expand Down Expand Up @@ -354,7 +353,6 @@ describe('Test Geo Processing Tools reducer', () => {
}
}, action);
expect(state.intersection.feature).toEqual(feature);
expect(state.flags.isIntersectionEnabled).toEqual(true);
});
it('SET_INTERSECTION_FIRST_ATTRIBUTE', () => {
const firstAttributeToRetain = "attr";
Expand Down
13 changes: 4 additions & 9 deletions web/client/reducers/geoProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import {
RESET_CONTROLS
} from '../actions/controls';
import { LOCATION_CHANGE } from 'connected-react-router';
import { checkIfIntersectionIsPossible } from '../utils/GeoProcessingUtils';

/**
* reducer for GeoProcessing
Expand Down Expand Up @@ -208,6 +207,10 @@ function geoProcessing( state = {
features: (state[action.source].features || []).concat(action.data.features || []),
totalCount: action.data.totalFeatures,
currentPage: action.nextPage
},
flags: {
...state.flags,
isIntersectionEnabled: action.source === "source" ? !action.geometryProperty?.type?.includes("Point") : state.flags.isIntersectionEnabled
}
} : {
...state,
Expand Down Expand Up @@ -308,10 +311,6 @@ function geoProcessing( state = {
...state.source,
feature: action.feature,
features: find(state.source.features, ft => ft.id === action.feature.id) ? state.source.features : [action.feature]
},
flags: {
...state.flags,
isIntersectionEnabled: checkIfIntersectionIsPossible(action?.feature, state?.intersection?.feature)
}
};
}
Expand Down Expand Up @@ -350,10 +349,6 @@ function geoProcessing( state = {
...state.intersection,
feature: action.feature,
features: find(state.intersection.features, ft => ft.id === action.feature.id) ? state.intersection.features : [action.feature]
},
flags: {
...state.flags,
isIntersectionEnabled: checkIfIntersectionIsPossible(state?.source?.feature, action?.feature)
}
};
}
Expand Down
6 changes: 5 additions & 1 deletion web/client/translations/data.de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -3977,7 +3977,10 @@
"noLayerSelected": "Bitte wählen Sie zuerst die Ebene aus.",
"successfulIntersection": "Der Schnittvorgang war erfolgreich und ein neuer Layer wurde erstellt und dem Inhaltsverzeichnis hinzugefügt",
"successfulBuffer": "Der Puffervorgang war erfolgreich und eine neue Ebene wurde erstellt und dem Inhaltsverzeichnis hinzugefügt",
"errorBuffer": "Der Puffervorgang ist fehlgeschlagen"
"errorBuffer": "Der Puffervorgang ist fehlgeschlagen",
"emptyIntersection": "Es gibt keine Schnittmenge zwischen den angegebenen Datensätzen.",
"errorGettingFC": "Es war nicht möglich, die Geometrie aus der Ebene zu erfassen {layerName}",
"errorGettingFeaturesList": "Es war nicht möglich, die Liste der Features aus dem Layer abzurufen {layerName}"
},
"warningTitle": "Warnung",
"warningBody": "Sie haben keine Funktion ausgewählt und dies kann die Leistung im Geoserver verlangsamen. Möchten Sie fortfahren?",
Expand All @@ -3988,6 +3991,7 @@
"invalidLayers": "Eine der von Ihnen ausgewählten Ebenen kann in diesem Prozess nicht verwendet werden",
"fillRequiredDataIntersection": "Bitte wählen Sie mindestens den Quell-Layer und den Schnittpunkt-Layer aus",
"fillRequiredDataBuffer": "Bitte wählen Sie mindestens die Quellebene aus",
"pointAndPolygon": "Sie können einen Punktlayer nur für den Schnittpunktlayer auswählen",
"siderBarBtn": "Geoverarbeitung",
"selectFeature": "Bitte wählen Sie eine Funktion aus",
"validFeature": "Diese Funktion ist gültig",
Expand Down
6 changes: 5 additions & 1 deletion web/client/translations/data.en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -3951,7 +3951,10 @@
"noLayerSelected": "Please select layer first.",
"successfulIntersection": "The intersection operation was successful and a new layer has ben created and added to the TOC",
"successfulBuffer": "The Buffer operation was successful and a new layer has ben created and added to the TOC",
"errorBuffer": "The Buffer operation has failed"
"errorBuffer": "The Buffer operation has failed",
"emptyIntersection": "There is no intersection between the given datasets.",
"errorGettingFC": "It was not possible to collect the geometry from the layer {layerName}",
"errorGettingFeaturesList": "It was not possible to fetch the list of features from the layer {layerName}"
},
"warningTitle": "Warning",
"warningBody": "You have not selected a feature and this may slow down performances in geoserver. Do you want to continue?",
Expand All @@ -3962,6 +3965,7 @@
"invalidLayers": "One of the layers you have selected cannot be used within this process",
"fillRequiredDataIntersection": "Please select at least the source layer and the intersection layer",
"fillRequiredDataBuffer": "Please select at least the source layer",
"pointAndPolygon": "You can select a point layer only for the intersection layer",
"siderBarBtn": "GeoProcessing",
"selectFeature": "Please select a feature",
"validFeature": "This feature is valid",
Expand Down
Loading
Loading