Map-independent projection/unprojection
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 12.4k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Description
This is opened in response to [this comment](https://github.com/mapbox/mapbox-gl-js/pull/5662#issuecomment-344646887) on PR #5662, suggesting I open an issue to discuss this use case.
### My Use Case
I have a react app which uses a map to geographically filter lots of results which appear in a sidebar – think [Zillow-style navigation](https://www.zillow.com/homes/for_sale/globalrelevanceex_sort/32.952215,-116.83033,32.372422,-117.451058_rect/10_zm/).
As is the idiom of react and other compositional frameworks, data flows unidirectionaly from the outermost component down the heirarchy of children. My goal here is to be able to calculate a GeoJSON polygon that represents the visible region of the globe, independant of actually rendering the map component.
Given the center point (Lng/Lat), zoom, pitch, bearing (all of which are stored in the URL), and the screen resolution, I need to get a `LngLat` for a given `Point` on the screen.
It's important that it's seperate from the rendered map for a few reasons:
1. This needs to be calculated to populate the list during server side rendering, where we can't actually draw the map.
2. We need to calculate this in different places:
- API calls over-fetch (essentially using a smaller zoom)
- Client side filtering considers panels that cover part of the map
3. OCD and the fact that we've maintained unidirectional data flow elsewhere 😉
Of course, the resulting polygon must be identical to the field of view in the map itself. This would be trivial, except for pitch and bearing.
### Initial Design
My initial strategy was to use the same `Transform` class used internally by the map. This worked quite well in the browser, but my [PR to export it](https://github.com/mapbox/mapbox-gl-js/pull/5662#issuecomment-344646887) exposed more internals than was desired. Additionally, when I began using it server-side, its requirement for WebGL broke in our current SSR strategy.
Here's the kind of thing I would do using the `Transform` class:
```es6
const transform = new Transform();
transform.center = { lng: geoLongitude, lat: geoLatitude };
transform.bearing = geoBearing;
transform.pitch = geoPitch;
transform.resize(window.innerWidth, window.innerHeight);
// get bounds for a search query
transform.zoom = geoZoom - 1;
const nw = transform.pointLocation(new Point(0, 0)).toArray();
const ne = transform.pointLocation(new Point(window.innerWidth, 0)).toArray();
const se = transform.pointLocation(new Point(window.innerWidth, window.innerHeight)).toArray();
const sw = transform.pointLocation(new Point(0, window.innerHeight)).toArray();
const coordinates = [[nw, ne, se, sw, nw]];
// figure out the "visible" center, taking into consideration the list and panels
transform.zoom = geoZoom;
const [ visibleLongitude, visibleLatitude ] = transform.pointLocation(new Point((transform.width + 654) / 2, transform.height / 2)).toArray();
```
Is there any reasonable way to accomplish this without the heavy internals of `Transform`?
Thanks!
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing the Transform class and its pointLocation usage, along with PR #5662 and the linked comment, to understand the requested separation from map rendering and the WebGL dependency. Define how projection and unprojection should accept the center, zoom, pitch, bearing, viewport size, and screen points, then verify that the resulting polygon and offset-center calculations match the rendered map and work for server-side rendering.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, react
- Domain
- computer-graphics, frontend, web-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100