From a83d168e409488d69db49a3d29f7b2563dd80e8c Mon Sep 17 00:00:00 2001 From: Riccardo Mari Date: Wed, 15 Mar 2017 11:17:10 +0100 Subject: [PATCH 1/2] Issue #1560. Fix Syntax error in themeEntries with node v6.10.0 and npm 3.10.10 on windows10 64bit --- themes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/themes.js b/themes.js index 5c112d63fe..2002b8cc97 100644 --- a/themes.js +++ b/themes.js @@ -7,7 +7,7 @@ const extractThemesPlugin = new ExtractTextPlugin({ }); -const themeEntries = () => { +const themeEntries = (() => { const globPath = path.join(__dirname, "web", "client", "themes", "*"); var files = glob.sync(globPath); return files.reduce((res, curr) => { @@ -16,7 +16,7 @@ const themeEntries = () => { return finalRes; }, {}); -}(); +})(); module.exports = { themeEntries, extractThemesPlugin From ecc1f8447d696bd48bf87534c04d18935eeb818a Mon Sep 17 00:00:00 2001 From: Riccardo Mari Date: Thu, 6 Apr 2017 16:54:32 +0200 Subject: [PATCH 2/2] Issue #1693 Elevation Slider tool Fix requested changes to Elev Slider --- package.json | 1 + web/client/components/TOC/DefaultLayer.jsx | 4 + .../TOC/fragments/SettingsModal.jsx | 20 ++- .../TOC/fragments/settings/Elevation.jsx | 117 ++++++++++++++++++ .../TOC/fragments/settings/ElevationChart.jsx | 74 +++++++++++ .../settings/ElevationChartTooltip.jsx | 33 +++++ .../settings/__tests__/Elevation-test.jsx | 63 ++++++++++ .../__tests__/ElevationChart-test.jsx | 89 +++++++++++++ .../TOC/fragments/settings/css/elevation.css | 44 +++++++ web/client/plugins/TOC.jsx | 3 + .../geoserver/testworkspace/testlayer/wms | 3 + web/client/translations/data.de-DE | 4 +- web/client/translations/data.en-US | 4 +- web/client/translations/data.fr-FR | 4 +- web/client/translations/data.it-IT | 4 +- 15 files changed, 462 insertions(+), 5 deletions(-) create mode 100644 web/client/components/TOC/fragments/settings/Elevation.jsx create mode 100644 web/client/components/TOC/fragments/settings/ElevationChart.jsx create mode 100644 web/client/components/TOC/fragments/settings/ElevationChartTooltip.jsx create mode 100644 web/client/components/TOC/fragments/settings/__tests__/Elevation-test.jsx create mode 100644 web/client/components/TOC/fragments/settings/__tests__/ElevationChart-test.jsx create mode 100644 web/client/components/TOC/fragments/settings/css/elevation.css diff --git a/package.json b/package.json index b14f979cc4..47cc2a4f57 100644 --- a/package.json +++ b/package.json @@ -143,6 +143,7 @@ "react-swipeable-views": "0.11.1", "react-twitter-widgets": "1.2.0", "react-widgets": "3.4.6", + "recharts": "0.21.2", "redux": "3.6.0", "redux-logger": "2.6.1", "redux-observable": "0.13.0", diff --git a/web/client/components/TOC/DefaultLayer.jsx b/web/client/components/TOC/DefaultLayer.jsx index d40466b5e1..0ec0adf490 100644 --- a/web/client/components/TOC/DefaultLayer.jsx +++ b/web/client/components/TOC/DefaultLayer.jsx @@ -38,8 +38,10 @@ var DefaultLayer = React.createClass({ activateSettingsTool: React.PropTypes.bool, activateQueryTool: React.PropTypes.bool, activateZoomTool: React.PropTypes.bool, + chartStyle: React.PropTypes.object, settingsText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), opacityText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), + elevationText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), saveText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), closeText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), confirmDeleteText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), @@ -118,6 +120,8 @@ var DefaultLayer = React.createClass({ includeDeleteButton={this.props.includeDeleteButtonInSettings} titleText={this.props.settingsText} opacityText={this.props.opacityText} + elevationText={this.props.elevationText} + chartStyle={this.props.chartStyle} saveText={this.props.saveText} closeText={this.props.closeText} groups={this.props.groups}/> diff --git a/web/client/components/TOC/fragments/SettingsModal.jsx b/web/client/components/TOC/fragments/SettingsModal.jsx index 777bf2d781..f06ab3a925 100644 --- a/web/client/components/TOC/fragments/SettingsModal.jsx +++ b/web/client/components/TOC/fragments/SettingsModal.jsx @@ -16,6 +16,7 @@ const ConfirmButton = require('../../buttons/ConfirmButton'); const General = require('./settings/General'); const Display = require('./settings/Display'); const WMSStyle = require('./settings/WMSStyle'); +const Elevation = require('./settings/Elevation'); const Portal = require('../../misc/Portal'); const assign = require('object-assign'); const Message = require('../../I18N/Message'); @@ -32,11 +33,13 @@ const SettingsModal = React.createClass({ retrieveLayerData: React.PropTypes.func, titleText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), opacityText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), + elevationText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), saveText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), deleteText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), confirmDeleteText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), closeText: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.element]), options: React.PropTypes.object, + chartStyle: React.PropTypes.object, buttonSize: React.PropTypes.string, closeGlyph: React.PropTypes.string, panelStyle: React.PropTypes.object, @@ -44,13 +47,15 @@ const SettingsModal = React.createClass({ includeCloseButton: React.PropTypes.bool, includeDeleteButton: React.PropTypes.bool, realtimeUpdate: React.PropTypes.bool, - groups: React.PropTypes.array + groups: React.PropTypes.array, + elevations: React.PropTypes.object }, getDefaultProps() { return { id: "mapstore-layer-settings", settings: {expanded: false}, options: {}, + elevations: {}, updateSettings: () => {}, hideSettings: () => {}, updateNode: () => {}, @@ -123,14 +128,27 @@ const SettingsModal = React.createClass({ o/>); } }, + renderElevationTab() { + if (this.props.element.type === "wms" && this.props.element.elevations) { + return ( this.updateParams({[key]: value}, this.props.realtimeUpdate)} />); + } + }, render() { const general = this.renderGeneral(); const display = this.renderDisplay(); const style = this.renderStyleTab(); + const elevation = this.renderElevationTab(); const tabs = ( }>{general} }>{display} } disabled={!style} >{style} + } disabled={!elevation} >{elevation} ); const footer = ( {this.props.includeCloseButton ? : } diff --git a/web/client/components/TOC/fragments/settings/Elevation.jsx b/web/client/components/TOC/fragments/settings/Elevation.jsx new file mode 100644 index 0000000000..a45e727f4c --- /dev/null +++ b/web/client/components/TOC/fragments/settings/Elevation.jsx @@ -0,0 +1,117 @@ +/** + * 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 Slider = require('react-nouislider'); +const ElevationChart = require('./ElevationChart'); +require('react-widgets/lib/less/react-widgets.less'); +require("./css/elevation.css"); + +module.exports = React.createClass({ + propTypes: { + elevationText: React.PropTypes.node, + element: React.PropTypes.object, + elevations: React.PropTypes.object, + onChange: React.PropTypes.func, + appState: React.PropTypes.object, + chartStyle: React.PropTypes.object + }, + getDefaultProps() { + return { + onChange: () => {} + }; + }, + shouldComponentUpdate(nextProps) { + if (nextProps.appState && nextProps.appState.initialState && nextProps.appState.initialState.params && nextProps.element.params) { + if (nextProps.appState.initialState.params[nextProps.elevations.name] !== + nextProps.element.params[nextProps.elevations.name]) { + return false; + } + return false; + } + return true; + }, + renderElevationsChart(elevations) { + if (this.props.elevations.showChart) { + return ( + + ); + } + }, + renderElevationsSlider(elevations) { + const values = elevations.values; + const min = 0; + const max = values.length - 1; + const dif = max - min; + const firstVal = parseFloat(values[0]); + const lastVal = parseFloat(values[values.length - 1]); + const start = this.props.element && + this.props.element.params && + this.props.element.params[this.props.elevations.name][0] || values[0]; + const elevationName = {}; + return ( +
+ { + elevationName[this.props.elevations.name] = value; + this.props.onChange("params", Object.assign({}, elevationName)); + }}/> +
+ ); + }, + render() { + const elevations = this.props.elevations; + return ( +
+ + {this.renderElevationsChart(elevations)} +
+
+ {this.renderElevationsSlider(elevations)} +
+
+
+ ); + }, + calculateRange(values, min, max, dif, firstVal, lastVal) { + let arr = []; + let percText = ""; + let range = {min: firstVal, max: lastVal}; + values.forEach(function(currentValue, i) { + arr[i] = ((i - min) / dif) * 100; + percText = arr[i] + "%"; + range[percText] = parseFloat(currentValue); + }); + return range; + } +}); diff --git a/web/client/components/TOC/fragments/settings/ElevationChart.jsx b/web/client/components/TOC/fragments/settings/ElevationChart.jsx new file mode 100644 index 0000000000..3492041c6e --- /dev/null +++ b/web/client/components/TOC/fragments/settings/ElevationChart.jsx @@ -0,0 +1,74 @@ +/** + * 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 {LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip} = require('recharts'); +const ElevationChartTooltip = require('./ElevationChartTooltip'); + +module.exports = React.createClass({ + propTypes: { + elevations: React.PropTypes.object, + chartStyle: React.PropTypes.object, + onChange: React.PropTypes.func + }, + getDefaultProps() { + return { + elevations: {}, + onChange: () => {}, + chartStyle: { + margin: { + top: 5, + right: 20, + left: 18, + bottom: 45 + }, + width: 600, + height: 200 + } + }; + }, + renderLineChart() { + return ( + + + + }/> + + + + ); + }, + render() { + return ( +
+ {this.renderLineChart()} +
+ ); + }, + formatData(values) { + let data = []; + values.map(function(o) { + data.push( + { + "name": this.props.elevations.name, + "value": parseFloat(this.props.elevations.positive ? o : -o) + } + ); + }, this); + return data; + } +}); diff --git a/web/client/components/TOC/fragments/settings/ElevationChartTooltip.jsx b/web/client/components/TOC/fragments/settings/ElevationChartTooltip.jsx new file mode 100644 index 0000000000..f1ee56b327 --- /dev/null +++ b/web/client/components/TOC/fragments/settings/ElevationChartTooltip.jsx @@ -0,0 +1,33 @@ +/** + * 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 CustomTooltip = React.createClass({ + propTypes: { + type: React.PropTypes.string, + payload: React.PropTypes.array, + label: React.PropTypes.string, + active: React.PropTypes.bool + }, + render() { + const {active} = this.props; + + if (active) { + const { payload} = this.props; + return ( +
+

{Math.abs(payload[0].value)}

+
+ ); + } + return null; + } +}); + +module.exports = CustomTooltip; diff --git a/web/client/components/TOC/fragments/settings/__tests__/Elevation-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/Elevation-test.jsx new file mode 100644 index 0000000000..1f5479c3c3 --- /dev/null +++ b/web/client/components/TOC/fragments/settings/__tests__/Elevation-test.jsx @@ -0,0 +1,63 @@ +/** + * Copyright 2015, 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. + */ + +var React = require('react'); +var ReactDOM = require('react-dom'); +var ReactTestUtils = require('react-addons-test-utils'); +var Elevation = require('../Elevation'); + +var expect = require('expect'); + +describe('test Layer Properties Elevation component', () => { + beforeEach((done) => { + document.body.innerHTML = '
'; + setTimeout(done); + }); + + afterEach((done) => { + ReactDOM.unmountComponentAtNode(document.getElementById("container")); + document.body.innerHTML = ''; + setTimeout(done); + }); + + it('tests component rendering', () => { + const l = { + name: 'testworkspace:testlayer', + title: 'Layer', + visibility: true, + storeIndex: 9, + type: 'shapefile', + url: 'base/web/client/test-resources/geoserver/wms', + params: { + "ELEVATION": ["1.5"] + }, + elevations: { + name: "ELEVATION", + units: "Meters", + positive: false, + showChart: true, + values: ["1.5", "5.0", "10.0", "15.0", "20.0", "25.0", "30.0"] + } + }; + const settings = { + options: {opacity: 1} + }; + const comp = ReactDOM.render( + , + document.getElementById("container") + ); + + expect(comp).toExist(); + const div = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "div" ); + expect(div).toExist(); + }); +}); diff --git a/web/client/components/TOC/fragments/settings/__tests__/ElevationChart-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/ElevationChart-test.jsx new file mode 100644 index 0000000000..1aff2544f8 --- /dev/null +++ b/web/client/components/TOC/fragments/settings/__tests__/ElevationChart-test.jsx @@ -0,0 +1,89 @@ +/** + * Copyright 2015, 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. + */ + +var React = require('react'); +var ReactDOM = require('react-dom'); +var ReactTestUtils = require('react-addons-test-utils'); +var ElevationChart = require('../ElevationChart'); + +var expect = require('expect'); + +describe('test Layer Properties Elevation Chart component', () => { + beforeEach((done) => { + document.body.innerHTML = '
'; + setTimeout(done); + }); + + afterEach((done) => { + ReactDOM.unmountComponentAtNode(document.getElementById("container")); + document.body.innerHTML = ''; + setTimeout(done); + }); + + it('tests component rendering', () => { + const l = { + name: 'testworkspace:testlayer', + title: 'Layer', + visibility: true, + storeIndex: 9, + type: 'shapefile', + url: 'base/web/client/test-resources/geoserver/wms', + params: { + "ELEVATION": ["1.5"] + }, + elevations: { + name: "ELEVATION", + units: "Meters", + positive: true, + values: ["1.5", "5.0", "10.0", "15.0", "20.0", "25.0", "30.0"] + } + }; + + const comp = ReactDOM.render( + , + document.getElementById("container") + ); + + expect(comp).toExist(); + const div = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "div" ); + expect(div).toExist(); + }); + + it('Render 1 line in simple LineChart', () => { + const l = { + name: 'testworkspace:testlayer', + title: 'Layer', + visibility: true, + storeIndex: 9, + type: 'shapefile', + url: 'base/web/client/test-resources/geoserver/wms', + params: { + "ELEVATION": ["1.5"] + }, + elevations: { + name: "ELEVATION", + units: "Meters", + positive: true, + values: ["1.5", "5.0", "10.0", "15.0", "20.0", "25.0", "30.0"] + } + }; + + const comp = ReactDOM.render( + , + document.getElementById("container") + ); + + expect(comp).toExist(); + + const chart = ReactDOM.findDOMNode(comp).querySelector('.recharts-line .recharts-line-curve'); + expect(chart).toExist(); + }); +}); diff --git a/web/client/components/TOC/fragments/settings/css/elevation.css b/web/client/components/TOC/fragments/settings/css/elevation.css new file mode 100644 index 0000000000..5f547d699a --- /dev/null +++ b/web/client/components/TOC/fragments/settings/css/elevation.css @@ -0,0 +1,44 @@ +#mapstore-elevation { + width: 600px; + margin-top: -46px; + margin-bottom: 50px; +} + +#mapstore-elevation .noUi-horizontal .noUi-handle { + left: -12px; +} + +#mapstore-elevation .noUi-tooltip { + width: auto; +} + +#mapstore-elevation .noUi-value { + text-align: right; + -ms-transform: rotate(270deg); /* IE 9 */ + -webkit-transform: rotate(270deg); /* Chrome, Safari, Opera */ + transform: rotate(270deg); +} + +#mapstore-elevation .noUi-value-horizontal { + margin-left: -30px; + margin-top: 22px; +} + +label#mapstore-elevation-label { + margin-bottom: 40px; +} + +.custom-tooltip { + width: auto; + margin: 0; + line-height: 24px; + border: 1px solid #C1C1C1; + background-color: rgba(255, 255, 255, 0.8); + padding: 5px; +} +.custom-tooltip .label { + margin: 0; + color: #82CA9D; + font-weight: bold; + font-size: 16px; +} diff --git a/web/client/plugins/TOC.jsx b/web/client/plugins/TOC.jsx index 8d720d6188..dbf4c61776 100644 --- a/web/client/plugins/TOC.jsx +++ b/web/client/plugins/TOC.jsx @@ -179,6 +179,7 @@ const LayerTree = React.createClass({ activateSettingsTool: React.PropTypes.bool, visibilityCheckType: React.PropTypes.string, settingsOptions: React.PropTypes.object, + chartStyle: React.PropTypes.object, currentZoomLvl: React.PropTypes.number, scales: React.PropTypes.array }, @@ -238,8 +239,10 @@ const LayerTree = React.createClass({ activateQueryTool={this.props.activateQueryTool} activateSettingsTool={this.props.activateSettingsTool} retrieveLayerData={this.props.retrieveLayerData} + chartStyle={this.props.chartStyle} settingsText={} opacityText={} + elevationText={} saveText={} closeText={} groups={this.props.groups} diff --git a/web/client/test-resources/geoserver/testworkspace/testlayer/wms b/web/client/test-resources/geoserver/testworkspace/testlayer/wms index 8dd5389112..fcd02eee40 100644 --- a/web/client/test-resources/geoserver/testworkspace/testlayer/wms +++ b/web/client/test-resources/geoserver/testworkspace/testlayer/wms @@ -141,6 +141,9 @@ EPSG:4326 + + 1.5,5.0,10.0,15.0,20.0,25.0,30.0,35.0,40.0,45.0,50.0,60.0,70.0,80.0,90.0,100.0,120.0,140.0,160.0,180.0,200.0,225.0,250.0,275.0,300.0,350.0,400.0,450.0,500.0,600.0,700.0,800.0,900.0,1000.0,1200.0,1400.0,1600.0,1800.0,2000.0,2400.0,2800.0 +