-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use OpenLayers instead of Mapbox #176
Conversation
* the types are included in the distribution since 6.6.0 (openlayers/openlayers@9e88134)
* not sure about tsconfig changes, took this from here: openlayers/ol-mapbox-style#269 (comment)
# Conflicts: # src/App.tsx # src/map/Map.tsx # src/map/Popup.tsx
src/api/graphhopper.d.ts
Outdated
@@ -54,6 +54,7 @@ export interface RoutingProfile { | |||
} | |||
|
|||
export interface Path extends BasePath { | |||
// todo: do we really need the geojson dependency? it was included in mapbox but its not included in OL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see such type definition in the ol package. They have the geojson stuff as actual classes, that's not compatible with this I think. So I would think yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I was more thinking about not using it at all. But let's just keep it for now.
/* todo: this is just copy-pasta from https://openlayers.org/en/latest/examples/popup.html, I have no idea what all this means, | ||
and the popup looks ugly still, at the very minimum the background color needs to be set though | ||
the popup design needs an overhaul anyway... https://github.com/graphhopper/graphhopper-maps/issues/136 | ||
*/ | ||
.popup { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compared to the react-map-gl wrapper OpenLayers does not provide a popup component. Therefore I used a simple div and copy-pasted some css from the OpenLayers examples for the context menu and path details popup. I'm pretty sure this can be improved...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a better template at hand at the moment. Since this is such a big PR already, I would suggest to go with this version.
/* todo: this is just copy-pasta from https://openlayers.org/en/latest/examples/popup.html, I have no idea what all this means, | ||
and the popup looks ugly still, at the very minimum the background color needs to be set though | ||
the popup design needs an overhaul anyway... https://github.com/graphhopper/graphhopper-maps/issues/136 | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as for the context menu
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also just use it as is and make an issue for styling this. This popup is indeed a little too big I think. It doesn't break anything though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. And we have this issue already: #136
// todo: use createMapMarker from heightgraph? | ||
// {createMapMarker(point.elevation, point.description)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This todo is just copied from master
src/map/Map.module.css
Outdated
.mapContainer { | ||
height: 100%; | ||
width: 100%; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, but I needed this to make the map visible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine. It alternatively, in App.module.css
the .map
class could have display: grid
, which should stretch the map container by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right this also works. I changed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this works in Firefox, Chrome and also when using the mobile view in dev tools. But for the aws deployment the map stayed invisible on mobile for @karussell and me (even though it worked for desktop). So I reverted it to my initial version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, this is odd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but I tried it twice and we both saw the same results. Feel free to try it again, but otherwise I'll leave as it is this for now.
@@ -96,6 +98,7 @@ const tfCycle: RasterStyle = { | |||
attribution: | |||
osmAttribution + | |||
', <a href="https://www.thunderforest.com/maps/opencyclemap/" target="_blank">Thunderforest Cycle</a>', | |||
tilePixelRatio: 2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way the raster tiles aren't blurry like they were for Mapbox
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the avoidance of fractional numbers for zoom that avoids the blurry look? (constrainResolution: true
)
See your comment openlayers/openlayers#12963 (comment)
And tilePixelRatio (additionally) improves blurry for high dpi devices? (but for mapbox we had these "2x
" in the URL)
(then also the question would be: could we set it to 1 for low dpi devices somehow?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the avoidance of fractional numbers for zoom that avoids the blurry look? (constrainResolution: true)
See your comment openlayers/openlayers#12963 (comment)
Yes, avoiding fractional zoom levels is part of it. But tilePixelRatio
is still needed I think. As far as I know we need to tell OpenLayers also about the resolution of our tiles. From the documentation: https://openlayers.org/en/latest/apidoc/module-ol_source_XYZ-XYZ.html
The pixel ratio used by the tile service. For example, if the tile service advertizes 256px by 256px tiles but actually sends 512px by 512px images (for retina/hidpi devices) then tilePixelRatio should be set to 2.
So we need to set this number depending on the tile service.
but for mapbox we had these "2x" in the URL
Yes, for some tile providers adding 2x
changes the resolution of the tiles they send, but either way we need to tell OpenLayers the resolution of the tiles we feed to it?
(then also the question would be: could we set it to 1 for low dpi devices somehow?)
Do you see blurry tiles on some device, or what do you want to improve here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also see this and the following comments: #159 (comment)
// The original client has these but those options yield cors errors with mapbox yields a cors error | ||
// lyrk, | ||
// wanderreitkarte, | ||
// This works but is extremely slow with mapbox | ||
lyrk, | ||
wanderreitkarte, | ||
// This one is extremely slow with mapbox and openlayers?! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lyrk and wanderreitkarte work with OpenLayers while they did not for Mapbox. sorbian is still very slow
src/stores/MapOptionsStore.ts
Outdated
@@ -208,7 +213,7 @@ export default class MapOptionsStore extends Store<MapOptionsStoreState> { | |||
`Could not find tile layer specified in config: '${config.defaultTiles}', using default instead` | |||
) | |||
return { | |||
selectedStyle: selectedStyle ? selectedStyle : mapTiler, | |||
selectedStyle: selectedStyle ? selectedStyle : osmOrg, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably want to change this to Omniscale
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And shall we even keep map tiler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use Omniscale as default (but keep map tiler)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But as the maptiler layers and mapilion are so slow we should probably mark them as experimental (e.g. gray out the label a bit?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I made Omniscale the default now. If the two vector layers are so slow that they should not be used why not remove them altogether? Or is there any case where they would be useful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For testing and improving I would keep them. We could then ask / contract e.g. OL maintainers to improve this somehow :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can keep them, but maybe we should remove them from the menu? We could still use them by setting the url parameter... They are too slow to use, aren't they? So why should users be confronted with this if it is just for internal testing? If we keep them in the menu we need to find a way to tell users that this is for testing of vector tiles only. Just graying them out doesn't really accomplish this? Or maybe we change the menu entries to MapTiler (slow)
or something?
tsconfig.json
Outdated
@@ -1,5 +1,6 @@ | |||
{ | |||
"compilerOptions": { | |||
"allowJs": true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this important we can try to get rid of this again but I needed it to make the code compile
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Which parts would not compile?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I just tried again and it seems to work also without this now :)
} | ||
|
||
receive(action: Action) { | ||
// todo: port old ViewportStore.test.ts or otherwise test this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll take a look at this, but leaving this as a reminder here for now.
# Conflicts: # src/actions/Actions.ts # src/stores/ViewportStore.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very good overall. The layers part being inside custom hooks turned out pretty I think. It is quite easy to get an idea what's going on.
I noticed, that the context menu sometimes doesn't open, after I have set some points via the context menu. It might be related to the change between wide and narrow layouts. Also, the context menu doesn't close anymore at all, except one selects something from it. I think this is a separate issue, which we have somewhere already thoug.
src/map/Map.module.css
Outdated
.mapContainer { | ||
height: 100%; | ||
width: 100%; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine. It alternatively, in App.module.css
the .map
class could have display: grid
, which should stretch the map container by default.
src/map/MapComponent.tsx
Outdated
|
||
/** A small react component that simply attaches our map instance to a div to show the map **/ | ||
export default function ({ map }: MapComponentProps) { | ||
const mapElement = useRef<any>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be useRef<HtmlDivElement | null>(null)
. I think this is how it is suggested by the react documentation.
src/api/graphhopper.d.ts
Outdated
@@ -54,6 +54,7 @@ export interface RoutingProfile { | |||
} | |||
|
|||
export interface Path extends BasePath { | |||
// todo: do we really need the geojson dependency? it was included in mapbox but its not included in OL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see such type definition in the ol package. They have the geojson stuff as actual classes, that's not compatible with this I think. So I would think yes.
/* todo: this is just copy-pasta from https://openlayers.org/en/latest/examples/popup.html, I have no idea what all this means, | ||
and the popup looks ugly still, at the very minimum the background color needs to be set though | ||
the popup design needs an overhaul anyway... https://github.com/graphhopper/graphhopper-maps/issues/136 | ||
*/ | ||
.popup { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a better template at hand at the moment. Since this is such a big PR already, I would suggest to go with this version.
src/layers/ContextMenu.tsx
Outdated
const [menuCoordinate, setMenuCoordinate] = useState<Coordinate | null>(null) | ||
const [overlay, setOverlay] = useState<Overlay | undefined>() | ||
|
||
const container = useRef() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be typed. useRef<HTMLDivElement | null
|
||
export default function usePathDetailsLayer(map: Map, pathDetails: PathDetailsStoreState) { | ||
useEffect(() => { | ||
removePathSegmentsLayer(map) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the custom hook is called usePathDetailsLayer
, the sub methods are named <something>PathSegementsLayer
. I think the naming should be the same, since it concerned about the same thing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm the layer is related to the path details diagram so usePathDetailsLayer
kind of make sense. But what it actually does is just highlight segments of the path that are above/below a certain elevation. I don't know.. I'm not sure if renaming is an improvement here. I'll add a comment that says what this layer does for now.
// todo: this is mostly duplicated from `Marker.tsx`. To use `Marker.tsx` we would probably need to add ol.Overlays, i.e. | ||
// create a div for each marker and insert the svg from `Marker.tsx`. | ||
export function createSvg({ color, number, size = 0 }: MarkerProps) { | ||
return `<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" width="${ | ||
// todo: we do not use width in Marker.tsx, but without this the markers are not shown in Firefox :( (but they are shown in Chrome...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to have this mounted as a Marker.tsx
component. This would indeed mean that we would have a wrapper div for each point in a route. One would then create a new rendering context for each point. Maybe this would be too complicated.
The way this works now, is that ol renders the svg on the canvas as an image directly. I'd assume that this is the most efficient way to do it.
Leave it like this, fix the translation and the number?
@@ -0,0 +1,56 @@ | |||
import { Action, ActionReceiver } from '@/stores/Dispatcher' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks quite nice so far. I think this is a good direction. (Meaning the entire module)
tsconfig.json
Outdated
@@ -1,5 +1,6 @@ | |||
{ | |||
"compilerOptions": { | |||
"allowJs": true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Which parts would not compile?
Ok cool, thanks for your review.
Hm, can you reproduce this consistently somehow?
Yes, I think it master it does not close when we click (see #135). But it does close when we pan the map. In this branch it only closes when we select an action. Let's fix both in #135? |
I just tried this briefly and it is easy to close the menu on click. Just add map.on('click', () => {
overlay?.setPosition(undefined)
setMenuCoordinate(null)
}) in |
Thanks a lot for this @easbar, great work! Everything works and due to the raster tile speed and precision the project feels even more polished :) I played a bit around and tried to find the longest shortest path ;) The marker offset is not yet perfect. E.g. have a look about the start of this route which seems to be in the sea instead of the land: http://localhost:3000/?point=-34.356099%2C18.497489&point=66.638129%2C179.986909&profile=car&layer=OpenStreetMap (Interestingly the marker in current master is also wrong but in a different way) Then there seems to be some bbox issue (but very likely this is related to #53) when the route cannot be found: |
Thanks for testing!
Yes, see also this discussion: #176 (comment)
Yes, this is strange. |
I fixed this here: dd0ade8. Now we see the map also for urls for which no route can be found. The problem was that at the time we set the initial bounding box the map's screen size was still undefined as it hadn't been rendered at that point yet. Therefore we need to estimate the map size as well. |
I'll add separate issues for some of the things we discussed here. |
Co-authored-by: Janekdererste <janek.laudan@mailbox.org>
This is a follow-up of #159, this time trying to actually do this migration rather than exploring its implications.
Here is a small walk-through:
package.json/dependencies: I removed the react-map-gl dependency (the react/mapbox wrapper including mapbox) obviously and added OpenLayers 6 (
ol
) instead. I also addedol-mapbox-style
to show the MapTiler tiles, even though we might remove this entirely since we rather focus on raster tiles for now. I also had to add thegeojson
dependency directly, because we were using it here and there and we got it from Mapbox so far.tsconfig.json: I just modified this to make the app run. Possibly we can revert parts of this especially if we remove
ol-mapbox-style
.Now to the more interesting changes: How do we create and show the map? First thing we need is the OL
Map
object. I'm not entirely sure where we should create it but right now I simply create it inmap.ts
and there is also a reference to it so we can import it from everywhere. The next thing we need to do is attach the map to the DOM. For this I addedMapComponent.tsx
which usesuseRef
to create a container div for the map. It simply receives the map object as a prop.The next thing we need to do is add some layers to the map. Let's start with the background layer. If we just used the same layer forever we could just add it to the map initially and would be done with it. But since we can change the layer via the layers option menu we need code that first removes the existing layer and then adds the new layer. This should run whenever the
styleOption
changes, however we don't want to run this code for every render call of ourApp
component. Therefore I useuseEffect
and put thisuseEffect
into another function calleduseBackgroundLayer
(a custom hook: https://reactjs.org/docs/hooks-custom.html). I added the other layers (like query points and paths) the same way. One advantage here is that we can add layers without passing all the props we need for the different layers into our previousMapComponent
.useBackgroundLayer
, just likeMapComponent
, simply receives the map (and thestyleOption
) as a function parameter inApp.tsx
. Note that if we did not useuseEffect
our 'remove-old-and-add-new-layer'-code would run for every render call.We also need to adjust the map view for certain actions like
RouteRequestSuccess
orSetSelectedPath
etc. Therefore I use anActionReceiver
that is registered at ourDispatcher
. It is calledMapActionReceiver
and created inindex.ts
similar to our stores. It also gets a reference to the map objects so it can adjust the map view for whateverAction
we want.This is basically it, most of the remaining code is simply about adding the other layers. I'll add a todo list and a few more comments inline.