diff --git a/web/client/components/mapcontrols/annotations/CoordinateEntry.jsx b/web/client/components/mapcontrols/annotations/CoordinateEntry.jsx index 23c9ab7685..db7d7b24d4 100644 --- a/web/client/components/mapcontrols/annotations/CoordinateEntry.jsx +++ b/web/client/components/mapcontrols/annotations/CoordinateEntry.jsx @@ -4,6 +4,7 @@ const PropTypes = require('prop-types'); const DecimalCoordinateEditor = require('./editors/DecimalCoordinateEditor'); const AeronauticalCoordinateEditor = require('./editors/AeronauticalCoordinateEditor'); const {isNil} = require('lodash'); +const no90Lat = require('./enhancers/no90Lat'); /** This component can render an input field in two different formats: 'decimal' or 'aeronautical' @@ -28,4 +29,4 @@ class CoordinateEntry extends React.Component { } } -module.exports = CoordinateEntry; +module.exports = no90Lat(CoordinateEntry); // TODO: remove no90Lat this when issue with coordinate 90 is fixed in annotations diff --git a/web/client/components/mapcontrols/annotations/editors/AeronauticalCoordinateEditor.jsx b/web/client/components/mapcontrols/annotations/editors/AeronauticalCoordinateEditor.jsx index 9804107a00..161574e40a 100644 --- a/web/client/components/mapcontrols/annotations/editors/AeronauticalCoordinateEditor.jsx +++ b/web/client/components/mapcontrols/annotations/editors/AeronauticalCoordinateEditor.jsx @@ -10,7 +10,7 @@ const {isNil} = require('lodash'); const decimalToAeronautical = require('../enhancers/decimalToAeronautical'); const coordinateTypePreset = require('../enhancers/coordinateTypePreset'); -const tempAreonauticalValue = require('../enhancers/tempAreonauticalValue'); +const tempAeronauticalValue = require('../enhancers/tempAeronauticalValue'); class CoordinateEntry extends React.Component { @@ -42,7 +42,7 @@ class CoordinateEntry extends React.Component { minutes: this.props.minutes, seconds: this.props.seconds, direction: this.props.direction, - [part]: newValue + [part]: part === "degrees" ? Math.min(newValue, this.props.maxDegrees) : newValue }; let seconds = newValues.seconds; let minutes = newValues.minutes + this.getSexagesimalStep(seconds); @@ -160,7 +160,7 @@ class CoordinateEntry extends React.Component { } module.exports = compose( + coordinateTypePreset, decimalToAeronautical, - tempAreonauticalValue, - coordinateTypePreset + tempAeronauticalValue )(CoordinateEntry); diff --git a/web/client/components/mapcontrols/annotations/editors/__tests__/AeronauticalCoordinateEditor-test.jsx b/web/client/components/mapcontrols/annotations/editors/__tests__/AeronauticalCoordinateEditor-test.jsx new file mode 100644 index 0000000000..dfdd84c9c2 --- /dev/null +++ b/web/client/components/mapcontrols/annotations/editors/__tests__/AeronauticalCoordinateEditor-test.jsx @@ -0,0 +1,49 @@ +const React = require('react'); +const ReactDOM = require('react-dom'); +const ReactTestUtils = require('react-dom/test-utils'); +const expect = require('expect'); +const AeronauticalCoordinateEditor = require('../AeronauticalCoordinateEditor'); + +describe('AeronauticalCoordinateEditor enhancer', () => { + beforeEach((done) => { + document.body.innerHTML = '
'; + setTimeout(done); + }); + afterEach((done) => { + ReactDOM.unmountComponentAtNode(document.getElementById("container")); + document.body.innerHTML = ''; + setTimeout(done); + }); + it('AeronauticalCoordinateEditor rendering with defaults', () => { + ReactDOM.render(, document.getElementById("container")); + const container = document.getElementById('container'); + const elements = container.querySelectorAll('input'); + expect(elements.length).toBe(3); + }); + it('AeronauticalCoordinateEditor rendering with 13.3333333333', () => { + ReactDOM.render(, document.getElementById("container")); + const container = document.getElementById('container'); + const elements = container.querySelectorAll('input'); + expect(elements.length).toBe(3); + expect(elements[0].value).toBe('13'); + expect(elements[1].value).toBe('20'); + expect(elements[2].value).toBe('0'); + }); + it('Test AeronauticalCoordinateEditor onChange', () => { + const actions = { + onChange: () => {} + }; + const spyonChange = expect.spyOn(actions, 'onChange'); + ReactDOM.render(, document.getElementById("container")); + const container = document.getElementById('container'); + + const elements = container.querySelectorAll('input'); + expect(elements.length).toBe(3); + expect(elements[0].value).toBe('19'); + expect(elements[1].value).toBe('0'); + expect(elements[2].value).toBe('0'); + ReactTestUtils.Simulate.change(elements[0], { target: { value: "20" } }); + expect(spyonChange).toHaveBeenCalled(); + expect(parseFloat(spyonChange.calls[0].arguments[0])).toBe(20); + }); +}); diff --git a/web/client/components/mapcontrols/annotations/enhancers/__tests__/decimalToAeronautical-test.js b/web/client/components/mapcontrols/annotations/enhancers/__tests__/decimalToAeronautical-test.js index c2547c12f6..f3941a94ed 100644 --- a/web/client/components/mapcontrols/annotations/enhancers/__tests__/decimalToAeronautical-test.js +++ b/web/client/components/mapcontrols/annotations/enhancers/__tests__/decimalToAeronautical-test.js @@ -38,6 +38,20 @@ describe("test the Annotations enahncers", () => { coordinate="lon" />), document.getElementById("container")); }); + it('decimalToAeronautical conversion correctly step on minutes and seconds', (done) => { + // 13.3333333333 should be 13 degrees, 20 minutes + const Sink = decimalToAeronautical(createSink(props => { + expect(props).toExist(); + expect(props.degrees).toBe(13); + expect(props.minutes).toBe(20); + expect(props.seconds).toBe(0); + done(); + })); + ReactDOM.render((), document.getElementById("container")); + }); it('convert from/to preserve precision', (done) => { const enhancer = compose( withState('value', 'onChange', 1.5), diff --git a/web/client/components/mapcontrols/annotations/enhancers/__tests__/no90Lat-test.js b/web/client/components/mapcontrols/annotations/enhancers/__tests__/no90Lat-test.js new file mode 100644 index 0000000000..22d441aad1 --- /dev/null +++ b/web/client/components/mapcontrols/annotations/enhancers/__tests__/no90Lat-test.js @@ -0,0 +1,50 @@ +const React = require('react'); +const ReactDOM = require('react-dom'); +const {createSink} = require('recompose'); +const expect = require('expect'); +const no90Lat = require('../no90Lat'); + +describe('no90Lat enhancer', () => { + beforeEach((done) => { + document.body.innerHTML = '
'; + setTimeout(done); + }); + afterEach((done) => { + ReactDOM.unmountComponentAtNode(document.getElementById("container")); + document.body.innerHTML = ''; + setTimeout(done); + }); + it('no90Lat rendering with defaults', (done) => { + const onChange = (v) => { + expect(parseFloat(v)).toBeLessThan(90); + }; + const Sink = no90Lat(createSink( props => { + expect(props).toExist(); + props.onChange("90"); + done(); + })); + ReactDOM.render(, document.getElementById("container")); + }); + it('no90Lat rendering with negative value', (done) => { + const onChange = (v) => { + expect(parseFloat(v)).toBeMoreThan(-90); + }; + const Sink = no90Lat(createSink(props => { + expect(props).toExist(); + props.onChange("-90"); + done(); + })); + ReactDOM.render(, document.getElementById("container")); + }); + it('no90Lat rendering with lon', (done) => { + const onChange = (v) => { + expect(parseFloat(v)).toBe(90); + }; + const Sink = no90Lat(createSink(props => { + expect(props).toExist(); + props.onChange("90"); + done(); + })); + ReactDOM.render(, document.getElementById("container")); + }); +}); diff --git a/web/client/components/mapcontrols/annotations/enhancers/decimalToAeronautical.js b/web/client/components/mapcontrols/annotations/enhancers/decimalToAeronautical.js index 8ed106b831..89728d4de1 100644 --- a/web/client/components/mapcontrols/annotations/enhancers/decimalToAeronautical.js +++ b/web/client/components/mapcontrols/annotations/enhancers/decimalToAeronautical.js @@ -2,11 +2,27 @@ const {compose, withHandlers, withProps} = require('recompose'); const convertDDToDMS = (D, lng) => { + let d = parseInt(D, 10); + let minFloat = Math.abs((D - d) * 60); + let m = Math.floor(minFloat); + let secFloat = (minFloat - m) * 60; + let s = Math.round(secFloat); + d = Math.abs(d); + + if (s === 60) { + m++; + s = 0; + } + if (m === 60) { + d++; + m = 0; + } + return { - degrees: Math.abs(0 | D), + degrees: d, direction: D < 0 ? lng ? 'W' : 'S' : lng ? 'E' : 'N', - minutes: Math.floor(Math.abs(0 | D % 1 * 60)), - seconds: Math.round(Math.abs((0 | D * 60 % 1 * 6000) / 100)) + minutes: m, + seconds: s }; }; diff --git a/web/client/components/mapcontrols/annotations/enhancers/no90Lat.js b/web/client/components/mapcontrols/annotations/enhancers/no90Lat.js new file mode 100644 index 0000000000..ac7b843eb2 --- /dev/null +++ b/web/client/components/mapcontrols/annotations/enhancers/no90Lat.js @@ -0,0 +1,17 @@ +const {compose, withHandlers} = require('recompose'); + +/** + * This workaround issues with annotations at 90 degrees lat. + * This avoid wrong input from user breaks the draw support + * // TODO: remove this in favor of some more general enhancer or some check inside draw support + */ +module.exports = compose( + withHandlers({ + onChange: ({ onChange = () => { }, maxLatitude = 89.9997222222, coordinate}) => v => + onChange( + Math.abs(parseFloat(v)) > maxLatitude && coordinate === "lat" + ? Math.sign(v) * maxLatitude + : v + ) + }) +); diff --git a/web/client/components/mapcontrols/annotations/enhancers/tempAreonauticalValue.js b/web/client/components/mapcontrols/annotations/enhancers/tempAeronauticalValue.js similarity index 100% rename from web/client/components/mapcontrols/annotations/enhancers/tempAreonauticalValue.js rename to web/client/components/mapcontrols/annotations/enhancers/tempAeronauticalValue.js