Decoupling pixospatial coordinate spaces from the Map for nav and broadening horizons.
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 12.4k
- Forks
- 2.4k
- PR merge metrics
- No merged PRs in 30d
Description
## Motivation
This is a revisiting of #8432 and may be useful to #10745 as well
First, I and my main customer love the terrain tech and the ability for our image overlays to drape over the terrain. That is super cool. We had seen you were experimenting with it a while back and it is in part why we chose mapbox-gl rather than leaflet as we appreciated where you are going with 3D abilities.
My own implementation of stuff is a blend of newer-school client-side tech while still leveraging map-server based image rendering over-layed on top. Thus far it has been a great marriage of abilities especially with the functionality we've built around the map to help discover things within it. We have, in fact, impressed folks as to the degree of what we have been able to do given there is no ESRI functionality in the mix.
However, going 3D presents a problem for overlaying imagery based on non-tiled map serving because in that context we need a relationship between extents and pixel dimensions.
What I've been interested in doing for supporting overlay tech within the 3D environment is to expand the rendered area of the unpitched 0-bearing map to be square and using something like the following formula:
width = height = Math.min(Math.max(3 * sd, 1.5 * ld), 4000);
// where sd = smaller dimension
// where ld = larger dimension
What that gives me is a square width and height in pixels for the desired image to render, such that it is never > 4000 x 4000 pixels
I can take that information and figure out the pixel coordinate for the sw corner and ne corner of the resulting image relative to the the map's current width and height.
But the problem is knowing what the lngLat equivalent coordinates are especially if the map has been pitched and has a non zero bearing, to get that I'll have to unpitch and unrotate the map attempt to get the coordinate then repitch and rerotate the map and hope I don't trigger a redraw, or do something to prevent that from happening even though technically I didn't move the map because I did these calculations between renderings.
Having worked with web-mapping tech for 15-20 years now it can get frustrating that extant and emerging web-tech still hasn't figured out the need for dedicated pixospatial contexts. I'm hoping your foray into 3D terrains will provide a new opportunity to articulate how pixospatial contexts could benefit both of us.
## What is a pixospatial context?
For one, it pixospatial a word I made up, others have referred to pixel-space to refer to stuff that happens on a computer. I'm playing on that to refer to the relationship between pixel coordinate space and spatial coordinate space. If you are mapping on a computer monitor there is always going to be some pixospatial relationship.
A pixospatial context, then, is view-port like in that it is a combination of pixel dimensions (width and height) and extents /LngLat bounds. Once established, there would be the ability to operate within either pixels or degrees (or whatever projected units).
## We do that already look at map.project() and map.unproject()
That's actually the problem. They are properties of Map. The pixospatial context is too tightly coupled with the map object.
For what it is worth every mapping tech I've seen couples the pixospatial context with a "map" or viewPort. But the map uses a pixospatialcontext as does any cameras as may other components. The map should not be considered the pixospatial context, it just uses one.
## The missing link
What is needed is something like turf.js but for pixosspatial contexts. Such that you can perform actions like panning and zooming and even pitching and rotating yet all you are affecting is the coordinate spaces with no coupling to anything beyond that. You do something with pixels or with LngLat values and extents and/or pixel dimensions change as appropriate.
## Well maybe you should make this request for Turf.js or d3 or library X
I may. But the problem is whatever it is you use to manipulate coordinates and to translate between pixels and degrees, I need THAT output to be in synch with your tech.
If I go out and find a library that can maintain units between pixels and degrees or web mercator meters and could do geodetic distance calculations etc it might actually better than what you use. Or it might be worse. If either were true it would mean I would not be in synch with whatever you are doing, meaning we will not be in the same coordinate space. I've leaned the hard way that each mapping tech has its own notion of things and to as much as possible leverage their engine to produce their results.
## How mapboxgl might benefit from pixospatial contexts
In #10745 I mention the need to be able to move vertically, and I babel a lot about unpitched un rotated view.
I would argue that the 3D camera is a secondary pixospatial context. And that the primary pixospatial context is the unpitched and unrotated view. I would further suggest that navigation should be possible using either and/or both and possible using pixel or lng/lat values.
What, for instance, would it mean to move 10 pixels north in a pitched and rotated map? It would mean:
const center = secondaryPSC.getCenter();
const centerPx = primaryPSC.getPxPoint(center);
centerPx.y+=10;
const loc = primaryPSC.getLngLatAt(centerPx);
primaryPSC.setCenter(loc);
secondaryPSC.setCenter(loc);
If the map is rendering based on the secondaryPSC then it it would update to the new location.
If you wanted to move 10 pixels in the bearing direction you could create a pixel polygon in turf and use its functions to transform the pixel at map center to n pixels in the bearing direction, project that and then get set the primary and secondaryPSC center to that spatial coordinate.
Where I wrote about elevation being unptiched and unrotated zoomed levelm If primary and secondary contexts are kept in synch on their common properties (map center and zoom level), then moving up is setting the primaryPSC's zoom level and then updating secondaryPSC based on applying pitch and bearing after applying primaryPSC's center and zoom and then updating the map context to the updated secondaryPSC.
## Map overlay use case
For my use case, I want to be able to maintain an overlay layer spatial context so that I have pixel dimensions to go with the LngLat coordinates for the overlay definition and such that I can use the pixospatial context to change the size using pixels rather than lngLat values. And I would monitor changes to the map's idle state to get the map/primaryPSC's zoom level and center so I can apply that to my layers' PSCs. When unpitched and unrotated I would use the map PSC's dimensions and extents, otherwise I'd apply my formula or similar formulae to guarantee there is less clipping at corners or toward the distance.
Technically, I want to share the same pixospatial context object instance across all of my overlay layers. But I want it to be decoupled from the map rendering and only modified by my controller code when the main map context changes.
## Design Alternatives
If you are asking me what the alternatives are, I would say investing in aspirin or other headache relievers because this is the darker side of screen based mapping.
The major advantage of mapbox-gl and similar libraries is that I could train someone fresh out of highschool with a minimum talent in JavaScript in how to make a map show up and how to get a coordinate under the point. Because you are that good.
However, once we are getting into programmatic aspect of things and having to know about projections and localized distortions and how they affect pixel dimensions etc. that can burn out even a senior developer.
On top of that throw in 3D and terrains and the complexity shoots off the charts if you are trying to do the math independent of the 3D tech. In fact I doubt y'all are doing that math either and that you are leveraging libraries and/or gpu functionality, which is great, I just want a little of that mojo too.
If you want mapbox-gl to remain easy to code for and to be able to bring other valuable visual information into the map, the ability to think in pixels rather than just in spatial coordinate spaces is critically important.
## Design
1) implement the notion of a pixospatial context. You could call it something simpler like coordSpace or something like that.
2) maintain a north2D (unpitched 0-bearing) coordSpace which maintains the map's current extents, center, and zoom level.
3) maintain one or more camera coordSpace instances. The camera coordSpace is where pitch and bearing are applied.
4) The current map functions for set/get center, set/get zoom would set the north2D coordSpace and any related cameras would also be updated.
5) any get/set bearing or get/set pitch commands on the map object would affect the current camera.
6) functions woudl be added to get/set the current camera
7) functions would be added to add/remove cameras (coordSpace instances).
### Mock-Up
(psudeo typescript)
const space = new mapboxgl.coordSpace( new LngLatBounds([-180,-90],[180,90]]) , 4000, 2000,map.getProjection());
space.setZoom(11);
space.panBy({x:25,y:0);
space.resize({x: 2000, y:2000, anchor: 'center');
const extents = space.getBounds();
map.setCoordSpace(space);
To be honest I don't want to overly specify what all should be possible initially. The idea though is to do much of what you can do to change the map view programmatically on Map right now sans all the ui-based interaction and event concerns.
And then over time the api could be expanded to allow for a fuller range of abilities to do via point or lnglat objects perhaps wrapping turf functionality with functions which can translate between pixel and spatial units and do more interesting translations of the the coordSpace.
As far as the end user is concerned it will not necessarily look much different except that image overlayers like me can provide more content to accurately drape over the cool new terrain.
For developers this would open some doors based on multi-view programmatic innovations. of which I can speak to its potential which is more limited by imagination and my ability to articulate that potential.
### Concepts
I think the lecture above about pixospatial more or less covers the concepts.
There may be better terminology to use, like coordSpace.
The only advantage of "pixospatial" as a term is that if we popularize it enough it would make an awesome scrabble word if you could add pixo to spatial. or pixo and ial to spat to scrabble ;).
As far as precedents are concerned ...
This is somewhat like the notion of auto-cad having several simultaneous viewports open at the same time so you can see things you are creating in 3D from several angles at the same time.
Although you could do something similar with mapboxgl right now it would be a little easier if you could send a coordSpace to the different maps and have them update from that.
It is different than that in that the coordSpace is decoupled from the map so you don't quite want to confuse viewPort with coordSpace because technically you aren't viewing anything.
It is also somewhat like having a context map and the 3D map at the same time but being able to navigate on either. Except, you don't have to have the context map displayed or as a DOM element in order to navigate in that space. Similarly the context map doesn't have to have the same dimensions as the current view because it is its own coordinate space.
The new precedents is that you would be able to think of doing what you would normally do on the main map in unmapped virtual spaces that can, if you choose to, affect the map view or to obtain coordinates from for doing spatial operations with. A spatial operation could be a data query or it could be rendering an image overlay. Or it could be positioning a drone, or whatever you can think to do with that information.
So, if you had a drone providing live video feed and the width and height and coordinates of the footage you could maintain a coordSpace for that and either move the map based on that space or move the drone n pixels "left" from a given pixel in the lower-right quadrant of its video feed, Then you you center the map on the lngLat value for that pixel so that the drone feed is now in view and overlay the video from the drone draped on 3D terrain in a pitched and rotated map.
Here's a good example objective ... being able to do the existing video overlay demo on the 3D terrain but then be able to move a marker using the arrow keys using the video feed's coordinate space as the x/y coordinates. where 0,0 is the upper left corner of the video and video.width, video.height is the lower right pixel.
If the rotation of the video is a bit hard to tackle first, then try one of your image overlay demos but on a 3D terrain pitched and rotated and move a marker in the image's pixel space, including allowing for going beyond the bounds of the image.
Ask yourself if or how would you do that now with the current API? And, can you see how this pixospatial notion might be useful in that context?
### Implementation
I think I've covered the general aspects of how it would be implemented, or at least what would need to change. It is generally a programming task of decoupling the pixospatial concepts out of the map and into their own classes. Then, making the map work with the new class(es). If done per the numbered list in an earlier section above you would maintain backward compatibility while expanding horizons ... which kind of literally is what I'm trying to do with my overlays.
I think the "fringe cases" here is more about how, currently, do you show the data that is at the fringes of the 3D terrain views. The fog tech is a great way to mask things at a distance, but doesn't solve the original problem here when you pitch a view how does an image overlayer like me know how to change the extents for my desired pixel dimensions relative to the initial dimensions?
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 existing Map.project() and Map.unproject() APIs, along with the current center, zoom, pitch, bearing, and navigation behavior described in the issue. Clarify the proposed coordSpace API and its relationship to the map and camera contexts before implementation; done should include an agreed scope and corresponding behavior for independent pixel and spatial coordinate operations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- 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