Open jailln opened 3 years ago
As far as I know web mercator is the only projection available. And it sounded like it suits peoples needs quite well. You can transform mouse positions and marker locations on-the-fly I guess. Or what would be your use case for anything different than web mercator?
Other projections would be really nice, for example to work with polar data or to avoid web mercator distortion issues, but it would be a HUGE undertaking for a general case. First you'd need to support the rendering and placement of non-web mercator tiles. Then you'd need to create your own basemap because the vector tile basemap pipeline Mapbox uses only supports Web Mercator data.
Note that you can hack alternative projections into Mapbox/Maplibre GL in specific circumstances. For example, the New York Times provides its interactive maps of the U.S. (e.g. elections and Covid cases) in an Equal Albers projection. (Note the northern border with Canada curves instead of being a straight line as it would be in Mercator).
If you're willing to do your own math for your custom vector tiles, you can achieve something like this by using the lat/lon boundaries of [-180, -90, 180, 90]
as an arbitrary canvas. So you'd convert your data from their coordinates in their non-Web Mercator projection to their relative points in this canvas without reprojecting. But of course those NYT maps can't use a Mapbox basemap because the Northeast of the U.S. would correspond to "upper right" in web mercator and load basemap data from Russia.
This seems quite out of scope for this library, as it basically only speaks Web Mercator. Other mapping libraries (ex. d3 as @kylebarron mentioned) are probably more fit for your needs here. There are also ways to "fool" web mercator for polar data etc.
As far as I know web mercator is the only projection available. And it sounded like it suits peoples needs quite well. You can transform mouse positions and marker locations on-the-fly I guess. Or what would be your use case for anything different than web mercator?
Mainly to avoid web mercator distortions issues for e.g. measurements or edition, without having to transform positions on the fly every time. Also, to display raster data in local projections.
Other projections would be really nice, for example to work with polar data or to avoid web mercator distortion issues, but it would be a HUGE undertaking for a general case. First you'd need to support the rendering and placement of non-web mercator tiles. Then you'd need to create your own basemap because the vector tile basemap pipeline Mapbox uses only supports Web Mercator data.
Note that you can hack alternative projections into Mapbox/Maplibre GL in specific circumstances. For example, the New York Times provides its interactive maps of the U.S. (e.g. elections and Covid cases) in an Equal Albers projection. (Note the northern border with Canada curves instead of being a straight line as it would be in Mercator).
If you're willing to do your own math for your custom vector tiles, you can achieve something like this by the lat/lon boundaries of
[-180, -90, 180, 90]
as an arbitrary canvas. So you'd convert your data from their coordinates in their non-Web Mercator projection to their relative points in this canvas without reprojecting. But of course those NYT maps can't use a Mapbox basemap because the Northeast of the U.S. would correspond to "upper right" in web mercator and load basemap data from Russia.
Thanks for this detailed answer! I guess that some parts of the code assume that tiles are are web-mercator and so th'at why it would require many changes to make it a general case.
This seems quite out of scope for this library, as it basically only speaks Web Mercator. Other mapping libraries (ex. d3 as @kylebarron mentioned) are probably more fit for your needs here. There are also ways to "fool" web mercator for polar data etc.
I'm suprised that you consider this out of scope, could you elaborate on why please ? Regarding d3, do you mean d3js ? It is more focused on dataviz than on GIS from what I know ?
Also, I just found out that the mapbox team have started working on this: https://github.com/mapbox/mapbox-gl-js/issues/3184#issuecomment-831385021
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.
mapbox-gl-js recently introduced non-mercator projection support in https://github.com/mapbox/mapbox-gl-js/pull/11124
TBH this would be extremely useful for rendering rasters representing graph-like data such as rivers.
You don't want to reproject/regrid those rasters to Pseudo-Mercator as you would completely disrupt the topology.
Is there an interest in moving this forward?
I think there is interest, yes. Are you in the slack channel @iacopoff?
As far as I know web mercator is the only projection available. And it sounded like it suits peoples needs quite well. You can transform mouse positions and marker locations on-the-fly I guess. Or what would be your use case for anything different than web mercator?
Another use case is the following: In The Netherlands, the government offers a free vector tile service of the whole country, which you can include in your own web apps, However, they offer the vector tiles in RD/EPSG:28992, basically a rectangular grid introduced by Napoleon with the center in Paris. The reason is that many open data in NL is in this format too. However, I cannot use them in MapLibre and therefore need to host my own vector tiles, which is a nuisance.
@erikvullings You might be able to use addProtocol
to convert the data from one projection to another, but I'm not sure if the tiles use the regular x-y-z grid that is used by maplibre, and in that case I'm not sure what your options are.
If this something you would like to introduce to maplibre you are more than welcome to submit a PR :-)
A hint where to find the code with which tiles are read into geometries would be very helpful.
The worker that reads the data from the network: https://github.com/maplibre/maplibre-gl-js/blob/bd37870b6fcc613a021c523d4eb5aa8638d71382/src/source/vector_tile_worker_source.ts#L134C9-L134C9 Worker tile is responsible for parsing the data: https://github.com/maplibre/maplibre-gl-js/blob/bd37870b6fcc613a021c523d4eb5aa8638d71382/src/source/worker_tile.ts#L89C19-L89C31 https://github.com/maplibre/maplibre-gl-js/blob/bd37870b6fcc613a021c523d4eb5aa8638d71382/src/source/worker_tile.ts#L102
This is just the parsing of the vector tiles, if you want pointer to other part of the code, let me know and I'll look them for you. Having this in this library would be awesome, I'd love to help anyone who would like to help push this through.
Wow that was a super quick answer. Considering there is proj4js reprojecting on the fly might be reasonably doable. There might be some caveats though that I am missing. As always the devil is probably in the detail. But I am using this library for a project of mine and I need support for other projections.
Let me know if you would like me to assign a bounty for it and which bounty size you think is appropriate. Bounty direction: https://github.com/maplibre/maplibre/issues/272
In order to avoid bounty size misalignment, I would consider creating a design first detailing where this change impacts as a first bounty and then decide on the implementation bounty size.
But these are just my thoughts, let me know how you would like to proceed.
I'll have a poke around and get a debug session going for a while before committing to anything. I might be biting off more than I can chew. However, this would be a project right up my alley and expertise.
Cool, looking forward to hearing more from you.
Note that there are multiple README.md
files scattered in this repo, each one with its own info, I would advise to try and read as many of them as you need.
Thanks @HarelM ! Definitely very interested, also in the native implementation since I did quite a bit of competitive programming with C++ and I've got extensive experience with TypeScript. Would be interested in the bounty as well as the work.
However, I'll need quite some time to familiarize myself with the project first. Planning on reading the native book and some source code before I drive into a design. If someone else beats me to it I'd encourage them. Maybe it could be a friendly competition as is the spirit of FOSS.
Would this need a styles change or would projections be a part of the API and handing it to the Map options?
If it is in styles, this might need quite a bit of thought and maybe discussion as hinted in the CONTRIBUTING.md file.
I guess you could provide optional projections in the style document and specify a default. But if we are reprojecting on the fly, why not just have it part of the API and Map options for now. That would be a faster way to get to the smaller goal.
What do you think?
I think we can safely start with the API. After that (or in parallel) we can have a discussion about how it should be in the style. I think the end goal would be to define it in the style, but a solution in the API would be a very good way to test it and see how it looks and behaves.
Apologies. Haven't replied for quite a while.
This is not the highest on my priority list at the moment. So I'm happy if someone else takes over.
However, I'm still very interested in this problem and apart from work and my current side project and of course family, this is the next item on my priority list. I expect to finish my first iteration on my side project in about 4 weeks which is when I'd need the projection feature of maplibre gl js.
I'd love to experiment with the performance of this library vs. Leaflet, but my application is for a game and so currently uses Leaflet's CRS.Simple
. Support for non-Earthlike CRS would be fantastic!
I am still keen to work on this. I am time-constrained as an engaged father who also has a full time job and many commitments though so please understand that I cannot sink too much time into this.
Also, I am concerned it might be quite hard to achieve. But that is also the attraction. Plus it would help a hobby project of mine.
Would it make sense to do the 2D part of this first, since it is easier?
Is it as simple as using the proj4js library and hooking that in somewhere?
I only really need the 2D part in my project. So, this is a selfish consideration. Also, it might be easier as a hack or an extension of sorts only reprojecting on the client. My interest is in that mainly, so that existing tiles can be used.
I think an initial work on this has started as part of https://github.com/maplibre/maplibre-gl-js/issues/307 and https://github.com/maplibre/maplibre/discussions/161 2D only is a possible way, I think the projection of the vector stuff might be complicated and I'm not sure proj4js would solve everything easily, as if you only project back to lat-lon you still have issues with the poles, which is the main reason as far as I understand, people need other projections. But I'm no export on this, below are the relevant people. cc: @Pheonor @kubapelc
Hi, first of all, I've only skimmed through the discussion, so I might have missed some context. I'm implementing vector globe projection, which is a similar problem. I'm basically doing reprojection on-the-fly in the vertex shader, taking vertices in tile coordinates (basically mercator) and projecting them to a sphere. In my code, both old mercator projection and new globe projection use the same vertex buffer.
There is an important caveat though - since I'm only reprojecting the vertices of a polygon, its edges remain straight, which is a problem especially for large polygons. What is a straight edge in mercator projection will become a curve on a globe. To fix this, I first subdivide every polygon (and other features too), so that I get a better approximation of a curve when reprojecting the vertices. This subdivision step is relatively complex and it's hard to make it fast enough (otherwise tile loading gets noticeably slower).
Since proj4js seems to only reproject individual coordinates, the problem of edges not getting curved would remain. The easiest way to implement other projections would be to use my vector globe which already subdivides polygons and edges, and "just" replace the mercator->globe projection in the vertex shader with a custom projection, and possibly tweak how much subdivision happens at what zoom level. This would also mean that the reprojection code would need to be rewritten to GLSL.
Cool @kubapelc !
Thanks for reaching out.
I think reprojection can assume that only lines exist. The line segments should be small enough that curvature does not need to be implemented or if, it should be a setting that is off by default, especially if there is a performance hit.
But that is just my opinion and a thought.
Think agile and small increments. Curving lines is definitely a nice to have rather than a must imho.
Effectively, 'globe' could be consider as a specific projection and to switch between 'mercator' and 'globe' a first step on a projection class have been implemented but with very limited support.
This class should at least to encapsulate the project / unproject methods instead of direct call to mercatorXfromLng
, mercatorYfromLat
, lngFromMercatorX
and latFromMercatorY
.
My current version is based on an abstract class:
import {LngLat} from './lng_lat';
/**
* A `ProjectedPoint` represents a projected point.
* It allows to store the 3D coordinates of any point.
*/
export class ProjectedPoint {
x: number;
y: number;
z: number;
}
/**
* A `Projection` abstract class to represent a projection coordinate system.
* It could represent a 3D globe projection or any 2D projection.
* It allows to project and unproject coordinate to and from the coordinate system.
*
* @group Geography and Geometry
*/
export abstract class Projection {
name: string;
constructor(name: string) {
this.name = name;
}
abstract isGlobe(zoom: number): boolean;
getFactor(_zoom: number): number { return 0.0; }
abstract project(lng: number, lat: number, zoom: number) : ProjectedPoint;
abstract unproject(x: number, y: number, zoom: number) : LngLat;
}
But propably we should add some options
into constructor to manage projection parameters.
As @kubapelc and @geekdenz explain, the tesselation of mesh to a sufficient level coud be sufficient to transform the mercator geometry into another projection and manage the different curvature.
Regarding multiple CRS, there can be varying interpretations:
Vector tiles are created using diverse CRS, but Maplibre simply renders them as-is, specifying the CRS of the tile either through the API or in the style.
Vector tiles are produced with one or more specific CRS, and Maplibre has the capability to project them dynamically.
I believe that the first option (1) is sufficient and simpler to implement.
@pka you did something like this, right?
@pka you did something like this, right?
I did option (1) to display tiles in Equal Earth projection with MapLibre:
I'm currently investigating to make the tile grid more similar to Web Mercator, but for many applications the coordinate transformation from geographic coordinates to the tile CRS is needed. I plan to provide that for Equal Earth projection for all major map viewer libraries.
@pka note that there's a globe effort which touches some aspects of this I believe. The first PR is underway here: #3783. I would recommend seeing which part is similar and what can be used to maybe create a PR that only addresses this part without the full implementation of a globe so that this can be added regardless of the globe's progress. Meaning if we can have this feature implemented and merged while continue working on the globe in parallel, this way we bring more value to customers early on.
I am all for the simple version. Two use case - one is large no. of vector tile sets being published in our national grid system. They are the common and natural basemaps to use. No. 2 is for work in Antarctica. Web mercator is seriously misleading there.
Still waiting on this to be resolved - is there still no equivalent of Leaflet's CRS.Simple
?
There might be a way to do this in an extension. Is this correct @HarelM ?
Depending on what you would like to achieve. In theory, you could create a source that traslates to wgs84 (or via addProtocol), but I'm not sure this is the definition of supporting different CRSs...
That wouldn't work for displaying vector tiles in CRS of choice. Option 1 would be an improvement for us on this - generally want to display vector tiles in the CRS they were created in. I would be keen to know how @pka achieved that. Was this on a fork of maplibre??
For anyone looking to reproject raster tiles sticking openlayers in a custom protocol kinda works surprisingly well.
This example doesn't handle cancellation and the tile fetching is completely separate so it will have separate concurrency limits and so on.
https://jsfiddle.net/nfq6uLe1/35/
ml.addProtocol(...
That's a really nifty idea. Thanks for sharing!
Many GIS need to have the ability to render maps in different coordinate reference systems, e.g. local ones, whether for precision or for displaying data of users in their source coordinate systems. From what I know, maplibre always displays maps using the web mercator projection (EPSG:3857).
Since I'm not familiar with maplibre code, I'm wondering how hard would it be to allow displaying maps in different projection systems ? Would it require a major refactoring ?
In addition, I'm also wondering if other people would be interested by this feature and if it could be something that goes in the roadmap ?
Cheers