Skip to content
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

Render vector tiles from graph storage #1572

Merged
merged 34 commits into from Jun 17, 2019

Conversation

@karussell
Copy link
Member

karussell commented Mar 19, 2019

Fixes #1456

We can now render the roads in a browser for debugging purposes with an acceptable performance - will be especially interesting with #1548:

image

After several trials I think I got something I find interesting:

  1. this PR is based on #1485 as it fetches all nodes&edges for a certain bounding box. Everything worked really well, did not yet stumbled over a bug although I should have found one due to the minor limitation listed there.
  2. this PR introduces a MVTResource that returns a "roads" layer as vector tiles in a binary and compact format. It uses https://github.com/wdtinc/mapbox-vector-tile-java which is nearly 10x faster compared to the other MVT library but still it is the slowest part in this visualization process. It converts real world coordinates into a "local" vector tiles coordinate system using JTS and something is a bit slow. Also the library is a bit low level and unusual to use (was a bit tricky to get it done), but now we can send also edge properties to the browser and customize this with just JavaScript.
  3. These vector tiles can be consumed with any client library supporting the mapbox vector tiles spec (MVT) and I found a way to consume this even in leaflet using Leaflet.VectorGrid https://gist.github.com/karussell/0720b3d3b297621c8bb10cf8c86b2906 and the best thing is: it is not so slow like all the things I tried before (highly likely due to the canvas renderer which was pointed out from @michaz already before :))
  4. this works also with maputnik (using this style)

TODO:

  • make it working as base layer for e.g. Mapbox GL JS (then it should work for deck.gl too)
  • remove a few things from the isochrone module (I started my work there)
  • make it working in deck.gl either with the base vector tile map or using the geojson-layer
  • allow to export GeoJSON alternatively (rename to /graph-explore endpoint or something) no. Use GeoJSON only for responses that are not easy to serve as tiles like shortest path trees. See also #1577
  • integrate this into our JS demo using Leaflet.VectorGrid
  • write a test via decoding the returned MVT stuff
  • With maputnik this gives me Error: http status 200 returned without content. Can we do better at returning 'an empty tile' ? fix later
karussell added 19 commits Oct 15, 2018
…s what's different to usual implementation
…d EdgeVisitor
…r big zoom
@karussell karussell added this to the 0.13 milestone Mar 19, 2019
@karussell karussell changed the title Visualize vector tiles from graph storage for debugging purposes Render vector tiles from graph storage for debugging purposes Mar 19, 2019
@karussell karussell removed this from the 0.13 milestone Mar 19, 2019
@karussell karussell changed the title Render vector tiles from graph storage for debugging purposes Render vector tiles from graph storage Mar 19, 2019
@easbar

This comment has been minimized.

Copy link
Collaborator

easbar commented Mar 21, 2019

Sounds very interesting, will have a closer look! Is this just about visualizing the route or the whole / parts of the graph ? For the purposes I was looking for in #1456 this here is my (probably still incomplete) wish list. Do you think we can do all (or most of) this using the mvt approach (<- sorry no idea atm what the possiblities/limitations are) ?

  • draw bounding box and show graph (including node/edge infos like id, weight, distance, ch level etc., possibly by clicking or hovering as a popup as otherwise it becomes hard to see anything)
  • show turn restrictions
  • zoom/fly to coordinate
  • zoom to a given node/edge id and/or draw bounding box around a certain node/edge id
  • somehow visualize loops and duplicate edges (this I have found is not so easy would have to split/slightly offset edges that lie on top of each other, or use some 3D features)
  • show more/less details depending on zoom level
  • show/hide details via UI (enable/disable checkbox, move slider etc.)

Update Looking at the screenshot it looks like the whole graph is already drawn and there is possibility for a popup showing the name of the road :)

@karussell

This comment has been minimized.

Copy link
Member Author

karussell commented Mar 21, 2019

Is this just about visualizing the route or the whole / parts of the graph ?

Whole graph

draw bounding box and show graph

no bounding box necessary as everything that is visible is shown (for every "tile" it does a request and the server response gives MVT data)

