diff --git a/package.json b/package.json index b87df76881..757b9b468a 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "react-color": "2.4.0", "react-confirm-button": "0.0.2", "react-copy-to-clipboard": "4.1.0", + "react-dock": "0.2.3", "react-dom": "0.14.8", "react-draggable": "1.3.4", "react-dnd": "2.1.3", @@ -133,7 +134,10 @@ "redux-undo": "0.5.0", "reselect": "2.5.1", "shpjs": "3.3.2", - "turf": "3.0.10", + "turf-buffer": "3.0.10", + "turf-intersect": "3.0.10", + "turf-union": "3.0.10", + "turf-bbox": "3.0.10", "url": "0.10.3", "w3c-schemas": "1.3.1", "xml2js": "0.4.17" diff --git a/web/client/actions/wfsquery.js b/web/client/actions/wfsquery.js new file mode 100644 index 0000000000..24542e4a3e --- /dev/null +++ b/web/client/actions/wfsquery.js @@ -0,0 +1,144 @@ +/** + * Copyright 2016, 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 FEATURE_TYPE_SELECTED = 'FEATURE_TYPE_SELECTED'; +const FEATURE_TYPE_LOADED = 'FEATURE_TYPE_LOADED'; +const FEATURE_LOADED = 'FEATURE_LOADED'; +const FEATURE_TYPE_ERROR = 'FEATURE_TYPE_ERROR'; +const FEATURE_ERROR = 'FEATURE_ERROR'; +const QUERY_RESULT = 'QUERY_RESULT'; +const QUERY_ERROR = 'QUERY_ERROR'; +const RESET_QUERY = 'RESET_QUERY'; + +const axios = require('../libs/ajax'); +function featureTypeSelected(url, typeName) { + return { + type: FEATURE_TYPE_SELECTED, + url, + typeName + }; +} +function featureTypeLoaded(typeName, featureType) { + return { + type: FEATURE_TYPE_LOADED, + typeName, + featureType + }; +} + +function featureTypeError(typeName, error) { + return { + type: FEATURE_TYPE_ERROR, + typeName, + error + }; +} + +function featureLoaded(typeName, feature) { + return { + type: FEATURE_LOADED, + typeName, + feature + }; +} + +function featureError(typeName, error) { + return { + type: FEATURE_ERROR, + typeName, + error + }; +} + +function querySearchResponse(result) { + return { + type: QUERY_RESULT, + result + }; +} + +function queryError(error) { + return { + type: QUERY_ERROR, + 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) => { + if (typeof response.data === 'object') { + dispatch(featureLoaded(typeName, response.data)); + } else { + try { + JSON.parse(response.data); + } catch(e) { + dispatch(featureError(typeName, 'Error from WFS: ' + e.message)); + } + + } + + }).catch((e) => { + dispatch(featureError(typeName, e)); + }); + }; +} + +function query(seachURL, data) { + return (dispatch) => { + return axios.post(seachURL + '?service=WFS&&outputFormat=json', data, { + timeout: 60000, + headers: {'Accept': 'application/json', 'Content-Type': 'application/json'} + }).then((response) => { + dispatch(querySearchResponse(response.data)); + }).catch((e) => { + dispatch(queryError(e)); + }); + }; +} + +function resetQuery() { + return { + type: RESET_QUERY + }; +} + +module.exports = { + FEATURE_TYPE_SELECTED, + FEATURE_TYPE_LOADED, + FEATURE_LOADED, + FEATURE_TYPE_ERROR, + FEATURE_ERROR, + QUERY_RESULT, + QUERY_ERROR, + RESET_QUERY, + featureTypeSelected, + describeFeatureType, + loadFeature, + query, + resetQuery +}; diff --git a/web/client/components/TOC/DefaultLayer.jsx b/web/client/components/TOC/DefaultLayer.jsx index 25767f74af..0f607e62d3 100644 --- a/web/client/components/TOC/DefaultLayer.jsx +++ b/web/client/components/TOC/DefaultLayer.jsx @@ -25,6 +25,7 @@ var DefaultLayer = React.createClass({ propertiesChangeHandler: React.PropTypes.func, retrieveLayerData: React.PropTypes.func, onToggle: React.PropTypes.func, + onToggleQuerypanel: React.PropTypes.func, onZoom: React.PropTypes.func, onSettings: React.PropTypes.func, style: React.PropTypes.object, @@ -36,6 +37,7 @@ var DefaultLayer = React.createClass({ activateLegendTool: React.PropTypes.bool, activateRemoveLayer: React.PropTypes.bool, activateSettingsTool: React.PropTypes.bool, + activateQueryTool: React.PropTypes.bool, activateZoomTool: React.PropTypes.bool, settingsText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), opacityText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), @@ -58,9 +60,11 @@ var DefaultLayer = React.createClass({ onZoom: () => {}, onSettings: () => {}, retrieveLayerData: () => {}, + onToggleQuerypanel: () => {}, activateRemoveLayer: false, activateLegendTool: false, activateSettingsTool: false, + activateQueryTool: false, activateZoomTool: false, includeDeleteButtonInSettings: false, modalOptions: {}, @@ -142,6 +146,16 @@ var DefaultLayer = React.createClass({ onClick={(node) => this.props.onToggle(node.id, node.expanded)}/> ); } + if (this.props.activateQueryTool) { + tools.push( + this.props.onToggleQuerypanel(node.url, node.name)}/> + ); + } if (this.props.activateZoomTool && this.props.node.bbox && !this.props.node.loadingError) { tools.push( { + return { + select: state.featuregrid && state.featuregrid.select || [], + selectAllActive: state.featuregrid && state.featuregrid.selectAll + }; +}, {})(require('./FeatureGrid')); + +const LocaleUtils = require('../../../utils/LocaleUtils'); +const I18N = require('../../../components/I18N/I18N'); +const Spinner = require('react-spinkit'); +const assign = require('object-assign'); + +const DockedFeatureGrid = React.createClass({ + propTypes: { + open: React.PropTypes.bool, + detailOpen: React.PropTypes.bool, + expanded: React.PropTypes.bool, + header: React.PropTypes.string, + features: React.PropTypes.oneOfType([ React.PropTypes.array, React.PropTypes.object]), + columnsDef: React.PropTypes.array, + map: React.PropTypes.object, + loadingGrid: React.PropTypes.bool, + loadingGridError: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.object + ]), + initWidth: React.PropTypes.oneOfType([ React.PropTypes.number, React.PropTypes.string ]), + params: React.PropTypes.object, + // featureGrigConfigUrl: React.PropTypes.string, + profile: React.PropTypes.string, + onDetail: React.PropTypes.func, + onShowDetail: React.PropTypes.func, + changeMapView: React.PropTypes.func, + // loadFeatureGridConfig: React.PropTypes.func, + onExpandFilterPanel: React.PropTypes.func, + selectFeatures: React.PropTypes.func, + totalFeatures: React.PropTypes.number, + pagination: React.PropTypes.bool, + filterFields: React.PropTypes.array, + groupFields: React.PropTypes.array, + spatialField: React.PropTypes.object, + featureTypeName: React.PropTypes.string, + ogcVersion: React.PropTypes.string, + onQuery: React.PropTypes.func, + searchUrl: React.PropTypes.string, + dataSourceOptions: React.PropTypes.object, + withMap: React.PropTypes.bool.isRequired, + onConfigureQuery: React.PropTypes.func, + attributes: React.PropTypes.array, + cleanError: React.PropTypes.func, + selectAllToggle: React.PropTypes.func, + templateProfile: React.PropTypes.string, + zoomToFeatureAction: React.PropTypes.func + }, + contextTypes: { + messages: React.PropTypes.object + }, + getInitialState() { + return {}; + }, + getDefaultProps() { + return { + open: true, + detailOpen: true, + loadingGrid: false, + loadingGridError: null, + attributes: [], + profile: null, + expanded: true, + header: "featuregrid.header", + features: [], + featureTypeName: null, + ogcVersion: "2.0", + columnsDef: [], + pagination: false, + params: {}, + groupFields: [], + filterFields: [], + spatialField: {}, + searchUrl: null, + dataSourceOptions: { + rowCount: -1, + pageSize: 10 + }, + initWidth: 600, + withMap: true, + templateProfile: 'default', + onDetail: () => {}, + onShowDetail: () => {}, + changeMapView: () => {}, + // loadFeatureGridConfig: () => {}, + onExpandFilterPanel: () => {}, + selectFeatures: () => {}, + onQuery: () => {}, + onConfigureQuery: () => {}, + cleanError: () => {}, + selectAllToggle: () => {} + }; + }, + componentWillMount() { + let height = getWindowSize().maxHeight - 108; + this.setState({width: `calc( ${this.props.initWidth} - 30px)`, height}); + if (this.props.pagination && this.props.dataSourceOptions.pageSize) { + this.dataSource = this.getDataSource(this.props.dataSourceOptions); + }else if ( this.props.pagination && !this.props.dataSourceOptions.pageSize) { + let newFilter = FilterUtils.getOgcAllPropertyValue(this.props.featureTypeName, this.props.attributes[0].attribute); + this.props.onConfigureQuery(this.props.searchUrl, newFilter, this.props.params, { + "maxFeatures": 15, + "startIndex": 0 + }); + } + }, + shouldComponentUpdate(nextProps) { + return Object.keys(this.props).reduce((prev, prop) => { + if ( !prev && (prop !== 'map' && this.props[prop] !== nextProps[prop])) { + return true; + } + return prev; + }, false); + }, + componentWillUpdate(nextProps) { + if (nextProps.initWidth !== this.props.initWidth) { + let height = getWindowSize().maxHeight - 108; + this.setState({width: `calc( ${this.props.initWidth} - 30px)`, height}); + } + if (!nextProps.loadingGrid && nextProps.pagination && (nextProps.dataSourceOptions !== this.props.dataSourceOptions)) { + this.dataSource = this.getDataSource(nextProps.dataSourceOptions); + } + if (!nextProps.loadingGrid && this.featureLoaded && nextProps.features !== this.props.features && Object.keys(nextProps.features).length > 0) { + let rowsThisPage = nextProps.features[this.getRequestId(this.featureLoaded)] || []; + this.featureLoaded.successCallback(rowsThisPage, nextProps.totalFeatures); + this.featureLoaded = null; + } + }, + onGridClose(filter) { + this.props.selectFeatures([]); + this.props.selectAllToggle(); + if (filter) { + this.props.onExpandFilterPanel(true); + } + }, + onResize(event, resize) { + let size = resize.size; + this.setState({width: size.width, height: size.height}); + + }, + getRequestId(params) { + return `${params.startRow}_${params.endRow}_${params.sortModel.map((m) => `${m.colId}_${m.sort}` ).join('_')}`; + }, + getSortAttribute(colId) { + let col = this.props.columnsDef.find((c) => colId === `properties.${c.field}`); + return col && col.sortAttribute ? col.sortAttribute : ''; + }, + getSortOptions(params) { + return params.sortModel.reduce((o, m) => ({sortBy: this.getSortAttribute(m.colId), sortOrder: m.sort}), {}); + }, + getFeatures(params) { + if (!this.props.loadingGrid) { + let reqId = this.getRequestId(params); + let rowsThisPage = this.props.features[reqId]; + if (rowsThisPage) { + params.successCallback(rowsThisPage, this.props.totalFeatures); + }else { + let pagination = {startIndex: params.startRow, maxFeatures: params.endRow - params.startRow}; + let filterObj = { + groupFields: this.props.groupFields, + filterFields: this.props.filterFields.filter((field) => field.value), + spatialField: this.props.spatialField, + pagination + }; + let filter = FilterUtils.toOGCFilter(this.props.featureTypeName, filterObj, this.props.ogcVersion, this.getSortOptions(params)); + this.featureLoaded = params; + this.sortModel = params.sortModel; + this.props.onQuery(this.props.searchUrl, filter, this.props.params, reqId); + } + } + }, + getDataSource(dataSourceOptions) { + return { + rowCount: dataSourceOptions.rowCount, + getRows: this.getFeatures, + pageSize: dataSourceOptions.pageSize, + overflowSize: 20 + }; + }, + renderHeader() { + const header = LocaleUtils.getMessageById(this.context.messages, this.props.header); + + return ( +
+ + + + {header} + + + + + + +
+ ); + }, + renderLoadingException(loadingError, msg) { + let exception; + if (isObject(loadingError)) { + exception = loadingError.status + + "(" + loadingError.statusText + ")" + + ": " + loadingError.data; + } else { + exception = loadingError; + } + + return ( + { + this.props.cleanError(false); + // this.onGridClose(true); + }}> + + + + +
{exception}
+
+ + +
+ ); + }, + render() { + let loadingError = this.props.loadingGridError; + if (loadingError) { + return ( + this.renderLoadingException(loadingError, "queryform.query_request_exception") + ); + } + + let cols = this.props.columnsDef.map((column) => { + if (!column.profiles || (column.profiles && column.profiles.indexOf(this.props.profile) !== -1)) { + return assign({}, column, {field: "properties." + column.field}); + } + }).filter((c) => c); + + if (this.sortModel && this.sortModel.length > 0) { + cols = cols.map((c) => { + let model = this.sortModel.find((m) => m.colId === c.field); + if ( model ) { + c.sort = model.sort; + } + return c; + }); + } + + let gridConf = this.props.pagination ? {dataSource: this.dataSource, features: []} : {features: this.props.features}; + + if (this.props.open || true) { + return ( + + +
+ +
Risultati - {this.props.totalFeatures !== -1 ? this.props.totalFeatures : ()}
+ + +
+ {this.props.loadingGrid ? (
+
+ +
+
) : null} +
+
+ ); + } + + return null; + }, + selectAll(select) { + if (select) { + let filterObj = { + groupFields: this.props.groupFields, + filterFields: this.props.filterFields.filter((field) => field.value), + spatialField: this.props.spatialField + }; + let SLD_BODY = FilterUtils.getSLD(this.props.featureTypeName, filterObj, '1.0'); + this.props.selectAllToggle(this.props.featureTypeName, SLD_BODY); + } else { + this.props.selectAllToggle(); + } + }, + selectFeatures(features) { + this.props.selectAllToggle(); + this.props.selectFeatures(features); + } +}); + +module.exports = DockedFeatureGrid; diff --git a/web/client/components/data/query/GroupField.jsx b/web/client/components/data/query/GroupField.jsx index 3f9c29db0e..b7bcd614e2 100644 --- a/web/client/components/data/query/GroupField.jsx +++ b/web/client/components/data/query/GroupField.jsx @@ -134,12 +134,12 @@ const GroupField = React.createClass({ { filterField.exception ? ( )}> - ) : ( - ) @@ -151,11 +151,9 @@ const GroupField = React.createClass({ renderGroupHeader(groupField) { const removeButton = groupField.groupId ? ( - - - ) : ( @@ -164,10 +162,12 @@ const GroupField = React.createClass({ return ( - {removeButton} - -
-
+ +
+ {removeButton} +
+
+ { @@ -175,17 +175,14 @@ const GroupField = React.createClass({ }) } fieldName="logic" - style={{width: "140px", marginTop: "3px"}} + style={{minWidth: "80px", display: "inline-block", position: "relative", top: "10px"}} fieldRowId={groupField.id} fieldValue={ LocaleUtils.getMessageById(this.context.messages, this.props.logicComboOptions.filter((opt) => groupField.logic === opt.logic)[0].name) } onUpdateField={this.updateLogicCombo}/> -
- - -
+
); @@ -207,7 +204,7 @@ const GroupField = React.createClass({ return element; }); - const removeButton = groupField.index <= this.props.groupLevels ? + const addButton = groupField.index <= this.props.groupLevels ? ( @@ -218,18 +215,14 @@ const GroupField = React.createClass({ return ( {this.renderGroupHeader(groupField)} - {container} - - - - - - {removeButton} - - +
{container}
+
+ + {addButton} +
); }, diff --git a/web/client/components/data/query/__tests__/ComboField-test.jsx b/web/client/components/data/query/__tests__/ComboField-test.jsx index a78fb9de24..e427d74581 100644 --- a/web/client/components/data/query/__tests__/ComboField-test.jsx +++ b/web/client/components/data/query/__tests__/ComboField-test.jsx @@ -62,4 +62,30 @@ describe('ComboField', () => { let rwPopup = comboFieldDOMNode.actual.getElementsByClassName('rw-popup-container rw-popup-animating')[0]; expect(rwPopup).toExist(); }); + + it('creates the ComboField with an exception message', () => { + let fieldOptions = [ + "attribute1", + "attribute2" + ]; + let fieldValue = "attribute2"; + let fieldRowId = 200; + + const combofield = ReactDOM.render( + , + document.getElementById("container") + ); + + expect(combofield).toExist(); + + const comboFieldDOMNode = expect(ReactDOM.findDOMNode(combofield)); + expect(comboFieldDOMNode).toExist(); + + }); }); diff --git a/web/client/components/data/query/__tests__/GroupField-test.jsx b/web/client/components/data/query/__tests__/GroupField-test.jsx index 17ac04f03a..e9b15997ee 100644 --- a/web/client/components/data/query/__tests__/GroupField-test.jsx +++ b/web/client/components/data/query/__tests__/GroupField-test.jsx @@ -101,11 +101,16 @@ describe('GroupField', () => { let groupPanel = containerGroupPanel.getElementsByClassName('panel-body')[0]; childNodes = groupPanel.childNodes; - expect(childNodes.length).toBe(4); + expect(childNodes.length).toBe(3); for (let i = 0; i < childNodes.length; i++) { let child = childNodes[i]; - expect(child.className === "logicHeader row" || child.className === "row").toBe(true); + expect( + child.className === "logicHeader row" + || child.className === "row" + || child.className === "query-content" + || child.className === "query-buttons" + ).toBe(true); } const buttons = document.getElementsByClassName('btn btn-default'); diff --git a/web/client/components/data/query/queryform.css b/web/client/components/data/query/queryform.css index 5804fe4143..40c36c2e21 100644 --- a/web/client/components/data/query/queryform.css +++ b/web/client/components/data/query/queryform.css @@ -13,6 +13,11 @@ margin-left: 1px; margin-right: 1px; } +.logicHeader .query-remove{ + float: right; + margin-right: 10px; + padding-top: 10px; +} #queryFormPanel .panel-title{ font-size: 16px; @@ -23,6 +28,54 @@ text-decoration:none; } +/*****************************/ +/* DRAWER MENU ***************/ +/*****************************/ +#mapstore-drawermenu #queryFormPanel .querypanel { + height: 100%; + max-height: 100%; +} +#mapstore-drawermenu #queryFormPanel .queryFormToolbar button:disabled{ + color: gray; +} +#mapstore-drawermenu #queryFormPanel .logicHeader { + margin: 0; + background-color: #078aa3; + color: white; +} +#mapstore-drawermenu #queryFormPanel .logicHeader .query-remove { + float: right; + padding-top: 0px; + top: 10px; + margin-right: 10px; +} + +#mapstore-drawermenu #queryFormPanel .btn.remove-filter-button { + padding: 3px 6px; +} + +#mapstore-drawermenu #queryFormPanel .logicHeader .group_label_a,#mapstore-drawermenu #queryFormPanel .logicHeader .group_label_b { + margin: auto 5px; +} + +/* nested groups */ +#mapstore-drawermenu #queryFormPanel .panel .panel .panel{ + margin-left: 10px; +} +#mapstore-drawermenu #queryFormPanel .panel .panel .panel > .panel-body{ + border: thin solid #078aa3; + margin-top: 1px; +} +#mapstore-drawermenu #queryFormPanel .query-content, +#mapstore-drawermenu #queryFormPanel .query-buttons +{ + margin-left: 3px; + margin-bottom: 3px; +} +/*****************************/ +/* DRAWER MENU END ***********/ +/*****************************/ + .detail_geom_button{ text-align: right; margin-top: 3px; diff --git a/web/client/examples/queryform/components/Results.jsx b/web/client/examples/queryform/components/Results.jsx index ff59c80fc9..c046905352 100644 --- a/web/client/examples/queryform/components/Results.jsx +++ b/web/client/examples/queryform/components/Results.jsx @@ -13,7 +13,7 @@ const JSONViewer = require('../../../components/data/identify/viewers/JSONViewer const {Panel} = require('react-bootstrap'); const Draggable = require('react-draggable'); -const {resetQuery} = require('../actions/query'); +const {resetQuery} = require('../actions/wfsquery'); const Results = React.createClass({ propTypes: { diff --git a/web/client/localConfig.json b/web/client/localConfig.json index 4fb0dbda1a..58bb307720 100644 --- a/web/client/localConfig.json +++ b/web/client/localConfig.json @@ -1,18 +1,18 @@ { - "proxyUrl": "/mapstore/proxy/?url=", - "geoStoreUrl": "/mapstore/rest/geostore/", + "proxyUrl": "/mapstore/proxy/?url=", + "geoStoreUrl": "/mapstore/rest/geostore/", "printUrl": "http://demo.geo-solutions.it/geoserver/pdf/info.json", "bingApiKey": "AhuXBu7ipR1gNbBfXhtUAyCZ6rkC5PkWpxs2MnMRZ1ZupxQfivjLCch22ozKSCAn", "mapquestApiKey": "__API_KEY_MAPQUEST__", - "initialMapFilter": "", + "initialMapFilter": "", "ignoreMobileCss" : true, "useAuthenticationRules": true, - "authenticationRules": [ - { - "urlPattern": ".*geostore.*", - "method": "basic" - } - ], + "authenticationRules": [ + { + "urlPattern": ".*geostore.*", + "method": "basic" + } + ], "plugins": { "mobile": [ { @@ -24,141 +24,141 @@ }, { "name": "DrawerMenu", - "cfg": { - "glyph": "1-stilo", - "buttonStyle": "primary", - "buttonClassName": "square-button", - "singleSection": true - } - }, - { - "name": "Identify", - "showIn": ["Settings"], - "cfg": { - "enableRevGeocode": true, - "wrapRevGeocode": false, - "panelClassName": "modal-dialog info-panel modal-content", - "headerClassName": "modal-header", - "bodyClassName": "modal-body info-wrap", - "headerGlyph": "", - "glyph": "map-marker", - "className": "square-button", - "closeGlyph": "1-close", - "style": { - "position": "absolute", - "width": "100%", - "bottom": "0px", - "zIndex": 1023, - "maxHeight": "70%", - "marginBottom": 0 - }, - "draggable": false, - "collapsible": true, - "viewerOptions": { - "container": "{context.ReactSwipe}", - "header": "{context.SwipeHeader}", - "collapsible": false - }, - "bodyClass": "mobile-feature-info" - } - }, + "cfg": { + "glyph": "1-stilo", + "buttonStyle": "primary", + "buttonClassName": "square-button", + "singleSection": true + } + }, + { + "name": "Identify", + "showIn": ["Settings"], + "cfg": { + "enableRevGeocode": true, + "wrapRevGeocode": false, + "panelClassName": "modal-dialog info-panel modal-content", + "headerClassName": "modal-header", + "bodyClassName": "modal-body info-wrap", + "headerGlyph": "", + "glyph": "map-marker", + "className": "square-button", + "closeGlyph": "1-close", + "style": { + "position": "absolute", + "width": "100%", + "bottom": "0px", + "zIndex": 1023, + "maxHeight": "70%", + "marginBottom": 0 + }, + "draggable": false, + "collapsible": true, + "viewerOptions": { + "container": "{context.ReactSwipe}", + "header": "{context.SwipeHeader}", + "collapsible": false + }, + "bodyClass": "mobile-feature-info" + } + }, { - "name": "Locate", - "cfg": { - "glyph": "1-position-1", + "name": "Locate", + "cfg": { + "glyph": "1-position-1", "btnConfig": { "className": "square-button" } - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "Home" - }, { - "name": "TOC", - "cfg": { - "visibilityCheckType": "glyph", - "settingsOptions": { - "includeCloseButton": false, - "closeGlyph": "1-close", - "asModal": false, - "buttonSize": "small" - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-layer", + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { + "name": "Home" + }, { + "name": "TOC", + "cfg": { + "visibilityCheckType": "glyph", + "settingsOptions": { + "includeCloseButton": false, + "closeGlyph": "1-close", + "asModal": false, + "buttonSize": "small" + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-layer", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "BackgroundSwitcher", - "cfg": { - "fluid": true, - "columnProperties": { + } + } + }, { + "name": "BackgroundSwitcher", + "cfg": { + "fluid": true, + "columnProperties": { "xs": 12, "sm": 12, "md": 12 } - }, - "override": { - "DrawerMenu": { - "glyph": "1-map", + }, + "override": { + "DrawerMenu": { + "glyph": "1-map", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "Settings", - "cfg": { - "wrapWithPanel": false, - "closeGlyph": "1-close", + } + } + }, { + "name": "Settings", + "cfg": { + "wrapWithPanel": false, + "closeGlyph": "1-close", "overrideSettings": { "history": false }, - "wrap": true - } - }, { - "name": "About", - "hideFrom": "DrawerMenu", - "cfg": { - "modalConfig": { - "useModal": false, - "closeGlyph": "1-close" - } - } - }, + "wrap": true + } + }, { + "name": "About", + "hideFrom": "DrawerMenu", + "cfg": { + "modalConfig": { + "useModal": false, + "closeGlyph": "1-close" + } + } + }, { - "name": "MousePosition", - "cfg": { - "id": "mapstore-mouseposition-mobile" - } + "name": "MousePosition", + "cfg": { + "id": "mapstore-mouseposition-mobile" + } }, - { - "name": "Search", - "cfg": { - "withToggle": ["max-width: 768px", "min-width: 768px"] - } - }, { + { + "name": "Search", + "cfg": { + "withToggle": ["max-width: 768px", "min-width: 768px"] + } + }, { "name": "Toolbar", "id": "NavigationBar", "stateSelector": "toolbar", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "id": "navigationBar" } }, { "name": "Toolbar", "id": "IdentifyBar", - "stateSelector": "identify", + "stateSelector": "identify", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "pressedButtonStyle": "success", "id": "identifyBar" }, @@ -172,186 +172,186 @@ } }, { - "name": "MapLoading", - "hide": true, - "cfg": { - "className": "ms2-loading" - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "Login", - "hide": true, + "name": "MapLoading", + "hide": true, + "cfg": { + "className": "ms2-loading" + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { + "name": "Login", + "hide": true, "cfg": { "nav": false, "menuProps": { "noCaret": true }, - "toolsCfg": [{ - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }] + "toolsCfg": [{ + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }] } - }, - "OmniBar", - { - "name": "BurgerMenu", - "hide": true - }, { - "name": "Expander", - "hide": true, + }, + "OmniBar", + "FeatureGrid", + { + "name": "BurgerMenu", + "hide": true + }, { + "name": "Expander", + "hide": true, "cfg":{ "className": "square-button" } - } + } ], - "desktop": [{ - "name": "Map", - "cfg": { - "toolsOptions": { - "scalebar": { - "leaflet": { - "position": "bottomright" - } - } - }, + "desktop": [{ + "name": "Map", + "cfg": { + "toolsOptions": { + "scalebar": { + "leaflet": { + "position": "bottomright" + } + } + }, "zoomControl": false - } - }, { - "name": "Help", - "cfg": { - "asPanel": false, - "closeGlyph": "1-close" - } - }, { - "name" : "Share", - "cfg": { - "closeGlyph" : "1-close" - } - }, { + } + }, { + "name": "Help", + "cfg": { + "asPanel": false, + "closeGlyph": "1-close" + } + }, { + "name" : "Share", + "cfg": { + "closeGlyph" : "1-close" + } + }, { "name": "DrawerMenu", - "cfg": { - "glyph": "1-stilo", - "buttonStyle": "primary", - "buttonClassName": "square-button", - "singleSection": true - } + "cfg": { + "glyph": "1-stilo", + "buttonStyle": "primary", + "buttonClassName": "square-button", + "singleSection": true + } }, { "name": "Identify", "showIn": ["IdentifyBar", "Settings"], - "cfg": { + "cfg": { "panelClassName": "modal-dialog info-panel modal-content", - "headerClassName": "modal-header", - "bodyClassName": "modal-body info-wrap", - "asPanel": false, - "headerGlyph": "", + "headerClassName": "modal-header", + "bodyClassName": "modal-body info-wrap", + "asPanel": false, + "headerGlyph": "", "glyph": "map-marker", - "className": "square-button", + "className": "square-button", "closeGlyph": "1-close", - "wrapRevGeocode": false, - "enableRevGeocode": true, - "viewerOptions": { - "container": "{context.ReactSwipe}", - "header": "{context.SwipeHeader}", - "headerOptions": { - "useButtons": true - }, - "containerProps": { - "continuous": false - }, - "collapsible": false - }, - "excludeOptions": [ - "links", - "allowedSRS", - "title", - "type", - "visibility", - "params", - "legend", - "statistics", - "columnDefs", - "advancedFilter", - "boundingBox", - "capabilities", - "describeCoverage", - "describeLayer", - "loading" - - ] - } + "wrapRevGeocode": false, + "enableRevGeocode": true, + "viewerOptions": { + "container": "{context.ReactSwipe}", + "header": "{context.SwipeHeader}", + "headerOptions": { + "useButtons": true + }, + "containerProps": { + "continuous": false + }, + "collapsible": false + }, + "excludeOptions": [ + "links", + "allowedSRS", + "title", + "type", + "visibility", + "params", + "legend", + "statistics", + "columnDefs", + "advancedFilter", + "boundingBox", + "capabilities", + "describeCoverage", + "describeLayer", + "loading" + ] + } }, "MadeWithLove", { - "name": "Locate", - "cfg": { - "glyph": "1-position-1", + "name": "Locate", + "cfg": { + "glyph": "1-position-1", "btnConfig": { "className": "square-button" } - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "Home" - }, { - "name": "TOC", - "cfg": { - "visibilityCheckType": "glyph", - "settingsOptions": { - "includeCloseButton": false, - "closeGlyph": "1-close", - "asModal": false, - "buttonSize": "small" - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-layer", + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { + "name": "Home" + }, { + "name": "TOC", + "cfg": { + "visibilityCheckType": "glyph", + "settingsOptions": { + "includeCloseButton": false, + "closeGlyph": "1-close", + "asModal": false, + "buttonSize": "small" + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-layer", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "BackgroundSwitcher", - "cfg": { - "fluid": true, - "columnProperties": { + } + } + }, { + "name": "BackgroundSwitcher", + "cfg": { + "fluid": true, + "columnProperties": { "xs": 12, "sm": 12, "md": 12 } - }, - "override": { - "DrawerMenu": { - "glyph": "1-map", + }, + "override": { + "DrawerMenu": { + "glyph": "1-map", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "Measure", + } + } + }, { + "name": "Measure", "cfg":{ "showResults": false, "lineGlyph": "1-measure-lenght", @@ -359,103 +359,103 @@ "bearingGlyph": "1-bearing", "useButtonGroup": false }, - "override": { - "DrawerMenu": { - "glyph": "1-stilo", + "override": { + "DrawerMenu": { + "glyph": "1-stilo", "buttonConfig": { "buttonClassName": "square-button no-border" } - } - } - }, { - "name": "MeasureResults", + } + } + }, { + "name": "MeasureResults", "cfg":{ "closeGlyph": "1-close", "withPanelAsContainer": false } - }, { - "name": "Print", - "cfg": { - "useFixedScales": false, - "syncMapPreview": true, - "mapPreviewOptions": { - "enableScalebox": false, - "enableRefresh": false - }, - "closeGlyph": "1-close", - "submitConfig": { - "buttonConfig": { - "bsSize": "small", - "bsStyle": "primary" - }, - "glyph": "" - }, - "previewOptions": { - "buttonStyle": "primary" - }, - "withPanelAsContainer": false - } - }, { - "name": "ShapeFile", - "cfg": { - "wrap": true, - "wrapWithPanel": false, - "closeGlyph": "1-close", - "buttonSize": "small" - } - }, { - "name": "Settings", - "hideFrom": ["Toolbar", "DrawerMenu"], - "cfg": { - "wrapWithPanel": false, - "closeGlyph": "1-close", + }, { + "name": "Print", + "cfg": { + "useFixedScales": false, + "syncMapPreview": true, + "mapPreviewOptions": { + "enableScalebox": false, + "enableRefresh": false + }, + "closeGlyph": "1-close", + "submitConfig": { + "buttonConfig": { + "bsSize": "small", + "bsStyle": "primary" + }, + "glyph": "" + }, + "previewOptions": { + "buttonStyle": "primary" + }, + "withPanelAsContainer": false + } + }, { + "name": "ShapeFile", + "cfg": { + "wrap": true, + "wrapWithPanel": false, + "closeGlyph": "1-close", + "buttonSize": "small" + } + }, { + "name": "Settings", + "hideFrom": ["Toolbar", "DrawerMenu"], + "cfg": { + "wrapWithPanel": false, + "closeGlyph": "1-close", "overrideSettings": { "history": false }, - "wrap": true - } - }, { - "name": "MetadataExplorer", - "cfg": { - "showGetCapLinks": false, - "addAuthentication": false, - "wrap": true, - "searchOnStarup": false, - "wrapWithPanel": false, - "closeGlyph": "1-close", - "initialCatalogURL": { - "csw": "http://demo.geo-solutions.it/geoserver/csw", - "wms": "http://demo.geo-solutions.it/geoserver/wms" - } - } - }, { - "name": "About", - "hideFrom": ["DrawerMenu"], - "cfg": { - "modalConfig": { - "useModal": false, - "closeGlyph": "1-close" - } - } - }, "MousePosition", { - "name": "Search", - "cfg": { - "withToggle": ["max-width: 768px", "min-width: 768px"] - } - }, { + "wrap": true + } + }, { + "name": "MetadataExplorer", + "cfg": { + "showGetCapLinks": false, + "addAuthentication": false, + "wrap": true, + "searchOnStarup": false, + "wrapWithPanel": false, + "closeGlyph": "1-close", + "initialCatalogURL": { + "csw": "http://demo.geo-solutions.it/geoserver/csw", + "wms": "http://demo.geo-solutions.it/geoserver/wms" + } + } + }, { + "name": "About", + "hideFrom": ["DrawerMenu"], + "cfg": { + "modalConfig": { + "useModal": false, + "closeGlyph": "1-close" + } + } + }, "MousePosition", { + "name": "Search", + "cfg": { + "withToggle": ["max-width: 768px", "min-width: 768px"] + } + }, { "name": "Toolbar", "id": "NavigationBar", "stateSelector": "toolbar", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "id": "navigationBar" } }, { "name": "Toolbar", "id": "IdentifyBar", - "stateSelector": "identify", + "stateSelector": "identify", "cfg": { - "buttonStyle": "primary", + "buttonStyle": "primary", "pressedButtonStyle": "success", "id": "identifyBar" }, @@ -469,86 +469,86 @@ } }, { - "name": "MapLoading", - "cfg": { - "className": "ms2-loading" - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, + "name": "MapLoading", + "cfg": { + "className": "ms2-loading" + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, { - "name": "Snapshot", - "cfg": { - "wrap": true, - "wrapWithPanel": false, - "closeGlyph": "1-close", - "buttonStyle": "primary" - } - }, + "name": "Snapshot", + "cfg": { + "wrap": true, + "wrapWithPanel": false, + "closeGlyph": "1-close", + "buttonStyle": "primary" + } + }, { "name":"ZoomIn", - "override": { - "Toolbar": { - "alwaysVisible": true - } - } + "override": { + "Toolbar": { + "alwaysVisible": true + } + } }, { "name":"ZoomOut", - "override": { - "Toolbar": { - "alwaysVisible": true - } - } + "override": { + "Toolbar": { + "alwaysVisible": true + } + } }, - "OmniBar", + "OmniBar", { - "name": "Login", + "name": "Login", "cfg": { "nav": false, "menuProps": { "noCaret": true }, - "toolsCfg": [{ - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }] + "toolsCfg": [{ + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }] } - }, - { - "name" : "Save", - "cfg": { - "closeGlyph" : "1-close" - } - },{ - "name" : "SaveAs", - "cfg": { - "closeGlyph" : "1-close" - } - }, - { - "name": "BurgerMenu" - }, { - "name": "Expander", + }, + { + "name" : "Save", + "cfg": { + "closeGlyph" : "1-close" + } + },{ + "name" : "SaveAs", + "cfg": { + "closeGlyph" : "1-close" + } + }, + { + "name": "BurgerMenu" + }, { + "name": "Expander", "cfg":{ "className": "square-button" } - }, { + }, { "name": "Undo", "cfg": { "glyph": "1-screen-backward", @@ -568,269 +568,269 @@ } }], "embedded": [ - { - "name": "Map", - "cfg": { - "zoomControl": false, - "tools": ["locate"] - } - }, { - "name": "Help" - }, { - "name": "DrawerMenu", - "cfg": { - "glyph": "1-stilo", - "buttonStyle": "primary", - "buttonClassName": "square-button", - "singleSection": true - } - }, - { - "name": "Identify", - "showIn": ["Settings"], - "cfg": { - "enableRevGeocode": true, - "wrapRevGeocode": false, - "panelClassName": "modal-dialog info-panel modal-content", - "headerClassName": "modal-header", - "bodyClassName": "modal-body info-wrap", - "headerGlyph": "", - "glyph": "map-marker", - "className": "square-button", - "closeGlyph": "1-close", - "style": { - "position": "absolute", - "width": "100%", - "bottom": "0px", - "zIndex": 1010, - "maxHeight": "70%", - "marginBottom": 0 - }, - "excludeOptions": [ - "links", - "allowedSRS", - "title", - "type", - "visibility", - "params", - "legend", - "statistics", - "columnDefs", - "advancedFilter", - "boundingBox", - "capabilities", - "describeCoverage", - "describeLayer", - "loading" + { + "name": "Map", + "cfg": { + "zoomControl": false, + "tools": ["locate"] + } + }, { + "name": "Help" + }, { + "name": "DrawerMenu", + "cfg": { + "glyph": "1-stilo", + "buttonStyle": "primary", + "buttonClassName": "square-button", + "singleSection": true + } + }, + { + "name": "Identify", + "showIn": ["Settings"], + "cfg": { + "enableRevGeocode": true, + "wrapRevGeocode": false, + "panelClassName": "modal-dialog info-panel modal-content", + "headerClassName": "modal-header", + "bodyClassName": "modal-body info-wrap", + "headerGlyph": "", + "glyph": "map-marker", + "className": "square-button", + "closeGlyph": "1-close", + "style": { + "position": "absolute", + "width": "100%", + "bottom": "0px", + "zIndex": 1010, + "maxHeight": "70%", + "marginBottom": 0 + }, + "excludeOptions": [ + "links", + "allowedSRS", + "title", + "type", + "visibility", + "params", + "legend", + "statistics", + "columnDefs", + "advancedFilter", + "boundingBox", + "capabilities", + "describeCoverage", + "describeLayer", + "loading" - ], - "draggable": false, - "collapsible": true, - "viewerOptions": { - "container": "{context.ReactSwipe}", - "header": "{context.SwipeHeader}", - "collapsible": false - }, - "bodyClass": "mobile-feature-info" - } - }, - { - "name": "Locate", - "cfg": { - "glyph": "1-position-1", - "btnConfig": { - "className": "square-button" - } - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, { - "name": "TOC", - "cfg": { - "visibilityCheckType": "glyph", - "settingsOptions": { - "includeCloseButton": false, - "closeGlyph": "1-close", - "asModal": false, - "buttonSize": "small" - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-layer", - "buttonConfig": { - "buttonClassName": "square-button no-border" - } - } - } - }, { - "name": "BackgroundSwitcher", - "cfg": { - "fluid": true, - "columnProperties": { - "xs": 12, - "sm": 12, - "md": 12 - } - }, - "override": { - "DrawerMenu": { - "glyph": "1-map", - "buttonConfig": { - "buttonClassName": "square-button no-border" - } - } - } - }, { - "name": "Settings", - "cfg": { - "wrapWithPanel": false, - "closeGlyph": "1-close", - "overrideSettings": { - "history": false - }, - "wrap": true - } - }, { - "name": "About", - "hideFrom": "DrawerMenu", - "cfg": { - "modalConfig": { - "useModal": false, - "closeGlyph": "1-close" - } - } - }, - { - "name": "MousePosition", - "cfg": { - "id": "mapstore-mouseposition-mobile" - } - }, - { - "name": "Search", - "cfg": { - "withToggle": ["max-width: 768px", "min-width: 768px"] - } - }, { - "name": "Toolbar", - "id": "NavigationBar", - "stateSelector": "toolbar", - "cfg": { - "buttonStyle": "primary", - "id": "navigationBar" - } - }, { - "name": "Toolbar", - "id": "IdentifyBar", - "stateSelector": "identify", - "cfg": { - "buttonStyle": "primary", - "pressedButtonStyle": "success", - "id": "identifyBar" - }, - "isDefault": false - }, - { - "name": "MapLoading", - "hide": true, - "cfg": { - "className": "ms2-loading" - }, - "override": { - "Toolbar": { - "alwaysVisible": true - } - } - }, - "OmniBar", - { - "name": "BurgerMenu", - "hide": true - } - ], - "page": [{ - "name": "OmniBar", - "cfg": { - "className": "navbar shadow navbar-home" - } - }, { - "name":"ManagerMenu", - "hide": true, - "cfg": { - "entries": [{ - "msgId": "users.title", - "glyph": "1-group-mod", - "path": "/manager/usermanager" - }] - } - }, { - "name": "Login", - "hide": true, + ], + "draggable": false, + "collapsible": true, + "viewerOptions": { + "container": "{context.ReactSwipe}", + "header": "{context.SwipeHeader}", + "collapsible": false + }, + "bodyClass": "mobile-feature-info" + } + }, + { + "name": "Locate", + "cfg": { + "glyph": "1-position-1", + "btnConfig": { + "className": "square-button" + } + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, "FeatureGrid", { + "name": "TOC", + "cfg": { + "visibilityCheckType": "glyph", + "settingsOptions": { + "includeCloseButton": false, + "closeGlyph": "1-close", + "asModal": false, + "buttonSize": "small" + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-layer", + "buttonConfig": { + "buttonClassName": "square-button no-border" + } + } + } + }, { + "name": "BackgroundSwitcher", + "cfg": { + "fluid": true, + "columnProperties": { + "xs": 12, + "sm": 12, + "md": 12 + } + }, + "override": { + "DrawerMenu": { + "glyph": "1-map", + "buttonConfig": { + "buttonClassName": "square-button no-border" + } + } + } + }, { + "name": "Settings", + "cfg": { + "wrapWithPanel": false, + "closeGlyph": "1-close", + "overrideSettings": { + "history": false + }, + "wrap": true + } + }, { + "name": "About", + "hideFrom": "DrawerMenu", + "cfg": { + "modalConfig": { + "useModal": false, + "closeGlyph": "1-close" + } + } + }, + { + "name": "MousePosition", + "cfg": { + "id": "mapstore-mouseposition-mobile" + } + }, + { + "name": "Search", + "cfg": { + "withToggle": ["max-width: 768px", "min-width: 768px"] + } + }, { + "name": "Toolbar", + "id": "NavigationBar", + "stateSelector": "toolbar", + "cfg": { + "buttonStyle": "primary", + "id": "navigationBar" + } + }, { + "name": "Toolbar", + "id": "IdentifyBar", + "stateSelector": "identify", + "cfg": { + "buttonStyle": "primary", + "pressedButtonStyle": "success", + "id": "identifyBar" + }, + "isDefault": false + }, + { + "name": "MapLoading", + "hide": true, + "cfg": { + "className": "ms2-loading" + }, + "override": { + "Toolbar": { + "alwaysVisible": true + } + } + }, + "OmniBar", + { + "name": "BurgerMenu", + "hide": true + } + ], + "page": [{ + "name": "OmniBar", + "cfg": { + "className": "navbar shadow navbar-home" + } + }, { + "name":"ManagerMenu", + "hide": true, + "cfg": { + "entries": [{ + "msgId": "users.title", + "glyph": "1-group-mod", + "path": "/manager/usermanager" + }] + } + }, { + "name": "Login", + "hide": true, "cfg": { "nav": false, "menuProps": { "noCaret": true - }, - "toolsCfg": [{ - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }, { - "buttonSize": "small", - "includeCloseButton": false, - "useModal": false, - "closeGlyph": "1-close" - }] + }, + "toolsCfg": [{ + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }, { + "buttonSize": "small", + "includeCloseButton": false, + "useModal": false, + "closeGlyph": "1-close" + }] - } - }, {"name": "Language"}, { - "name" : "Attribution", - "hide": true - }, { - "name": "Footer", - "cfg": { - "bottom": true - } - }], + } + }, {"name": "Language"}, { + "name" : "Attribution", + "hide": true + }, { + "name": "Footer", + "cfg": { + "bottom": true + } + }], - "maps": [ - {"name": "Header"}, - {"name": "Fork"}, - {"name": "MapSearch"}, - {"name": "HomeDescription"}, - {"name": "MapType"}, - {"name": "CreateNewMap"}, - {"name": "Maps"}, - {"name": "Examples"} - ], - "manager": [{ - "name": "Header" - }, "Redirect",{ - "name": "Manager", - "cfg": { - "navStyle": { - "flex": "inherit" - } - } - }, { - "name": "Home", - "hide": true - }, { - "name": "UserManager", - "hide": true, - "cfg": { - "id": "usermanager" - } - }] - } + "maps": [ + {"name": "Header"}, + {"name": "Fork"}, + {"name": "MapSearch"}, + {"name": "HomeDescription"}, + {"name": "MapType"}, + {"name": "CreateNewMap"}, + {"name": "Maps"}, + {"name": "Examples"} + ], + "manager": [{ + "name": "Header" + }, "Redirect",{ + "name": "Manager", + "cfg": { + "navStyle": { + "flex": "inherit" + } + } + }, { + "name": "Home", + "hide": true + }, { + "name": "UserManager", + "hide": true, + "cfg": { + "id": "usermanager" + } + }] + } } diff --git a/web/client/plugins/DrawerMenu.jsx b/web/client/plugins/DrawerMenu.jsx index 2bd835570d..4631b41f69 100644 --- a/web/client/plugins/DrawerMenu.jsx +++ b/web/client/plugins/DrawerMenu.jsx @@ -23,7 +23,8 @@ const {partialRight} = require('lodash'); const Menu = connect((state) => ({ show: state.controls.drawer && state.controls.drawer.enabled, - activeKey: state.controls.drawer && state.controls.drawer.menu + activeKey: state.controls.drawer && state.controls.drawer.menu, + width: (state.controls.queryPanel && state.controls.queryPanel.enabled) ? 700 : 300 }), { onToggle: toggleControl.bind(null, 'drawer', null), onChoose: partialRight(setControlProperty.bind(null, 'drawer', 'menu'), true), diff --git a/web/client/plugins/FeatureGrid.jsx b/web/client/plugins/FeatureGrid.jsx new file mode 100644 index 0000000000..4d89a0f02c --- /dev/null +++ b/web/client/plugins/FeatureGrid.jsx @@ -0,0 +1,25 @@ +/** + * Copyright 2016, 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 {connect} = require('react-redux'); + +module.exports = { + FeatureGridPlugin: connect((state) => ({ + features: state.query && state.query.result && state.query.result.features, + initWidth: "100%", + columnsDef: state.query && state.query.typeName && state.query.featureTypes + && state.query.featureTypes[state.query.typeName] + && state.query.featureTypes[state.query.typeName] + && state.query.featureTypes[state.query.typeName].attributes + && state.query.featureTypes[state.query.typeName].attributes.filter(attr => attr.name !== "geometry") + .map((attr) => ({ + headerName: attr.label, + field: attr.attribute + })), + totalFeatures: state.query && state.query.result && state.query.result.totalFeatures + }))(require('../components/data/featuregrid/DockedFeatureGrid')) +}; diff --git a/web/client/plugins/QueryPanel.jsx b/web/client/plugins/QueryPanel.jsx new file mode 100644 index 0000000000..2a26e19972 --- /dev/null +++ b/web/client/plugins/QueryPanel.jsx @@ -0,0 +1,235 @@ +/** + * Copyright 2016, 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 React = require('react'); +const {connect} = require('react-redux'); +const Sidebar = require('react-sidebar').default; +const {createSelector} = require('reselect'); +const {changeLayerProperties, changeGroupProperties, toggleNode, + sortNode, showSettings, hideSettings, updateSettings, updateNode, removeNode, getLayerCapabilities} = require('../actions/layers'); +const {zoomToExtent} = require('../actions/map'); +const {toggleControl} = require('../actions/controls'); + +const {groupsSelector} = require('../selectors/layers'); + +const LayersUtils = require('../utils/LayersUtils'); + +// include application component +const QueryBuilder = require('../components/data/query/QueryBuilder'); + +const {bindActionCreators} = require('redux'); +const { + // QueryBuilder action functions + addGroupField, + addFilterField, + removeFilterField, + updateFilterField, + updateExceptionField, + updateLogicCombo, + removeGroupField, + changeCascadingValue, + expandAttributeFilterPanel, + expandSpatialFilterPanel, + selectSpatialMethod, + selectSpatialOperation, + removeSpatialSelection, + showSpatialSelectionDetails, + reset, + changeDwithinValue, + zoneGetValues, + zoneSearch, + zoneChange +} = require('../actions/queryform'); + +const {query} = require('../actions/wfsquery'); + +const { + changeDrawingStatus, + endDrawing +} = require('../actions/draw'); + +// connecting a Dumb component to the store +// makes it a smart component +// we both connect state => props +// and actions to event handlers +const SmartQueryForm = connect((state) => { + return { + // QueryBuilder props + useMapProjection: state.queryform.useMapProjection, + groupLevels: state.queryform.groupLevels, + groupFields: state.queryform.groupFields, + filterFields: state.queryform.filterFields, + spatialField: state.queryform.spatialField, + showDetailsPanel: state.queryform.showDetailsPanel, + toolbarEnabled: state.queryform.toolbarEnabled, + attributePanelExpanded: state.queryform.attributePanelExpanded, + spatialPanelExpanded: state.queryform.spatialPanelExpanded, + searchUrl: "http://demo.geo-solutions.it/geoserver/ows?service=WFS", + featureTypeName: "topp:states", + ogcVersion: "1.1.0", + resultTitle: "Query Result", + showGeneratedFilter: false + }; +}, dispatch => { + return { + attributeFilterActions: bindActionCreators({ + onAddGroupField: addGroupField, + onAddFilterField: addFilterField, + onRemoveFilterField: removeFilterField, + onUpdateFilterField: updateFilterField, + onUpdateExceptionField: updateExceptionField, + onUpdateLogicCombo: updateLogicCombo, + onRemoveGroupField: removeGroupField, + onChangeCascadingValue: changeCascadingValue, + onExpandAttributeFilterPanel: expandAttributeFilterPanel + }, dispatch), + spatialFilterActions: bindActionCreators({ + onExpandSpatialFilterPanel: expandSpatialFilterPanel, + onSelectSpatialMethod: selectSpatialMethod, + onSelectSpatialOperation: selectSpatialOperation, + onChangeDrawingStatus: changeDrawingStatus, + onRemoveSpatialSelection: removeSpatialSelection, + onShowSpatialSelectionDetails: showSpatialSelectionDetails, + onEndDrawing: endDrawing, + onChangeDwithinValue: changeDwithinValue, + zoneFilter: zoneGetValues, + zoneSearch, + zoneChange + }, dispatch), + queryToolbarActions: bindActionCreators({ + onQuery: query, + onReset: reset, + onChangeDrawingStatus: changeDrawingStatus + }, dispatch) + }; +})(QueryBuilder); + +const tocSelector = createSelector( + [ + (state) => state.controls && state.controls.toolbar && state.controls.toolbar.active === 'toc', + groupsSelector, + (state) => state.layers.settings || {expanded: false, options: {opacity: 1}}, + (state) => state.controls && state.controls.queryPanel && state.controls.queryPanel.enabled || false + ], (enabled, groups, settings, querypanelEnabled) => ({ + enabled, + groups, + settings, + querypanelEnabled + }) +); + +const LayerTree = React.createClass({ + propTypes: { + id: React.PropTypes.number, + buttonContent: React.PropTypes.node, + groups: React.PropTypes.array, + settings: React.PropTypes.object, + querypanelEnabled: React.PropTypes.bool, + groupStyle: React.PropTypes.object, + groupPropertiesChangeHandler: React.PropTypes.func, + layerPropertiesChangeHandler: React.PropTypes.func, + onToggleGroup: React.PropTypes.func, + onToggleLayer: React.PropTypes.func, + onToggleQuery: React.PropTypes.func, + onZoomToExtent: React.PropTypes.func, + retrieveLayerData: React.PropTypes.func, + onSort: React.PropTypes.func, + onSettings: React.PropTypes.func, + hideSettings: React.PropTypes.func, + updateSettings: React.PropTypes.func, + updateNode: React.PropTypes.func, + removeNode: React.PropTypes.func, + activateRemoveLayer: React.PropTypes.bool, + activateLegendTool: React.PropTypes.bool, + activateZoomTool: React.PropTypes.bool, + activateSettingsTool: React.PropTypes.bool, + visibilityCheckType: React.PropTypes.string, + settingsOptions: React.PropTypes.object + }, + getDefaultProps() { + return { + groupPropertiesChangeHandler: () => {}, + layerPropertiesChangeHandler: () => {}, + retrieveLayerData: () => {}, + onToggleGroup: () => {}, + onToggleLayer: () => {}, + onToggleQuery: () => {}, + onZoomToExtent: () => {}, + onSettings: () => {}, + updateNode: () => {}, + removeNode: () => {}, + activateLegendTool: true, + activateZoomTool: true, + activateSettingsTool: true, + activateRemoveLayer: true, + visibilityCheckType: "checkbox", + settingsOptions: {}, + querypanelEnabled: false + }; + }, + getNoBackgroundLayers(group) { + return group.name !== 'background'; + }, + renderSidebar() { + return ( + +
+ + ); + }, + renderQueryPanel() { + return (
+ +
); + }, + render() { + return this.renderSidebar(); + } +}); + +const QueryPanelPlugin = connect(tocSelector, { + groupPropertiesChangeHandler: changeGroupProperties, + layerPropertiesChangeHandler: changeLayerProperties, + retrieveLayerData: getLayerCapabilities, + onToggleGroup: LayersUtils.toggleByType('groups', toggleNode), + onToggleLayer: LayersUtils.toggleByType('layers', toggleNode), + onToggleQuery: toggleControl.bind(null, 'queryPanel', null), + onSort: LayersUtils.sortUsing(LayersUtils.sortLayers, sortNode), + onSettings: showSettings, + onZoomToExtent: zoomToExtent, + hideSettings, + updateSettings, + updateNode, + removeNode +})(LayerTree); + +module.exports = { + QueryPanelPlugin, + reducers: { + queryform: require('../reducers/queryform'), + query: require('../reducers/query') + } +}; diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index d6314b6dae..d47a341b7c 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -8,9 +8,12 @@ const React = require('react'); const {connect} = require('react-redux'); const {createSelector} = require('reselect'); +const {Button, Glyphicon} = require('react-bootstrap'); + const {changeLayerProperties, changeGroupProperties, toggleNode, sortNode, showSettings, hideSettings, updateSettings, updateNode, removeNode, getLayerCapabilities} = require('../actions/layers'); const {zoomToExtent} = require('../actions/map'); +const {toggleControl} = require('../actions/controls'); const {groupsSelector} = require('../selectors/layers'); @@ -21,15 +24,114 @@ const assign = require('object-assign'); const layersIcon = require('./toolbar/assets/img/layers.png'); +// include application component +const QueryBuilder = require('../components/data/query/QueryBuilder'); + +const {bindActionCreators} = require('redux'); +const { + // QueryBuilder action functions + addGroupField, + addFilterField, + removeFilterField, + updateFilterField, + updateExceptionField, + updateLogicCombo, + removeGroupField, + changeCascadingValue, + expandAttributeFilterPanel, + expandSpatialFilterPanel, + selectSpatialMethod, + selectSpatialOperation, + removeSpatialSelection, + showSpatialSelectionDetails, + reset, + changeDwithinValue, + zoneGetValues, + zoneSearch, + zoneChange +} = require('../actions/queryform'); + +const {query, featureTypeSelected, describeFeatureType} = require('../actions/wfsquery'); + +const { + changeDrawingStatus, + endDrawing +} = require('../actions/draw'); + +// connecting a Dumb component to the store +// makes it a smart component +// we both connect state => props +// and actions to event handlers +const SmartQueryForm = connect((state) => { + return { + // QueryBuilder props + useMapProjection: state.queryform.useMapProjection, + groupLevels: state.queryform.groupLevels, + groupFields: state.queryform.groupFields, + filterFields: state.queryform.filterFields, + attributes: state.query && state.query.typeName && state.query.featureTypes && state.query.featureTypes[state.query.typeName] && state.query.featureTypes[state.query.typeName].attributes, + spatialField: state.queryform.spatialField, + showDetailsPanel: state.queryform.showDetailsPanel, + toolbarEnabled: state.queryform.toolbarEnabled, + attributePanelExpanded: state.queryform.attributePanelExpanded, + spatialPanelExpanded: state.queryform.spatialPanelExpanded, + featureTypeConfigUrl: state.query && state.query.url, + searchUrl: state.query && state.query.url, + featureTypeName: state.query && state.query.typeName, + ogcVersion: "1.1.0", + params: {typeName: state.query && state.query.typeName}, + resultTitle: "Query Result", + showGeneratedFilter: false + }; +}, dispatch => { + return { + + attributeFilterActions: bindActionCreators({ + onLoadFeatureTypeConfig: (url, params) => { + return describeFeatureType(url, params.typeName); + }, + onAddGroupField: addGroupField, + onAddFilterField: addFilterField, + onRemoveFilterField: removeFilterField, + onUpdateFilterField: updateFilterField, + onUpdateExceptionField: updateExceptionField, + onUpdateLogicCombo: updateLogicCombo, + onRemoveGroupField: removeGroupField, + onChangeCascadingValue: changeCascadingValue, + onExpandAttributeFilterPanel: expandAttributeFilterPanel + }, dispatch), + spatialFilterActions: bindActionCreators({ + onExpandSpatialFilterPanel: expandSpatialFilterPanel, + onSelectSpatialMethod: selectSpatialMethod, + onSelectSpatialOperation: selectSpatialOperation, + onChangeDrawingStatus: changeDrawingStatus, + onRemoveSpatialSelection: removeSpatialSelection, + onShowSpatialSelectionDetails: showSpatialSelectionDetails, + onEndDrawing: endDrawing, + onChangeDwithinValue: changeDwithinValue, + zoneFilter: zoneGetValues, + zoneSearch, + zoneChange + }, dispatch), + queryToolbarActions: bindActionCreators({ + onQuery: query, + onReset: reset, + onChangeDrawingStatus: changeDrawingStatus + }, dispatch) + }; +})(QueryBuilder); + const tocSelector = createSelector( [ (state) => state.controls && state.controls.toolbar && state.controls.toolbar.active === 'toc', groupsSelector, - (state) => state.layers.settings || {expanded: false, options: {opacity: 1}} - ], (enabled, groups, settings) => ({ + (state) => state.layers.settings || {expanded: false, options: {opacity: 1}}, + (state) => state.controls && state.controls.queryPanel && state.controls.queryPanel.enabled || false + ], (enabled, groups, settings, querypanelEnabled) => ({ enabled, groups, - settings + settings, + querypanelEnabled }) ); @@ -43,11 +145,13 @@ const LayerTree = React.createClass({ buttonContent: React.PropTypes.node, groups: React.PropTypes.array, settings: React.PropTypes.object, + querypanelEnabled: React.PropTypes.bool, groupStyle: React.PropTypes.object, groupPropertiesChangeHandler: React.PropTypes.func, layerPropertiesChangeHandler: React.PropTypes.func, onToggleGroup: React.PropTypes.func, onToggleLayer: React.PropTypes.func, + onToggleQuery: React.PropTypes.func, onZoomToExtent: React.PropTypes.func, retrieveLayerData: React.PropTypes.func, onSort: React.PropTypes.func, @@ -59,6 +163,7 @@ const LayerTree = React.createClass({ activateRemoveLayer: React.PropTypes.bool, activateLegendTool: React.PropTypes.bool, activateZoomTool: React.PropTypes.bool, + activateQueryTool: React.PropTypes.bool, activateSettingsTool: React.PropTypes.bool, visibilityCheckType: React.PropTypes.string, settingsOptions: React.PropTypes.object @@ -70,6 +175,7 @@ const LayerTree = React.createClass({ retrieveLayerData: () => {}, onToggleGroup: () => {}, onToggleLayer: () => {}, + onToggleQuery: () => {}, onZoomToExtent: () => {}, onSettings: () => {}, updateNode: () => {}, @@ -79,16 +185,14 @@ const LayerTree = React.createClass({ activateSettingsTool: true, activateRemoveLayer: true, visibilityCheckType: "checkbox", - settingsOptions: {} + settingsOptions: {}, + querypanelEnabled: false }; }, getNoBackgroundLayers(group) { return group.name !== 'background'; }, - render() { - if (!this.props.groups) { - return
; - } + renderTOC() { return (
@@ -104,6 +208,7 @@ const LayerTree = React.createClass({ } @@ -127,6 +233,21 @@ const LayerTree = React.createClass({
); + }, + renderQueryPanel() { + return (
+ + +
); + }, + render() { + if (!this.props.groups) { + return
; + } + if (this.props.querypanelEnabled) { + return this.renderQueryPanel(); + } + return this.renderTOC(); } }); @@ -136,6 +257,12 @@ const TOCPlugin = connect(tocSelector, { retrieveLayerData: getLayerCapabilities, onToggleGroup: LayersUtils.toggleByType('groups', toggleNode), onToggleLayer: LayersUtils.toggleByType('layers', toggleNode), + onToggleQuery: (url, name) => { + return (dispatch) => { + dispatch(featureTypeSelected(url, name)); + dispatch(toggleControl('queryPanel', null)); + }; + }, onSort: LayersUtils.sortUsing(LayersUtils.sortLayers, sortNode), onSettings: showSettings, onZoomToExtent: zoomToExtent, @@ -167,5 +294,8 @@ module.exports = { priority: 2 } }), - reducers: {} + reducers: { + queryform: require('../reducers/queryform'), + query: require('../reducers/query') + } }; diff --git a/web/client/plugins/drawer/drawer.css b/web/client/plugins/drawer/drawer.css index 4104bc4dbc..939703339d 100644 --- a/web/client/plugins/drawer/drawer.css +++ b/web/client/plugins/drawer/drawer.css @@ -19,16 +19,6 @@ box-shadow: none; } -.nav-menu { - - max-width:300px; - -} -div.nav-menu{ - background: rgba(255,255,255,1); - left:-300px; -} - #mapstore-drawermenu > div > div:nth-child(2) { display: none; } diff --git a/web/client/product/plugins.js b/web/client/product/plugins.js index ac68ddcd33..ef9f8287c9 100644 --- a/web/client/product/plugins.js +++ b/web/client/product/plugins.js @@ -57,7 +57,9 @@ module.exports = { SharePlugin: require('../plugins/Share'), SavePlugin: require('../plugins/Save'), SaveAsPlugin: require('../plugins/SaveAs'), - CreateNewMapPlugin: require('../plugins/CreateNewMap') + CreateNewMapPlugin: require('../plugins/CreateNewMap'), + QueryPanelPlugin: require('../plugins/QueryPanel'), + FeatureGridPlugin: require('../plugins/FeatureGrid') }, requires: { ReactSwipe: require('react-swipeable-views').default, diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js new file mode 100644 index 0000000000..fd65fc02b1 --- /dev/null +++ b/web/client/reducers/query.js @@ -0,0 +1,124 @@ +/** + * Copyright 2016, 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 { + FEATURE_TYPE_SELECTED, + FEATURE_TYPE_LOADED, + FEATURE_TYPE_ERROR, + FEATURE_LOADED, + FEATURE_ERROR, + QUERY_RESULT, + QUERY_ERROR, + RESET_QUERY +} = require('../actions/wfsquery'); + +const assign = require('object-assign'); + +const types = { + 'xsd:string': 'string', + 'xsd:dateTime': 'date', + 'xsd:number': '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, + values: feature.features.reduce((previous, current) => { + if (previous.indexOf(current.properties[attribute]) === -1) { + return [...previous, current.properties[attribute]].sort(); + } + return previous; + }, []) + })).reduce((previous, current) => { + return assign(previous, { + [current.attribute]: current.values.map((value) => ({ + id: value, + name: value + })) + }); + }, {}); +}; + +const initialState = { + featureTypes: {}, + data: {}, + result: '', + resultError: null +}; + +function query(state = initialState, action) { + switch (action.type) { + case FEATURE_TYPE_SELECTED: { + return assign({}, state, { + typeName: action.typeName, + url: action.url + }); + } + case FEATURE_TYPE_LOADED: { + return assign({}, state, { + featureTypes: assign({}, state.featureTypes, {[action.typeName]: extractInfo(action.featureType)}) + }); + } + case FEATURE_TYPE_ERROR: { + return assign({}, state, { + featureTypes: assign({}, state.featureTypes, {[action.typeName]: {error: action.error}}) + }); + } + case FEATURE_LOADED: { + return assign({}, state, { + data: assign({}, state.data, {[action.typeName]: extractData(action.feature)}) + }); + } + case FEATURE_ERROR: { + return assign({}, state, { + featureTypes: assign({}, state.data, {[action.typeName]: {error: action.error}}) + }); + } + case QUERY_RESULT: { + return assign({}, state, { + result: action.result, + resultError: null + }); + } + case QUERY_ERROR: { + return assign({}, state, { + result: '', + resultError: action.error + }); + } + case RESET_QUERY: { + return assign({}, state, { + result: '', + resultError: null + }); + } + default: + return state; + } +} + +module.exports = query; diff --git a/web/client/reducers/queryform.js b/web/client/reducers/queryform.js index 94a78565e3..fe658a75f2 100644 --- a/web/client/reducers/queryform.js +++ b/web/client/reducers/queryform.js @@ -43,7 +43,8 @@ const { const assign = require('object-assign'); -const {union, bbox} = require('turf'); +const union = require('turf-union'); +const bbox = require('turf-bbox'); const initialState = { searchUrl: null, diff --git a/web/client/utils/FilterUtils.jsx b/web/client/utils/FilterUtils.jsx index f03576de06..3d3d93cd70 100644 --- a/web/client/utils/FilterUtils.jsx +++ b/web/client/utils/FilterUtils.jsx @@ -839,6 +839,28 @@ const FilterUtils = { } return (strFilter && strFilter.length > 0) ? strFilter : false; + }, + getOgcAllPropertyValue: function(featureTypeName, attribute) { + return ` + + `; + }, + getSLD: function(ftName, json, version) { + let filter = this.toOGCFilter(ftName, json, version); + let sIdx = filter.search( `<${this.nsplaceholder}:Filter>`); + if (sIdx !== -1) { + let eIndx = filter.search( ``); + filter = filter.substr(sIdx, eIndx - sIdx); + } else { + filter = ''; + } + return `${ftName}${filter}circle#0000FF20`; } };