Skip to content

Commit 203a815

Browse files
committed
merged master
2 parents 66fed2e + 3305d66 commit 203a815

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+10670
-18560
lines changed

.github/workflows/test_and_build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ on: push
33

44
jobs:
55
test_and_build:
6-
runs-on: ubuntu-22.04
6+
runs-on: ubuntu-24.04
77
steps:
88
- name: Checkout
9-
uses: actions/checkout@v3
9+
uses: actions/checkout@v4
1010

1111
- name: Setup Node.js
12-
uses: actions/setup-node@v3
12+
uses: actions/setup-node@v4
1313
with:
1414
registry-url: https://registry.npmjs.org/
1515
node-version: v20.14.0

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ A route planner user interface for the [GraphHopper routing engine](https://gith
44

55
[Try it out](https://graphhopper.com/maps/)!
66

7+
We have developed an intuite and simple user interface to find routes:
8+
79
[![GraphHopper Maps route planner](https://www.graphhopper.com/wp-content/uploads/2023/03/gh-maps-202303.png)](https://graphhopper.com/maps/)
810

11+
With autocomplete, alternative routes, information along the route and POI search and everything available in all major browsers including mobile browsers. Read more details about it [here](https://www.graphhopper.com/maps-route-planner/).
12+
913
## Turn-by-Turn navigation
1014

1115
There is [an experimental `navi` branch](https://github.com/graphhopper/graphhopper-maps/tree/navi) that implements turn-by-turn navigation
@@ -54,3 +58,4 @@ This project uses
5458
* the [codemirror](https://codemirror.net/) code editor for the custom model editor.
5559
* many icons from Google's [open source font library](https://fonts.google.com/icons).
5660
* many more open source projects - see the package.json
61+

config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ const config = {
3131
'track_type',
3232
'country',
3333
],
34-
snapPreventions: ['ferry'],
3534
},
3635

3736
// Use 'profiles' to define which profiles are visible and how. Useful if the /info endpoint contains too many or too "ugly" profile

package-lock.json

Lines changed: 6516 additions & 17784 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
"test": "jest"
1616
},
1717
"dependencies": {
18-
"custom-model-editor": "github:graphhopper/custom-model-editor#3a46b6981d170b7eb70d621bbb92caed149e5a97",
18+
"custom-model-editor": "github:graphhopper/custom-model-editor#5ebd80570329f7abfc95c39624be1f1a379cf392",
1919
"geojson": "^0.5.0",
2020
"heightgraph": "github:easbar/Leaflet.Heightgraph#5f4f0b1fff3646aa071981381f5955c9e6f111f0",
21-
"ol": "9.2.3",
22-
"ol-mapbox-style": "12.3.2",
23-
"react": "^18.2.0",
24-
"react-dom": "^18.2.0",
25-
"react-responsive": "^9.0.0"
21+
"ol": "10.4.0",
22+
"ol-mapbox-style": "12.5.0",
23+
"react": "^18.3.1",
24+
"react-dom": "^18.3.1",
25+
"react-responsive": "^10.0.1"
2626
},
2727
"devDependencies": {
2828
"@svgr/webpack": "^8.0.1",
@@ -41,7 +41,7 @@
4141
"prettier": "2.8.8",
4242
"source-map-loader": "^4.0.0",
4343
"style-loader": "^3.3.1",
44-
"ts-jest": "^29.0.5",
44+
"ts-jest": "^29.1.4",
4545
"ts-loader": "^9.4.2",
4646
"typescript": "^5.1.3",
4747
"webpack": "^5.75.0",

src/App.tsx

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
getMapFeatureStore,
88
getMapOptionsStore,
99
getPathDetailsStore,
10+
getPOIsStore,
1011
getQueryStore,
1112
getRouteStore,
1213
getSettingsStore,
@@ -42,7 +43,8 @@ import PlainButton from '@/PlainButton'
4243
import useAreasLayer from '@/layers/UseAreasLayer'
4344
import useExternalMVTLayer from '@/layers/UseExternalMVTLayer'
4445
import LocationButton from '@/map/LocationButton'
45-
import { SettingsContext } from './contexts/SettingsContext'
46+
import { SettingsContext } from '@/contexts/SettingsContext'
47+
import usePOIsLayer from '@/layers/UsePOIsLayer'
4648

4749
export const POPUP_CONTAINER_ID = 'popup-container'
4850
export const SIDEBAR_CONTENT_ID = 'sidebar-content'
@@ -56,6 +58,7 @@ export default function App() {
5658
const [mapOptions, setMapOptions] = useState(getMapOptionsStore().state)
5759
const [pathDetails, setPathDetails] = useState(getPathDetailsStore().state)
5860
const [mapFeatures, setMapFeatures] = useState(getMapFeatureStore().state)
61+
const [pois, setPOIs] = useState(getPOIsStore().state)
5962

6063
const map = getMap()
6164

@@ -68,6 +71,7 @@ export default function App() {
6871
const onMapOptionsChanged = () => setMapOptions(getMapOptionsStore().state)
6972
const onPathDetailsChanged = () => setPathDetails(getPathDetailsStore().state)
7073
const onMapFeaturesChanged = () => setMapFeatures(getMapFeatureStore().state)
74+
const onPOIsChanged = () => setPOIs(getPOIsStore().state)
7175

7276
getSettingsStore().register(onSettingsChanged)
7377
getQueryStore().register(onQueryChanged)
@@ -77,6 +81,7 @@ export default function App() {
7781
getMapOptionsStore().register(onMapOptionsChanged)
7882
getPathDetailsStore().register(onPathDetailsChanged)
7983
getMapFeatureStore().register(onMapFeaturesChanged)
84+
getPOIsStore().register(onPOIsChanged)
8085

8186
onQueryChanged()
8287
onInfoChanged()
@@ -85,6 +90,7 @@ export default function App() {
8590
onMapOptionsChanged()
8691
onPathDetailsChanged()
8792
onMapFeaturesChanged()
93+
onPOIsChanged()
8894

8995
return () => {
9096
getSettingsStore().register(onSettingsChanged)
@@ -95,6 +101,7 @@ export default function App() {
95101
getMapOptionsStore().deregister(onMapOptionsChanged)
96102
getPathDetailsStore().deregister(onPathDetailsChanged)
97103
getMapFeatureStore().deregister(onMapFeaturesChanged)
104+
getPOIsStore().deregister(onPOIsChanged)
98105
}
99106
}, [])
100107

@@ -108,11 +115,19 @@ export default function App() {
108115
usePathsLayer(map, route.routingResult.paths, route.selectedPath, query.queryPoints)
109116
useQueryPointsLayer(map, query.queryPoints)
110117
usePathDetailsLayer(map, pathDetails)
118+
usePOIsLayer(map, pois)
119+
111120
const isSmallScreen = useMediaQuery({ query: '(max-width: 44rem)' })
112121
return (
113122
<SettingsContext.Provider value={settings}>
114123
<div className={styles.appWrapper}>
115-
<MapPopups map={map} pathDetails={pathDetails} mapFeatures={mapFeatures} />
124+
<MapPopups
125+
map={map}
126+
pathDetails={pathDetails}
127+
mapFeatures={mapFeatures}
128+
poiState={pois}
129+
query={query}
130+
/>
116131
<ContextMenu map={map} route={route} queryPoints={query.queryPoints} />
117132
{isSmallScreen ? (
118133
<SmallScreenLayout
@@ -178,7 +193,7 @@ function LargeScreenLayout({ query, route, map, error, mapOptions, encodedValues
178193
drawAreas={drawAreas}
179194
/>
180195
)}
181-
<Search points={query.queryPoints} profile={query.routingProfile} />
196+
<Search points={query.queryPoints} profile={query.routingProfile} map={map} />
182197
<div>{!error.isDismissed && <ErrorMessage error={error} />}</div>
183198
<RoutingResults
184199
info={route.routingResult.info}
@@ -225,6 +240,7 @@ function SmallScreenLayout({ query, route, map, error, mapOptions, encodedValues
225240
error={error}
226241
encodedValues={encodedValues}
227242
drawAreas={drawAreas}
243+
map={map}
228244
/>
229245
</div>
230246
<div className={styles.smallScreenMap}>

src/Converters.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ export function milliSecondsToText(ms: number) {
1212
return (hourText ? hourText + ' ' : '') + minutes + ' min'
1313
}
1414

15-
const distanceFormat = new Intl.NumberFormat(navigator.language, { maximumFractionDigits: 1 })
15+
let distanceFormat: Intl.NumberFormat = new Intl.NumberFormat('en', { maximumFractionDigits: 1 })
16+
export function setDistanceFormat(_distanceFormat: Intl.NumberFormat) {
17+
distanceFormat = _distanceFormat
18+
}
1619

1720
export function metersToText(meters: number, showDistanceInMiles: boolean, forceSmallUnits: boolean = false) {
1821
if (showDistanceInMiles) {

src/NavBar.ts

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
import { coordinateToText } from '@/Converters'
2-
import { Bbox } from '@/api/graphhopper'
32
import Dispatcher from '@/stores/Dispatcher'
43
import { ClearPoints, SelectMapLayer, SetBBox, SetQueryPoints, SetVehicleProfile } from '@/actions/Actions'
54
// import the window like this so that it can be mocked during testing
65
import { window } from '@/Window'
7-
import QueryStore, {
8-
Coordinate,
9-
getBBoxFromCoord,
10-
QueryPoint,
11-
QueryPointType,
12-
QueryStoreState,
13-
} from '@/stores/QueryStore'
6+
import QueryStore, { getBBoxFromCoord, QueryPoint, QueryPointType, QueryStoreState } from '@/stores/QueryStore'
147
import MapOptionsStore, { MapOptionsStoreState } from './stores/MapOptionsStore'
15-
import { getApi } from '@/api/Api'
8+
import { ApiImpl, getApi } from '@/api/Api'
9+
import { AddressParseResult } from '@/pois/AddressParseResult'
10+
import { getQueryStore } from '@/stores/Stores'
1611

1712
export default class NavBar {
1813
private readonly queryStore: QueryStore
@@ -116,6 +111,19 @@ export default class NavBar {
116111
if (parsedPoints.some(p => !p.isInitialized && p.queryText.length > 0)) {
117112
const promises = parsedPoints.map(p => {
118113
if (p.isInitialized) return Promise.resolve(p)
114+
const result = AddressParseResult.parse(p.queryText, false)
115+
if (result.hasPOIs() && result.location) {
116+
// two stage POI search: 1. use extracted location to get coordinates 2. do reverse geocoding with this coordinates
117+
return getApi()
118+
.geocode(result.location, 'nominatim')
119+
.then(res => {
120+
if (res.hits.length == 0) return p
121+
getApi()
122+
.reverseGeocode(result.query, res.hits[0].extent)
123+
.then(res => AddressParseResult.handleGeocodingResponse(res, result))
124+
return p
125+
})
126+
}
119127
return (
120128
getApi()
121129
.geocode(p.queryText, 'nominatim')
@@ -129,7 +137,7 @@ export default class NavBar {
129137
}
130138
})
131139
// if the geocoding request fails we just keep the point as it is, just as if no results were found
132-
.catch(() => Promise.resolve(p))
140+
.catch(() => p)
133141
)
134142
})
135143
const points = await Promise.all(promises)
@@ -151,7 +159,7 @@ export default class NavBar {
151159
const bbox =
152160
initializedPoints.length == 1
153161
? getBBoxFromCoord(initializedPoints[0].coordinate)
154-
: NavBar.getBBoxFromUrlPoints(initializedPoints.map(p => p.coordinate))
162+
: ApiImpl.getBBoxPoints(initializedPoints.map(p => p.coordinate))
155163
if (bbox) Dispatcher.dispatch(new SetBBox(bbox))
156164
return Dispatcher.dispatch(new SetQueryPoints(points))
157165
}
@@ -169,18 +177,4 @@ export default class NavBar {
169177
this.mapStore.state
170178
).toString()
171179
}
172-
173-
private static getBBoxFromUrlPoints(urlPoints: Coordinate[]): Bbox | null {
174-
const bbox: Bbox = urlPoints.reduce(
175-
(res: Bbox, c) => [
176-
Math.min(res[0], c.lng),
177-
Math.min(res[1], c.lat),
178-
Math.max(res[2], c.lng),
179-
Math.max(res[3], c.lat),
180-
],
181-
[180, 90, -180, -90] as Bbox
182-
)
183-
// return null if the bbox is not valid, e.g. if no url points were given at all
184-
return bbox[0] < bbox[2] && bbox[1] < bbox[3] ? bbox : null
185-
}
186180
}

src/actions/Actions.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Action } from '@/stores/Dispatcher'
22
import { Coordinate, QueryPoint } from '@/stores/QueryStore'
33
import { ApiInfo, Bbox, Path, RoutingArgs, RoutingProfile, RoutingResult } from '@/api/graphhopper'
44
import { PathDetailsPoint } from '@/stores/PathDetailsStore'
5+
import { POI } from '@/stores/POIsStore'
56
import { Settings } from '@/stores/SettingsStore'
67

78
export class InfoReceived implements Action {
@@ -246,3 +247,19 @@ export class UpdateSettings implements Action {
246247
this.updatedSettings = updatedSettings
247248
}
248249
}
250+
251+
export class SelectPOI implements Action {
252+
readonly selected: POI | null
253+
254+
constructor(selected: POI | null) {
255+
this.selected = selected
256+
}
257+
}
258+
259+
export class SetPOIs implements Action {
260+
readonly pois: POI[]
261+
262+
constructor(pois: POI[]) {
263+
this.pois = pois
264+
}
265+
}

0 commit comments

Comments
 (0)