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 #1692 WFS spatial query works only if geometry field name is the_geom #1773

Merged
merged 3 commits into from
May 3, 2017
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
2 changes: 1 addition & 1 deletion web/client/actions/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function textSearch(format, url, startPosition, maxRecords, text, options) {
function addLayerAndDescribe(layer) {
return (dispatch, getState) => {
const state = getState();
const layers = state && state.layers;
const layers = state && state.layers && state.layers.flat;
const id = LayersUtils.getLayerId(layer, layers || []);
dispatch(addLayer({...layer, id}));
if (layer.type === 'wms') {
Expand Down
11 changes: 10 additions & 1 deletion web/client/actions/queryform.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const EXPAND_ATTRIBUTE_PANEL = 'EXPAND_ATTRIBUTE_PANEL';
const EXPAND_SPATIAL_PANEL = 'EXPAND_SPATIAL_PANEL';
const SELECT_SPATIAL_METHOD = 'SELECT_SPATIAL_METHOD';
const SELECT_SPATIAL_OPERATION = 'SELECT_SPATIAL_OPERATION';
const CHANGE_SPATIAL_ATTRIBUTE = 'CHANGE_SPATIAL_ATTRIBUTE';
const REMOVE_SPATIAL_SELECT = 'REMOVE_SPATIAL_SELECT';
const SHOW_SPATIAL_DETAILS = 'SHOW_SPATIAL_DETAILS';
// const QUERY_FORM_SEARCH = 'QUERY_FORM_SEARCH';
Expand Down Expand Up @@ -133,6 +134,13 @@ function selectSpatialOperation(operation, fieldName) {
};
}

function changeSpatialAttribute(attribute) {
return {
type: CHANGE_SPATIAL_ATTRIBUTE,
attribute
};
}

function removeSpatialSelection() {
return {
type: REMOVE_SPATIAL_SELECT
Expand All @@ -159,7 +167,6 @@ function changeDwithinValue(distance) {
response: response
};
}

function wfsLoadError(e) {
return {
type: WFS_LOAD_ERROR,
Expand Down Expand Up @@ -297,6 +304,7 @@ module.exports = {
EXPAND_SPATIAL_PANEL,
SELECT_SPATIAL_METHOD,
SELECT_SPATIAL_OPERATION,
CHANGE_SPATIAL_ATTRIBUTE,
REMOVE_SPATIAL_SELECT,
SHOW_SPATIAL_DETAILS,
// QUERY_FORM_SEARCH,
Expand Down Expand Up @@ -333,6 +341,7 @@ module.exports = {
expandSpatialFilterPanel,
selectSpatialMethod,
selectSpatialOperation,
changeSpatialAttribute,
removeSpatialSelection,
showSpatialSelectionDetails,
query,
Expand Down
23 changes: 2 additions & 21 deletions web/client/actions/wfsquery.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,26 +76,6 @@ function queryError(error) {
};
}

function describeFeatureType(baseUrl, typeName) {
return (dispatch) => {
return axios.get(baseUrl + '?service=WFS&version=1.1.0&request=DescribeFeatureType&typeName=' + typeName + '&outputFormat=application/json').then((response) => {
if (typeof response.data === 'object') {
dispatch(featureTypeLoaded(typeName, response.data));
} else {
try {
JSON.parse(response.data);
} catch(e) {
dispatch(featureTypeError(typeName, 'Error from WFS: ' + e.message));
}

}

}).catch((e) => {
dispatch(featureTypeError(typeName, e));
});
};
}

function loadFeature(baseUrl, typeName) {
return (dispatch) => {
return axios.get(baseUrl + '?service=WFS&version=1.1.0&request=GetFeature&typeName=' + typeName + '&outputFormat=application/json').then((response) => {
Expand Down Expand Up @@ -196,7 +176,8 @@ module.exports = {
QUERY_ERROR,
RESET_QUERY,
featureTypeSelected,
describeFeatureType,
featureTypeLoaded,
featureTypeError,
loadFeature,
createQuery,
query,
Expand Down
16 changes: 1 addition & 15 deletions web/client/components/data/query/QueryBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ const QueryBuilder = React.createClass({
onUpdateLogicCombo: () => {},
onRemoveGroupField: () => {},
onChangeCascadingValue: () => {},
onExpandAttributeFilterPanel: () => {},
onLoadFeatureTypeConfig: () => {}
onExpandAttributeFilterPanel: () => {}
},
spatialFilterActions: {
onExpandSpatialFilterPanel: () => {},
Expand All @@ -107,19 +106,6 @@ const QueryBuilder = React.createClass({
}
};
},
componentDidMount() {
if (this.props.featureTypeConfigUrl && this.props.attributes.length < 1) {
this.props.attributeFilterActions.onLoadFeatureTypeConfig(
this.props.featureTypeConfigUrl, this.props.params);
}
},
componentWillReceiveProps(props) {
let url = props.featureTypeConfigUrl;
let params = props.params !== this.props.params ? props.params : this.props.params;
if (url !== this.props.featureTypeConfigUrl) {
this.props.attributeFilterActions.onLoadFeatureTypeConfig(url, params);
}
},
render() {
if (this.props.featureTypeError !== "") {
return (<div style={{margin: "0 auto", "text-align": "center"}}>{this.props.featureTypeErrorText}</div>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,20 +145,13 @@ describe('QueryBuilder', () => {

it('creates the QueryBuilder component in error state', () => {

let attributeFilterActions = {
onLoadFeatureTypeConfig: () => {}
};
let spy = expect.spyOn(attributeFilterActions, 'onLoadFeatureTypeConfig');

const querybuilder = ReactDOM.render(<QueryBuilder
featureTypeError={"true"}
featureTypeErrorText={"bla bla"}
attributeFilterActions={attributeFilterActions}
featureTypeConfigUrl={"randomurl"} />,
document.getElementById("container"));

expect(querybuilder).toExist();
expect(spy.calls.length).toEqual(1);
});

it('creates the QueryBuilder component with empty filter support', () => {
Expand Down
77 changes: 77 additions & 0 deletions web/client/epics/wfsquery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2017, GeoSolutions Sas.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

const Rx = require('rxjs');
const axios = require('../libs/ajax');
const {changeSpatialAttribute} = require('../actions/queryform');
const {FEATURE_TYPE_SELECTED, featureTypeLoaded, featureTypeError} = require('../actions/wfsquery');

const types = {
'xsd:string': 'string',
'xsd:dateTime': 'date',
'xsd:number': 'number',
'xsd:int': 'number'
};
const fieldConfig = {};
const extractInfo = (data) => {
return {
geometry: data.featureTypes[0].properties
.filter((attribute) => attribute.type.indexOf('gml:') === 0)
.map((attribute) => {
let conf = {
label: attribute.name,
attribute: attribute.name,
type: 'geometry',
valueId: "id",
valueLabel: "name",
values: []
};
conf = fieldConfig[attribute.name] ? {...conf, ...fieldConfig[attribute.name]} : conf;
return conf;
}),
attributes: data.featureTypes[0].properties
.filter((attribute) => attribute.type.indexOf('gml:') !== 0)
.map((attribute) => {
let conf = {
label: attribute.name,
attribute: attribute.name,
type: types[attribute.type],
valueId: "id",
valueLabel: "name",
values: []
};
conf = fieldConfig[attribute.name] ? {...conf, ...fieldConfig[attribute.name]} : conf;
return conf;
})
};
};

const featureTypeSelectedEpic = action$ =>
action$.ofType(FEATURE_TYPE_SELECTED).switchMap(action => {
return Rx.Observable.defer( () =>
axios.get(action.url + '?service=WFS&version=1.1.0&request=DescribeFeatureType&typeName=' + action.typeName + '&outputFormat=application/json'))
.map((response) => {
if (typeof response.data === 'object' && response.data.featureTypes && response.data.featureTypes[0]) {
const info = extractInfo(response.data);
const geometry = info.geometry[0] && info.geometry[0].attribute ? info.geometry[0].attribute : 'the_geom';
return Rx.Observable.from([featureTypeLoaded(action.typeName, info), changeSpatialAttribute(geometry)]);
}
try {
JSON.parse(response.data);
} catch(e) {
return Rx.Observable.from([featureTypeError(action.typeName, 'Error from WFS: ' + e.message)]);
}
return Rx.Observable.from([featureTypeError(action.typeName, 'Error: feature types are empty')]);
})
.mergeAll()
.catch(e => Rx.Observable.of(featureTypeError(action.typeName, e.message)));
});

module.exports = {
featureTypeSelectedEpic
};
5 changes: 4 additions & 1 deletion web/client/plugins/QueryPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const LayersUtils = require('../utils/LayersUtils');
// include application component
const QueryBuilder = require('../components/data/query/QueryBuilder');

const {featureTypeSelectedEpic} = require('../epics/wfsquery');

const {bindActionCreators} = require('redux');
const {
// QueryBuilder action functions
Expand Down Expand Up @@ -234,5 +236,6 @@ module.exports = {
reducers: {
queryform: require('../reducers/queryform'),
query: require('../reducers/query')
}
},
epics: {featureTypeSelectedEpic}
};
5 changes: 1 addition & 4 deletions web/client/plugins/TOC.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const {
zoneChange
} = require('../actions/queryform');

const {createQuery, toggleQueryPanel, describeFeatureType} = require('../actions/wfsquery');
const {createQuery, toggleQueryPanel} = require('../actions/wfsquery');

const {
changeDrawingStatus,
Expand Down Expand Up @@ -92,9 +92,6 @@ const SmartQueryForm = connect((state) => {
return {

attributeFilterActions: bindActionCreators({
onLoadFeatureTypeConfig: (url, params) => {
return describeFeatureType(url, params.typeName);
},
onAddGroupField: addGroupField,
onAddFilterField: addFilterField,
onRemoveFilterField: removeFilterField,
Expand Down
28 changes: 1 addition & 27 deletions web/client/reducers/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,6 @@ const {RESET_CONTROLS} = require('../actions/controls');

const assign = require('object-assign');

const types = {
'xsd:string': 'string',
'xsd:dateTime': 'date',
'xsd:number': 'number',
'xsd:int': 'number'
};
const fieldConfig = {};
const extractInfo = (featureType) => {
return {
attributes: featureType.featureTypes[0].properties
.filter((attribute) => attribute.type.indexOf('gml:') !== 0)
.map((attribute) => {
let conf = {
label: attribute.name,
attribute: attribute.name,
type: types[attribute.type],
valueId: "id",
valueLabel: "name",
values: []
};
conf = fieldConfig[attribute.name] ? {...conf, ...fieldConfig[attribute.name]} : conf;
return conf;
})
};
};

const extractData = (feature) => {
return ['STATE_NAME', 'STATE_ABBR', 'SUB_REGION', 'STATE_FIPS' ].map((attribute) => ({
attribute,
Expand Down Expand Up @@ -86,7 +60,7 @@ function query(state = initialState, action) {
}
case FEATURE_TYPE_LOADED: {
return assign({}, state, {
featureTypes: assign({}, state.featureTypes, {[action.typeName]: extractInfo(action.featureType)})
featureTypes: assign({}, state.featureTypes, {[action.typeName]: action.featureType})
});
}
case FEATURE_TYPE_ERROR: {
Expand Down
4 changes: 4 additions & 0 deletions web/client/reducers/queryform.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const {
EXPAND_SPATIAL_PANEL,
SELECT_SPATIAL_METHOD,
SELECT_SPATIAL_OPERATION,
CHANGE_SPATIAL_ATTRIBUTE,
REMOVE_SPATIAL_SELECT,
SHOW_SPATIAL_DETAILS,
QUERY_FORM_RESET,
Expand Down Expand Up @@ -167,6 +168,9 @@ function queryform(state = initialState, action) {
case SELECT_SPATIAL_OPERATION: {
return assign({}, state, {spatialField: assign({}, state.spatialField, {[action.fieldName]: action.operation})});
}
case CHANGE_SPATIAL_ATTRIBUTE: {
return assign({}, state, { spatialField: assign({}, state.spatialField, {attribute: action.attribute}) });
}
case CHANGE_DRAWING_STATUS: {
if (action.owner === "queryform" && action.status === "start") {
return assign({}, state, {toolbarEnabled: false});
Expand Down