diff --git a/frontend/cypress/e2e/allegation-details-create.cy.ts b/frontend/cypress/e2e/allegation-details-create.cy.ts index f91579028..67bec9a33 100644 --- a/frontend/cypress/e2e/allegation-details-create.cy.ts +++ b/frontend/cypress/e2e/allegation-details-create.cy.ts @@ -115,8 +115,8 @@ describe("Complaint Create Page spec - Create View", () => { cy.get('dd[id="comp-details-violation-observed"]').contains(createCallDetails.violationObservedString); cy.get('dd[id="comp-details-location"]').contains(createCallDetails.location); cy.get('dd[id="comp-details-location-description"]').should("have.text", createCallDetails.locationDescription); - cy.get('span[id="call-details-x-coordinate"]').contains(createCallDetails.xCoord); - cy.get('span[id="call-details-y-coordinate"]').contains(createCallDetails.yCoord); + cy.get('span[id="geo-details-x-coordinate"]').contains(createCallDetails.xCoord); + cy.get('span[id="geo-details-y-coordinate"]').contains(createCallDetails.yCoord); cy.get('dd[id="comp-details-community"]').contains(createCallDetails.community); cy.get('dd[id="comp-details-office"]').contains(createCallDetails.office); cy.get('dd[id="comp-details-zone"]').contains(createCallDetails.zone); diff --git a/frontend/cypress/e2e/allegation-details-edit.cy.ts b/frontend/cypress/e2e/allegation-details-edit.cy.ts index f1d016361..91b9c7c80 100644 --- a/frontend/cypress/e2e/allegation-details-edit.cy.ts +++ b/frontend/cypress/e2e/allegation-details-edit.cy.ts @@ -401,16 +401,16 @@ describe("Complaint Edit Page spec - Edit Allegation View", () => { cy.get("#attractants-pair-id input").should("not.exist"); // X Coordinate - cy.get('[for="comp-details-edit-x-coordinate-input"]').should(($label) => { + cy.get('[for="input-x-coordinate"]').should(($label) => { expect($label).to.contain.text("Longitude"); }); - cy.get("#comp-details-edit-x-coordinate-input-div").should("exist"); + cy.get("#input-x-coordinate").should("exist"); // Y Coordinate - cy.get('[for="comp-details-edit-y-coordinate-input"]').should(($label) => { + cy.get('[for="input-y-coordinate"]').should(($label) => { expect($label).to.contain.text("Latitude"); }); - cy.get("#comp-details-edit-y-coordinate-input-div").should("exist"); + cy.get("#input-y-coordinate").should("exist"); // Area/Community cy.get("#area-community-pair-id label").should(($label) => { diff --git a/frontend/cypress/e2e/hwcr-details-create-enter-coordinates.cy.ts b/frontend/cypress/e2e/hwcr-details-create-enter-coordinates.cy.ts index 49767f442..2f33a50a8 100644 --- a/frontend/cypress/e2e/hwcr-details-create-enter-coordinates.cy.ts +++ b/frontend/cypress/e2e/hwcr-details-create-enter-coordinates.cy.ts @@ -67,11 +67,11 @@ describe("Complaint Create Page spec - Enter Coordinates - Create View", () => { cy.selectItemById("reported-select-id", createCallerInformation.reported); - cy.get("#comp-details-edit-x-coordinate-input").click({ force: true }); - cy.get("#comp-details-edit-x-coordinate-input").clear().type(createCallDetails.xCoord); + cy.get("#input-x-coordinate").click({ force: true }); + cy.get("#input-x-coordinate").clear().type(createCallDetails.xCoord); - cy.get("#comp-details-edit-y-coordinate-input").click({ force: true }); - cy.get("#comp-details-edit-y-coordinate-input").clear().type(createCallDetails.yCoord); + cy.get("#input-y-coordinate").click({ force: true }); + cy.get("#input-y-coordinate").clear().type(createCallDetails.yCoord); cy.get("#complaint-location-description-textarea-id").click({ force: true, @@ -114,8 +114,8 @@ describe("Complaint Create Page spec - Enter Coordinates - Create View", () => { }); cy.get('dd[id="comp-details-location-description"]').should("have.text", createCallDetails.locationDescription); - cy.get('span[id="call-details-x-coordinate"]').contains(createCallDetails.xCoord); - cy.get('span[id="call-details-y-coordinate"]').contains(createCallDetails.yCoord); + cy.get('span[id="geo-details-x-coordinate"]').contains(createCallDetails.xCoord); + cy.get('span[id="geo-details-y-coordinate"]').contains(createCallDetails.yCoord); cy.get('dd[id="comp-details-community"]').contains(createCallDetails.community); cy.get('dd[id="comp-details-office"]').contains(createCallDetails.office); cy.get('dd[id="comp-details-zone"]').contains(createCallDetails.zone); diff --git a/frontend/cypress/e2e/hwcr-details-create.cy.ts b/frontend/cypress/e2e/hwcr-details-create.cy.ts index cfeb55664..d2166747f 100644 --- a/frontend/cypress/e2e/hwcr-details-create.cy.ts +++ b/frontend/cypress/e2e/hwcr-details-create.cy.ts @@ -113,8 +113,8 @@ describe("Complaint Create Page spec - Create View", () => { cy.get('dd[id="comp-details-location"]').contains(createCallDetails.location); cy.get('dd[id="comp-details-location-description"]').should("have.text", createCallDetails.locationDescription); - cy.get('span[id="call-details-x-coordinate"]').contains(createCallDetails.xCoord); - cy.get('span[id="call-details-y-coordinate"]').contains(createCallDetails.yCoord); + cy.get('span[id="geo-details-x-coordinate"]').contains(createCallDetails.xCoord); + cy.get('span[id="geo-details-y-coordinate"]').contains(createCallDetails.yCoord); cy.get('dd[id="comp-details-community"]').contains(createCallDetails.community); cy.get('dd[id="comp-details-office"]').contains(createCallDetails.office); cy.get('dd[id="comp-details-zone"]').contains(createCallDetails.zone); diff --git a/frontend/cypress/e2e/hwcr-details-edit.cy.ts b/frontend/cypress/e2e/hwcr-details-edit.cy.ts index 2b4208163..db4cc9410 100644 --- a/frontend/cypress/e2e/hwcr-details-edit.cy.ts +++ b/frontend/cypress/e2e/hwcr-details-edit.cy.ts @@ -353,16 +353,16 @@ describe("Complaint Edit Page spec - Edit View", () => { cy.get("#attractants-pair-id input").should("exist"); // X Coordinate - cy.get('[for="comp-details-edit-x-coordinate-input"]').should(($label) => { + cy.get('[for="input-x-coordinate"]').should(($label) => { expect($label).to.contain.text("Longitude"); }); - cy.get("#comp-details-edit-x-coordinate-input-div").should("exist"); + cy.get("#input-x-coordinate").should("exist"); // Y Coordinate - cy.get('[for="comp-details-edit-y-coordinate-input"]').should(($label) => { + cy.get('[for="input-y-coordinate"]').should(($label) => { expect($label).to.contain.text("Latitude"); }); - cy.get("#comp-details-edit-y-coordinate-input-div").should("exist"); + cy.get("#input-y-coordinate").should("exist"); // Area/Community cy.get("#area-community-pair-id label").should(($label) => { diff --git a/frontend/cypress/e2e/hwcr-outcome-equipment.cy.ts b/frontend/cypress/e2e/hwcr-outcome-equipment.cy.ts index ab6daa477..879da12e0 100644 --- a/frontend/cypress/e2e/hwcr-outcome-equipment.cy.ts +++ b/frontend/cypress/e2e/hwcr-outcome-equipment.cy.ts @@ -37,8 +37,8 @@ describe("HWCR Outcome Equipment", () => { let inputs = [ "#equipment-type-div", "#equipment-address-div", - "#equipment-coordinate-div", - "#equipment-coordinate-div", + "#equipment-coordinates", + "#equipment-coordinates", "#equipment-officer-set-div", ]; cy.hasErrorMessage(inputs, "Errors creating equipment"); @@ -63,7 +63,7 @@ describe("HWCR Outcome Equipment", () => { equipmentType: "Bear snare", }; cy.get("#equipment-copy-address-button").click(); - cy.get("#equipment-copy-coordinates-button").click(); + cy.get("#copy-coordinates-button").click(); cy.fillInHWCSection(sectionParams).then(() => { cy.validateHWCSection(sectionParams); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0f6c432f0..b3b840703 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -102,6 +102,7 @@ "uncontrollable": "^8.0.2", "urlpattern-polyfill": "^9.0.0", "util": "^0.12.5", + "utm-latlng": "^1.0.8", "uuid": "^9.0.1", "warning": "^4.0.3", "web-vitals": "^3.0.0" @@ -24428,6 +24429,11 @@ "node": ">= 0.4.0" } }, + "node_modules/utm-latlng": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/utm-latlng/-/utm-latlng-1.0.8.tgz", + "integrity": "sha512-jQ5l1BNTzNgas7jXOIh0jU0m5PU5LtmjYvRiLwR4iymYYAhEhQ6N0MGCnLjv5iO958b9tLMMSmzdbIIhLtXqCg==" + }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 90dddf04e..ed564691a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -96,6 +96,7 @@ "uncontrollable": "^8.0.2", "urlpattern-polyfill": "^9.0.0", "util": "^0.12.5", + "utm-latlng": "^1.0.8", "uuid": "^9.0.1", "warning": "^4.0.3", "web-vitals": "^3.0.0" diff --git a/frontend/src/app/common/methods.tsx b/frontend/src/app/common/methods.tsx index a2dd221ac..9509a661d 100644 --- a/frontend/src/app/common/methods.tsx +++ b/frontend/src/app/common/methods.tsx @@ -16,6 +16,7 @@ import { GirType } from "../types/app/code-tables/gir-type"; import { WildlifeComplaint as WildlifeComplaintDto } from "../types/app/complaints/wildlife-complaint"; import { AllegationComplaint as AllegationComplaintDto } from "../types/app/complaints/allegation-complaint"; import { GeneralIncidentComplaint as GeneralIncidentComplaintDto } from "../types/app/complaints/general-complaint"; +let utmObj = require("utm-latlng"); type Coordinate = number[] | string[] | undefined; @@ -232,6 +233,15 @@ export const bcBoundaries = { maxLongitude: -114.0337, }; +export const bcUtmBoundaries = { + minEasting: 280220.6, + maxEasting: 720184.9, + minNorthing: 5346051.7, + maxNorthing: 6655120.8, +}; + +export const bcUtmZoneNumbers = ["7", "8", "9", "10", "11"]; + // given coordinates, return true if within BC or false if not within BC export const isWithinBC = (coordinates: Coordinate): boolean => { if (!coordinates) { @@ -424,3 +434,33 @@ export const getThumbnailDataURL = async (file: File): Promise => { export function isPositiveNum(number: string) { return !isNaN(Number(number)) && Number(number) >= 0; } + +export const formatLatLongCoordinate = (input: string | undefined): string | undefined => { + const regex = /-?(?:\d+(\.\d+)?|.\d+)/; + if (input && regex.exec(input)) { + const tokens = input.split("."); + if (tokens.length > 1) { + const decimals = tokens[1].length; + if (decimals <= 7) { + return input; + } else { + return tokens[0] + "." + tokens[1].substring(0, 7); + } + } + } + return input; +}; + +export const latLngToUtm = (lat: string, lng: string): { easting: string; northing: string; zone: string } => { + const regex = /-?(?:\d+(\.\d+)?|.\d+)/; + if (regex.exec(lat) && regex.exec(lng) && ![lat, lng].includes("0")) { + let utm = new utmObj(); + const utmCoordinates = utm.convertLatLngToUtm(lat, lng, 3); + return { + easting: utmCoordinates.Easting.toFixed(0), + northing: utmCoordinates.Northing.toFixed(0), + zone: utmCoordinates.ZoneNumber ?? "", + }; + } + return { easting: "", northing: "", zone: "" }; +}; diff --git a/frontend/src/app/components/common/comp-coordinate-input.tsx b/frontend/src/app/components/common/comp-coordinate-input.tsx new file mode 100644 index 000000000..37a4dceab --- /dev/null +++ b/frontend/src/app/components/common/comp-coordinate-input.tsx @@ -0,0 +1,442 @@ +import { FC, useEffect, useState } from "react"; +import Option from "../../types/app/option"; +import { CompRadioGroup } from "./comp-radiogroup"; +import { bcBoundaries, bcUtmBoundaries, formatLatLongCoordinate } from "../../common/methods"; +import { CompSelect } from "./comp-select"; +import { Button } from "react-bootstrap"; +let utmObj = require("utm-latlng"); + +type Props = { + id?: string; + utmZones?: Array