Skip to content

Commit

Permalink
Mapstore2 Elevation Slider tool (#1697)
Browse files Browse the repository at this point in the history
* Issue #1560. Fix Syntax error in themeEntries with node v6.10.0 and npm 3.10.10 on windows10 64bit

* Issue #1693 Elevation Slider tool

Fix requested changes to Elev Slider
  • Loading branch information
mricca authored and MV88 committed Apr 7, 2017
1 parent f7437a4 commit b41c97a
Show file tree
Hide file tree
Showing 15 changed files with 462 additions and 5 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions web/client/components/TOC/DefaultLayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]),
Expand Down Expand Up @@ -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}/>
Expand Down
20 changes: 19 additions & 1 deletion web/client/components/TOC/fragments/SettingsModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -32,25 +33,29 @@ 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,
panelClassName: React.PropTypes.string,
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: () => {},
Expand Down Expand Up @@ -123,14 +128,27 @@ const SettingsModal = React.createClass({
o/>);
}
},
renderElevationTab() {
if (this.props.element.type === "wms" && this.props.element.elevations) {
return (<Elevation
elevationText={this.props.elevationText}
chartStyle={this.props.chartStyle}
element={this.props.element}
elevations={this.props.element.elevations}
appState={this.state || {}}
onChange={(key, value) => 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 = (<Tabs defaultActiveKey={1} id="layerProperties-tabs">
<Tab eventKey={1} title={<Message msgId="layerProperties.general" />}>{general}</Tab>
<Tab eventKey={2} title={<Message msgId="layerProperties.display" />}>{display}</Tab>
<Tab eventKey={3} title={<Message msgId="layerProperties.style" />} disabled={!style} >{style}</Tab>
<Tab eventKey={4} title={<Message msgId="layerProperties.elevation" />} disabled={!elevation} >{elevation}</Tab>
</Tabs>);
const footer = (<span role="footer">
{this.props.includeCloseButton ? <Button bsSize={this.props.buttonSize} onClick={this.onClose}>{this.props.closeText}</Button> : <span/>}
Expand Down
117 changes: 117 additions & 0 deletions web/client/components/TOC/fragments/settings/Elevation.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<ElevationChart
onChange={this.props.onChange}
elevations={elevations}
chartStyle={this.props.chartStyle}/>
);
}
},
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 (
<div id="mapstore-elevation">
<Slider
snap= {true}
start={[parseFloat((start))] || [0.0]}
range= {this.calculateRange(values, min, max, dif, firstVal, lastVal)}
behaviour= "tap"
pips= {{
mode: 'range',
stepped: true,
density: 10,
format: {
to: function( value ) {
return parseFloat(value).toFixed(1);
}
}
}}
tooltips={!this.props.elevations.showChart}
onChange={(value) => {
elevationName[this.props.elevations.name] = value;
this.props.onChange("params", Object.assign({}, elevationName));
}}/>
</div>
);
},
render() {
const elevations = this.props.elevations;
return (
<div>
<label
id="mapstore-elevation-label"
key="elevation-label"
className="control-label"
style={this.props.elevations.showChart ? {marginBottom: "10px"} : {marginBottom: "90px"}}>
{this.props.elevationText}: ({this.props.elevations.units})
</label>
{this.renderElevationsChart(elevations)}
<div>
<div key="elevation">
{this.renderElevationsSlider(elevations)}
</div>
</div>
</div>
);
},
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;
}
});
74 changes: 74 additions & 0 deletions web/client/components/TOC/fragments/settings/ElevationChart.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<LineChart margin={this.props.chartStyle.margin} width={this.props.chartStyle.width} height={this.props.chartStyle.height} data={this.formatData(this.props.elevations.values)}>
<XAxis
hide={true}
dataKey="name"/>
<YAxis
hide={true}/>
<Tooltip content={<ElevationChartTooltip/>}/>
<CartesianGrid
strokeDasharray="3 3"
horizontal={false}/>
<Line
type="monotone"
dataKey="value"
stroke="#82ca9d"
activeDot={{r: 8}}/>
</LineChart>
);
},
render() {
return (
<div>
{this.renderLineChart()}
</div>
);
},
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;
}
});
Original file line number Diff line number Diff line change
@@ -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 (
<div className="custom-tooltip">
<p className="label">{Math.abs(payload[0].value)}</p>
</div>
);
}
return null;
}
});

module.exports = CustomTooltip;
Original file line number Diff line number Diff line change
@@ -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 = '<div id="container"></div>';
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(
<Elevation
elevationText={"Text"}
element={l}
elevations={l.elevations}
settings={settings} />,
document.getElementById("container")
);

expect(comp).toExist();
const div = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "div" );
expect(div).toExist();
});
});
Loading

0 comments on commit b41c97a

Please sign in to comment.