implemented total route length

main
Inga 🏳‍🌈 1 year ago
parent c675cf36ee
commit 221467fa27
  1. 7
      README.md
  2. 6
      package-lock.json
  3. 1
      package.json
  4. 4
      src/routePlanner/index.tsx
  5. 10
      src/routePlanner/style.css
  6. 12
      src/routePlanner/total.tsx
  7. 4
      src/routePlanner/types.ts
  8. 16
      src/shared/routes.ts

@ -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). 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), 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. 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: 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; 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 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); (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 ### 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. 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. 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), There are tests for non-`preact` code (collection-related methods and main reducer, with 100% code coverage),

6
package-lock.json generated

@ -7,6 +7,7 @@
"dependencies": { "dependencies": {
"@dwayneparton/geojson-to-gpx": "^0.0.30", "@dwayneparton/geojson-to-gpx": "^0.0.30",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"haversine-distance": "^1.2.1",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"nanoid": "^5.0.3", "nanoid": "^5.0.3",
"preact": "^10.13.1", "preact": "^10.13.1",
@ -5567,6 +5568,11 @@
"node": ">= 0.4" "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": { "node_modules/hosted-git-info": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz",

@ -14,6 +14,7 @@
"dependencies": { "dependencies": {
"@dwayneparton/geojson-to-gpx": "^0.0.30", "@dwayneparton/geojson-to-gpx": "^0.0.30",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"haversine-distance": "^1.2.1",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"nanoid": "^5.0.3", "nanoid": "^5.0.3",
"preact": "^10.13.1", "preact": "^10.13.1",

@ -2,6 +2,7 @@ import { ExportComponent } from './export';
import { useMarkers } from './hooks/useMarkers'; import { useMarkers } from './hooks/useMarkers';
import { MapComponent } from './map'; import { MapComponent } from './map';
import { MarkersComponent } from './markers'; import { MarkersComponent } from './markers';
import { TotalComponent } from './total';
import './style.css'; import './style.css';
@ -19,6 +20,9 @@ export const RoutePlanner = () => {
markers={markers} markers={markers}
/> />
</section> </section>
<section class="total">
<TotalComponent markers={markers} />
</section>
<section class="export"> <section class="export">
<ExportComponent markers={markers} /> <ExportComponent markers={markers} />
</section> </section>

@ -4,10 +4,11 @@ section.route-planner {
column-gap: 1rem; column-gap: 1rem;
row-gap: 1rem; row-gap: 1rem;
grid-template-columns: 1fr 2fr; 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: grid-template-areas:
"header map" "header map"
"markers map" "markers map"
"total map"
"export map"; "export map";
} }
@ -53,6 +54,10 @@ section.route-planner > section.markers li button[disabled] {
visibility: hidden; visibility: hidden;
} }
section.route-planner > section.total {
grid-area: total;
}
section.route-planner > section.export { section.route-planner > section.export {
grid-area: export; grid-area: export;
} }
@ -88,10 +93,11 @@ section.route-planner .map-container .leaflet-tooltip-pane .text {
@media (max-width: 1000px) { @media (max-width: 1000px) {
section.route-planner { section.route-planner {
grid-template-columns: 1fr; 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: grid-template-areas:
"header" "header"
"markers" "markers"
"total"
"export" "export"
"map"; "map";
} }

@ -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`}</>;
};

@ -26,6 +26,10 @@ export type MarkersProps = {
onMarkersReorder: (params: ReorderListParams) => void; onMarkersReorder: (params: ReorderListParams) => void;
}; };
export type TotalProps = {
markers: Marker[];
};
export type MapProps = { export type MapProps = {
markers: Marker[]; markers: Marker[];
onMapClick: (coordinates: Coordinates) => void; onMapClick: (coordinates: Coordinates) => void;

@ -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;
};
Loading…
Cancel
Save