diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts
index 28de5ad2..71f1896b 100644
--- a/frontend/react/src/constants/constants.ts
+++ b/frontend/react/src/constants/constants.ts
@@ -360,6 +360,7 @@ export enum OlympusState {
MEASURE = "Measure",
TRAINING = "Training",
ADMIN = "Admin",
+ IMPORT_IMAGE_OVERLAY = "Import image overlay"
}
export const NO_SUBSTATE = "No substate";
diff --git a/frontend/react/src/ui/components/olnumberinput.tsx b/frontend/react/src/ui/components/olnumberinput.tsx
index 0f981a4f..d10d9594 100644
--- a/frontend/react/src/ui/components/olnumberinput.tsx
+++ b/frontend/react/src/ui/components/olnumberinput.tsx
@@ -8,6 +8,7 @@ export function OlNumberInput(props: {
max: number;
minLength?: number;
className?: string;
+ internalClassName?: string;
tooltip?: string | (() => JSX.Element | JSX.Element[]);
tooltipPosition?: string;
tooltipRelativeToParent?: boolean;
@@ -34,7 +35,10 @@ export function OlNumberInput(props: {
`}
>
{
setHoverTimeout(
diff --git a/frontend/react/src/ui/modals/imageoverlaymodal.tsx b/frontend/react/src/ui/modals/imageoverlaymodal.tsx
new file mode 100644
index 00000000..9a353a7d
--- /dev/null
+++ b/frontend/react/src/ui/modals/imageoverlaymodal.tsx
@@ -0,0 +1,180 @@
+import React, { useEffect, useState } from "react";
+import { Modal } from "./components/modal";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
+import { getApp } from "../../olympusapp";
+import { NO_SUBSTATE, OlympusState } from "../../constants/constants";
+import { AppStateChangedEvent } from "../../events";
+import { ImageOverlay, LatLng, LatLngBounds } from "leaflet";
+import { OlNumberInput } from "../components/olnumberinput";
+import { OlStringInput } from "../components/olstringinput";
+
+export function ImageOverlayModal(props: { open: boolean }) {
+ const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
+ const [appSubState, setAppSubState] = useState(NO_SUBSTATE);
+ const [bound1Lat, setBound1Lat] = useState("0");
+ const [bound1Lon, setBound1Lon] = useState("0");
+ const [bound2Lat, setBound2Lat] = useState("0");
+ const [bound2Lon, setBound2Lon] = useState("0");
+ const [importData, setImportData] = useState("");
+ const [showWarning, setShowWarning] = useState(false);
+
+ useEffect(() => {
+ AppStateChangedEvent.on((appState, appSubState) => {
+ setAppState(appState);
+ setAppSubState(appSubState);
+ });
+ }, []);
+
+ useEffect(() => {
+ if (appState !== OlympusState.IMPORT_IMAGE_OVERLAY) return;
+
+ setImportData("");
+ var input = document.createElement("input");
+ input.type = "file";
+
+ input.onchange = async (e) => {
+ // @ts-ignore TODO
+ var file = e.target?.files[0];
+ var reader = new FileReader();
+ // Read the file content as image data URL
+ reader.readAsDataURL(file);
+ reader.onload = (readerEvent) => {
+ // @ts-ignore TODO
+ var content = readerEvent.target.result;
+ if (content) {
+ setImportData(content as string);
+ }
+ };
+ };
+
+ input.click();
+ }, [appState, appSubState]);
+
+ return (
+
+
+
+
+ Import Image Overlay
+
+
+
Enter the corner coordinates of the image overlay to be imported.
+
+
+
Corner 1 latitude
+
+
{
+ setBound1Lat(ev.target.value);
+ }}
+ />
+
+
Corner 1 longitude
+
+
{
+ setBound1Lon(ev.target.value);
+ }}
+ />
+
+
+
+
Corner 2 latitude
+
+
{
+ setBound2Lat(ev.target.value);
+ }}
+ />
+
+
Corner 2 longitude
+
+
{
+ setBound2Lon(ev.target.value);
+ }}
+ />
+
+
+
+ Please enter valid latitude and longitude values in decimal degrees format (e.g. 37.7749, -122.4194). Latitude must be between -90 and 90, and longitude must be between -180 and 180.
+
+
+
+
+
+ {
+ if (
+ isNaN(Number(bound1Lat)) || Number(bound1Lat) < -90 || Number(bound1Lat) > 90 ||
+ isNaN(Number(bound1Lon)) || Number(bound1Lon) < -180 || Number(bound1Lon) > 180 ||
+ isNaN(Number(bound2Lat)) || Number(bound2Lat) < -90 || Number(bound2Lat) > 90 ||
+ isNaN(Number(bound2Lon)) || Number(bound2Lon) < -180 || Number(bound2Lon) > 180
+ ) {
+ setShowWarning(true)
+ return;
+ }
+ setShowWarning(false)
+
+ const bounds = new LatLngBounds([
+ [Number(bound1Lat), Number(bound1Lon)],
+ [Number(bound2Lat), Number(bound2Lon)]
+ ]
+ )
+
+ let overlay = new ImageOverlay(importData, bounds);
+ overlay.addTo(getApp().getMap());
+
+ getApp().setState(OlympusState.IDLE);
+ }}
+ className={`
+ mb-2 me-2 ml-auto flex content-center items-center
+ gap-2 rounded-sm bg-blue-700 px-5 py-2.5 text-sm
+ font-medium text-white
+ dark:bg-blue-600 dark:hover:bg-blue-700
+ dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Continue
+
+
+
+ getApp().setState(OlympusState.IDLE)}
+ className={`
+ mb-2 me-2 flex content-center items-center gap-2
+ rounded-sm border-[1px] bg-blue-700 px-5 py-2.5
+ text-sm font-medium text-white
+ dark:border-gray-600 dark:bg-gray-800
+ dark:text-gray-400 dark:hover:bg-gray-700
+ dark:focus:ring-blue-800
+ focus:outline-none focus:ring-4 focus:ring-blue-300
+ hover:bg-blue-800
+ `}
+ >
+ Back
+
+
+
+
+ );
+}
diff --git a/frontend/react/src/ui/panels/mainmenu.tsx b/frontend/react/src/ui/panels/mainmenu.tsx
index 92c5fa72..781d514b 100644
--- a/frontend/react/src/ui/panels/mainmenu.tsx
+++ b/frontend/react/src/ui/panels/mainmenu.tsx
@@ -142,6 +142,31 @@ export function MainMenu(props: { open: boolean; onClose: () => void; children?:
/>
+ {
+ getApp().setState(OlympusState.IMPORT_IMAGE_OVERLAY);
+ }}
+ >
+ {/*
*/}
+ Import image overlay
+
+
+
+
);
diff --git a/frontend/react/src/ui/ui.tsx b/frontend/react/src/ui/ui.tsx
index 65c33705..858b5425 100644
--- a/frontend/react/src/ui/ui.tsx
+++ b/frontend/react/src/ui/ui.tsx
@@ -31,6 +31,7 @@ import { ImportExportModal } from "./modals/importexportmodal";
import { WarningModal } from "./modals/warningmodal";
import { TrainingModal } from "./modals/trainingmodal";
import { AdminModal } from "./modals/adminmodal";
+import { ImageOverlayModal } from "./modals/imageoverlaymodal";
export function UI() {
const [appState, setAppState] = useState(OlympusState.NOT_INITIALIZED);
@@ -74,6 +75,7 @@ export function UI() {
+
>
)}