Skip to content

Commit

Permalink
Merge pull request #465 from hotosm/feat/UI-fixes
Browse files Browse the repository at this point in the history
Feat/Show the percentage of images from within a project area and other minor enhancements
  • Loading branch information
nrjadkry authored Feb 10, 2025
2 parents 14e045a + a760b10 commit 2caf6dc
Show file tree
Hide file tree
Showing 15 changed files with 193 additions and 24 deletions.
1 change: 1 addition & 0 deletions src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@tanstack/react-table": "^8.9.3",
"@turf/area": "^7.0.0",
"@turf/bbox": "^7.0.0",
"@turf/boolean-point-in-polygon": "^7.2.0",
"@turf/centroid": "^7.0.0",
"@turf/flatten": "^7.0.0",
"@turf/helpers": "^7.0.0",
Expand Down
26 changes: 26 additions & 0 deletions src/frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 7 additions & 11 deletions src/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import '@hotosm/ui/dist/style.css';

// Workaround required, as @hotosm/gcp-editor already imports all components
if (!customElements.get('hot-tracking')) {
import('@hotosm/ui/components/tracking/tracking');
}

import '@hotosm/ui/dist/style.css';
import { useLocation } from 'react-router-dom';
import { initDomToCode } from 'dom-to-code';
import { ToastContainer } from 'react-toastify';
Expand All @@ -28,6 +23,11 @@ import {
getPromptDialogContent,
} from '@Constants/modalContents';
import ScrollToTop from '@Components/common/ScrollToTop';
// import HotTracker from '@Components/common/HotTracking';

// if (!customElements.get('hot-tracking')) {
// import('@hotosm/ui/components/tracking/tracking');
// }

export default function App() {
const dispatch = useTypedDispatch();
Expand Down Expand Up @@ -107,11 +107,7 @@ export default function App() {
</div>
<ScrollToTop />
</div>
<hot-tracking
style={{position: 'fixed', bottom: '0%'}}
site-id={'35'} domain={'dronetm.org'}
force={true}>
</hot-tracking>
{/* <HotTracker /> */}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ import { useTypedDispatch, useTypedSelector } from '@Store/hooks';
import convertExifDataToGeoJson from '@Utils/exifDataToGeoJson';
import sortByDatetime from '@Utils/sortArrayUsingDate';
import { NavigationControl, AttributionControl, Map } from 'maplibre-gl';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'react-toastify';
import callApiSimultaneously from '@Utils/callApiSimultaneously';
import chunkArray from '@Utils/createChunksOfArray';
import { getImageUploadLink } from '@Services/droneOperator';
import { useMutation } from '@tanstack/react-query';
import { postTaskStatus } from '@Services/project';
import widthCalulator from '@Utils/percentageCalculator';
import { point } from '@turf/helpers';
import booleanPointInPolygon from '@turf/boolean-point-in-polygon';
import FilesUploadingPopOver from '../LoadingBox';

const ImageMapBox = () => {
Expand Down Expand Up @@ -55,6 +57,9 @@ const ImageMapBox = () => {
state => state.droneOperatorTask.uploadProgress,
);
const modalState = useTypedSelector(state => state.common.showModal);
const taskAreaPolygon = useTypedSelector(
state => state.droneOperatorTask.taskAreaPolygon,
);

useEffect(() => {
if (!modalState) {
Expand Down Expand Up @@ -198,6 +203,21 @@ const ImageMapBox = () => {
mutate(filesData);
}

const pointsInsideTaskArea = useMemo(() => {
let numberOfPointsInsideTaskArea = 0;
filesExifData?.forEach((element: any) => {
const imagePoint = point([
element.coordinates.longitude,
element.coordinates.latitude,
]);

if (booleanPointInPolygon(imagePoint, taskAreaPolygon?.features[0])) {
numberOfPointsInsideTaskArea++;
}
});
return numberOfPointsInsideTaskArea;
}, [filesExifData, taskAreaPolygon]);

return (
<>
<FlexColumn className="naxatw-h-[calc(100vh-220px)] naxatw-gap-5">
Expand Down Expand Up @@ -312,10 +332,28 @@ const ImageMapBox = () => {
closePopupOnButtonClick
/>
)}

<VectorLayer
map={map as Map}
id="task-polygon-image-selection"
geojson={taskAreaPolygon as GeojsonType}
layerOptions={{
type: 'fill',
paint: {
'fill-color': '#98BBC8',
'fill-outline-color': '#484848',
'fill-opacity': 0.6,
},
}}
/>
</MapContainer>
<p className="naxatw-text-lg naxatw-font-medium">
{filesExifData.length} Images Selected
</p>
<p className="naxatw-text-lg naxatw-font-medium naxatw-text-yellow-400">
{(pointsInsideTaskArea / filesExifData.length) * 100 || 0} % of the
uploaded images are from within the project area.
</p>
</div>
<div className="naxatw-mx-auto naxatw-w-fit">
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
setRotatedFlightPlan,
setSelectedTakeOffPoint,
setSelectedTakeOffPointOption,
setTaskAreaPolygon,
setTaskAssetsInformation,
setWaypointMode,
} from '@Store/actions/droneOperatorTask';
Expand Down Expand Up @@ -170,7 +171,7 @@ const MapSection = ({ className }: { className?: string }) => {
select: (projectRes: any) => {
const taskPolygon = projectRes.data;
const { geometry } = taskPolygon.outline;
return {
const taskAreaPolygon = {
type: 'FeatureCollection',
features: [
{
Expand All @@ -183,6 +184,8 @@ const MapSection = ({ className }: { className?: string }) => {
},
],
};
dispatch(setTaskAreaPolygon(taskAreaPolygon));
return taskAreaPolygon;
},
onSuccess: () => {
if (map) {
Expand Down Expand Up @@ -479,6 +482,16 @@ const MapSection = ({ className }: { className?: string }) => {
showTaskArea ? 'none' : 'visible',
);
setShowTaskArea(!showTaskArea);

if (taskDataPolygon && !showTaskArea && map) {
const bbox = getBbox(
taskDataPolygon as FeatureCollection,
) as LngLatBoundsLike;
map?.fitBounds(bbox as LngLatBoundsLike, {
padding: 105,
duration: 500,
});
}
};

const handleToggleOrthophoto = () => {
Expand All @@ -491,6 +504,18 @@ const MapSection = ({ className }: { className?: string }) => {
};
// end toggle layers

const zoomToExtent = () => {
if (taskWayPointsData) {
const bbox = getBbox(
taskWayPointsData.geojsonAsLineString as FeatureCollection,
) as LngLatBoundsLike;
map?.fitBounds(bbox as LngLatBoundsLike, {
padding: 105,
duration: 500,
});
}
};

const handleSaveStartingPoint = () => {
const { geometry: startingPonyGeometry } = newTakeOffPoint as Record<
string,
Expand Down Expand Up @@ -852,6 +877,20 @@ const MapSection = ({ className }: { className?: string }) => {
</div>
</Button>

<Button
variant="ghost"
className="naxatw-grid naxatw-h-[1.85rem] naxatw-place-items-center naxatw-border naxatw-border-gray-400 naxatw-bg-[#F5F5F5] !naxatw-px-[0.315rem]"
onClick={() => zoomToExtent()}
>
<ToolTip
name="zoom_out_map"
message="Zoom to task area"
symbolType="material-icons"
iconClassName="!naxatw-text-xl !naxatw-text-black naxatw-w-[1.25rem]"
className="naxatw-mt-[-4px]"
/>
</Button>

{taskAssetsInformation?.assets_url && (
<Button
variant="ghost"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useTypedSelector } from '@Store/hooks';
import BaseLayerSwitcherUI from '@Components/common/BaseLayerSwitcher';
import { useEffect } from 'react';
import { FeatureCollection } from 'geojson';
import hasErrorBoundary from '@Utils/hasErrorBoundary';

interface IMapSectionProps {
projectData: Record<string, any>;
Expand Down Expand Up @@ -126,4 +127,4 @@ const MapSection = ({ projectData }: IMapSectionProps) => {
);
};

export default MapSection;
export default hasErrorBoundary(MapSection);
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import MapSection from '@Components/IndividualProject/ExportSection/MapSection';
import dtmLogo from '@Assets/images/DTM-logo-black.svg';
import Image from '@Components/RadixComponents/Image';
import hasErrorBoundary from '@Utils/hasErrorBoundary';

interface IExportSectionProps {
projectData: Record<string, any>;
Expand Down Expand Up @@ -71,4 +72,4 @@ const ExportSection = ({ projectData }: IExportSectionProps) => {
);
};

export default ExportSection;
export default hasErrorBoundary(ExportSection);
36 changes: 32 additions & 4 deletions src/frontend/src/components/IndividualProject/MapSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ const MapSection = ({ projectData }: { projectData: Record<string, any> }) => {
}, [map, taskStates]);

// zoom to layer in the project area
useEffect(() => {
if (!tasksData) return;
const bbox = useMemo(() => {
if (!tasksData) return null;
const tasksCollectiveGeojson = tasksData?.reduce(
(acc, curr) => {
return {
Expand All @@ -140,9 +140,15 @@ const MapSection = ({ projectData }: { projectData: Record<string, any> }) => {
features: [],
},
);
const bbox = getBbox(tasksCollectiveGeojson as FeatureCollection);
return getBbox(tasksCollectiveGeojson as FeatureCollection);
}, [tasksData]);

useEffect(() => {
if (!bbox) return;
map?.fitBounds(bbox as LngLatBoundsLike, { padding: 25, duration: 500 });
}, [map, tasksData]);
}, [map, bbox]);

// end zoom to layer

const getPopupUI = useCallback(
(properties: Record<string, any>) => {
Expand Down Expand Up @@ -212,6 +218,11 @@ const MapSection = ({ projectData }: { projectData: Record<string, any> }) => {
setShowOverallOrthophoto(!showOverallOrthophoto);
};

const handleZoomToExtent = () => {
if (!bbox) return;
map?.fitBounds(bbox as LngLatBoundsLike, { padding: 25, duration: 500 });
};

return (
<>
<MapContainer
Expand Down Expand Up @@ -406,6 +417,23 @@ const MapSection = ({ projectData }: { projectData: Record<string, any> }) => {
)
}
/>

{/* additional controls */}
<div className="naxatw-absolute naxatw-left-[0.575rem] naxatw-top-[5.75rem] naxatw-z-30 naxatw-flex naxatw-h-fit naxatw-w-fit naxatw-flex-col naxatw-gap-3">
<Button
variant="ghost"
className="naxatw-grid naxatw-h-[1.85rem] naxatw-place-items-center naxatw-border naxatw-border-gray-400 naxatw-bg-[#F5F5F5] !naxatw-px-[0.315rem]"
onClick={() => handleZoomToExtent()}
>
<ToolTip
name="zoom_out_map"
message="Zoom to project area"
symbolType="material-icons"
iconClassName="!naxatw-text-xl !naxatw-text-black"
className="naxatw-mt-[-4px]"
/>
</Button>
</div>
<Legend />
</MapContainer>

Expand Down
Loading

0 comments on commit 2caf6dc

Please sign in to comment.