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;
+};