Skip to content

Commit

Permalink
FeatureInfo coordinates in aeronautical format (#3676)
Browse files Browse the repository at this point in the history
  • Loading branch information
offtherailz committed Apr 5, 2019
1 parent db35bee commit c461d1b
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 93 deletions.
7 changes: 2 additions & 5 deletions web/client/components/I18N/Number.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
const PropTypes = require('prop-types');
const React = require('react');
const {isNil} = require('lodash');
const {FormattedNumber} = require('react-intl');
class NumberFormat extends React.Component {
static propTypes = {
Expand All @@ -18,12 +19,8 @@ class NumberFormat extends React.Component {
intl: PropTypes.object
};

static defaultProps = {
value: new Date()
};

render() {
return this.context.intl ? <FormattedNumber value={this.props.value} {...this.props.numberParams}/> : <span>{this.props.value && this.props.value.toString() || ''}</span>;
return this.context.intl ? <FormattedNumber value={this.props.value} {...this.props.numberParams} /> : <span>{!isNil(this.props.value) && !isNaN(this.props.value) && this.props.value.toString && this.props.value.toString() || ''}</span>;
}
}

Expand Down
22 changes: 3 additions & 19 deletions web/client/components/data/identify/GeocodeViewer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const React = require('react');
const ResizableModal = require('../../misc/ResizableModal');
const Portal = require('../../misc/Portal');
const Message = require('../../I18N/Message');
const {Glyphicon, Row, Col} = require('react-bootstrap');
const {Glyphicon} = require('react-bootstrap');

/**
* Component for rendering lat and lng of the current selected point
Expand All @@ -23,23 +23,8 @@ const {Glyphicon, Row, Col} = require('react-bootstrap');
* @prop {node} revGeocodeDisplayName text/info displayed on modal
*/

module.exports = ({latlng, enableRevGeocode, hideRevGeocode = () => {}, showModalReverse, revGeocodeDisplayName, showCoordinateEditor = false}) => {

let lngCorrected = null;
if (latlng) {
/* lngCorrected is the converted longitude in order to have the value between
the range (-180 / +180).*/
lngCorrected = latlng && Math.round(latlng.lng * 100000) / 100000;
/* the following formula apply the converion */
lngCorrected = lngCorrected - 360 * Math.floor(lngCorrected / 360 + 0.5);
}

return enableRevGeocode && latlng && lngCorrected ? (
<Row key="ms-geocode-coords" className="ms-geoscode-viewer text-center" style={{display: showCoordinateEditor ? "none" : "block"}}>
{!showCoordinateEditor &&
(<Col xs={12}>
<div className="ms-geocode-coords">{latlng ? 'Lat: ' + (Math.round(latlng.lat * 100000) / 100000) + '- Long: ' + lngCorrected : null}</div>
</Col>)}
module.exports = ({latlng, enableRevGeocode, hideRevGeocode = () => {}, showModalReverse, revGeocodeDisplayName}) => {
return enableRevGeocode && latlng ? (
<Portal>
<ResizableModal
fade
Expand All @@ -59,6 +44,5 @@ module.exports = ({latlng, enableRevGeocode, hideRevGeocode = () => {}, showModa
</div>
</ResizableModal>
</Portal>
</Row>
) : null;
};
19 changes: 10 additions & 9 deletions web/client/components/data/identify/IdentifyContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const DockablePanel = require('../../misc/panels/DockablePanel');
const GeocodeViewer = require('./GeocodeViewer');
const ResizableModal = require('../../misc/ResizableModal');
const Portal = require('../../misc/Portal');

const Coordinate = require('./coordinates/Coordinate');
/**
* Component for rendering Identify Container inside a Dockable container
* @memberof components.data.identify
Expand All @@ -26,9 +26,6 @@ const Portal = require('../../misc/Portal');
* @prop {function} getToolButtons must return an array of object representing the toolbar buttons, eg (props) => [{ glyph: 'info-sign', tooltip: 'hello!'}]
* @prop {function} getNavigationButtons must return an array of navigation buttons, eg (props) => [{ glyph: 'info-sign', tooltip: 'hello!'}]
*/

const IdentifyEditor = require('../../../plugins/identify/IdentifyEditor');

module.exports = props => {
const {
enabled,
Expand Down Expand Up @@ -98,15 +95,19 @@ module.exports = props => {
style={dockStyle}
showFullscreen={showFullscreen}
zIndex={zIndex}
header={[enabledCoordEditorButton && showCoordinateEditor &&
<IdentifyEditor
header={[
<Coordinate
key="coordinate-editor"
removeVisible={false}
formatCoord={formatCoord}
coordinate={point.latlng ? {lat: point.latlng.lat, lon: lngCorrected } : {lat: "", lon: ""}}
enabledCoordEditorButton={enabledCoordEditorButton}
onChange={onChangeClickPoint}
onChangeFormat={onChangeFormat}
/> || null,
edit={showCoordinateEditor}
coordinate={{
lat: latlng && latlng.lat,
lon: lngCorrected
}}
/>,
<GeocodeViewer latlng={latlng} revGeocodeDisplayName={revGeocodeDisplayName} {...props}/>,
<Row key="button-row" className="text-center" style={{position: 'relative'}}>
<Col key="tools" xs={12}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,55 +24,6 @@ describe('GeocodeViewer', () => {
setTimeout(done);
});

it('creates the GeocodeViewer component with defaults', () => {
ReactDOM.render(
<GeocodeViewer />,
document.getElementById("container")
);
const geocodeViewer = document.getElementsByClassName('ms-geoscode-viewer');
expect(geocodeViewer.length).toBe(0);
});

it('creates the GeocodeViewer no lat lng', () => {
ReactDOM.render(
<GeocodeViewer enableRevGeocode/>,
document.getElementById("container")
);
const geocodeViewer = document.getElementsByClassName('ms-geoscode-viewer');
expect(geocodeViewer.length).toBe(0);
});

it('creates the GeocodeViewer enable, showCoordinateEditor=false', () => {
ReactDOM.render(
<GeocodeViewer
enableRevGeocode
showCoordinateEditor={false}
latlng={{lat: 40, lng: 10}}
lngCorrected={10}/>,
document.getElementById("container")
);
const geocodeViewer = document.getElementsByClassName('ms-geoscode-viewer');
expect(geocodeViewer.length).toBe(1);
const coords = document.getElementsByClassName('ms-geocode-coords')[0];
expect(coords.innerHTML.indexOf('Lat:') !== -1).toBe(true);
expect(coords.innerHTML.indexOf('Long:') !== -1).toBe(true);
});

it('creates the GeocodeViewer enable, showCoordinateEditor=true', () => {
ReactDOM.render(
<GeocodeViewer
enableRevGeocode
showCoordinateEditor
latlng={{lat: 40, lng: 10}}
lngCorrected={10}/>,
document.getElementById("container")
);
const geocodeViewer = document.getElementsByClassName('ms-geoscode-viewer');
expect(geocodeViewer.length).toBe(1);
const coords = document.getElementsByClassName('ms-geocode-coords')[0];
expect(coords).toNotExist();
});

it('creates the GeocodeViewer hide', () => {
ReactDOM.render(
<GeocodeViewer
Expand Down
40 changes: 40 additions & 0 deletions web/client/components/data/identify/coordinates/Coordinate.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2019, 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 Editor = require('./Editor');
const Viewer = require('./Viewer');

/**
* Visualize the coordinate editor or viewer for the point clicked
* @prop {object} coordinate coordinate in format `lat` `lon`
* @prop {string} formatCoord `decimal` or `aeronautical`
* @prop {boolean} edit true to visualize in edit mode
* @prop {function} onChange handler to change the point coordinates. 2st argument is the key `lat` or `lon`, 2nd argument is the new numeric value.
* @prop {function} onChangeFormat handler to change the formatCoord. 1st argument is the formatCoord string.
*/
module.exports = ({
coordinate = {},
formatCoord,
edit,
onChange = () => {},
onChangeFormat = () => {}
}) =>
edit ?
(<Editor
removeVisible={false}
formatCoord={formatCoord}
coordinate={coordinate || {lat: "", lon: ""}}
onChange={onChange}
onChangeFormat={onChangeFormat}
/>)
: (<Viewer
className="text-center"
formatCoord={formatCoord}
coordinate={coordinate || { lat: "", lon: "" }}
/>);

Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
*/

const React = require('react');
const CoordinatesRow = require('../../components/misc/coordinateeditors/CoordinatesRow');
const CoordinatesRow = require('../../../misc/coordinateeditors/CoordinatesRow');

const IdentifyEditor = (props) => (
const Editor = (props) => (
<CoordinatesRow
key="IdentifyEditor"
format={props.formatCoord || "decimal"}
Expand All @@ -28,12 +28,12 @@ const IdentifyEditor = (props) => (
}}
key={"GFI row coord editor"}
component={props.coordinate || {}}
customClassName="GFI-coord-editor"
customClassName="coord-editor"
isDraggable={false}
showDraggable={false}
formatVisible
showLabels
removeVisible={false}
/>);

module.exports = IdentifyEditor;
module.exports = Editor;
59 changes: 59 additions & 0 deletions web/client/components/data/identify/coordinates/Viewer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2019, 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 { Row, Col } = require('react-bootstrap');
const {isNil} = require('lodash');
const NumberFormat = require('../../../I18N/Number');
const decimalToAeronautical = require('../../../misc/coordinateeditors/enhancers/decimalToAeronautical');

/**
* Format 1 decimal coordinate into degrees, minutes, seconds, direction format.
* @prop {value} value to format
*
*/
const AeronauticalCoordinate = decimalToAeronautical(({
degrees = 0,
minutes = 0,
seconds = 0,
direction,
integerFormat,
decimalFormat
}) => (<span className="coordinate-dms">
<NumberFormat key="latD" numberParams={integerFormat} value={degrees} />
<span>°&nbsp;</span><NumberFormat key="latM" numberParams={integerFormat} value={minutes} /><span>&apos;&nbsp;</span>
<NumberFormat key="latS" numberParams={decimalFormat} value={seconds} /><span>&apos;&apos;&nbsp;</span>
&nbsp;<span>{direction}</span>
</span>));

/**
* Display coordinates in "decimal" or "aeronautical" formats.
* TODO: maybe is better move formatting components in some common place.
*/
module.exports = ({
integerFormat = {style: "decimal", minimumIntegerDigits: 2, maximumFractionDigits: 0},
decimalFormat = {style: "decimal", minimumIntegerDigits: 2, maximumFractionDigits: 4, minimumFractionDigits: 4},
coordinate = {},
formatCoord = "decimal",
className
}) =>
(<Row className={className}>
{
(<Col xs={12}>
{(isNil(coordinate.lat) || isNil(coordinate.lon))
? null
: formatCoord === "decimal"
? <div className="ms-coordinates-decimal">Lat: <NumberFormat value={(Math.round(coordinate.lat * 100000) / 100000)} /> - Long: <NumberFormat value={coordinate.lon} /></div>
: <div className="ms-coordinates-aeronautical">
<span>Lat: <AeronauticalCoordinate integerFormat={integerFormat} decimalFormat={decimalFormat} value={coordinate.lat} /></span>
<span> - </span>
<span> Long: <AeronauticalCoordinate coordinate="lon" integerFormat={integerFormat} decimalFormat={decimalFormat} value={coordinate.lon} /></span>
</div>
}
</Col>)}
</Row>);

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2019, 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 ReactDOM = require('react-dom');
const ReactTestUtils = require('react-dom/test-utils');

const expect = require('expect');
const Coordinate = require('../Coordinate');
describe('Identify Coordinate 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('Coordinate rendering with defaults', () => {
ReactDOM.render(<Coordinate />, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.text-center');
expect(el).toExist();
});
it('Coordinate rendering with content', () => {
ReactDOM.render(<Coordinate coordinate={{lat: 1, lon: 1}} />, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.ms-coordinates-decimal');
expect(el).toExist();
});
it('Coordinate edit mode', () => {
ReactDOM.render(<Coordinate edit coordinate={{ lat: 1, lon: 1 }} />, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.coord-editor');
expect(el).toExist();
});
it('Test Editor onChangeFormat correctly passed', () => {
const actions = {
onChangeFormat: () => { }
};
const spyonChange = expect.spyOn(actions, 'onChangeFormat');
ReactDOM.render(<Coordinate edit onChangeFormat={actions.onChangeFormat} />, document.getElementById("container"));
ReactTestUtils.Simulate.click(document.querySelector('a > span')); // <-- trigger event callback
expect(spyonChange).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const React = require('react');
const ReactDOM = require('react-dom');
const ReactTestUtils = require('react-dom/test-utils');
const expect = require('expect');
const Editor = require('../Editor');
describe('Identify Coordinate Editor 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('Editor rendering with defaults', () => {
ReactDOM.render(<Editor />, document.getElementById("container"));
const container = document.getElementById('container');
const el = container.querySelector('.coord-editor');
expect(el).toExist();
});
it('Test Editor onChange correctly passed and argument mapping', () => {
const actions = {
onChange: () => {}
};
const spyonChange = expect.spyOn(actions, 'onChange');
ReactDOM.render(<Editor onChange={actions.onChange} />, document.getElementById("container"));
ReactTestUtils.Simulate.change(document.querySelector('input'), { target: { value: 20} }); // <-- trigger event callback
expect(spyonChange).toHaveBeenCalled();
expect(spyonChange.calls[0].arguments[0]).toBe('lat');
expect(spyonChange.calls[0].arguments[1]).toBe(20);
});
it('Test Editor onChangeFormat correctly passed', () => {
const actions = {
onChangeFormat: () => { }
};
const spyonChange = expect.spyOn(actions, 'onChangeFormat');
ReactDOM.render(<Editor onChangeFormat={actions.onChangeFormat} />, document.getElementById("container"));
ReactTestUtils.Simulate.click(document.querySelector('a > span')); // <-- trigger event callback
expect(spyonChange).toHaveBeenCalled();
});
});
Loading

0 comments on commit c461d1b

Please sign in to comment.