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

Load vector tiles as uint8arrays (raw protobuf binary)? #8374

Closed
erik-h11 opened this issue Jun 20, 2019 · 11 comments
Closed

Load vector tiles as uint8arrays (raw protobuf binary)? #8374

erik-h11 opened this issue Jun 20, 2019 · 11 comments

Comments

@erik-h11
Copy link

I have to render a data set that is both large (millions of features) and changes frequently (several times a minute).

Most of the data (in particular, the geometries) is actually static; it's just some of the feature tags that change. Based on the tags, I want to render the features in a particular color, and I also want to render them in a particular order (otherwise, some features that need to stand out risk being buried by others).

I'm currently working on a server-side implementation of these requirements: I have a tile server that retrieves the base vector tiles from a cache, deserializes them into Java objects, adds the appropriate tags, sorts the features, serializes them into new vector tiles, and sends those to the browser.

This looks like it's going to be a decent solution, but I'd prefer to do all this client-side.

Now, I know that it's possible to make rendering property such as line-color look up a value in a javascript object, but I'd have to fill that object with values for every feature, since I don't know which tiles are being rendered. This would mean sending large amounts of data to all clients all the time. With this approach, I would also not be able to change the order in which tiles are rendered.

With a client-side approach, I'd be able to ask the server for just the tags for the features in the tiles to be rendered, and there would not be any need to send entire vector tiles.

Therefore, I think it would be useful to have a way to load a vector tile from a binary array. I can think of two ways:

  1. specify a javascrript function to call, returning the vector tile as an uint8array (this function would probably need some arguments such as the x/y/z and a base URL)
  2. a function with an unit8array for argument, returning another uint8array; this would just be a hook to manipulate the protobuf object, and would still require mapbox-gl-js to retrieve a base vector tile first (possibly from the browser cache).

If there's another way to achieve my goals, let me know.

@andrewharvey
Copy link
Collaborator

I think you should be able to address what you want here with some combination of queryRenderedFeatures to get features in view and setFeatureState to join properties to geometries client side.

@erik-h11
Copy link
Author

Thanks. I now see that queryRenderedFeatures can easily be used to get information on all features on the map, not just at a given point. However, retrieving and setting the properties is not my only requirement: I also need to be able to change the z-coordinate (rendering order) for some features. Is this possible?

@andrewharvey
Copy link
Collaborator

If you could clarify exactly what feature you're asking for?

I also need to be able to change the z-coordinate (rendering order) for some features. Is this possible?

See #1349 #4361.

@erik-h11
Copy link
Author

The code I'm writing is for displaying traffic conditions (in particular, speeds). I'm trying to make sure that congested roads are visible even when zoomed out. For instance,
image -->
image ; as you can see, you can't really tell the various roadways apart when zoomed out, which means that there is a very real risk of having a non-congested road occluding its congested neighbor.

So, I'm talking about sorting within a single data layer, based on data that is changing frequently. I think this is similar to the issues described in the tickets you listed.

@andrewharvey
Copy link
Collaborator

So, I'm talking about sorting within a single data layer, based on data that is changing frequently. I think this is similar to the issues described in the tickets you listed.

Ok, so I'll close this ticket to keep the discussion at #1349 or #4361.

@erik-h11
Copy link
Author

Well, even if this z-order issue is a duplicate of those other tickets: I still think my original idea of being able to manipulate the protobuf data client-side has merits. I haven't looked into the mapbox-gl-js code, but I imagine that for every tile, you'd have to load the binary data and process it. Calling a function to manipulate the data after loading it seems pretty easy, could you give this idea a second thought?

@andrewharvey
Copy link
Collaborator

I still think my original idea of being able to manipulate the protobuf data client-side has merits.

Is that #4261? There's also a good discussion of this in #2671 which from my understanding covers your ask. There are two approaches you can use currently data join -> https://docs.mapbox.com/mapbox-gl-js/example/data-join/ or using feature states -> https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/.

@erik-h11
Copy link
Author

I believe that all the issues/examples you referenced are about joining an external data set somehow, but none seem to contain my idea of manipulating the vector tiles in their binary (protobuf) form.

I have now implemented this server-side: when the tile server receives a request for a tile, it retrieves a .mvt tile from its cache (this tile contains just static data on roadways); then, it parses that binary data using Java code that I generated with the protoc compiler and the MVT .proto definition. The server then constructs a new vector tile with the static data and traffic speeds from another source; the latter are inserted as attributes (tags) per road segment. In this process, I make sure that these features are sorted by increasing traffic congestion, so that congested roads are rendered last and won't be occluded.

I'd prefer to implement this process client-side in Javascript. It should be straightforward to implement, but I'd need to have a way to manipulate the tile (as binary data) after it has been loaded from the server, and before it goes into the rendering engine.

As for other approaches: your suggestion of using queryRenderedFeatures/setFeatureState seems useful for merging the data, but from what I understand, the z-order issue remains.

@asheemmamoowala
Copy link
Contributor

Most of the data (in particular, the geometries) is actually static; it's just some of the feature tags that change. Based on the tags, I want to render the features in a particular color, and I also want to render them in a particular order (otherwise, some features that need to stand out risk being buried by others).

@erik-h11 #8383 implements the ability to use data-driven-styling to affect sort-order for line layers which is what affects the z-order priority. That PR in combination with feature state (or other data-join method) should be sufficient to achieve the above requirement.

it's possible to make rendering property such as line-color look up a value in a javascript object, but I'd have to fill that object with values for every feature, since I don't know which tiles are being rendered.

You can use the dataloading event to know when a tile is being loaded:

map.on('dataloading', (event) => {
    if (event.dataType === 'source' && event.tile) {
        console.log (event.tile);
    }
})

Therefore, I think it would be useful to have a way to load a vector tile from a binary array. I can think of two ways:

  1. specify a javascrript function to call ...
  2. a function with an unit8array for argument, returning another uint8array; ...

Vector tile protobufs are read lazily and on Web Workers, which makes running arbitrary JS methods very complicated. The above suggestions are not easily implemented either for the gl-js library or from a developer's standpoint.

@erik-h11
Copy link
Author

That PR may be just the missing piece... I'll try to get this to work with the functionality you and Andrew mentioned. Thanks!

@andrewharvey
Copy link
Collaborator

That PR may be just the missing piece...

Ha no wonder I didn't see it before, only released 15 hours ago, you got lucky!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants