maplibre / maplibre-gl-js

MapLibre GL JS - Interactive vector tile maps in the browser
https://maplibre.org/maplibre-gl-js/docs/
Other
6.76k stars 729 forks source link

Single-tile rendering mode #166

Closed davenquinn closed 3 years ago

davenquinn commented 3 years ago

Motivation

Mapbox's renderer is the best in the world for vector tiles, and the future of the 1.x series and open API clearly resides with MapLibre. However, there are other open mapping platforms, notably Cesium and Leaflet, that excel at other aspects (namely high-fidelity 3D and lightweight/no-frills mapping). It would be amazing to use the capabilities of the MapLibre renderer in these projects.

For this, I propose a "single-tile" rendering mode for vector tiles. This would allow tiles to be generated from a map style and exported to a canvas or image element for inclusion in other rendering pipelines. This was requested in Mapbox GL JS (https://github.com/mapbox/mapbox-gl-js/issues/4420) but rejected out of hand.

Prior art

Land Technologies created a fork that implements this functionality. This fork has been used successfully in a few contexts (see the vector tiles tracking issue on Cesium JS as well as my recent work at Macrostrat). However, it is out of date, and moving to a MapLibre renderer would provide a strong base for future work (for example, we'd like to figure out how to render labels separately from the basemap) as well as likely increase performance.

Potential issues and limitations

Although I haven't yet delved significantly into the code, this might be a significant and complicated additional API to support, especially if options for partial renders and custom layers are supported. Given that, a potential alternative solution might be to expose hooks within the MapLibre API so that this functionality can be implemented as a wrapping library.

I'm happy to investigate how this could be implemented a bit further; I am also happy to defer to others' expertise. Is this a feasible or desirable API break from mapbox-gl-js?

syncush commented 3 years ago

just to add my 2 cents here, AFAIK, mapbox-gl-js uses mapbox-gl-native in order to render a single tile, a C++ implementation with node bindings (compatible to node v10 which is in end-of-life state).

https://github.com/maptiler/tileserver-gl has implemented a single tile rendering + tile rasterizer service with these packages.

wipfli commented 3 years ago

If I understand you correctly you are asking if it is possible to produce raster tiles from vector data with maplibre as the rendering engine, right?

If this is the case, maybe you can explain further what your use case is? One great thing about vector maps is step-less zooming. Would you want to produce raster tiles for certain zoom levels?

syncush commented 3 years ago

In my case, I would like to write style in mapbox-gl-style and be able to support both vector tiles and raster tiles without implementing the same style twice in two different technologies since I can't control what web map component users might want to use(leaflet, maplibre-gl-js, mapbox-gl-js, Openlayers, ESRI, Cesium, mapzen, etc...)

being able to render raster tiles on the fly in the backend using the mapbox-gl-style saves a lot of cartographic work and reduces the tech stack to a minimum while being able to support many different web map components.

wipfli commented 3 years ago

Then tileserver-gl is probably what you want to use.

syncush commented 3 years ago

We've written a proxy server that utilizes tileserver-gl code of rasterization, but we are also would like to be able to support WGS84 and other non-mercator projections, is it part of maplibre-gl-js and maplibre-gl-native roadmap?

dropping the link for the project https://github.com/MapColonies/vector-tiles-rasterizer

tuukka commented 3 years ago

@syncush Interesting. Could you perhaps convert the coordinates of the tile request and the vector tile content so that when tileserver-gl rasterizes the tile, the resulting image is according to the projection you need? This way, there would be no need to complicate maplibre-gl-{js,native}. Going forward, the conversions could be merged into tileserver-gl.

melancholiai commented 3 years ago

converting the coordinates to the wanted projection won't result in an image with the requested projection because mapbox-gl-native's rastering utilities are nongeneric and support only EPSG:3857 projection. Currently I'm looking into the mapbox-gl-native c++ code, to accumulate the cost of supporting EPSG:4326 projection, for instance they manage some calculations in a projection class, all the methods rely on ESPG:3857.

davenquinn commented 3 years ago

I think I am missing something in this discussion. mapbox-gl-js renders the vector tiles to images itself on the client side. This is similar to, but quite distinct from, the work that tileserver-gl accomplishes on the server side. I agree that, for building static tiles, working on the server side may be preferred. However, I'd like to render vector tiles dynamically, much as mapbox-gl-js does, using filters etc. from a style specification. This requires the work to be done on the client side. The only difference from the current operation of this library is that I'd like to overlay the resulting images on another map constructed in Cesium.

Basically, this library has one of the best WebGL renderers for vector tiles available, and I'd like to use it in a somewhat "standalone" mode to generate tiles on demand.

github-actions[bot] commented 3 years ago

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] commented 3 years ago

This issue was closed because it has been stalled for 7 days with no activity.

vittrup88 commented 2 years ago

I definitely get your point of having a single tile render. I really would like to use this as a tile render for other mapping engines, in my case that would be Openlayers. It would just be great to use the native render from MapLibre directly in frontend. My reason to do this in frontend is that I'm working on a analysis openlayers client and we have alot of diffrent filters and the user can filter their may to their needs. So there are so many options and we use vector tiles instead of rendered tiles from backend to get a great looking app, with interactive hover and much more.

At the moment to get the best performance I have build a service in our app that will select the best performing render based on the clients possible renders. Right now that is eg. Openlayers Native, and Openlayers rendered in offscreen canvas (good performance). Here I would like to add MapLibre since we already use mapbox styles this would be a no brainer to start implementing and we have already made room for it.

I see a few ways doing this.

  1. MapLibre single tile render (like Land Technologies)
  2. MapLibre in Offscreen Worker (then both Openlayers and MapLibre needs to run WebMercator - that is ok, openlayers have examples doing this with Mapbox GL JS, works and looks really nice)
  3. MapLibre using WebAssembly (https://github.com/mapbox/mapbox-gl-js/issues/4835 and example here: https://avnav.com/mbgl/)

I really like both 1 and 2. So back to it. Thanks @davenquinn for linking to the Land Technologies fork. I have tried compiling it and got it working. It is running on an old Mapbox version, but it render. I have made a small custom project where I have copied most of the code but I have imported the used MapLibre modules instead of putting it into the core. This means it works more or less like a plugin or it depends on MapLibre. That way it can be move to the core later. I do have some problems. Some exports have been made manually and build in MapLibre. Maybe I'm doing the imports wrong in typescript but I have to look into it. Second I got it to render some polygons, but am really struggling to get lines rendering. Some of the code from Land Technologies fork does not work because of change in the API and I am not 100% into MapLibre code.

davenquinn commented 2 years ago

Hi @vittrup88 and thanks for the thoughts!

Just FYI, I have started working on this last month, taking the approach of the Land Technologies renderer, but trying to import current Maplibre code rather than forking. Right now it's still running on a fork, but I'm trying to keep it up to date with the rest of the codebase. See it here: https://github.com/davenquinn/maplibre-gl-js/tree/pluggable-render. It's probably similar to yours! Once I get the fork working OK, I will try to spin this out into a separate module, either making PRs for the necessary hooks into maplibre-gl or directly importing maplibre source code.

My goals are pretty similar to yours (not a shock given our aligned approach thus far), except for the Cesium platform. Because I'm working in Cesium, which does not especially care about web mercator, I have taken your option 1 for now. But I'm hoping to get a little lower level and have some hooks into the label rendering, etc. that are obviously not good fits for single-tile rendering but instead require screen-space layout.

Eventually this will hopefully lead to a Cesium-based digital globe with an optional visualization/filtering API based on vector tiles. It'll be used at macrostrat.org and other Earth data visualization properties. (Side note, I've already got the LandTech renderer working successfully for a Mars science visualization, see https://dev.macrostrat.org/mars/syrtis-jezero/layers#a=355&e=6&overlays=%2Cgeology&ve=1.5&x=78.051&y=16.047&z=602097.)

In short, I think we are moving towards a really great approach and I suggest we team up for a mutually satisfying solution.

HarelM commented 2 years ago

It's great to hear that you guys are working on that! Thanks for sharing. I'm guessing that some of the problems that you might encounter are related to things that are concerning multiple tiles like collision detection. I think a plugin for start would be great and then we can think if there's a way to incorporate this into MapLibre as a first class citizen.

vittrup88 commented 2 years ago

Hi @davenquinn that sound really awesome. If I can help in any way I will try to. I see your BasicPainter (same structure as Land Technologies). I don't see any example in the debug dir? Am I missing something?

You already have things rendering like polygon, line and Point I guess on your Cesium Example. Do you have example in your fork or should we make any?

Should we keep the discussion here or on your branch?

We will clearly make this a plugin when things are running, and we know what exports are needed!

vittrup88 commented 2 years ago

Hi @HarelM Do you know if anyone are working on Offscreen Canvas support ? This would also be a great option since this could remove problems with labeling. That way the Offscreen Render will just send an image back for display which will give nice rendering on zoom and labels. There is already an example at openlayers with Mapbox GL JS running in a native container but I really would like it to run Offscreen for even better integration and performance.

HarelM commented 2 years ago

I'm not aware of anyone working on offscreen canvas support, but the best place to ask would be slack I think.

davenquinn commented 2 years ago

Hi @vittrup88 sorry for the confusion. Right now all of the work to actually use the renderer I have created is happening in another project: https://github.com/davenquinn/cesium-vector-provider. This one includes examples. Apologies are in order, though – I've sort of overloaded that repository currently with several submodules being developed in concert towards a virtual globe...so it is no longer just about a "Cesium vector provider". Consequently, the examples may be complex to get running.

The most relevant file is here: https://github.com/davenquinn/cesium-vector-provider/blob/master/src/base.ts

I think we will need to decide where to do this work, but a fork or branch of Maplibre seems the most appropriate.

davenquinn commented 2 years ago

Adding some updates here: I've gotten this to work a bit better for raster and hillshade layers. It's still quite rough, but the technique shows promise. On the left is Maplibre GL atop Cesium, and on the right is Mapbox GL v2. The Cesium version loads lower-res hillshade tiles than Mapbox and everything is a bit more janky. But it's headed in the right direction I think...code is here

image

davenquinn commented 2 years ago

FYI, I've just done a major clean-up of the code for the cesium-vector-provider module and created a demo website similar to the screenshot above.

There is quite a laundry list of improvements needed for a high-quality implementation of vector maps within Cesium (labels, glyphs, and efficiency/caching harmonization foremost). A major ongoing task will be to stabilize the API links with Maplibre GL and begin to plot what hooks will be needed within the codebase here to make this work sustainably. I'd love to collaborate with @vittrup88 and others interested in implementing Maplibre-based renderers within other mapping stacks, to see if we can find a way to support multiple used cases here.