diff --git a/backend/src/roads/road.controller.ts b/backend/src/roads/road.controller.ts index cc78227f..df3c3d96 100644 --- a/backend/src/roads/road.controller.ts +++ b/backend/src/roads/road.controller.ts @@ -16,11 +16,18 @@ export class RoadController { /** * Get all the roads in the database. * - * @author Kerbourc'h + * @author Kerbourc'h, Chen */ - @Get('paths') - async getRoadsPaths() { - return await this.service.getRoadsPaths(); + @Get('paths/:wayId?') + async getRoadsInfo(@Param('wayId') wayId?: OSMWayId) { + if (wayId) { + // Get the length of the specific way + const wayData = await this.service.getWayData(wayId); + return { length: wayData.length }; + } else { + // Get all roads if no specific wayId is provided + return await this.service.getRoadsPaths(); + } } /** diff --git a/frontend/src/Components/Map/Hooks/ForceMapUpdate.tsx b/frontend/src/Components/Map/Hooks/ForceMapUpdate.tsx index 1fc1b4c8..5b535392 100644 --- a/frontend/src/Components/Map/Hooks/ForceMapUpdate.tsx +++ b/frontend/src/Components/Map/Hooks/ForceMapUpdate.tsx @@ -7,14 +7,20 @@ interface Props { triggerUpdate?: number; /** The coordinates of the map center*/ position?: LatLng; + /** Zoom level for the map */ + zoomLevel?: number; } /** * This component is used to force an update of the map. It is used in conjunction with the useMap hook. * - * @author Kerbourc'h + * @author Kerbourc'h, Chen */ -const ForceMapUpdate: React.FC = ({ triggerUpdate, position }) => { +const ForceMapUpdate: React.FC = ({ + triggerUpdate, + position, + zoomLevel, +}) => { const map = useMap(); useEffect(() => { @@ -24,9 +30,9 @@ const ForceMapUpdate: React.FC = ({ triggerUpdate, position }) => { // TODO: not working when moving too little useEffect(() => { if (position) { - map.flyTo(position); + map.flyTo(position, zoomLevel); } - }, [position]); + }, [position, zoomLevel]); return null; }; diff --git a/frontend/src/pages/Main.tsx b/frontend/src/pages/Main.tsx index a196de70..a35d2e51 100644 --- a/frontend/src/pages/Main.tsx +++ b/frontend/src/pages/Main.tsx @@ -1,7 +1,7 @@ import { FC, useCallback, useEffect, useState } from 'react'; import Hamburger from '../Components/Map/Inputs/Hamburger'; -import { IRoad } from '../models/path'; -import { getRoadsPaths } from '../queries/road'; +import { IRoad, WayId } from '../models/path'; +import { getRoadsPaths, getWayLength } from '../queries/road'; import { LatLng, SurveyListItem } from '../models/models'; import { FeatureCollection } from 'geojson'; import { getAllConditions } from '../queries/conditions'; @@ -32,6 +32,39 @@ import InfoButton from '../Components/Conditions/InfoButton'; import UploadPanel from '../Components/Conditions/UploadPanel'; import ProgressCircle from '../Components/ProgressCircle'; +/** +/* Function to calculate the zoom level +/* +/* @author Chen +*/ +const calculateZoomLevel = (maxDistance: number): number => { + let slope = -7.8e-5; // Default + let intercept = 12.6; // Default + if (maxDistance <= 900) { + slope = -7.8e-5; + intercept = 16.5; + } else if (maxDistance > 900 && maxDistance <= 2500) { + slope = -7.8e-5; + intercept = 14.1; + } else if (maxDistance > 2500 && maxDistance <= 8000) { + slope = -7.9e-5; + intercept = 13.8; + } else if (maxDistance > 8000 && maxDistance <= 20000) { + slope = -7.8e-5; + intercept = 14; + } else if (maxDistance > 20000 && maxDistance <= 40000) { + slope = -6.7e-5; + intercept = 14; + } else if (maxDistance > 40000 && maxDistance <= 100000) { + slope = -5e-5; + intercept = 14; + } else { + return 10; + } + const ZoomInParam = slope * maxDistance + intercept; + return ZoomInParam; +}; + /** * Component rendering the main page * @@ -196,6 +229,43 @@ const Main: FC = () => { }); }, []); + // Control Zoom level by the length of roads when selecting a road on the map + const [zoomLevel, setZoomLevel] = useState(13); // Default zoom level + const [wayLength, setWayLength] = useState(null); + + /** + /* Function to calculate the total length of roads + /* + /* @author Chen + */ + const fetchAndSumWayLengths = useCallback( + async (branches: WayId[][]) => { + try { + const promises = branches.flat().map( + (wayId) => + new Promise((resolve) => { + getWayLength(wayId, resolve); + }), + ); + const lengths = await Promise.all(promises); + const totalLength = lengths.reduce((sum, length) => sum + length, 0); + setWayLength(totalLength); + } catch (error) { + console.error('Error fetching way lengths:', error); + setWayLength(0); + } + }, + [getWayLength, setWayLength], + ); + + // Zoom in when calling onSelectedPath + useEffect(() => { + if (wayLength !== null) { + const zoom = calculateZoomLevel(wayLength); + setZoomLevel(zoom); + } + }, [wayLength]); + return ( <>
@@ -313,6 +383,10 @@ const Main: FC = () => { if (selectedRoadIdx === -1 && selectedSurvey == null) { // If no road is selected, select the road setSelectedRoadIdx(index); + // Get information of road branches for Zoom in + const selectedRoad = roads[index]; + const Branches = selectedRoad.branches; + fetchAndSumWayLengths(Branches); // Call fetchAndSumWayLengths to calculate parameters and zoom in } else if (selectedRoadIdx != -1) { // if a road is selected, go to the inspect page for the clicked road branch navigate( @@ -344,7 +418,7 @@ const Main: FC = () => { } }} /> - + {isUploadPanelOpened && ( { post('/roads/data', wayIds, callback); }; + +/** + * Fetch the length of a specific way by its OSMWayId. + * + * @param wayId the OSMWayId of the way + * @param callback the function called back when the request is responded + * @returns nothing, but the callback will be called with the length + * + * @author Chen + */ +export const getWayLength = ( + wayId: string, + callback: (length: number) => void, +) => { + get(`/roads/paths/${wayId}`, (data: { length: number }) => { + callback(data.length); + }); +};