From 221467fa2716155c18f6f2fc8d9cc5e45b89137f Mon Sep 17 00:00:00 2001 From: Inga Date: Sun, 19 Nov 2023 14:13:47 +0000 Subject: [PATCH] implemented total route length --- README.md | 7 +++++-- package-lock.json | 6 ++++++ package.json | 1 + src/routePlanner/index.tsx | 4 ++++ src/routePlanner/style.css | 10 ++++++++-- src/routePlanner/total.tsx | 12 ++++++++++++ src/routePlanner/types.ts | 4 ++++ src/shared/routes.ts | 16 ++++++++++++++++ 8 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 src/routePlanner/total.tsx create mode 100644 src/shared/routes.ts diff --git a/README.md b/README.md index 5188798..b0c4919 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ As komoot website says, "[challenge] definitely is a great chance to learn somet after hearing about it from Xe Iaso (who sadly stopped development of xer Xeact). It was definitely an experience setting up a project with `preact` (their defaults regarding linting and typechecking are not exactly sensible), -but also the resulting bundle is just 64KB, gzipped (42 of which is Leaflet). +but also the resulting bundle is just 65KB, gzipped (42 of which is Leaflet). And all the actual code should be compatible with Facebook's `react` as well. Since using third-party react components is disallowed by this challenge, I only use the following third-party libraries: @@ -39,7 +39,8 @@ Since using third-party react components is disallowed by this challenge, I only 4. `Sortable.js` (non-react package), because there is no way in hell I'm going to try to implement drag and drop myself in a cross-platform way; I have life; 5. `@dwayneparton/geojson-to-gpx`, an unpopular but tiny (2KB) package to create gpx files (as opposed to much more popular `gpx-builder` which is 584KB with all its dependencies); -6. `file-saver`, because implementing my own in a cross-platform way would be a waste of time. +6. `file-saver`, because implementing my own in a cross-platform way would be a waste of time; +7. `haversine-distance` to compute distances between points (and total route length). ### How to run @@ -65,6 +66,8 @@ So I also added editable long marker labels (first four letters of unique marker I also added "move up" and "move down" buttons for every marker, because for some people it can be much easier to use compared to drag&drop. +And I also compute and display total route length (without considering the altitude changes), I think this might be convenient. + The final design only remotely resembles the screenshot above, because I'm not a designer and I spent too much time on actually implementing the logic already. There are tests for non-`preact` code (collection-related methods and main reducer, with 100% code coverage), diff --git a/package-lock.json b/package-lock.json index 1280aab..416e3db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "dependencies": { "@dwayneparton/geojson-to-gpx": "^0.0.30", "file-saver": "^2.0.5", + "haversine-distance": "^1.2.1", "leaflet": "^1.9.4", "nanoid": "^5.0.3", "preact": "^10.13.1", @@ -5567,6 +5568,11 @@ "node": ">= 0.4" } }, + "node_modules/haversine-distance": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/haversine-distance/-/haversine-distance-1.2.1.tgz", + "integrity": "sha512-rQpG89d6NlAis0eqOSFXDqNU/GZcMPlHNVMqTSzD16niD9s1fDK8T6kwrK0WJ7OMU+iRNy3cgGYnNQihMqmaHg==" + }, "node_modules/hosted-git-info": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", diff --git a/package.json b/package.json index 36bb2b3..5fe27fa 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dependencies": { "@dwayneparton/geojson-to-gpx": "^0.0.30", "file-saver": "^2.0.5", + "haversine-distance": "^1.2.1", "leaflet": "^1.9.4", "nanoid": "^5.0.3", "preact": "^10.13.1", diff --git a/src/routePlanner/index.tsx b/src/routePlanner/index.tsx index 0d71e04..7d48bf6 100644 --- a/src/routePlanner/index.tsx +++ b/src/routePlanner/index.tsx @@ -2,6 +2,7 @@ import { ExportComponent } from './export'; import { useMarkers } from './hooks/useMarkers'; import { MapComponent } from './map'; import { MarkersComponent } from './markers'; +import { TotalComponent } from './total'; import './style.css'; @@ -19,6 +20,9 @@ export const RoutePlanner = () => { markers={markers} /> +
+ +
diff --git a/src/routePlanner/style.css b/src/routePlanner/style.css index abe97f6..b5e21cb 100644 --- a/src/routePlanner/style.css +++ b/src/routePlanner/style.css @@ -4,10 +4,11 @@ section.route-planner { column-gap: 1rem; row-gap: 1rem; grid-template-columns: 1fr 2fr; - grid-template-rows: min-content 1fr min-content; + grid-template-rows: min-content 1fr min-content min-content; grid-template-areas: "header map" "markers map" + "total map" "export map"; } @@ -53,6 +54,10 @@ section.route-planner > section.markers li button[disabled] { visibility: hidden; } +section.route-planner > section.total { + grid-area: total; +} + section.route-planner > section.export { grid-area: export; } @@ -88,10 +93,11 @@ section.route-planner .map-container .leaflet-tooltip-pane .text { @media (max-width: 1000px) { section.route-planner { grid-template-columns: 1fr; - grid-template-rows: 1fr min-content 2fr; + grid-template-rows: min-content 1fr min-content min-content 2fr; grid-template-areas: "header" "markers" + "total" "export" "map"; } diff --git a/src/routePlanner/total.tsx b/src/routePlanner/total.tsx new file mode 100644 index 0000000..eb5d053 --- /dev/null +++ b/src/routePlanner/total.tsx @@ -0,0 +1,12 @@ +import { useMemo } from 'preact/hooks'; +import type { TotalProps } from './types'; +import { getRouteLength } from '../shared/routes'; + +export const TotalComponent = ({ markers }: TotalProps) => { + const routeLength = useMemo( + () => getRouteLength(markers.map(({ coordinates }) => coordinates)), + [markers], + ); + + return <>{`Total route length: ${routeLength.toFixed(0)}m`}; +}; diff --git a/src/routePlanner/types.ts b/src/routePlanner/types.ts index 4643975..b229d62 100644 --- a/src/routePlanner/types.ts +++ b/src/routePlanner/types.ts @@ -26,6 +26,10 @@ export type MarkersProps = { onMarkersReorder: (params: ReorderListParams) => void; }; +export type TotalProps = { + markers: Marker[]; +}; + export type MapProps = { markers: Marker[]; onMapClick: (coordinates: Coordinates) => void; diff --git a/src/shared/routes.ts b/src/shared/routes.ts new file mode 100644 index 0000000..3b7a6f2 --- /dev/null +++ b/src/shared/routes.ts @@ -0,0 +1,16 @@ +import haversineDistance from 'haversine-distance'; +import { Coordinates } from './types'; + +export const getRouteLength = (points: Coordinates[]) => { + let result = 0; + for (let i = 0; i < points.length; i++) { + const currentPoint = points[i]; + const previousPoint = points[i - 1]; + if (!currentPoint || !previousPoint) { + continue; + } + result += haversineDistance(previousPoint, currentPoint); + } + + return result; +};