and there is possibility for a popup showing the name of the road :)

You can actually show every data you like be it as a color or if you click on the road via a popup, yes :)

show turn restrictions

👍 ... a bit tricky from the GH perspective but easy as this should be just 2 roads we have to transmit :)

show more/less details depending on zoom level

This can be easily done. And is done already. E.g. for the overview it just returns junction nodes and deeper zooms gives full way geometry

somehow visualize loops and duplicate edges (this I have found is not so easy would have to split/slightly offset edges that lie on top of each other, or use some 3D features)

Could be color-encoded or something, sure.

zoom/fly to coordinate

As we can easily integrate this into GH Maps as overlay or basemap this should be possible like with normal leaflet functionality: https://graphhopper.com/maps/?point=41.95949%2C2.790527

show/hide details via UI (enable/disable checkbox, move slider etc.)

Definitely possible.

We could pack all data we have into the MVT and evaluate them on the client side. The current problem is for many points it takes long to convert our data into MVT on the server side. Still it already works reasonable fast, especially if you have just a few cities or even less.

@easbar

This comment has been minimized.

Copy link
Collaborator

easbar commented Mar 21, 2019

Cool!

The current problem is for many points it takes long to convert our data into MVT on the server side. Still it already works reasonable fast, especially if you have just a few cities or even less.

A single or even a part of a single city would be sufficient for debugging routing problems I think.

show turn restrictions ... this get tricky though :)

Ok but why ? Could they not be somehow attached as metadata ? At a minimum we should be able to use an extra endpoint that serves turn restriction information (for example per node id or bounding box) and use this as an extra layer (<- can we draw an extra layer on top of the vector tiles from the client side?)

@karussell

This comment has been minimized.

Copy link
Member Author

karussell commented Mar 21, 2019

Ok but why ? Could they not be somehow attached as metadata ?

Sorry, my wording was bad. I just meant the graphhopper part ... currently we would have to loop through all edges to find if there are turn costs instead of saying "please give me all turn cost in this area". If we have this data we can easily return this as e.g. a multiline with turn_restriction=true or something

can we draw an extra layer on top of the vector tiles from the client side

Yes, this should be possible. But multiple layers are not necessary as we can use a single layer and evaluate properties of it feature by feature

@easbar

This comment has been minimized.

Copy link
Collaborator

easbar commented Mar 21, 2019

currently we would have to loop through all edges to find if there are turn costs instead of saying

but with locationIndex.query(bbox) we can get all nodes in a bbox and then we retrieve all turn restrictions for these node ids ?

But multiple layers are not necessary as we can use a single layer and evaluate properties of it feature by feature

Ok yes if we collect all data on the server with a single request we create one layer.

@easbar

This comment has been minimized.

Copy link
Collaborator

easbar commented Mar 24, 2019

For the graph debugging I think I will be using this modified maputnik version and I might add some stuff like showing the route or shortest path tree. The mvt tiles are already integrated (and can also be chosen via the 'Open' button).

@easbar

This comment has been minimized.

Copy link
Collaborator

easbar commented Mar 24, 2019

I am getting this error

ERROR [2019-03-24 15:26:15,096] org.glassfish.jersey.server.ServerRuntime$Responder: An I/O error has occurred while writing a response message entity to the container output stream.
! java.io.IOException: Broken pipe
! at sun.nio.ch.FileDispatcherImpl.writev0(Native Method)
! at sun.nio.ch.SocketDispatcher.writev(SocketDispatcher.java:51)
! at sun.nio.ch.IOUtil.write(IOUtil.java:148)
! at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:504)
! at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:266)
! ... 83 common frames omitted
! Causing: org.eclipse.jetty.io.EofException: null
! at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:286)
! at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:429)

quite regularly. Any idea ?

@easbar

This comment has been minimized.

Copy link
Collaborator

easbar commented Mar 24, 2019

Do you have an example on how to do this? Which json did you enter in the datasources?

It is basically this (satellite + your speed-colored road layers): https://github.com/easbar/editor/blob/graphhopper_debug/src/gh/assets/gh-mvt-style.json

