From 2736f57e73e057f15c306253c3e4c304f5bbf501 Mon Sep 17 00:00:00 2001 From: luandro Date: Tue, 16 Mar 2021 20:10:06 -0300 Subject: [PATCH 01/22] feat: Add coordinate system page to settings with ability to change between LatLon, UTM, DMS, DD --- messages/en.json | 28 +++++ src/frontend/AppStack.js | 3 + src/frontend/hooks/useCoordinateSystem.js | 39 +++++++ src/frontend/lib/utils.js | 69 ++++++++++-- .../screens/Settings/CoordinateSystem.js | 100 ++++++++++++++++++ src/frontend/screens/Settings/Settings.js | 20 ++++ 6 files changed, 250 insertions(+), 9 deletions(-) create mode 100644 src/frontend/hooks/useCoordinateSystem.js create mode 100644 src/frontend/screens/Settings/CoordinateSystem.js diff --git a/messages/en.json b/messages/en.json index f2e16074b..3836829d3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -58,6 +58,26 @@ "description": "Title for category chooser screen", "message": "Choose what is happening" }, + "screens.CoordinateSystem.dd": { + "description": "Decimal Degrees coordinate system", + "message": "Decimal Degrees (DD)" + }, + "screens.CoordinateSystem.dms": { + "description": "Degrees/Minutes/Seconds coordinate system", + "message": "Degrees/Minutes/Seconds (DMS)" + }, + "screens.CoordinateSystem.latlong": { + "description": "Latitude and Longitude coordinate system", + "message": "Latitude and Longitude" + }, + "screens.CoordinateSystem.title": { + "description": "Title coordinate system screen", + "message": "Coordinate System" + }, + "screens.CoordinateSystem.utm": { + "description": "Universal Transverse Mercator coordinate system", + "message": "Universal Transverse Mercator (UTM)" + }, "screens.GpsModal.details": { "description": "Section title for details about current position", "message": "Details" @@ -296,6 +316,14 @@ "description": "Title of error dialog when there is an error importing a config file", "message": "Import Error" }, + "screens.Settings.coordinateSystem": { + "description": "Settings for coordinate systems", + "message": "Coordinate System" + }, + "screens.Settings.coordinateSystemDesc": { + "description": "Description of the 'Coordinate System' page", + "message": "UTM, Lat/Long, DMS" + }, "screens.Settings.currentConfig": { "description": "Label for name & version of current configuration", "message": "Current Configuration" diff --git a/src/frontend/AppStack.js b/src/frontend/AppStack.js index a9924e86a..afb24e70e 100644 --- a/src/frontend/AppStack.js +++ b/src/frontend/AppStack.js @@ -21,6 +21,7 @@ import CustomHeaderLeft from "./sharedComponents/CustomHeaderLeft"; import ProjectConfig from "./screens/Settings/ProjectConfig"; import AboutMapeo from "./screens/Settings/AboutMapeo"; import LanguageSettings from "./screens/Settings/LanguageSettings"; +import CoordinateSystem from "./screens/Settings/CoordinateSystem"; import HomeHeader from "./sharedComponents/HomeHeader"; const HomeTabs = createBottomTabNavigator( @@ -68,6 +69,8 @@ const AppStack = createStackNavigator( // $FlowFixMe LanguageSettings, // $FlowFixMe + CoordinateSystem, + // $FlowFixMe PhotosModal: PhotosModal, // $FlowFixMe CategoryChooser: CategoryChooser, diff --git a/src/frontend/hooks/useCoordinateSystem.js b/src/frontend/hooks/useCoordinateSystem.js new file mode 100644 index 000000000..87aa3bf21 --- /dev/null +++ b/src/frontend/hooks/useCoordinateSystem.js @@ -0,0 +1,39 @@ +import React from "react"; +import AsyncStorage from "@react-native-community/async-storage"; + +const STORE_KEY = "@MapeoCoordinateSystem@2"; +const defaultValue = "utm"; + +export default () => { + const [value, setLocalValue] = React.useState(defaultValue); + const [isLoading, setIsLoading] = React.useState(true); + const setValue = async value => { + setIsLoading(true); + await AsyncStorage.setItem(STORE_KEY, value); + setLocalValue(value); + setIsLoading(false); + }; + React.useEffect(() => { + const getValue = async () => { + try { + setIsLoading(true); + + const value = await AsyncStorage.getItem(STORE_KEY); + + if (value === null) { + return setValue(defaultValue); + } + + if (value !== null) { + setLocalValue(value); + } + } catch (e) { + setValue(defaultValue); + } finally { + setIsLoading(false); + } + }; + getValue(); + }, []); + return [value, setValue, isLoading]; +}; diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index 740ad9939..43c459401 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -121,15 +121,27 @@ export function getLastPhotoAttachment( return filterPhotosFromAttachments(attachments).pop(); } -export function formatCoords({ - lon, - lat, - format = "utm", -}: { - lon: number, - lat: number, - format?: "utm", -}): string { +// Coordinates conversions +function toDegreesMinutesAndSeconds(coordinate) { + var absolute = Math.abs(coordinate); + var degrees = Math.floor(absolute); + var minutesNotTruncated = (absolute - degrees) * 60; + var minutes = Math.floor(minutesNotTruncated); + var seconds = Math.floor((minutesNotTruncated - minutes) * 60); + + return `${degrees} ${minutes} ${seconds}`; +} + +function convertToDMS(lat, lon) { + var latitude = toDegreesMinutesAndSeconds(lat); + var latitudeCardinal = lat >= 0 ? "N" : "S"; + + var longitude = toDegreesMinutesAndSeconds(lon); + var longitudeCardinal = lon >= 0 ? "E" : "W"; + return `${latitude} ${latitudeCardinal} ${longitude} ${longitudeCardinal}`; +} + +function convertToUTM(lat, lon) { try { let { easting, northing, zoneNum, zoneLetter } = fromLatLon(lat, lon); easting = leftPad(easting.toFixed(), 6, "0"); @@ -143,6 +155,45 @@ export function formatCoords({ } } +function convertDMSToDD(degrees, minutes, seconds, direction) { + let dd = Number(degrees) + Number(minutes) / 60 + Number(seconds) / (60 * 60); + if (direction === "S" || direction === "W") { + dd = dd * -1; + } // Don't do anything for N or E + return dd; +} + +function convertToDD(lat, lon) { + const [latD, latM, latS, latDi, lonD, lonM, lonS, lonDi] = convertToDMS( + lat, + lon + ).split(" "); + const latDD = convertDMSToDD(latD, latM, latS, latDi); + const lonDD = convertDMSToDD(lonD, lonM, lonS, lonDi); + return `${latDD} ${lonDD}`; +} + +export function formatCoords({ + lon, + lat, + format = "utm", +}: { + lon: number, + lat: number, + format?: "utm", +}): string { + switch (format) { + case "utm": + return convertToUTM(lat, lon); + case "dms": + return convertToDMS(lat, lon); + case "dd": + return convertToDD(lat, lon); + default: + return convertToUTM(lat, lon); + } +} + export function getProp(tags: any, fieldKey: Key, defaultValue: any) { // TODO: support deeply nested tags. const shallowKey = Array.isArray(fieldKey) ? fieldKey[0] : fieldKey; diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js new file mode 100644 index 000000000..fd7f9cbe7 --- /dev/null +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -0,0 +1,100 @@ +// @flow +import React from "react"; +import { ScrollView } from "react-native"; +import { FormattedMessage, defineMessages, useIntl } from "react-intl"; + +import useCoodinateSystem from "../../hooks/useCoordinateSystem"; +import LocationContext from "../../context/LocationContext"; +import { formatCoords } from "../../lib/utils"; + +import HeaderTitle from "../../sharedComponents/HeaderTitle"; +import Loading from "../../sharedComponents/Loading"; +import SelectOne from "./SelectOne"; + +const m = defineMessages({ + title: { + id: "screens.CoordinateSystem.title", + defaultMessage: "Coordinate System", + description: "Title coordinate system screen", + }, + latlong: { + id: "screens.CoordinateSystem.latlong", + defaultMessage: "Latitude and Longitude", + description: "Latitude and Longitude coordinate system", + }, + dms: { + id: "screens.CoordinateSystem.dms", + defaultMessage: "Degrees/Minutes/Seconds (DMS)", + description: "Degrees/Minutes/Seconds coordinate system", + }, + utm: { + id: "screens.CoordinateSystem.utm", + defaultMessage: "Universal Transverse Mercator (UTM)", + description: "Universal Transverse Mercator coordinate system", + }, + dd: { + id: "screens.CoordinateSystem.dd", + defaultMessage: "Decimal Degrees (DD)", + description: "Decimal Degrees coordinate system", + }, +}); + +const CoordinateSystem = () => { + const intl = useIntl(); + const location = React.useContext(LocationContext); + const [system, setSystem] = useCoodinateSystem(); + if (!location) return ; + else { + const { latitude = 0, longitude = 0 } = location.position.coords; + const options = [ + { + value: "latlong", + label: intl.formatMessage({ ...m.latlong }), + hint: `${latitude} ${longitude}`, + }, + { + value: "dms", + label: intl.formatMessage({ ...m.dms }), + hint: formatCoords({ + lon: longitude, + lat: latitude, + format: "dms", + }), + }, + { + value: "utm", + label: intl.formatMessage({ ...m.utm }), + hint: formatCoords({ + lon: longitude, + lat: latitude, + format: "utm", + }), + }, + { + value: "dd", + label: intl.formatMessage({ ...m.dd }), + hint: formatCoords({ + lon: longitude, + lat: latitude, + format: "dd", + }), + }, + ]; + + return ( + + + + ); + } +}; + +CoordinateSystem.navigationOptions = { + headerTitle: () => ( + + + + ), +}; + +export default CoordinateSystem; diff --git a/src/frontend/screens/Settings/Settings.js b/src/frontend/screens/Settings/Settings.js index a02bf0e0a..61cac45db 100644 --- a/src/frontend/screens/Settings/Settings.js +++ b/src/frontend/screens/Settings/Settings.js @@ -48,6 +48,16 @@ const m = defineMessages({ defaultMessage: "Version and build number", description: "Description of the 'About Mapeo' page", }, + coordinateSystem: { + id: "screens.Settings.coordinateSystem", + defaultMessage: "Coordinate System", + description: "Settings for coordinate systems", + }, + coordinateSystemDesc: { + id: "screens.Settings.coordinateSystemDesc", + defaultMessage: "UTM, Lat/Long, DMS, DD", + description: "Description of the 'Coordinate System' page", + }, }); const Settings = () => { @@ -84,6 +94,16 @@ const Settings = () => { secondary={} > + navigate("CoordinateSystem")} + testID="settingsCoodinatesButton" + > + + } + secondary={} + > + ); }; From cf4be4fa217cd7ab4ad45de012b016548ba0b7a7 Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 17 Mar 2021 08:32:34 -0300 Subject: [PATCH 02/22] fix: Add latlon to formatCoords switch --- src/frontend/lib/utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index 43c459401..4e71a8d22 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -183,6 +183,8 @@ export function formatCoords({ format?: "utm", }): string { switch (format) { + case "latlon": + return `${lat} ${lon}`; case "utm": return convertToUTM(lat, lon); case "dms": From 81953d003d8fa1406abede67c111a9f626cb4b8e Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 17 Mar 2021 08:41:27 -0300 Subject: [PATCH 03/22] feat: Add information on GpsModal based on selected coordinate system --- messages/en.json | 16 +++++++- src/frontend/screens/GpsModal.js | 37 +++++++++++++++++-- .../screens/Settings/CoordinateSystem.js | 8 ++-- .../sharedComponents/FormattedData.js | 12 +++++- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/messages/en.json b/messages/en.json index 3836829d3..461187405 100644 --- a/messages/en.json +++ b/messages/en.json @@ -66,7 +66,7 @@ "description": "Degrees/Minutes/Seconds coordinate system", "message": "Degrees/Minutes/Seconds (DMS)" }, - "screens.CoordinateSystem.latlong": { + "screens.CoordinateSystem.latlon": { "description": "Latitude and Longitude coordinate system", "message": "Latitude and Longitude" }, @@ -90,6 +90,18 @@ "description": "Section title for time of last GPS update", "message": "Last update" }, + "screens.GpsModal.locationDD": { + "description": "Section title for DD coordinates", + "message": "Coordinates Decimal Degrees" + }, + "screens.GpsModal.locationDMS": { + "description": "Section title for DMS coordinates", + "message": "Coordinates DMS" + }, + "screens.GpsModal.locationLatLon": { + "description": "Section title for latitude and longitude coordinates", + "message": "Coordinates Latitude and Longitude" + }, "screens.GpsModal.locationSensors": { "description": "Heading for section about location sensor status", "message": "Sensor Status" @@ -322,7 +334,7 @@ }, "screens.Settings.coordinateSystemDesc": { "description": "Description of the 'Coordinate System' page", - "message": "UTM, Lat/Long, DMS" + "message": "UTM, Lat/Long, DMS, DD" }, "screens.Settings.currentConfig": { "description": "Label for name & version of current configuration", diff --git a/src/frontend/screens/GpsModal.js b/src/frontend/screens/GpsModal.js index 21bfe3d23..2ff1e5198 100644 --- a/src/frontend/screens/GpsModal.js +++ b/src/frontend/screens/GpsModal.js @@ -5,6 +5,8 @@ import Text from "../sharedComponents/Text"; import { defineMessages, FormattedMessage, useIntl } from "react-intl"; import LocationContext from "../context/LocationContext"; +import useCoodinateSystem from "../hooks/useCoordinateSystem"; + import { FormattedCoords } from "../sharedComponents/FormattedData"; import DateDistance from "../sharedComponents/DateDistance"; import HeaderTitle from "../sharedComponents/HeaderTitle"; @@ -27,6 +29,21 @@ const m = defineMessages({ defaultMessage: "Coordinates UTM", description: "Section title for UTM coordinates", }, + locationLatLon: { + id: "screens.GpsModal.locationLatLon", + defaultMessage: "Coordinates Latitude and Longitude", + description: "Section title for latitude and longitude coordinates", + }, + locationDMS: { + id: "screens.GpsModal.locationDMS", + defaultMessage: "Coordinates DMS", + description: "Section title for DMS coordinates", + }, + locationDD: { + id: "screens.GpsModal.locationDD", + defaultMessage: "Coordinates Decimal Degrees", + description: "Section title for DD coordinates", + }, details: { id: "screens.GpsModal.details", defaultMessage: "Details", @@ -63,6 +80,21 @@ type Props = { const GpsModal = ({ navigation }: Props) => { const location = React.useContext(LocationContext); const { formatMessage: t } = useIntl(); + const [system] = useCoodinateSystem(); + const coordinateMessage = () => { + switch (system) { + case "latlon": + return ; + case "utm": + return ; + case "dms": + return ; + case "dd": + return ; + default: + return ; + } + }; return ( @@ -76,13 +108,12 @@ const GpsModal = ({ navigation }: Props) => { /> {location.position && ( <> - - - + {coordinateMessage()} diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js index fd7f9cbe7..9b3d01f22 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -17,8 +17,8 @@ const m = defineMessages({ defaultMessage: "Coordinate System", description: "Title coordinate system screen", }, - latlong: { - id: "screens.CoordinateSystem.latlong", + latlon: { + id: "screens.CoordinateSystem.latlon", defaultMessage: "Latitude and Longitude", description: "Latitude and Longitude coordinate system", }, @@ -48,8 +48,8 @@ const CoordinateSystem = () => { const { latitude = 0, longitude = 0 } = location.position.coords; const options = [ { - value: "latlong", - label: intl.formatMessage({ ...m.latlong }), + value: "latlon", + label: intl.formatMessage({ ...m.latlon }), hint: `${latitude} ${longitude}`, }, { diff --git a/src/frontend/sharedComponents/FormattedData.js b/src/frontend/sharedComponents/FormattedData.js index 0213c99cc..7d92cfb49 100644 --- a/src/frontend/sharedComponents/FormattedData.js +++ b/src/frontend/sharedComponents/FormattedData.js @@ -29,8 +29,16 @@ const m = defineMessages({ // pattern of the other components in this file (which take a Field, Observation // or Preset as a prop) because it is also used in contexts other than // observation coords, e.g. for displaying current GPS coords. -export const FormattedCoords = ({ lat, lon }: { lat: number, lon: number }) => { - return <>{formatCoords({ lon, lat })}; +export const FormattedCoords = ({ + lat, + lon, + format, +}: { + lat: number, + lon: number, + format: string, +}) => { + return <>{formatCoords({ lon, lat, format })}; }; // Render the translated value of a translatable Field property (one of From 330c8823a54bb5df0dcf3915eac119c407ce8800 Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 17 Mar 2021 09:04:26 -0300 Subject: [PATCH 04/22] feat: Show coordinates on observation screens based on selected system --- src/frontend/screens/Observation/ObservationShare.js | 4 +++- src/frontend/screens/Observation/ObservationView.js | 4 +++- src/frontend/screens/ObservationEdit/ObservationEditView.js | 4 +++- src/frontend/sharedComponents/LocationField.js | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/frontend/screens/Observation/ObservationShare.js b/src/frontend/screens/Observation/ObservationShare.js index fbaf0f5f5..35d5d2f69 100644 --- a/src/frontend/screens/Observation/ObservationShare.js +++ b/src/frontend/screens/Observation/ObservationShare.js @@ -15,6 +15,7 @@ import { import { getProp } from "../../lib/utils"; import type { PresetWithFields } from "../../context/ConfigContext"; import type { Observation } from "../../context/ObservationsContext"; +import useCoodinateSystem from "../../hooks/useCoordinateSystem"; const m = defineMessages({ alertSubject: { @@ -52,6 +53,7 @@ export const ShareSubject = ({ observation, preset }: ShareMessageProps) => { export const ShareMessage = ({ observation, preset }: ShareMessageProps) => { const { formatMessage: t } = useIntl(); + const [system] = useCoodinateSystem(); const { value } = observation; const { lon, lat } = value; @@ -81,7 +83,7 @@ export const ShareMessage = ({ observation, preset }: ShareMessageProps) => {
{lon != null && lat != null ? ( - + ) : null}

{value.tags.notes ?

{value.tags.notes}

: null} diff --git a/src/frontend/screens/Observation/ObservationView.js b/src/frontend/screens/Observation/ObservationView.js index 84d833b5d..c44ef16dc 100644 --- a/src/frontend/screens/Observation/ObservationView.js +++ b/src/frontend/screens/Observation/ObservationView.js @@ -33,6 +33,7 @@ import type { PresetWithFields } from "../../context/ConfigContext"; import type { Observation } from "../../context/ObservationsContext"; import useMapStyle from "../../hooks/useMapStyle"; import useDeviceId from "../../hooks/useDeviceId"; +import useCoodinateSystem from "../../hooks/useCoordinateSystem"; import Loading from "../../sharedComponents/Loading"; import OfflineMapLayers from "../../sharedComponents/OfflineMapLayers"; import { ShareMessage, ShareSubject, renderToString } from "./ObservationShare"; @@ -125,6 +126,7 @@ const ObservationView = ({ onPressDelete, }: ODVProps) => { const intl = useIntl(); + const [system] = useCoodinateSystem(); const { formatMessage: t } = intl; const deviceId = useDeviceId(); const isMine = deviceId === observation.value.deviceId; @@ -173,7 +175,7 @@ const ObservationView = ({ - + diff --git a/src/frontend/screens/ObservationEdit/ObservationEditView.js b/src/frontend/screens/ObservationEdit/ObservationEditView.js index e227e97cc..d96c57db8 100644 --- a/src/frontend/screens/ObservationEdit/ObservationEditView.js +++ b/src/frontend/screens/ObservationEdit/ObservationEditView.js @@ -55,10 +55,12 @@ const m = defineMessages({ const LocationView = ({ longitude, latitude, + system, accuracy, }: { longitude?: number | null, latitude?: number | null, + system?: string, accuracy?: number, }) => ( @@ -75,7 +77,7 @@ const LocationView = ({ style={{ marginRight: 5 }} /> - + {accuracy === undefined ? null : ( diff --git a/src/frontend/sharedComponents/LocationField.js b/src/frontend/sharedComponents/LocationField.js index 1ed7802fe..2a866339d 100644 --- a/src/frontend/sharedComponents/LocationField.js +++ b/src/frontend/sharedComponents/LocationField.js @@ -3,6 +3,7 @@ import * as React from "react"; import omit from "lodash/omit"; import useDraftObservation from "../hooks/useDraftObservation"; +import useCoodinateSystem from "../hooks/useCoordinateSystem"; import LocationContext from "../context/LocationContext"; type Props = { @@ -25,6 +26,7 @@ type Props = { const LocationField = ({ children }: Props) => { const [{ value }, { updateDraft }] = useDraftObservation(); const location = React.useContext(LocationContext); + const [system] = useCoodinateSystem(); React.useEffect(() => { if (!location.position || !value) return; @@ -55,6 +57,7 @@ const LocationField = ({ children }: Props) => { return children({ longitude: value.lon, latitude: value.lat, + system, accuracy: value.metadata && value.metadata.location && From 32fbe0a6ca49bf9a9111562a9304014401bf7ad7 Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 17 Mar 2021 09:13:14 -0300 Subject: [PATCH 05/22] fix: Add value check before setting AsyncStorage --- src/frontend/hooks/useCoordinateSystem.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/frontend/hooks/useCoordinateSystem.js b/src/frontend/hooks/useCoordinateSystem.js index 87aa3bf21..8e96c21ab 100644 --- a/src/frontend/hooks/useCoordinateSystem.js +++ b/src/frontend/hooks/useCoordinateSystem.js @@ -8,10 +8,12 @@ export default () => { const [value, setLocalValue] = React.useState(defaultValue); const [isLoading, setIsLoading] = React.useState(true); const setValue = async value => { - setIsLoading(true); - await AsyncStorage.setItem(STORE_KEY, value); - setLocalValue(value); - setIsLoading(false); + if (value) { + setIsLoading(true); + await AsyncStorage.setItem(STORE_KEY, value); + setLocalValue(value); + setIsLoading(false); + } }; React.useEffect(() => { const getValue = async () => { From 2c07212c1548715289acf8c59569390386c19f0e Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 24 Mar 2021 18:06:51 -0300 Subject: [PATCH 06/22] fix: Have Coordinate System before About Mapeo on configuration menu --- src/frontend/screens/Settings/Settings.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/frontend/screens/Settings/Settings.js b/src/frontend/screens/Settings/Settings.js index 61cac45db..3548d21ed 100644 --- a/src/frontend/screens/Settings/Settings.js +++ b/src/frontend/screens/Settings/Settings.js @@ -84,16 +84,6 @@ const Settings = () => { secondary={} > - navigate("AboutMapeo")} - testID="settingsAboutButton" - > - - } - secondary={} - > - navigate("CoordinateSystem")} testID="settingsCoodinatesButton" @@ -104,6 +94,16 @@ const Settings = () => { secondary={} > + navigate("AboutMapeo")} + testID="settingsAboutButton" + > + + } + secondary={} + > + ); }; From f8530301e3d1e8c3e6e1689cb8e5f3fc9bc7fdf5 Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 24 Mar 2021 18:46:01 -0300 Subject: [PATCH 07/22] fix: Remove Decimal Degrees which is same as Lat Long --- messages/en.json | 6 +----- src/frontend/lib/utils.js | 2 -- src/frontend/screens/GpsModal.js | 2 -- src/frontend/screens/Settings/CoordinateSystem.js | 11 +---------- src/frontend/screens/Settings/Settings.js | 2 +- 5 files changed, 3 insertions(+), 20 deletions(-) diff --git a/messages/en.json b/messages/en.json index 461187405..06ad259ee 100644 --- a/messages/en.json +++ b/messages/en.json @@ -58,10 +58,6 @@ "description": "Title for category chooser screen", "message": "Choose what is happening" }, - "screens.CoordinateSystem.dd": { - "description": "Decimal Degrees coordinate system", - "message": "Decimal Degrees (DD)" - }, "screens.CoordinateSystem.dms": { "description": "Degrees/Minutes/Seconds coordinate system", "message": "Degrees/Minutes/Seconds (DMS)" @@ -334,7 +330,7 @@ }, "screens.Settings.coordinateSystemDesc": { "description": "Description of the 'Coordinate System' page", - "message": "UTM, Lat/Long, DMS, DD" + "message": "UTM, Lat/Long, DMS" }, "screens.Settings.currentConfig": { "description": "Label for name & version of current configuration", diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index 4e71a8d22..ec25df645 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -189,8 +189,6 @@ export function formatCoords({ return convertToUTM(lat, lon); case "dms": return convertToDMS(lat, lon); - case "dd": - return convertToDD(lat, lon); default: return convertToUTM(lat, lon); } diff --git a/src/frontend/screens/GpsModal.js b/src/frontend/screens/GpsModal.js index 2ff1e5198..60fe006f0 100644 --- a/src/frontend/screens/GpsModal.js +++ b/src/frontend/screens/GpsModal.js @@ -89,8 +89,6 @@ const GpsModal = ({ navigation }: Props) => { return ; case "dms": return ; - case "dd": - return ; default: return ; } diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js index 9b3d01f22..6af8642b7 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -69,16 +69,7 @@ const CoordinateSystem = () => { lat: latitude, format: "utm", }), - }, - { - value: "dd", - label: intl.formatMessage({ ...m.dd }), - hint: formatCoords({ - lon: longitude, - lat: latitude, - format: "dd", - }), - }, + } ]; return ( diff --git a/src/frontend/screens/Settings/Settings.js b/src/frontend/screens/Settings/Settings.js index 3548d21ed..e6f0a7c6f 100644 --- a/src/frontend/screens/Settings/Settings.js +++ b/src/frontend/screens/Settings/Settings.js @@ -55,7 +55,7 @@ const m = defineMessages({ }, coordinateSystemDesc: { id: "screens.Settings.coordinateSystemDesc", - defaultMessage: "UTM, Lat/Long, DMS, DD", + defaultMessage: "UTM, Lat/Long, DMS", description: "Description of the 'Coordinate System' page", }, }); From 873a6b59deb8b8d76b73bc9a109ee316d2bbfe6b Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 24 Mar 2021 18:50:11 -0300 Subject: [PATCH 08/22] fix: Round decimal degrees to 6 decimal places --- src/frontend/lib/utils.js | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index ec25df645..e44f851e2 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -155,22 +155,15 @@ function convertToUTM(lat, lon) { } } -function convertDMSToDD(degrees, minutes, seconds, direction) { - let dd = Number(degrees) + Number(minutes) / 60 + Number(seconds) / (60 * 60); - if (direction === "S" || direction === "W") { - dd = dd * -1; - } // Don't do anything for N or E - return dd; -} - -function convertToDD(lat, lon) { - const [latD, latM, latS, latDi, lonD, lonM, lonS, lonDi] = convertToDMS( - lat, - lon - ).split(" "); - const latDD = convertDMSToDD(latD, latM, latS, latDi); - const lonDD = convertDMSToDD(lonD, lonM, lonS, lonDi); - return `${latDD} ${lonDD}`; +function formatLatLong(lat, lon) { + const decimals = 6; + const formattedLat = Number( + Math.round(lat + "e" + decimals) + "e-" + decimals + ); + const formattedLon = Number( + Math.round(lon + "e" + decimals) + "e-" + decimals + ); + return `${formattedLat} ${formattedLon}`; } export function formatCoords({ @@ -184,7 +177,7 @@ export function formatCoords({ }): string { switch (format) { case "latlon": - return `${lat} ${lon}`; + return formatLatLong(lat, lon); case "utm": return convertToUTM(lat, lon); case "dms": From 48e815b2ccbe7afa956494d070a8bcb8445cd7e1 Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 24 Mar 2021 18:57:45 -0300 Subject: [PATCH 09/22] chore: Cleanup and add better formating for DMS --- src/frontend/hooks/useCoordinateSystem.js | 3 --- src/frontend/lib/utils.js | 2 +- src/frontend/screens/Settings/CoordinateSystem.js | 9 ++------- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/frontend/hooks/useCoordinateSystem.js b/src/frontend/hooks/useCoordinateSystem.js index 8e96c21ab..5cef89658 100644 --- a/src/frontend/hooks/useCoordinateSystem.js +++ b/src/frontend/hooks/useCoordinateSystem.js @@ -19,13 +19,10 @@ export default () => { const getValue = async () => { try { setIsLoading(true); - const value = await AsyncStorage.getItem(STORE_KEY); - if (value === null) { return setValue(defaultValue); } - if (value !== null) { setLocalValue(value); } diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index e44f851e2..fb784a4e6 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -129,7 +129,7 @@ function toDegreesMinutesAndSeconds(coordinate) { var minutes = Math.floor(minutesNotTruncated); var seconds = Math.floor((minutesNotTruncated - minutes) * 60); - return `${degrees} ${minutes} ${seconds}`; + return `${degrees}° ${minutes}' ${seconds}"`; } function convertToDMS(lat, lon) { diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js index 6af8642b7..fc4324c93 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -32,18 +32,13 @@ const m = defineMessages({ defaultMessage: "Universal Transverse Mercator (UTM)", description: "Universal Transverse Mercator coordinate system", }, - dd: { - id: "screens.CoordinateSystem.dd", - defaultMessage: "Decimal Degrees (DD)", - description: "Decimal Degrees coordinate system", - }, }); const CoordinateSystem = () => { const intl = useIntl(); const location = React.useContext(LocationContext); const [system, setSystem] = useCoodinateSystem(); - if (!location) return ; + if (!location || !location.position) return ; else { const { latitude = 0, longitude = 0 } = location.position.coords; const options = [ @@ -69,7 +64,7 @@ const CoordinateSystem = () => { lat: latitude, format: "utm", }), - } + }, ]; return ( From 930346b115769f23aff95148a496819cc296d15c Mon Sep 17 00:00:00 2001 From: luandro Date: Wed, 24 Mar 2021 19:16:03 -0300 Subject: [PATCH 10/22] fix: Add 3 decimal degrees to DMS coordinates --- src/frontend/lib/utils.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index fb784a4e6..06efcc24b 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -123,21 +123,24 @@ export function getLastPhotoAttachment( // Coordinates conversions function toDegreesMinutesAndSeconds(coordinate) { - var absolute = Math.abs(coordinate); - var degrees = Math.floor(absolute); - var minutesNotTruncated = (absolute - degrees) * 60; - var minutes = Math.floor(minutesNotTruncated); - var seconds = Math.floor((minutesNotTruncated - minutes) * 60); - - return `${degrees}° ${minutes}' ${seconds}"`; + const absolute = Math.abs(coordinate); + const degrees = Math.floor(absolute); + const minutesNotTruncated = (absolute - degrees) * 60; + const minutes = Math.floor(minutesNotTruncated); + const seconds = (minutesNotTruncated - minutes) * 60; + const decimals = 3; + const formattedSeconds = Number( + Math.round(seconds + "e" + decimals) + "e-" + decimals + ); + return `${degrees}° ${minutes}' ${formattedSeconds}"`; } function convertToDMS(lat, lon) { - var latitude = toDegreesMinutesAndSeconds(lat); - var latitudeCardinal = lat >= 0 ? "N" : "S"; + const latitude = toDegreesMinutesAndSeconds(lat); + const latitudeCardinal = lat >= 0 ? "N" : "S"; - var longitude = toDegreesMinutesAndSeconds(lon); - var longitudeCardinal = lon >= 0 ? "E" : "W"; + const longitude = toDegreesMinutesAndSeconds(lon); + const longitudeCardinal = lon >= 0 ? "E" : "W"; return `${latitude} ${latitudeCardinal} ${longitude} ${longitudeCardinal}`; } From 70b3d1c40133c121cd44d77f63df071f92b3892a Mon Sep 17 00:00:00 2001 From: luandro Date: Fri, 26 Mar 2021 14:44:13 -0300 Subject: [PATCH 11/22] fix: Pass lat/lon explicitly within an object on formatting functions --- src/frontend/lib/utils.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index 06efcc24b..e61f5bea2 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -135,7 +135,7 @@ function toDegreesMinutesAndSeconds(coordinate) { return `${degrees}° ${minutes}' ${formattedSeconds}"`; } -function convertToDMS(lat, lon) { +function convertToDMS({ lat, lon }) { const latitude = toDegreesMinutesAndSeconds(lat); const latitudeCardinal = lat >= 0 ? "N" : "S"; @@ -144,7 +144,7 @@ function convertToDMS(lat, lon) { return `${latitude} ${latitudeCardinal} ${longitude} ${longitudeCardinal}`; } -function convertToUTM(lat, lon) { +function convertToUTM({ lat, lon }) { try { let { easting, northing, zoneNum, zoneLetter } = fromLatLon(lat, lon); easting = leftPad(easting.toFixed(), 6, "0"); @@ -158,7 +158,7 @@ function convertToUTM(lat, lon) { } } -function formatLatLong(lat, lon) { +function formatDD({ lat, lon }) { const decimals = 6; const formattedLat = Number( Math.round(lat + "e" + decimals) + "e-" + decimals @@ -176,17 +176,17 @@ export function formatCoords({ }: { lon: number, lat: number, - format?: "utm", + format?: "utm" | "dd" | "dms", }): string { switch (format) { - case "latlon": - return formatLatLong(lat, lon); + case "dd": + return formatDD({ lat, lon }); case "utm": - return convertToUTM(lat, lon); + return convertToUTM({ lat, lon }); case "dms": - return convertToDMS(lat, lon); + return convertToDMS({ lat, lon }); default: - return convertToUTM(lat, lon); + return convertToUTM({ lat, lon }); } } From 31c127a8ac34ce02ddc513bcd46081566345c158 Mon Sep 17 00:00:00 2001 From: luandro Date: Fri, 26 Mar 2021 15:06:52 -0300 Subject: [PATCH 12/22] fix: Use Decimal Degrees naming instead of lat/lon --- src/frontend/lib/utils.js | 2 +- src/frontend/screens/GpsModal.js | 4 ++-- .../screens/Settings/CoordinateSystem.js | 18 +++++++++++------- src/frontend/screens/Settings/Settings.js | 2 +- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index e61f5bea2..cde3a3446 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -166,7 +166,7 @@ function formatDD({ lat, lon }) { const formattedLon = Number( Math.round(lon + "e" + decimals) + "e-" + decimals ); - return `${formattedLat} ${formattedLon}`; + return `lat ${formattedLat} lon ${formattedLon}`; } export function formatCoords({ diff --git a/src/frontend/screens/GpsModal.js b/src/frontend/screens/GpsModal.js index 60fe006f0..1c42defda 100644 --- a/src/frontend/screens/GpsModal.js +++ b/src/frontend/screens/GpsModal.js @@ -83,8 +83,8 @@ const GpsModal = ({ navigation }: Props) => { const [system] = useCoodinateSystem(); const coordinateMessage = () => { switch (system) { - case "latlon": - return ; + case "dd": + return ; case "utm": return ; case "dms": diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js index fc4324c93..b4ae94537 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -17,10 +17,10 @@ const m = defineMessages({ defaultMessage: "Coordinate System", description: "Title coordinate system screen", }, - latlon: { - id: "screens.CoordinateSystem.latlon", - defaultMessage: "Latitude and Longitude", - description: "Latitude and Longitude coordinate system", + dd: { + id: "screens.CoordinateSystem.dd", + defaultMessage: "Decimal Degrees", + description: "Decimal Degrees coordinate system", }, dms: { id: "screens.CoordinateSystem.dms", @@ -43,9 +43,13 @@ const CoordinateSystem = () => { const { latitude = 0, longitude = 0 } = location.position.coords; const options = [ { - value: "latlon", - label: intl.formatMessage({ ...m.latlon }), - hint: `${latitude} ${longitude}`, + value: "dd", + label: intl.formatMessage({ ...m.dd }), + hint: formatCoords({ + lon: longitude, + lat: latitude, + format: "dd", + }), }, { value: "dms", diff --git a/src/frontend/screens/Settings/Settings.js b/src/frontend/screens/Settings/Settings.js index e6f0a7c6f..28a0a5e2a 100644 --- a/src/frontend/screens/Settings/Settings.js +++ b/src/frontend/screens/Settings/Settings.js @@ -55,7 +55,7 @@ const m = defineMessages({ }, coordinateSystemDesc: { id: "screens.Settings.coordinateSystemDesc", - defaultMessage: "UTM, Lat/Long, DMS", + defaultMessage: "DD, UTM, DMS", description: "Description of the 'Coordinate System' page", }, }); From 72fa39ebebc6e44ccfac1a860502198ecc189bea Mon Sep 17 00:00:00 2001 From: luandro Date: Fri, 26 Mar 2021 15:13:18 -0300 Subject: [PATCH 13/22] chore: Extract messages and add DD to naming in config menu --- messages/en.json | 10 +++++----- src/frontend/screens/Settings/CoordinateSystem.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/messages/en.json b/messages/en.json index 06ad259ee..aac8e7845 100644 --- a/messages/en.json +++ b/messages/en.json @@ -58,14 +58,14 @@ "description": "Title for category chooser screen", "message": "Choose what is happening" }, + "screens.CoordinateSystem.dd": { + "description": "Decimal Degrees coordinate system", + "message": "Decimal Degrees (DD)" + }, "screens.CoordinateSystem.dms": { "description": "Degrees/Minutes/Seconds coordinate system", "message": "Degrees/Minutes/Seconds (DMS)" }, - "screens.CoordinateSystem.latlon": { - "description": "Latitude and Longitude coordinate system", - "message": "Latitude and Longitude" - }, "screens.CoordinateSystem.title": { "description": "Title coordinate system screen", "message": "Coordinate System" @@ -330,7 +330,7 @@ }, "screens.Settings.coordinateSystemDesc": { "description": "Description of the 'Coordinate System' page", - "message": "UTM, Lat/Long, DMS" + "message": "DD, UTM, DMS" }, "screens.Settings.currentConfig": { "description": "Label for name & version of current configuration", diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js index b4ae94537..52f8cf268 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -19,7 +19,7 @@ const m = defineMessages({ }, dd: { id: "screens.CoordinateSystem.dd", - defaultMessage: "Decimal Degrees", + defaultMessage: "Decimal Degrees (DD)", description: "Decimal Degrees coordinate system", }, dms: { From e4ee80d66e96ef146b6f55e86b86cf594e18a943 Mon Sep 17 00:00:00 2001 From: luandro Date: Thu, 1 Apr 2021 10:19:25 -0300 Subject: [PATCH 14/22] chore: Add settings context --- src/frontend/context/SettingsContext.js | 84 +++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/frontend/context/SettingsContext.js diff --git a/src/frontend/context/SettingsContext.js b/src/frontend/context/SettingsContext.js new file mode 100644 index 000000000..085250b8a --- /dev/null +++ b/src/frontend/context/SettingsContext.js @@ -0,0 +1,84 @@ +// @flow +import * as React from "react"; +import AsyncStorage from "@react-native-community/async-storage"; + +const STORE_KEY = "@MapeoSettings@36"; + +export type SettingsContextType = { + coordinateSystem?: string, +}; + +const initialState = { + coordinateSystem: "utm", +}; + +const defaultContext = [initialState, () => {}]; +const SettingsContext = React.createContext(defaultContext); + +function reducer(state: State, action: Action): State { + switch (action.type) { + case "set": { + return action.value; + } + case "set_coordinate_system": { + try { + return { ...state, coordinateSystem: action.value }; + } catch { + return state; + } + } + default: + return state; + } +} + +const getData = async dispatch => { + try { + // AsyncStorage.clear() + const state = await AsyncStorage.getItem(STORE_KEY); + if (state) { + const parsedState = JSON.parse(state); + dispatch({ type: "set", value: parsedState }); + return parsedState; + } else return initialState; + } catch (e) { + console.log("Failed to fetch the data from storage"); + return initialState; + } +}; + +const saveData = async value => { + try { + const stringified = JSON.stringify(value); + await AsyncStorage.setItem(STORE_KEY, stringified); + return value; + } catch (e) { + console.log("Failed to save the data to the storage"); + } +}; + +export const SettingsProvider = ({ children }: { children: React.Node }) => { + const didMountRef = React.useRef(false); + const [state, dispatch] = React.useReducer(reducer, defaultContext[0]); + const contextValue = React.useMemo(() => [state, dispatch], [ + state, + dispatch, + ]); + React.useEffect(() => { + if (didMountRef.current) { + saveData(contextValue[0]); + } else { + didMountRef.current = true; + getData(dispatch); + } + }, [contextValue]); + return ( + + {children} + + ); +}; + +export default SettingsContext; From 4caa86245e7e6a314a1ca27aec7fe92c1e3ff2df Mon Sep 17 00:00:00 2001 From: luandro Date: Thu, 1 Apr 2021 10:22:05 -0300 Subject: [PATCH 15/22] chore: Update screens and components to use settings context --- src/frontend/context/AppProvider.js | 5 ++- src/frontend/hooks/useCoordinateSystem.js | 38 ------------------- src/frontend/screens/GpsModal.js | 10 +++-- .../screens/Observation/ObservationShare.js | 9 +++-- .../screens/Observation/ObservationView.js | 12 ++++-- .../screens/Settings/CoordinateSystem.js | 11 ++++-- .../sharedComponents/LocationField.js | 9 +++-- 7 files changed, 38 insertions(+), 56 deletions(-) delete mode 100644 src/frontend/hooks/useCoordinateSystem.js diff --git a/src/frontend/context/AppProvider.js b/src/frontend/context/AppProvider.js index c69d38480..a30172e31 100644 --- a/src/frontend/context/AppProvider.js +++ b/src/frontend/context/AppProvider.js @@ -4,6 +4,7 @@ import * as React from "react"; import { LocationProvider } from "./LocationContext"; import { ObservationsProvider } from "./ObservationsContext"; import { ConfigProvider } from "./ConfigContext"; +import { SettingsProvider } from "./SettingsContext"; import { DraftObservationProvider } from "./DraftObservationContext"; // This is a convenience wrapper for providing all App contexts to the tree, @@ -12,7 +13,9 @@ const AppProvider = ({ children }: { children: React.Node }) => ( - {children} + + {children} + diff --git a/src/frontend/hooks/useCoordinateSystem.js b/src/frontend/hooks/useCoordinateSystem.js deleted file mode 100644 index 5cef89658..000000000 --- a/src/frontend/hooks/useCoordinateSystem.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from "react"; -import AsyncStorage from "@react-native-community/async-storage"; - -const STORE_KEY = "@MapeoCoordinateSystem@2"; -const defaultValue = "utm"; - -export default () => { - const [value, setLocalValue] = React.useState(defaultValue); - const [isLoading, setIsLoading] = React.useState(true); - const setValue = async value => { - if (value) { - setIsLoading(true); - await AsyncStorage.setItem(STORE_KEY, value); - setLocalValue(value); - setIsLoading(false); - } - }; - React.useEffect(() => { - const getValue = async () => { - try { - setIsLoading(true); - const value = await AsyncStorage.getItem(STORE_KEY); - if (value === null) { - return setValue(defaultValue); - } - if (value !== null) { - setLocalValue(value); - } - } catch (e) { - setValue(defaultValue); - } finally { - setIsLoading(false); - } - }; - getValue(); - }, []); - return [value, setValue, isLoading]; -}; diff --git a/src/frontend/screens/GpsModal.js b/src/frontend/screens/GpsModal.js index 1c42defda..d34d4f939 100644 --- a/src/frontend/screens/GpsModal.js +++ b/src/frontend/screens/GpsModal.js @@ -5,7 +5,7 @@ import Text from "../sharedComponents/Text"; import { defineMessages, FormattedMessage, useIntl } from "react-intl"; import LocationContext from "../context/LocationContext"; -import useCoodinateSystem from "../hooks/useCoordinateSystem"; +import SettingsContext from "../context/SettingsContext"; import { FormattedCoords } from "../sharedComponents/FormattedData"; import DateDistance from "../sharedComponents/DateDistance"; @@ -79,10 +79,12 @@ type Props = { const GpsModal = ({ navigation }: Props) => { const location = React.useContext(LocationContext); + const { + settings: { coordinateSystem }, + } = React.useContext(SettingsContext); const { formatMessage: t } = useIntl(); - const [system] = useCoodinateSystem(); const coordinateMessage = () => { - switch (system) { + switch (coordinateSystem) { case "dd": return ; case "utm": @@ -111,7 +113,7 @@ const GpsModal = ({ navigation }: Props) => { diff --git a/src/frontend/screens/Observation/ObservationShare.js b/src/frontend/screens/Observation/ObservationShare.js index 35d5d2f69..c59fc64e4 100644 --- a/src/frontend/screens/Observation/ObservationShare.js +++ b/src/frontend/screens/Observation/ObservationShare.js @@ -15,7 +15,7 @@ import { import { getProp } from "../../lib/utils"; import type { PresetWithFields } from "../../context/ConfigContext"; import type { Observation } from "../../context/ObservationsContext"; -import useCoodinateSystem from "../../hooks/useCoordinateSystem"; +import SettingsContext from "../../context/SettingsContext"; const m = defineMessages({ alertSubject: { @@ -53,7 +53,10 @@ export const ShareSubject = ({ observation, preset }: ShareMessageProps) => { export const ShareMessage = ({ observation, preset }: ShareMessageProps) => { const { formatMessage: t } = useIntl(); - const [system] = useCoodinateSystem(); + const { + settings: { coordinateSystem }, + } = React.useContext(SettingsContext); + const { value } = observation; const { lon, lat } = value; @@ -83,7 +86,7 @@ export const ShareMessage = ({ observation, preset }: ShareMessageProps) => {
{lon != null && lat != null ? ( - + ) : null}

{value.tags.notes ?

{value.tags.notes}

: null} diff --git a/src/frontend/screens/Observation/ObservationView.js b/src/frontend/screens/Observation/ObservationView.js index c44ef16dc..020635811 100644 --- a/src/frontend/screens/Observation/ObservationView.js +++ b/src/frontend/screens/Observation/ObservationView.js @@ -30,10 +30,10 @@ import { } from "../../lib/styles"; import { TouchableOpacity } from "../../sharedComponents/Touchables"; import type { PresetWithFields } from "../../context/ConfigContext"; +import SettingsContext from "../../context/SettingsContext"; import type { Observation } from "../../context/ObservationsContext"; import useMapStyle from "../../hooks/useMapStyle"; import useDeviceId from "../../hooks/useDeviceId"; -import useCoodinateSystem from "../../hooks/useCoordinateSystem"; import Loading from "../../sharedComponents/Loading"; import OfflineMapLayers from "../../sharedComponents/OfflineMapLayers"; import { ShareMessage, ShareSubject, renderToString } from "./ObservationShare"; @@ -126,7 +126,9 @@ const ObservationView = ({ onPressDelete, }: ODVProps) => { const intl = useIntl(); - const [system] = useCoodinateSystem(); + const { + settings: { coordinateSystem }, + } = React.useContext(SettingsContext); const { formatMessage: t } = intl; const deviceId = useDeviceId(); const isMine = deviceId === observation.value.deviceId; @@ -175,7 +177,11 @@ const ObservationView = ({ - + diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js index 52f8cf268..40e49e528 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -2,9 +2,9 @@ import React from "react"; import { ScrollView } from "react-native"; import { FormattedMessage, defineMessages, useIntl } from "react-intl"; - -import useCoodinateSystem from "../../hooks/useCoordinateSystem"; import LocationContext from "../../context/LocationContext"; +import SettingsContext from "../../context/SettingsContext"; + import { formatCoords } from "../../lib/utils"; import HeaderTitle from "../../sharedComponents/HeaderTitle"; @@ -37,7 +37,12 @@ const m = defineMessages({ const CoordinateSystem = () => { const intl = useIntl(); const location = React.useContext(LocationContext); - const [system, setSystem] = useCoodinateSystem(); + const { settings, dispatch } = React.useContext(SettingsContext); + const setSystem = React.useCallback( + value => dispatch({ type: "set_coordinate_system", value }), + [dispatch] + ); + const system = settings.coordinateSystem; if (!location || !location.position) return ; else { const { latitude = 0, longitude = 0 } = location.position.coords; diff --git a/src/frontend/sharedComponents/LocationField.js b/src/frontend/sharedComponents/LocationField.js index 2a866339d..899481db2 100644 --- a/src/frontend/sharedComponents/LocationField.js +++ b/src/frontend/sharedComponents/LocationField.js @@ -3,8 +3,8 @@ import * as React from "react"; import omit from "lodash/omit"; import useDraftObservation from "../hooks/useDraftObservation"; -import useCoodinateSystem from "../hooks/useCoordinateSystem"; import LocationContext from "../context/LocationContext"; +import SettingsContext from "../context/SettingsContext"; type Props = { children: ({ @@ -26,8 +26,9 @@ type Props = { const LocationField = ({ children }: Props) => { const [{ value }, { updateDraft }] = useDraftObservation(); const location = React.useContext(LocationContext); - const [system] = useCoodinateSystem(); - + const { + settings: { coordinateSystem }, + } = React.useContext(SettingsContext); React.useEffect(() => { if (!location.position || !value) return; const draftHasManualLocation = @@ -57,7 +58,7 @@ const LocationField = ({ children }: Props) => { return children({ longitude: value.lon, latitude: value.lat, - system, + system: coordinateSystem, accuracy: value.metadata && value.metadata.location && From d4499e8428a6c781ee81f47cf22f4313a3e7bc94 Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Fri, 16 Apr 2021 17:51:36 +0100 Subject: [PATCH 16/22] fixes & cleanup --- src/frontend/context/SettingsContext.js | 95 +++++++------------ src/frontend/hooks/useSettingsValue.js | 10 ++ src/frontend/screens/GpsModal.js | 43 ++++----- .../screens/Observation/ObservationShare.js | 6 +- .../screens/Observation/ObservationView.js | 6 +- .../ObservationEdit/ObservationEditView.js | 54 ++++++----- .../screens/Settings/CoordinateSystem.js | 68 +++++-------- src/frontend/screens/Settings/SelectOne.js | 2 +- .../sharedComponents/LocationField.js | 6 +- 9 files changed, 116 insertions(+), 174 deletions(-) create mode 100644 src/frontend/hooks/useSettingsValue.js diff --git a/src/frontend/context/SettingsContext.js b/src/frontend/context/SettingsContext.js index 085250b8a..b325bb83a 100644 --- a/src/frontend/context/SettingsContext.js +++ b/src/frontend/context/SettingsContext.js @@ -1,82 +1,51 @@ // @flow import * as React from "react"; -import AsyncStorage from "@react-native-community/async-storage"; +import createPersistedState from "../hooks/usePersistedState"; -const STORE_KEY = "@MapeoSettings@36"; +// Increment if the shape of settings changes, but try to avoid doing this +// because it will reset everybody's settings back to the defaults = bad :( +const STORE_KEY = "@MapeoSettings@1"; -export type SettingsContextType = { - coordinateSystem?: string, +export type CoordinateSystem = "utm" | "dd" | "dms"; + +export type SettingsState = { + coordinateSystem: CoordinateSystem, }; -const initialState = { +type SettingsContextType = [ + SettingsState, + (key: $Keys, value: any) => void +]; + +const DEFAULT_SETTINGS = { coordinateSystem: "utm", }; -const defaultContext = [initialState, () => {}]; -const SettingsContext = React.createContext(defaultContext); +const SettingsContext = React.createContext([ + DEFAULT_SETTINGS, + () => {}, +]); -function reducer(state: State, action: Action): State { - switch (action.type) { - case "set": { - return action.value; - } - case "set_coordinate_system": { - try { - return { ...state, coordinateSystem: action.value }; - } catch { - return state; - } - } - default: - return state; - } -} +const usePersistedState = createPersistedState(STORE_KEY); -const getData = async dispatch => { - try { - // AsyncStorage.clear() - const state = await AsyncStorage.getItem(STORE_KEY); - if (state) { - const parsedState = JSON.parse(state); - dispatch({ type: "set", value: parsedState }); - return parsedState; - } else return initialState; - } catch (e) { - console.log("Failed to fetch the data from storage"); - return initialState; - } -}; +export const SettingsProvider = ({ children }: { children: React.Node }) => { + const [state, status, setState] = usePersistedState( + DEFAULT_SETTINGS + ); -const saveData = async value => { - try { - const stringified = JSON.stringify(value); - await AsyncStorage.setItem(STORE_KEY, stringified); - return value; - } catch (e) { - console.log("Failed to save the data to the storage"); - } -}; + const setSettings = React.useCallback( + (key, value) => setState({ ...state, [key]: value }), + [setState, state] + ); -export const SettingsProvider = ({ children }: { children: React.Node }) => { - const didMountRef = React.useRef(false); - const [state, dispatch] = React.useReducer(reducer, defaultContext[0]); - const contextValue = React.useMemo(() => [state, dispatch], [ + const contextValue = React.useMemo(() => [state, setSettings], [ state, - dispatch, + setSettings, ]); - React.useEffect(() => { - if (didMountRef.current) { - saveData(contextValue[0]); - } else { - didMountRef.current = true; - getData(dispatch); - } - }, [contextValue]); + return ( - - {children} + + {status === "loading" ? null : children} ); }; diff --git a/src/frontend/hooks/useSettingsValue.js b/src/frontend/hooks/useSettingsValue.js new file mode 100644 index 000000000..a118196cf --- /dev/null +++ b/src/frontend/hooks/useSettingsValue.js @@ -0,0 +1,10 @@ +// @flow +import React from "react"; +import SettingsContext, { + type SettingsState, +} from "../context/SettingsContext"; + +export default function useSettingsValue(key: $Keys) { + const [state] = React.useContext(SettingsContext); + return state[key]; +} diff --git a/src/frontend/screens/GpsModal.js b/src/frontend/screens/GpsModal.js index d34d4f939..62cd09adf 100644 --- a/src/frontend/screens/GpsModal.js +++ b/src/frontend/screens/GpsModal.js @@ -5,7 +5,7 @@ import Text from "../sharedComponents/Text"; import { defineMessages, FormattedMessage, useIntl } from "react-intl"; import LocationContext from "../context/LocationContext"; -import SettingsContext from "../context/SettingsContext"; +import useSettingsValue from "../hooks/useSettingsValue"; import { FormattedCoords } from "../sharedComponents/FormattedData"; import DateDistance from "../sharedComponents/DateDistance"; @@ -24,7 +24,7 @@ const m = defineMessages({ defaultMessage: "Last update", description: "Section title for time of last GPS update", }, - locationUTM: { + utm: { id: "screens.GpsModal.locationUTM", defaultMessage: "Coordinates UTM", description: "Section title for UTM coordinates", @@ -34,12 +34,12 @@ const m = defineMessages({ defaultMessage: "Coordinates Latitude and Longitude", description: "Section title for latitude and longitude coordinates", }, - locationDMS: { + dms: { id: "screens.GpsModal.locationDMS", defaultMessage: "Coordinates DMS", description: "Section title for DMS coordinates", }, - locationDD: { + dd: { id: "screens.GpsModal.locationDD", defaultMessage: "Coordinates Decimal Degrees", description: "Section title for DD coordinates", @@ -79,22 +79,11 @@ type Props = { const GpsModal = ({ navigation }: Props) => { const location = React.useContext(LocationContext); - const { - settings: { coordinateSystem }, - } = React.useContext(SettingsContext); + // This is necessary for Flow type checking (if we use location.position in a + // conditional it does not know if something else can change it) + const { position, provider } = location; + const coordinateSystem = useSettingsValue("coordinateSystem"); const { formatMessage: t } = useIntl(); - const coordinateMessage = () => { - switch (coordinateSystem) { - case "dd": - return ; - case "utm": - return ; - case "dms": - return ; - default: - return ; - } - }; return ( @@ -106,20 +95,22 @@ const GpsModal = ({ navigation }: Props) => { style={styles.rowValue} date={new Date(getLastUpdateText(location))} /> - {location.position && ( + {position && ( <> - {coordinateMessage()} + + + - {Object.entries(location.position.coords).map(([key, value]) => ( + {Object.entries(position.coords).map(([key, value]) => ( { ))} )} - {location.provider && ( + {provider && ( <> - {Object.entries(location.provider).map(([key, value]) => ( + {Object.entries(provider).map(([key, value]) => ( { export const ShareMessage = ({ observation, preset }: ShareMessageProps) => { const { formatMessage: t } = useIntl(); - const { - settings: { coordinateSystem }, - } = React.useContext(SettingsContext); + const coordinateSystem = useSettingsValue("coordinateSystem"); const { value } = observation; const { lon, lat } = value; diff --git a/src/frontend/screens/Observation/ObservationView.js b/src/frontend/screens/Observation/ObservationView.js index 020635811..e10e9e7bc 100644 --- a/src/frontend/screens/Observation/ObservationView.js +++ b/src/frontend/screens/Observation/ObservationView.js @@ -30,10 +30,10 @@ import { } from "../../lib/styles"; import { TouchableOpacity } from "../../sharedComponents/Touchables"; import type { PresetWithFields } from "../../context/ConfigContext"; -import SettingsContext from "../../context/SettingsContext"; import type { Observation } from "../../context/ObservationsContext"; import useMapStyle from "../../hooks/useMapStyle"; import useDeviceId from "../../hooks/useDeviceId"; +import useSettingsValue from "../../hooks/useSettingsValue"; import Loading from "../../sharedComponents/Loading"; import OfflineMapLayers from "../../sharedComponents/OfflineMapLayers"; import { ShareMessage, ShareSubject, renderToString } from "./ObservationShare"; @@ -126,9 +126,7 @@ const ObservationView = ({ onPressDelete, }: ODVProps) => { const intl = useIntl(); - const { - settings: { coordinateSystem }, - } = React.useContext(SettingsContext); + const coordinateSystem = useSettingsValue("coordinateSystem"); const { formatMessage: t } = intl; const deviceId = useDeviceId(); const isMine = deviceId === observation.value.deviceId; diff --git a/src/frontend/screens/ObservationEdit/ObservationEditView.js b/src/frontend/screens/ObservationEdit/ObservationEditView.js index d96c57db8..ef73839ac 100644 --- a/src/frontend/screens/ObservationEdit/ObservationEditView.js +++ b/src/frontend/screens/ObservationEdit/ObservationEditView.js @@ -14,6 +14,7 @@ import { CategoryCircleIcon, } from "../../sharedComponents/icons"; import useDraftObservation from "../../hooks/useDraftObservation"; +import useSettingsValue from "../../hooks/useSettingsValue"; import ThumbnailScrollView from "../../sharedComponents/ThumbnailScrollView"; import TextButton from "../../sharedComponents/TextButton"; import { BLACK, LIGHT_GREY, LIGHT_BLUE } from "../../lib/styles"; @@ -55,39 +56,40 @@ const m = defineMessages({ const LocationView = ({ longitude, latitude, - system, accuracy, }: { longitude?: number | null, latitude?: number | null, - system?: string, accuracy?: number, -}) => ( - - {longitude == null || latitude == null ? ( - - - - ) : ( - <> - - - +}) => { + const system = useSettingsValue("coordinateSystem"); + return ( + + {longitude == null || latitude == null ? ( + + - {accuracy === undefined ? null : ( - - {" ±" + accuracy.toFixed(2) + "m"} + ) : ( + <> + + + - )} - - )} - -); + {accuracy === undefined ? null : ( + + {" ±" + accuracy.toFixed(2) + "m"} + + )} + + )} + + ); +}; const CategoryView = ({ preset = {}, diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateSystem.js index 40e49e528..fbe9d0e08 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateSystem.js @@ -8,7 +8,6 @@ import SettingsContext from "../../context/SettingsContext"; import { formatCoords } from "../../lib/utils"; import HeaderTitle from "../../sharedComponents/HeaderTitle"; -import Loading from "../../sharedComponents/Loading"; import SelectOne from "./SelectOne"; const m = defineMessages({ @@ -34,54 +33,33 @@ const m = defineMessages({ }, }); +// Default location used to show how coordinates will be formatted. Uses current +// user location if available +const EXAMPLE_LOCATION = { longitude: -72.312023, latitude: -10.38787 }; + const CoordinateSystem = () => { const intl = useIntl(); const location = React.useContext(LocationContext); - const { settings, dispatch } = React.useContext(SettingsContext); - const setSystem = React.useCallback( - value => dispatch({ type: "set_coordinate_system", value }), - [dispatch] - ); - const system = settings.coordinateSystem; - if (!location || !location.position) return ; - else { - const { latitude = 0, longitude = 0 } = location.position.coords; - const options = [ - { - value: "dd", - label: intl.formatMessage({ ...m.dd }), - hint: formatCoords({ - lon: longitude, - lat: latitude, - format: "dd", - }), - }, - { - value: "dms", - label: intl.formatMessage({ ...m.dms }), - hint: formatCoords({ - lon: longitude, - lat: latitude, - format: "dms", - }), - }, - { - value: "utm", - label: intl.formatMessage({ ...m.utm }), - hint: formatCoords({ - lon: longitude, - lat: latitude, - format: "utm", - }), - }, - ]; + const [{ coordinateSystem }, setSettings] = React.useContext(SettingsContext); + + const { latitude: lat, longitude: lon } = + (location.position && location.position.coords) || EXAMPLE_LOCATION; - return ( - - - - ); - } + const options = ["dd", "dms", "utm"].map(format => ({ + value: format, + label: intl.formatMessage(m[format]), + hint: formatCoords({ lon, lat, format }), + })); + + return ( + + setSettings("coordinateSystem", format)} + /> + + ); }; CoordinateSystem.navigationOptions = { diff --git a/src/frontend/screens/Settings/SelectOne.js b/src/frontend/screens/Settings/SelectOne.js index 3697c3289..6a97e51e6 100644 --- a/src/frontend/screens/Settings/SelectOne.js +++ b/src/frontend/screens/Settings/SelectOne.js @@ -24,7 +24,7 @@ const SelectOne = ({ value, options, onChange }: Props) => ( onChange(value === item.value ? null : item.value)} + onPress={() => value !== item.value && onChange(item.value)} > { const [{ value }, { updateDraft }] = useDraftObservation(); const location = React.useContext(LocationContext); - const { - settings: { coordinateSystem }, - } = React.useContext(SettingsContext); + React.useEffect(() => { if (!location.position || !value) return; const draftHasManualLocation = @@ -58,7 +55,6 @@ const LocationField = ({ children }: Props) => { return children({ longitude: value.lon, latitude: value.lat, - system: coordinateSystem, accuracy: value.metadata && value.metadata.location && From a41fddf854424ac3b4e46211c7a7f3b32a12b39a Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Fri, 16 Apr 2021 18:32:01 +0100 Subject: [PATCH 17/22] remove unused message --- src/frontend/screens/GpsModal.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/frontend/screens/GpsModal.js b/src/frontend/screens/GpsModal.js index 62cd09adf..03e9f4a55 100644 --- a/src/frontend/screens/GpsModal.js +++ b/src/frontend/screens/GpsModal.js @@ -29,11 +29,6 @@ const m = defineMessages({ defaultMessage: "Coordinates UTM", description: "Section title for UTM coordinates", }, - locationLatLon: { - id: "screens.GpsModal.locationLatLon", - defaultMessage: "Coordinates Latitude and Longitude", - description: "Section title for latitude and longitude coordinates", - }, dms: { id: "screens.GpsModal.locationDMS", defaultMessage: "Coordinates DMS", From 9e8eeaaa244f223f26fc8c2e82d48af9a195cf1c Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Fri, 16 Apr 2021 18:44:20 +0100 Subject: [PATCH 18/22] Update language to coordinate format --- messages/en.json | 34 ++++++++----------- src/frontend/AppStack.js | 4 +-- src/frontend/context/SettingsContext.js | 6 ++-- src/frontend/screens/GpsModal.js | 6 ++-- .../screens/Observation/ObservationShare.js | 4 +-- .../screens/Observation/ObservationView.js | 4 +-- .../ObservationEdit/ObservationEditView.js | 4 +-- ...oordinateSystem.js => CoordinateFormat.js} | 32 ++++++++--------- src/frontend/screens/Settings/Settings.js | 22 ++++++------ 9 files changed, 56 insertions(+), 60 deletions(-) rename src/frontend/screens/Settings/{CoordinateSystem.js => CoordinateFormat.js} (65%) diff --git a/messages/en.json b/messages/en.json index aac8e7845..de2516ca3 100644 --- a/messages/en.json +++ b/messages/en.json @@ -58,20 +58,20 @@ "description": "Title for category chooser screen", "message": "Choose what is happening" }, - "screens.CoordinateSystem.dd": { - "description": "Decimal Degrees coordinate system", + "screens.CoordinateFormat.dd": { + "description": "Decimal Degrees coordinate format", "message": "Decimal Degrees (DD)" }, - "screens.CoordinateSystem.dms": { - "description": "Degrees/Minutes/Seconds coordinate system", + "screens.CoordinateFormat.dms": { + "description": "Degrees/Minutes/Seconds coordinate format", "message": "Degrees/Minutes/Seconds (DMS)" }, - "screens.CoordinateSystem.title": { - "description": "Title coordinate system screen", - "message": "Coordinate System" + "screens.CoordinateFormat.title": { + "description": "Title coordinate format screen", + "message": "Coordinate Format" }, - "screens.CoordinateSystem.utm": { - "description": "Universal Transverse Mercator coordinate system", + "screens.CoordinateFormat.utm": { + "description": "Universal Transverse Mercator coordinate format", "message": "Universal Transverse Mercator (UTM)" }, "screens.GpsModal.details": { @@ -94,10 +94,6 @@ "description": "Section title for DMS coordinates", "message": "Coordinates DMS" }, - "screens.GpsModal.locationLatLon": { - "description": "Section title for latitude and longitude coordinates", - "message": "Coordinates Latitude and Longitude" - }, "screens.GpsModal.locationSensors": { "description": "Heading for section about location sensor status", "message": "Sensor Status" @@ -324,13 +320,13 @@ "description": "Title of error dialog when there is an error importing a config file", "message": "Import Error" }, - "screens.Settings.coordinateSystem": { - "description": "Settings for coordinate systems", - "message": "Coordinate System" + "screens.Settings.coordinateFormat": { + "description": "Settings for coordinate format", + "message": "Coordinate Format" }, - "screens.Settings.coordinateSystemDesc": { - "description": "Description of the 'Coordinate System' page", - "message": "DD, UTM, DMS" + "screens.Settings.coordinateFormatDesc": { + "description": "Description of the 'Coordinate Format' page", + "message": "Choose how coordinates are displayed" }, "screens.Settings.currentConfig": { "description": "Label for name & version of current configuration", diff --git a/src/frontend/AppStack.js b/src/frontend/AppStack.js index afb24e70e..449d86d5b 100644 --- a/src/frontend/AppStack.js +++ b/src/frontend/AppStack.js @@ -21,7 +21,7 @@ import CustomHeaderLeft from "./sharedComponents/CustomHeaderLeft"; import ProjectConfig from "./screens/Settings/ProjectConfig"; import AboutMapeo from "./screens/Settings/AboutMapeo"; import LanguageSettings from "./screens/Settings/LanguageSettings"; -import CoordinateSystem from "./screens/Settings/CoordinateSystem"; +import CoordinateFormat from "./screens/Settings/CoordinateFormat"; import HomeHeader from "./sharedComponents/HomeHeader"; const HomeTabs = createBottomTabNavigator( @@ -69,7 +69,7 @@ const AppStack = createStackNavigator( // $FlowFixMe LanguageSettings, // $FlowFixMe - CoordinateSystem, + CoordinateFormat, // $FlowFixMe PhotosModal: PhotosModal, // $FlowFixMe diff --git a/src/frontend/context/SettingsContext.js b/src/frontend/context/SettingsContext.js index b325bb83a..38a53b4e5 100644 --- a/src/frontend/context/SettingsContext.js +++ b/src/frontend/context/SettingsContext.js @@ -6,10 +6,10 @@ import createPersistedState from "../hooks/usePersistedState"; // because it will reset everybody's settings back to the defaults = bad :( const STORE_KEY = "@MapeoSettings@1"; -export type CoordinateSystem = "utm" | "dd" | "dms"; +export type CoordinateFormat = "utm" | "dd" | "dms"; export type SettingsState = { - coordinateSystem: CoordinateSystem, + coordinateFormat: CoordinateFormat, }; type SettingsContextType = [ @@ -18,7 +18,7 @@ type SettingsContextType = [ ]; const DEFAULT_SETTINGS = { - coordinateSystem: "utm", + coordinateFormat: "utm", }; const SettingsContext = React.createContext([ diff --git a/src/frontend/screens/GpsModal.js b/src/frontend/screens/GpsModal.js index 03e9f4a55..5cda881ae 100644 --- a/src/frontend/screens/GpsModal.js +++ b/src/frontend/screens/GpsModal.js @@ -77,7 +77,7 @@ const GpsModal = ({ navigation }: Props) => { // This is necessary for Flow type checking (if we use location.position in a // conditional it does not know if something else can change it) const { position, provider } = location; - const coordinateSystem = useSettingsValue("coordinateSystem"); + const coordinateFormat = useSettingsValue("coordinateFormat"); const { formatMessage: t } = useIntl(); return ( @@ -93,13 +93,13 @@ const GpsModal = ({ navigation }: Props) => { {position && ( <> - + diff --git a/src/frontend/screens/Observation/ObservationShare.js b/src/frontend/screens/Observation/ObservationShare.js index bdadf727d..fb4395af8 100644 --- a/src/frontend/screens/Observation/ObservationShare.js +++ b/src/frontend/screens/Observation/ObservationShare.js @@ -53,7 +53,7 @@ export const ShareSubject = ({ observation, preset }: ShareMessageProps) => { export const ShareMessage = ({ observation, preset }: ShareMessageProps) => { const { formatMessage: t } = useIntl(); - const coordinateSystem = useSettingsValue("coordinateSystem"); + const coordinateFormat = useSettingsValue("coordinateFormat"); const { value } = observation; const { lon, lat } = value; @@ -84,7 +84,7 @@ export const ShareMessage = ({ observation, preset }: ShareMessageProps) => {
{lon != null && lat != null ? ( - + ) : null}

{value.tags.notes ?

{value.tags.notes}

: null} diff --git a/src/frontend/screens/Observation/ObservationView.js b/src/frontend/screens/Observation/ObservationView.js index e10e9e7bc..54b735562 100644 --- a/src/frontend/screens/Observation/ObservationView.js +++ b/src/frontend/screens/Observation/ObservationView.js @@ -126,7 +126,7 @@ const ObservationView = ({ onPressDelete, }: ODVProps) => { const intl = useIntl(); - const coordinateSystem = useSettingsValue("coordinateSystem"); + const coordinateFormat = useSettingsValue("coordinateFormat"); const { formatMessage: t } = intl; const deviceId = useDeviceId(); const isMine = deviceId === observation.value.deviceId; @@ -176,7 +176,7 @@ const ObservationView = ({ diff --git a/src/frontend/screens/ObservationEdit/ObservationEditView.js b/src/frontend/screens/ObservationEdit/ObservationEditView.js index ef73839ac..0e9ed3cfa 100644 --- a/src/frontend/screens/ObservationEdit/ObservationEditView.js +++ b/src/frontend/screens/ObservationEdit/ObservationEditView.js @@ -62,7 +62,7 @@ const LocationView = ({ latitude?: number | null, accuracy?: number, }) => { - const system = useSettingsValue("coordinateSystem"); + const format = useSettingsValue("coordinateFormat"); return ( {longitude == null || latitude == null ? ( @@ -78,7 +78,7 @@ const LocationView = ({ style={{ marginRight: 5 }} /> - + {accuracy === undefined ? null : ( diff --git a/src/frontend/screens/Settings/CoordinateSystem.js b/src/frontend/screens/Settings/CoordinateFormat.js similarity index 65% rename from src/frontend/screens/Settings/CoordinateSystem.js rename to src/frontend/screens/Settings/CoordinateFormat.js index fbe9d0e08..f35a1bdb5 100644 --- a/src/frontend/screens/Settings/CoordinateSystem.js +++ b/src/frontend/screens/Settings/CoordinateFormat.js @@ -12,24 +12,24 @@ import SelectOne from "./SelectOne"; const m = defineMessages({ title: { - id: "screens.CoordinateSystem.title", - defaultMessage: "Coordinate System", - description: "Title coordinate system screen", + id: "screens.CoordinateFormat.title", + defaultMessage: "Coordinate Format", + description: "Title coordinate format screen", }, dd: { - id: "screens.CoordinateSystem.dd", + id: "screens.CoordinateFormat.dd", defaultMessage: "Decimal Degrees (DD)", - description: "Decimal Degrees coordinate system", + description: "Decimal Degrees coordinate format", }, dms: { - id: "screens.CoordinateSystem.dms", + id: "screens.CoordinateFormat.dms", defaultMessage: "Degrees/Minutes/Seconds (DMS)", - description: "Degrees/Minutes/Seconds coordinate system", + description: "Degrees/Minutes/Seconds coordinate format", }, utm: { - id: "screens.CoordinateSystem.utm", + id: "screens.CoordinateFormat.utm", defaultMessage: "Universal Transverse Mercator (UTM)", - description: "Universal Transverse Mercator coordinate system", + description: "Universal Transverse Mercator coordinate format", }, }); @@ -37,10 +37,10 @@ const m = defineMessages({ // user location if available const EXAMPLE_LOCATION = { longitude: -72.312023, latitude: -10.38787 }; -const CoordinateSystem = () => { +const CoordinateFormat = () => { const intl = useIntl(); const location = React.useContext(LocationContext); - const [{ coordinateSystem }, setSettings] = React.useContext(SettingsContext); + const [{ coordinateFormat }, setSettings] = React.useContext(SettingsContext); const { latitude: lat, longitude: lon } = (location.position && location.position.coords) || EXAMPLE_LOCATION; @@ -52,17 +52,17 @@ const CoordinateSystem = () => { })); return ( - + setSettings("coordinateSystem", format)} + onChange={format => setSettings("coordinateFormat", format)} /> ); }; -CoordinateSystem.navigationOptions = { +CoordinateFormat.navigationOptions = { headerTitle: () => ( @@ -70,4 +70,4 @@ CoordinateSystem.navigationOptions = { ), }; -export default CoordinateSystem; +export default CoordinateFormat; diff --git a/src/frontend/screens/Settings/Settings.js b/src/frontend/screens/Settings/Settings.js index 28a0a5e2a..4eaa7dd93 100644 --- a/src/frontend/screens/Settings/Settings.js +++ b/src/frontend/screens/Settings/Settings.js @@ -48,15 +48,15 @@ const m = defineMessages({ defaultMessage: "Version and build number", description: "Description of the 'About Mapeo' page", }, - coordinateSystem: { - id: "screens.Settings.coordinateSystem", - defaultMessage: "Coordinate System", - description: "Settings for coordinate systems", + coordinateFormat: { + id: "screens.Settings.coordinateFormat", + defaultMessage: "Coordinate Format", + description: "Settings for coordinate format", }, - coordinateSystemDesc: { - id: "screens.Settings.coordinateSystemDesc", - defaultMessage: "DD, UTM, DMS", - description: "Description of the 'Coordinate System' page", + coordinateFormatDesc: { + id: "screens.Settings.coordinateFormatDesc", + defaultMessage: "Choose how coordinates are displayed", + description: "Description of the 'Coordinate Format' page", }, }); @@ -85,13 +85,13 @@ const Settings = () => { >
navigate("CoordinateSystem")} + onPress={() => navigate("CoordinateFormat")} testID="settingsCoodinatesButton" > } - secondary={} + primary={} + secondary={} > Date: Fri, 16 Apr 2021 18:47:22 +0100 Subject: [PATCH 19/22] Use Number.toFixed() for rounding coordinates --- src/frontend/lib/utils.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index cde3a3446..91d9b6949 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -128,11 +128,7 @@ function toDegreesMinutesAndSeconds(coordinate) { const minutesNotTruncated = (absolute - degrees) * 60; const minutes = Math.floor(minutesNotTruncated); const seconds = (minutesNotTruncated - minutes) * 60; - const decimals = 3; - const formattedSeconds = Number( - Math.round(seconds + "e" + decimals) + "e-" + decimals - ); - return `${degrees}° ${minutes}' ${formattedSeconds}"`; + return `${degrees}° ${minutes}' ${seconds.toFixed(3)}"`; } function convertToDMS({ lat, lon }) { @@ -159,14 +155,7 @@ function convertToUTM({ lat, lon }) { } function formatDD({ lat, lon }) { - const decimals = 6; - const formattedLat = Number( - Math.round(lat + "e" + decimals) + "e-" + decimals - ); - const formattedLon = Number( - Math.round(lon + "e" + decimals) + "e-" + decimals - ); - return `lat ${formattedLat} lon ${formattedLon}`; + return `Lat ${lat.toFixed(6)} Lon ${lon.toFixed(6)}`; } export function formatCoords({ From 46fe0301fa26d9409f63ef9062d5cc2f77718f8f Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Fri, 16 Apr 2021 19:42:46 +0100 Subject: [PATCH 20/22] Fix flow type error --- src/frontend/sharedComponents/FormattedData.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/sharedComponents/FormattedData.js b/src/frontend/sharedComponents/FormattedData.js index 7d92cfb49..7e321d44b 100644 --- a/src/frontend/sharedComponents/FormattedData.js +++ b/src/frontend/sharedComponents/FormattedData.js @@ -7,6 +7,7 @@ import DateDistance from "./DateDistance"; import { type Observation } from "../context/ObservationsContext"; import { formats } from "../context/IntlContext"; import type { Field, Preset, PresetWithFields } from "../context/ConfigContext"; +import { type CoordinateFormat } from "../context/SettingsContext"; const m = defineMessages({ noAnswer: { @@ -36,7 +37,7 @@ export const FormattedCoords = ({ }: { lat: number, lon: number, - format: string, + format: CoordinateFormat, }) => { return <>{formatCoords({ lon, lat, format })}; }; From fa8a738dfb9a466ca15088b1c923d4e47b218c39 Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Fri, 16 Apr 2021 19:51:11 +0100 Subject: [PATCH 21/22] Add comma to DMS formatting --- src/frontend/lib/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index 91d9b6949..419083e9b 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -137,7 +137,7 @@ function convertToDMS({ lat, lon }) { const longitude = toDegreesMinutesAndSeconds(lon); const longitudeCardinal = lon >= 0 ? "E" : "W"; - return `${latitude} ${latitudeCardinal} ${longitude} ${longitudeCardinal}`; + return `${latitude} ${latitudeCardinal}, ${longitude} ${longitudeCardinal}`; } function convertToUTM({ lat, lon }) { From 7e02d1917c159705aaee4f1504382671dff5a841 Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Fri, 16 Apr 2021 19:51:29 +0100 Subject: [PATCH 22/22] Format lat/long DD according to Nat Geo style guide --- src/frontend/lib/utils.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/frontend/lib/utils.js b/src/frontend/lib/utils.js index 419083e9b..d33ca3d42 100644 --- a/src/frontend/lib/utils.js +++ b/src/frontend/lib/utils.js @@ -131,6 +131,8 @@ function toDegreesMinutesAndSeconds(coordinate) { return `${degrees}° ${minutes}' ${seconds.toFixed(3)}"`; } +// Style from National Geographic style guide +// https://sites.google.com/a/ngs.org/ngs-style-manual/home/L/latitude-and-longitude function convertToDMS({ lat, lon }) { const latitude = toDegreesMinutesAndSeconds(lat); const latitudeCardinal = lat >= 0 ? "N" : "S"; @@ -154,8 +156,14 @@ function convertToUTM({ lat, lon }) { } } +// Style from National Geographic style guide +// https://sites.google.com/a/ngs.org/ngs-style-manual/home/L/latitude-and-longitude function formatDD({ lat, lon }) { - return `Lat ${lat.toFixed(6)} Lon ${lon.toFixed(6)}`; + const formattedLat = Math.abs(lat).toFixed(6); + const formattedLon = Math.abs(lon).toFixed(6); + const latCardinal = lat >= 0 ? "N" : "S"; + const lonCardinal = lon >= 0 ? "E" : "W"; + return `${formattedLat}° ${latCardinal}, ${formattedLon}° ${lonCardinal}`; } export function formatCoords({