From 3d3225a35e72bb1893c632888356871e21c2d8f1 Mon Sep 17 00:00:00 2001 From: Murray Groves Date: Fri, 1 Mar 2024 00:58:27 +0000 Subject: [PATCH 1/4] Undirect graph I am yet to find a road you can only walk in one direction --- backend/safety.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/safety.py b/backend/safety.py index 5c112e1dc..69e70682e 100644 --- a/backend/safety.py +++ b/backend/safety.py @@ -54,6 +54,7 @@ def crime_score(crime, score): G = ox.load_graphml("bristol.graphml") print("Graph loaded!") +G = nx.MultiGraph(G) nx.set_edge_attributes(G, values=4, name="speed_kph") G = ox.add_edge_travel_times(G) G = add_edge_safety_index(G) From affacab7a59c0b9fa4f5b0fade665c6748e92990 Mon Sep 17 00:00:00 2001 From: Murray Groves Date: Fri, 1 Mar 2024 01:14:11 +0000 Subject: [PATCH 2/4] Use BrowserRouter, start/end shown in URL --- saferoute/package.json | 2 +- saferoute/src/App.tsx | 209 ++-------------------------------- saferoute/src/pages/map.tsx | 219 ++++++++++++++++++++++++++++++++++++ saferoute/yarn.lock | 20 ++++ 4 files changed, 247 insertions(+), 203 deletions(-) create mode 100644 saferoute/src/pages/map.tsx diff --git a/saferoute/package.json b/saferoute/package.json index 5539c528b..f0b97a5fb 100644 --- a/saferoute/package.json +++ b/saferoute/package.json @@ -19,12 +19,12 @@ "@types/react-dom": "^18.0.0", "maplibre-gl": "^4.0.2", "otp-ui": "^0.1.2", - "react": "^18.2.0", "react-dom": "^18.2.0", "react-google-autocomplete": "^2.7.3", "react-google-places-autocomplete": "^4.0.1", "react-map-gl": "^7.1.7", + "react-router-dom": "^6.22.2", "react-scripts": "5.0.1", "typescript": "^4.4.2", "web-vitals": "^2.1.0" diff --git a/saferoute/src/App.tsx b/saferoute/src/App.tsx index 88d17f8bb..6be2a3b43 100644 --- a/saferoute/src/App.tsx +++ b/saferoute/src/App.tsx @@ -1,212 +1,17 @@ -import React, { useEffect, RefObject, useState } from "react"; -import { Layer, Source } from "react-map-gl/maplibre"; - import "./App.css"; -import { Marker } from "react-map-gl/maplibre"; -import { FormControlLabel, FormGroup, Paper, Switch, ToggleButton } from "@mui/material"; -import { usePlacesWidget } from "react-google-autocomplete"; -import { ClickableMap } from "./ClickableMap"; import "maplibre-gl/dist/maplibre-gl.css"; -import { LatLong } from "./types"; -import { Feature, LineString } from "geojson"; -function coordsToFeature(coords: LatLong[]): Feature { - return { - type: "Feature", - properties: {}, - geometry: { - type: "LineString", - coordinates: coords.map((coord) => [coord[1], coord[0]]), - }, - }; -} +import { BrowserRouter, Route, Router, Routes, useNavigate } from 'react-router-dom'; +import MapPage from "./pages/map"; function App() { - const [start, setStart] = React.useState(); - const [end, setEnd] = React.useState(); - const [markers, setMarkers] = React.useState([]); - const [newRoute, setNewRoute] = useState(); - const [weight, setWeight] = useState(true); - - useEffect(() => { - if (!start || !end) { - setNewRoute(undefined); - return; - } - fetch(`${process.env.REACT_APP_HOSTPATH}/api/route?start=${start}&end=${end}&weight=${weight ? 'combined_index': 'travel_time'}`).then( - (response) => { - response.json().then((data) => { - let coordsList = data["route"]; - console.log(coordsList); - setNewRoute(coordsList); - }); - } - ); - }, [start, end, weight]); - - useEffect(() => { - fetch("https://overpass-api.de/api/interpreter", { - method: "POST", - // headers: { - // Accept: "application/json", - // "Content-Type": "application/json", - // }, - body: - "data=" + - encodeURIComponent(` - [out:json]; - node(51.4935,-2.57634,51.5935,-2.67634); -out; - `), - }) - .then((response) => response.json()) - .then((x) => - setMarkers( - x.elements - .filter((e: any) => e.tags?.amenity) - .map((e: any) => [e.lon, e.lat]) - ) - ); - }, [start, end]); - - let refs = usePlacesWidget({ - apiKey: process.env.REACT_APP_GOOGLE_KEY, - onPlaceSelected: (place) => { - if (place.geometry?.location?.lat() === undefined) - return console.log("no location"); - if (place.geometry?.location?.lng() === undefined) - return console.log("no location"); - let loc: LatLong = [ - place.geometry?.location?.lat(), - place.geometry?.location?.lng(), - ]; - setStart(loc); - }, - options: { types: [], bounds: { - north: 51.5, - south: 51.4, - west: -2.66834, - east: -2.45634, - }}, - }); - - const fromRef = refs.ref; - - refs = usePlacesWidget({ - apiKey: process.env.REACT_APP_GOOGLE_KEY, - onPlaceSelected: (place) => { - if (place.geometry?.location?.lat() === undefined) - return console.log("no location"); - if (place.geometry?.location?.lng() === undefined) - return console.log("no location"); - let loc: LatLong = [ - place.geometry?.location?.lat(), - place.geometry?.location?.lng(), - ]; - setEnd(loc); - }, - options: { types: [], bounds: { - north: 51.5, - south: 51.4, - west: -2.66834, - east: -2.45634, - } }, - }); - - const toRef = refs.ref; - return ( -
- - } - placeholder="From" - style={{ - border: "none", - width: "90%", - height: "100%", - fontSize: "120%", - backgroundColor: "rgba(0, 0, 0, 0)", - outline: "none", - borderColor: "transparent", - }} - /> - - - - } - placeholder="To" - style={{ - border: "none", - width: "90%", - height: "100%", - fontSize: "120%", - backgroundColor: "rgba(0, 0, 0, 0)", - outline: "none", - borderColor: "transparent", - }} - /> - - - - - setWeight(e.target.checked)} />} label={"Fastest/Safest"}/> - - - - - - {newRoute && ( - - - - )} - {markers.map((marker, index) => ( - - ))} - -
+ + + } /> + + ); } diff --git a/saferoute/src/pages/map.tsx b/saferoute/src/pages/map.tsx new file mode 100644 index 000000000..dafd54088 --- /dev/null +++ b/saferoute/src/pages/map.tsx @@ -0,0 +1,219 @@ +import React, { useEffect, RefObject, useState } from "react"; +import { Layer, Source } from "react-map-gl/maplibre"; + +import "../App.css"; +import { Marker } from "react-map-gl/maplibre"; +import { FormControlLabel, FormGroup, Paper, Switch, ToggleButton } from "@mui/material"; + +import { usePlacesWidget } from "react-google-autocomplete"; +import { ClickableMap } from "../ClickableMap"; +import "maplibre-gl/dist/maplibre-gl.css"; +import { LatLong } from "../types"; +import { Feature, LineString } from "geojson"; + +import { useNavigate } from 'react-router-dom'; + +function coordsToFeature(coords: LatLong[]): Feature { + return { + type: "Feature", + properties: {}, + geometry: { + type: "LineString", + coordinates: coords.map((coord) => [coord[1], coord[0]]), + }, + }; +} + +export default () => { + const [start, setStart] = React.useState(); + const [end, setEnd] = React.useState(); + const [markers, setMarkers] = React.useState([]); + const [newRoute, setNewRoute] = useState(); + const [weight, setWeight] = useState(true); + + const navigate = useNavigate(); + + useEffect(() => { + if (!start || !end) { + setNewRoute(undefined); + return; + } + fetch(`${process.env.REACT_APP_HOSTPATH}/api/route?start=${start}&end=${end}&weight=${weight ? 'combined_index': 'travel_time'}`).then( + (response) => { + response.json().then((data) => { + let coordsList = data["route"]; + console.log(coordsList); + setNewRoute(coordsList); + }); + } + ); + }, [start, end, weight]); + + useEffect(() => { + navigate('/?start=' + start + '&end=' + end) + }, [start, end]) + + useEffect(() => { + fetch("https://overpass-api.de/api/interpreter", { + method: "POST", + // headers: { + // Accept: "application/json", + // "Content-Type": "application/json", + // }, + body: + "data=" + + encodeURIComponent(` + [out:json]; + node(51.4935,-2.57634,51.5935,-2.67634); +out; + `), + }) + .then((response) => response.json()) + .then((x) => + setMarkers( + x.elements + .filter((e: any) => e.tags?.amenity) + .map((e: any) => [e.lon, e.lat]) + ) + ); + }, [start, end]); + + let refs = usePlacesWidget({ + apiKey: process.env.REACT_APP_GOOGLE_KEY, + onPlaceSelected: (place) => { + if (place.geometry?.location?.lat() === undefined) + return console.log("no location"); + if (place.geometry?.location?.lng() === undefined) + return console.log("no location"); + let loc: LatLong = [ + place.geometry?.location?.lat(), + place.geometry?.location?.lng(), + ]; + setStart(loc); + }, + options: { types: [], bounds: { + north: 51.5, + south: 51.4, + west: -2.66834, + east: -2.45634, + }}, + }); + + const fromRef = refs.ref; + + refs = usePlacesWidget({ + apiKey: process.env.REACT_APP_GOOGLE_KEY, + onPlaceSelected: (place) => { + if (place.geometry?.location?.lat() === undefined) + return console.log("no location"); + if (place.geometry?.location?.lng() === undefined) + return console.log("no location"); + let loc: LatLong = [ + place.geometry?.location?.lat(), + place.geometry?.location?.lng(), + ]; + setEnd(loc); + }, + options: { types: [], bounds: { + north: 51.5, + south: 51.4, + west: -2.66834, + east: -2.45634, + } }, + }); + + const toRef = refs.ref; + + return ( +
+ + } + placeholder="From" + style={{ + border: "none", + width: "90%", + height: "100%", + fontSize: "120%", + backgroundColor: "rgba(0, 0, 0, 0)", + outline: "none", + borderColor: "transparent", + }} + /> + + + + } + placeholder="To" + style={{ + border: "none", + width: "90%", + height: "100%", + fontSize: "120%", + backgroundColor: "rgba(0, 0, 0, 0)", + outline: "none", + borderColor: "transparent", + }} + /> + + + + + setWeight(e.target.checked)} />} label={"Fastest/Safest"}/> + + + + + + {newRoute && ( + + + + )} + {markers.map((marker, index) => ( + + ))} + +
+ ); +} \ No newline at end of file diff --git a/saferoute/yarn.lock b/saferoute/yarn.lock index 12de7e2d8..2cc26204f 100644 --- a/saferoute/yarn.lock +++ b/saferoute/yarn.lock @@ -2025,6 +2025,11 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== +"@remix-run/router@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.2.tgz#35726510d332ba5349c6398d13259d5da184553d" + integrity sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q== + "@rollup/plugin-babel@^5.2.0": version "5.3.1" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" @@ -8811,6 +8816,21 @@ react-refresh@^0.11.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== +react-router-dom@^6.22.2: + version "6.22.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.2.tgz#8233968a8a576f3006e5549c80f3527d2598fc9c" + integrity sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ== + dependencies: + "@remix-run/router" "1.15.2" + react-router "6.22.2" + +react-router@6.22.2: + version "6.22.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.22.2.tgz#27e77e4c635a5697693b922d131d773451c98a5b" + integrity sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw== + dependencies: + "@remix-run/router" "1.15.2" + react-scripts@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003" From f299f2072e10f3e902aff13bc4fe17e2f31d7499 Mon Sep 17 00:00:00 2001 From: Murray Groves Date: Fri, 1 Mar 2024 01:37:57 +0000 Subject: [PATCH 3/4] Map loads start/end from URL --- saferoute/src/App.tsx | 4 +++- saferoute/src/pages/map.tsx | 12 ++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/saferoute/src/App.tsx b/saferoute/src/App.tsx index 6be2a3b43..bec73683c 100644 --- a/saferoute/src/App.tsx +++ b/saferoute/src/App.tsx @@ -4,12 +4,14 @@ import "maplibre-gl/dist/maplibre-gl.css"; import { BrowserRouter, Route, Router, Routes, useNavigate } from 'react-router-dom'; import MapPage from "./pages/map"; +import { Button } from "@mui/material"; function App() { return ( - } /> + } /> + } /> ); diff --git a/saferoute/src/pages/map.tsx b/saferoute/src/pages/map.tsx index dafd54088..da40b2299 100644 --- a/saferoute/src/pages/map.tsx +++ b/saferoute/src/pages/map.tsx @@ -11,7 +11,7 @@ import "maplibre-gl/dist/maplibre-gl.css"; import { LatLong } from "../types"; import { Feature, LineString } from "geojson"; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; function coordsToFeature(coords: LatLong[]): Feature { return { @@ -32,6 +32,14 @@ export default () => { const [weight, setWeight] = useState(true); const navigate = useNavigate(); + const params = useParams(); + useEffect(() => { + if (params.start && params.end) { + console.log("parm") + setStart(params.start.split(',').map(parseFloat) as LatLong); + setEnd(params.end.split(',').map(parseFloat) as LatLong); + } + }, []) useEffect(() => { if (!start || !end) { @@ -50,7 +58,7 @@ export default () => { }, [start, end, weight]); useEffect(() => { - navigate('/?start=' + start + '&end=' + end) + navigate('/map/' + start + '/' + end) }, [start, end]) useEffect(() => { From fe8ca713468eb55f6463ddb64c1ed072e7e9ce13 Mon Sep 17 00:00:00 2001 From: Murray Date: Thu, 25 Apr 2024 15:49:51 +0000 Subject: [PATCH 4/4] Fix image tag --- continuous-development/docker-compose.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/continuous-development/docker-compose.yml b/continuous-development/docker-compose.yml index 18f066f8f..657dc89f2 100644 --- a/continuous-development/docker-compose.yml +++ b/continuous-development/docker-compose.yml @@ -1,3 +1,6 @@ +name: + saferoute + services: backend: container_name: saferoute-backend @@ -5,12 +8,15 @@ services: ports: - 8920:8080 pull_policy: always + restart_policy: unless_stopped frontend: - container_name: cityfarm-frontend + container_name: saferoute-frontend image: ghcr.io/murraygroves/saferoute/frontend:latest environment: - REACT_APP_HOSTPATH=https://saferoute.murraygrov.es + - REACT_APP_GOOGLE_KEY=AIzaSyCJVcENXdtmm-BnM6IOR5AXjn7_AnRgdBo ports: - 8921:3000 - pull_policy: always \ No newline at end of file + pull_policy: always + restart_policy: unless_stopped