@karussell

This comment has been minimized.

Copy link
Member Author

karussell commented Mar 24, 2019

quite regularly. Any idea ?

Highly likely those exceptions are the result of tiles that the browser initially asked for, but then due to zoom level or bbox changes they are not needed anymore and explicitly rejected from the browser (connection dropped or something).

So it should not happen if you zoom so slow that all tiles can render. Would be nice if we can somehow properly "fix" them.

It is basically this (satellite + your speed-colored road layers)

Ah, thanks! (For firefox I had a minor glitched due to mixed content and so I had to replace localhost with 127.0.0.1: http://127.0.0.1:8989/mvt/{z}/{x}/{y}.mvt)

gh-mvt-style.zip

@boldtrn

This comment has been minimized.

Copy link
Member

boldtrn commented Mar 24, 2019

but then due to zoom level or bbox changes they are not needed anymore and explicitly rejected from the browser (connection dropped or something).

Yes, the client usually cancels these requests. You can see that the easily in the network tab in the developer tools.

@easbar

This comment has been minimized.

Copy link
Collaborator

easbar commented Mar 25, 2019

Yes, the client usually cancels these requests. You can see that the easily in the network tab in the developer tools.

Maybe there is an option to delay the client-side requests so not to many requests are sent before the zooming 'stops' at the point one really wants to zoom to ?

@boldtrn

This comment has been minimized.

Copy link
Member

boldtrn commented Mar 25, 2019

Maybe there is an option to delay the client-side requests so not to many requests are sent before the zooming 'stops' at the point one really wants to zoom to ?

IMHO both the client and GH work correctly. The problem seems to be that dropwizard does not catch this exception gracefully. Also see #1558

karussell added 4 commits May 31, 2019
@karussell karussell changed the base branch from loc_index to master May 31, 2019
@karussell karussell added this to the 0.13 milestone May 31, 2019
@crazycapivara

This comment has been minimized.

Copy link

crazycapivara commented Jun 13, 2019

I really like the vector tiles.

Here is an example using the mvt endpoint with openlayers for R

devtools::install_github("crazycapivara/openlayers")

library(openlayers)

map_source <- "http://localhost:8989/mvt/{z}/{x}/{y}.mvt"

# Basic example
ol() %>%
  add_vector_tiles(map_source) %>%
  set_view(9.503174, 52.583026, 12)

Adding some styles from here:

fn <- "https://gist.github.com/crazycapivara/5efd362173604e25b985ce1cf68f2dad/raw/069281132f093ce11a874daaf6293e0397770408/gh-ol-mvt-style.js"
style <- read_js_function(fn)

ol() %>%
  add_vector_tiles(map_source, style = style) %>%
  set_view(9.503174, 52.583026, 12)

# Add a raster layer as well ...
ol() %>%
  add_stamen_tiles("toner", options = list(name = "stamen")) %>%
  add_vector_tiles(map_source, style = style, options = list(name = "gh")) %>%
  set_view(9.503174, 52.583026, 12) %>%
  add_layer_switcher()
karussell added 2 commits Jun 15, 2019
@karussell

This comment has been minimized.

Copy link
Member Author

karussell commented Jun 15, 2019

@crazycapivara nice, thanks!

Here it is now also possible to include more information. On the server side you need the graph.encoded_values property, see https://github.com/graphhopper/graphhopper/blob/master/config-example.yml#L19

And on the client side you request a sub set of them, see bf3ece8#diff-fdabf1e86cc9047458301bd084fc7a8b

Additionally we now include a different level of details in different zoom levels based on the road_class (OSM highway tag)

image

(Please note that in the screenshots still a background layer is used that draws the layers like city names and road names)

karussell added 2 commits Jun 16, 2019
@karussell karussell merged commit 3b9c033 into master Jun 17, 2019
5 checks passed
5 checks passed
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details
security/snyk - pom.xml (karussell) No new issues
Details
security/snyk - web/package.json (karussell) No new issues
Details
@karussell karussell deleted the experimental-mvt branch Jun 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.