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

Support rendering in multiple CRS #168

Open
jailln opened this issue Jun 8, 2021 · 32 comments
Open

Support rendering in multiple CRS #168

jailln opened this issue Jun 8, 2021 · 32 comments
Labels
enhancement New feature or request PR is more than welcomed Extra attention is needed

Comments

@jailln
Copy link

jailln commented Jun 8, 2021

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

@wipfli
Copy link
Member

wipfli commented Jun 8, 2021

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?

@kylebarron
Copy link
Contributor

kylebarron commented Jun 8, 2021

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).

image

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.

@davenquinn
Copy link

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.

@jailln
Copy link
Author

jailln commented Jun 15, 2021

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).

image

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: mapbox/mapbox-gl-js#3184 (comment)

@github-actions
Copy link
Contributor

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 github-actions bot added Stale and removed Stale labels Oct 15, 2021
@PacoDu
Copy link

PacoDu commented Oct 22, 2021

mapbox-gl-js recently introduced non-mercator projection support in mapbox/mapbox-gl-js#11124

@iacopoff
Copy link

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?

@wipfli
Copy link
Member

wipfli commented Mar 15, 2022

I think there is interest, yes. Are you in the slack channel @iacopoff?

@wipfli
Copy link
Member

wipfli commented Mar 15, 2022

@HarelM HarelM added enhancement New feature or request PR is more than welcomed Extra attention is needed labels Aug 17, 2022
@erikvullings
Copy link

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.

@HarelM
Copy link
Member

HarelM commented Jun 22, 2023

@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 :-)

@geekdenz
Copy link

geekdenz commented Aug 6, 2023

A hint where to find the code with which tiles are read into geometries would be very helpful.

@HarelM
Copy link
Member

HarelM commented Aug 6, 2023

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

const feature = sourceLayer.feature(index);

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.

@geekdenz
Copy link

geekdenz commented Aug 6, 2023

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.

@HarelM
Copy link
Member

HarelM commented Aug 6, 2023

Let me know if you would like me to assign a bounty for it and which bounty size you think is appropriate.
Bounty direction:
maplibre/maplibre#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.

@geekdenz
Copy link

geekdenz commented Aug 6, 2023

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.

@HarelM
Copy link
Member

HarelM commented Aug 6, 2023

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.

@geekdenz
Copy link

geekdenz commented Aug 7, 2023

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.

@geekdenz
Copy link

geekdenz commented Aug 7, 2023

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?

@HarelM
Copy link
Member

HarelM commented Aug 8, 2023

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.

@geekdenz
Copy link

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.

@eslindsey
Copy link

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!

@geekdenz
Copy link

geekdenz commented Jan 21, 2024

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.

@HarelM
Copy link
Member

HarelM commented Jan 21, 2024

I think an initial work on this has started as part of #307 and maplibre/maplibre#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

@kubapelc
Copy link
Contributor

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.

@geekdenz
Copy link

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.

@Pheonor
Copy link
Contributor

Pheonor commented Jan 21, 2024

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.

@exotfboy
Copy link

exotfboy commented Mar 4, 2024

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.

@wipfli
Copy link
Member

wipfli commented Mar 4, 2024

@pka you did something like this, right?

@pka
Copy link

pka commented Mar 4, 2024

@pka you did something like this, right?

I did option (1) to display tiles in Equal Earth projection with MapLibre:

image

(Screencast)

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.

@HarelM
Copy link
Member

HarelM commented Mar 6, 2024

@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.

@scaddenp
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request PR is more than welcomed Extra attention is needed
Projects
None yet
Development

No branches or pull requests