diff --git a/package-lock.json b/package-lock.json index 18b279f..6f9f892 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "react-input-range": "^1.3.0", "react-leaflet": "^2.7.0", "react-leaflet-draw": "0.19.0", + "react-leaflet-semicircle": "^2.0.12", "react-modal": "^3.16.1", "react-select": "^5.7.7", "react-virtualized-auto-sizer": "^1.0.20", @@ -29646,6 +29647,11 @@ "resolved": "https://registry.npmjs.org/leaflet-editable/-/leaflet-editable-1.2.0.tgz", "integrity": "sha512-wG11JwpL8zqIbypTop6xCRGagMuWw68ihYu4uqrqc5Ep0wnEJeyob7NB2Rt5t74Oih4rwJ3OfwaGbzdowOGfYQ==" }, + "node_modules/leaflet-semicircle": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/leaflet-semicircle/-/leaflet-semicircle-2.0.4.tgz", + "integrity": "sha512-7FV9kGBMSQ1CNZYPxoPCEVpDpiIoh/owZV91BWUKYFik5WuaYij0zGEWOE0elTUlRyTHFJHW4rNfQJ2fqreqnA==" + }, "node_modules/legacy-swc-helpers": { "name": "@swc/helpers", "version": "0.4.14", @@ -36659,6 +36665,20 @@ "react-leaflet": "^2.0.0" } }, + "node_modules/react-leaflet-semicircle": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/react-leaflet-semicircle/-/react-leaflet-semicircle-2.0.12.tgz", + "integrity": "sha512-zE4thWNJ17CLiNs4wgGkr3nogNo3N6ETHT78O2nIqPrkNd+rT0FrdGrk9PEkT1tfjnmspX4hw0tV6nCot43QZw==", + "dependencies": { + "leaflet-semicircle": "^2.0.4" + }, + "peerDependencies": { + "leaflet": "^1.0.0", + "react": "^15.0.0 || ^16.0.0 || ^17.0.0", + "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0", + "react-leaflet": "^2.0.0" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", @@ -66486,6 +66506,11 @@ "resolved": "https://registry.npmjs.org/leaflet-editable/-/leaflet-editable-1.2.0.tgz", "integrity": "sha512-wG11JwpL8zqIbypTop6xCRGagMuWw68ihYu4uqrqc5Ep0wnEJeyob7NB2Rt5t74Oih4rwJ3OfwaGbzdowOGfYQ==" }, + "leaflet-semicircle": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/leaflet-semicircle/-/leaflet-semicircle-2.0.4.tgz", + "integrity": "sha512-7FV9kGBMSQ1CNZYPxoPCEVpDpiIoh/owZV91BWUKYFik5WuaYij0zGEWOE0elTUlRyTHFJHW4rNfQJ2fqreqnA==" + }, "legacy-swc-helpers": { "version": "npm:@swc/helpers@0.4.14", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", @@ -71809,6 +71834,14 @@ "lodash-es": "^4.17.10" } }, + "react-leaflet-semicircle": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/react-leaflet-semicircle/-/react-leaflet-semicircle-2.0.12.tgz", + "integrity": "sha512-zE4thWNJ17CLiNs4wgGkr3nogNo3N6ETHT78O2nIqPrkNd+rT0FrdGrk9PEkT1tfjnmspX4hw0tV6nCot43QZw==", + "requires": { + "leaflet-semicircle": "^2.0.4" + } + }, "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", diff --git a/package.json b/package.json index f301ffa..2c8fbd2 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "react-input-range": "^1.3.0", "react-leaflet": "^2.7.0", "react-leaflet-draw": "0.19.0", + "react-leaflet-semicircle": "^2.0.12", "react-modal": "^3.16.1", "react-select": "^5.7.7", "react-virtualized-auto-sizer": "^1.0.20", diff --git a/src/components/MapPreview/MapPreview.js b/src/components/MapPreview/MapPreview.js index 804ecc5..953a302 100644 --- a/src/components/MapPreview/MapPreview.js +++ b/src/components/MapPreview/MapPreview.js @@ -1,6 +1,7 @@ import React, { useRef } from 'react'; import PropTypes from 'prop-types'; -import { Polygon } from 'react-leaflet'; +import { Polygon, Circle, Polyline } from 'react-leaflet'; +import { SemiCircle } from 'react-leaflet-semicircle'; import { useLayers } from '../../hooks'; import { LayersContext } from '../../context'; @@ -46,6 +47,7 @@ const MapPreview = ({ onDrawEdited, featureRef = useRef(), emptyMap = false, + azimuthDisplay }) => { const layers = useLayers(availableLayers, fetchLayerData); @@ -176,6 +178,120 @@ const MapPreview = ({ } } + let featuresArray = []; + + if (!emptyMap) { + featuresArray = features.map((feature) => { + const { geometry, properties } = feature; + + const { + shapeOptions = {}, + onClick, + onMouseover, + onMouseout + } = properties; + + const { style = {} } = shapeOptions; + + const featureProps = { + ...style, + onClick, + onMouseover, + onMouseout + }; + + if (geometry.type === 'Point') { + const latLngs = latLngFromGeoJson(feature); + + return latLngs.map(({ lat, lng }, index) => { + return ( + + ); + }); + } + + if (geometry.type === 'Polygon') { + const coordinates = coordinatesFromGeoJson({ + type: 'FeatureCollection', + features: [feature] + }); + + return coordinates.map((set) => { + return set.map((position, index) => { + const fixedPosition = position.map((coordinates) => [ + coordinates[1], + coordinates[0] + ]); + return ( + + ); + }); + }); + } + + return null; + }); + } + + if (azimuthDisplay) { + const { center, start, stop } = azimuthDisplay; + + featuresArray.push( + + ); + + if (start === stop) { + const angleRadians = start * Math.PI / 180; + const earthRadius = 6371000; // meters + const [centerLat, centerLon] = center; + + const endLat = centerLat + + (1500 / earthRadius) + * Math.cos(angleRadians) + * (180 / Math.PI); + + const endLon = centerLon + + (1500 / earthRadius) + * Math.sin(angleRadians) + * (180 / Math.PI) + / Math.cos(centerLat * Math.PI / 180); + + featuresArray.push( + + ); + } else if (!((start === 0 && stop === 360) || (start === 360 && stop === 0))) { + featuresArray.push( + + ); + } + } + return (
@@ -189,65 +305,7 @@ const MapPreview = ({ controlOptions={drawControlOptions} shapeOptions={shapeOptions} > - {!emptyMap && features.map((feature) => { - const { geometry, properties } = feature; - - const { - shapeOptions = {}, - onClick, - onMouseover, - onMouseout - } = properties; - - const { style = {} } = shapeOptions; - - const featureProps = { - ...style, - onClick, - onMouseover, - onMouseout - }; - - if (geometry.type === 'Point') { - const latLngs = latLngFromGeoJson(feature); - - return latLngs.map(({ lat, lng }, index) => { - return ( - - ); - }); - } - - if (geometry.type === 'Polygon') { - const coordinates = coordinatesFromGeoJson({ - type: 'FeatureCollection', - features: [feature] - }); - - return coordinates.map((set) => { - return set.map((position, index) => { - const fixedPosition = position.map((coordinates) => [ - coordinates[1], - coordinates[0] - ]); - return ( - - ); - }); - }); - } - - return null; - })} + {featuresArray}
@@ -320,7 +378,8 @@ MapPreview.propTypes = { onDrawCreated: PropTypes.func, onDrawEdited: PropTypes.func, featureRef: PropTypes.object, - emptyMap: PropTypes.bool + emptyMap: PropTypes.bool, + azimuthDisplay: PropTypes.object }; export default MapPreview; diff --git a/src/components/MapPreview/MapPreview.test.js b/src/components/MapPreview/MapPreview.test.js index 7b32683..6409e38 100644 --- a/src/components/MapPreview/MapPreview.test.js +++ b/src/components/MapPreview/MapPreview.test.js @@ -1,4 +1,6 @@ import React from 'react'; +import { Circle, Polyline } from 'react-leaflet'; +import { SemiCircle } from 'react-leaflet-semicircle'; import { shallow } from 'enzyme'; import MapPreview from './'; @@ -70,4 +72,110 @@ describe('MapPreview', () => { ); }); }); + + describe('Azimuth Display', () => { + const baseAzimuthParams = { + center: ALEXANDRIA_COORDINATES + }; + + it('should render only a circle if start=0 and stop=360', () => { + const mapPreview = shallow( + + ); + const mapPreviewCircle = mapPreview.find(Circle); + const mapPreviewSemicircle = mapPreview.find(SemiCircle); + const mapPreviewPolyline = mapPreview.find(Polyline); + + expect(mapPreviewCircle).toHaveLength(1); + expect(mapPreviewSemicircle).toHaveLength(0); + expect(mapPreviewPolyline).toHaveLength(0); + }); + + it('should render only a circle if start=360 and stop=0', () => { + const mapPreview = shallow( + + ); + const mapPreviewCircle = mapPreview.find(Circle); + const mapPreviewSemicircle = mapPreview.find(SemiCircle); + const mapPreviewPolyline = mapPreview.find(Polyline); + + expect(mapPreviewCircle).toHaveLength(1); + expect(mapPreviewSemicircle).toHaveLength(0); + expect(mapPreviewPolyline).toHaveLength(0); + }); + + it('should render a circle and polyline if start=stop', () => { + const mapPreview = shallow( + + ); + const mapPreviewCircle = mapPreview.find(Circle); + const mapPreviewSemicircle = mapPreview.find(SemiCircle); + const mapPreviewPolyline = mapPreview.find(Polyline); + + expect(mapPreviewCircle).toHaveLength(1); + expect(mapPreviewSemicircle).toHaveLength(0); + expect(mapPreviewPolyline).toHaveLength(1); + }); + + it('should render a circle and semicircle if start > stop', () => { + const mapPreview = shallow( + + ); + const mapPreviewCircle = mapPreview.find(Circle); + const mapPreviewSemicircle = mapPreview.find(SemiCircle); + const mapPreviewPolyline = mapPreview.find(Polyline); + + expect(mapPreviewCircle).toHaveLength(1); + expect(mapPreviewSemicircle).toHaveLength(1); + expect(mapPreviewPolyline).toHaveLength(0); + }); + + it('should render a circle and semicircle if start < stop', () => { + const mapPreview = shallow( + + ); + const mapPreviewCircle = mapPreview.find(Circle); + const mapPreviewSemicircle = mapPreview.find(SemiCircle); + const mapPreviewPolyline = mapPreview.find(Polyline); + + expect(mapPreviewCircle).toHaveLength(1); + expect(mapPreviewSemicircle).toHaveLength(1); + expect(mapPreviewPolyline).toHaveLength(0); + }); + }); }); diff --git a/src/hooks/useInput.js b/src/hooks/useInput.js index 6176cfc..38cf58b 100644 --- a/src/hooks/useInput.js +++ b/src/hooks/useInput.js @@ -43,7 +43,9 @@ const INPUT_PROPS_WHITELIST = [ 'isMulti', 'inputProps', 'ref', - 'selectionStart' + 'selectionStart', + 'min', + 'max' ]; const INPUT_LIST_TYPES = ['radio', 'checkbox'];