Can turfjs provide a coordinate transform between geo coords and application coords - coordinates

I need a transform from geo coord system to/from another coord system.
I'd think the obvious way to do this would be to give the two bounding boxes of the systems.
So if I had a geo bbox, in lon/lat coords, and a non-geo bbox that corresponds to that but in my own coordinates, I'd like a xfm that could convert geo-to-me and me-to-geo.
I need this for an agent based programming environment that has its own coord system based on minX, minY, maxX, maxY, just as Turf expresses bboxes. It would also work for transforming between geo coords and pixels in a canvas, for example.
I couldn't find such a coord transform in Turf but I may be missing it, or there may be an easy way to do it with Turf primitives.
Is there a way to use Turf for such a coord transform?

I'd use proj4js which handles projection conversions.
Your canvas can be represented in Pseudo-Mercator ([x,y]) while turf will continue working in WGS84 ([lon, lat]).
const xyCoordsToLatLong = (xy_pair) => {
return proj4(PSEUDO_MERC_DATUM, WGS84_DATUM, xy_pair);
}
const latLongCoordsToXY = (latlong_pair) => {
return proj4(WGS84_DATUM, PSEUDO_MERC_DATUM, latlong_pair);
}
Then, a canvas of bbox [0,0,500,200] would be represented as [0, 0, 0.004491576420597608, 0.0017966305679443192].
Full demo here.

Related

Swift: What matrix should be used to convert 3D point to 2D in ARKit/Scenekit

I am trying to use ARCamera matrix to do the conversion of 3D point to 2D in ARkit/Scenekit. Previously, I used projectpoint to get the projected x and y coordinate which is working fine. However, the app is significantly slowed down and would crash for appending long recordings.
So I turn into another approach: using the ARCamera parameter to do the conversion on my own. The Apple document for projectionMatrix did not give much. So I looked into the theory about projection matrix The Perspective and Orthographic Projection Matrix and Metal Tutorial. From my understanding that when we have a 3D points P=(x,y,z), in theory we should be able to just get the 2D point like so: P'(2D)=P(3D)*projectionMatrix.
I am assuming that's would be the case, so I did:
func session(_ session: ARSession, didUpdate frame: ARFrame) {
guard let arCamera = session.currentFrame?.camera else { return }
//intrinsics: a matrix that converts between the 2D camera plane and 3D world coordinate space.
//projectionMatrix: a transform matrix appropriate for rendering 3D content to match the image captured by the camera.
print("ARCamera ProjectionMatrix = \(arCamera.projectionMatrix)")
print("ARCamera Intrinsics = \(arCamera.intrinsics)")
}
I am able to get the projection matrix and intrinsics (I even tired to get intrinsics to see whether it changes) but they are all the same for each frame.
ARCamera ProjectionMatrix = simd_float4x4([[1.774035, 0.0, 0.0, 0.0], [0.0, 2.36538, 0.0, 0.0], [-0.0011034012, 0.00073593855, -0.99999976, -1.0], [0.0, 0.0, -0.0009999998, 0.0]])
ARCamera Intrinsics = simd_float3x3([[1277.3052, 0.0, 0.0], [0.0, 1277.3052, 0.0], [720.29443, 539.8974, 1.0]])...
I am not too sure I understand what is happening here as I am expecting that the projection matrix will be different for each frame. Can someone explain the theory here with projection matrix in scenekit/ARKit and validate my approach? Am I using the right matrix or do I miss something here in the code?
Thank you so much in advance!
You'd likely need to use the camera's transform matrix as well, as this is what changes between frames as the user moves around the real world camera and the virtual camera's transform is updated to best match that. Composing that together with the projection matrix should allow you to get into screen space.

Can map.getBounds be executed for a different coordinate system?

I execute the following code in my leaflet webmap
map.getBounds().getWest() + "&y1=" +
map.getBounds().getSouth() + "&x2=" +
map.getBounds().getEast() + "&y2=" +
map.getBounds().getNorth()
This results in a result showing me four coordinates in the WGS84 (standard) coordinate system.
Is there any way to alter this so it will output 28992 coordinates instead?
I guess that by "28992 coordinates" you're referring to the EPSG:28992 Coordinate Reference System.
The canonical way to use "non-standard" CRSs in Leaflet is to leverage proj4leaflet. This answer assumes that you're already doing so.
So the getBounds() method of L.Map always returns a L.LatLngBounds instance, which refer to unprojected WGS84 coordinates. However, we can use the map's CRS to project a L.LatLng into a L.Point with the projected coordinates, in the map's display CRS; e.g.
var map = L.map('containerId`, { crs: crsForEpsg28992 });
var foo = map.options.crs.project(L.latLng([60.3,21.1]));
var qux = map.options.crs.project(map.getCenter());
Because of how map projections work (they rotate and bend the coordinate spaces), and because of how proj4js is implemented, it's not possible to project a bounding box into a bounding box. (In most cases, the projection of a bounding box would be a curved polygon!). This image from an article by Gregor Aisch illustrates the issue:
We can, however, do an approximation: project the four corners of the bounding box, e.g.:
var mapBounds = map.getBounds();
var crs = map.options.crs;
var nw = crs.project(mapBounds.getNorthWest());
var ne = crs.project(mapBounds.getNorthEast());
var sw = crs.project(mapBounds.getSouthWest());
var se = crs.project(mapBounds.getSouthEast());
We can even create a L.Bounds (but not a L.LatLngBounds!) from those projected coordinates; that'll be a bbox in the specified CRS that contains all corners, e.g.:
var bbox = L.bounds([nw, ne, sw, se]);
It's not gonna be perfect, but that approximation should be enough for most use cases.
See also this working example (based off on one of the proj4leaflet examples), which should further illustrate the issue.

How to calculate location’s long/lat based on its bbox coordinates

please could anyone help?
I need to use a map.toFly() method to interpolate between 2 locations.
According to the Mapbox documentation, I need to pass in an object describing the destination I want to fly to. The object has to have a center property holding an array with centre Long/lat coordinates of the destination I need to be taken to.
https://docs.mapbox.com/mapbox-gl-js/example/flyto/
My problem with implementing the method is that I only have bounding box coordinates of the 2 locations between which I need to interpolate . I can’t do something like this:
map.flyTo(bbox)
Does anyone know how to obtain
centre Long/lat coordinates of each location based on their bbox coordinates?
Assuming you have 2 LngLatBounds objects you can call the getCenter() method.
var point1 = bounds1.getCenter();
var point2 = bounds2.getCenter();
where both bounds1 and bounds2 are objects of the type LngLatBounds.
Check:
https://docs.mapbox.com/mapbox-gl-js/api/#lnglatbounds#getcenter
Edit: for the values you gave in your comment it would be for the first bounds:
var sw1 = new mapboxgl.LngLat(110.2672863, -7.1144639);
var ne1 = new mapboxgl.LngLat(110.5088836, -6.9319917);
var bounds1 = new mapboxgl.LngLatBounds(sw1, ne1);
Note: Mapbox GL uses longitude, latitude coordinate order (as opposed to latitude, longitude).

Can I transform the embedded interactivity coordinate system result from lat lon to epsg 3857 (web mercator)?

When using the embedded interactivity embedded interactivity function on the iOS SDK I can click a map tile and get the data coords in LAT LON but I would like to have the result in EPSG 3857 WGS 84...is this possible? If so what is the transform command?
Looks like I am going to answer my own question. I could not find a transform function. So I passed the LAT LONG coordinate pair to the webservice and did the transformation on the server.
You can do this with Leaflet/Mapbox.
Here's an example
var latlng = L.latLng(45, -120);
var sphericalMercator = L.Projection.SphericalMercator.project(latlng);
sperhicalMercator.x => -2.0943951023931953
sphericalMercator.y => 0.8813735870195429
You'll still need to multiply these coordinates by 6378137 (earth's radius in meters) (why Leaflet doesn't do this, I don't know...), and you get the spherical mercator equivalent to the original lat/long (45, -120):
(-13358338.8952, 5621521.48619)

How to draw real world coordinates rotated relative to the device around a center coordinate?

I'm working on a simple location-aware game where the current location of the user is shown on a game map, as well as the locations of other players around him. It's not using MKMapView but a custom game map with no streets.
How can I translate the other lat/long coordinates of other players into CGPoint values to represent them in the world scale game map with a fixed scale like 50 meters = 50 points in screen, and orient all the points such that the user can see in which direction he would have to go to reach another player?
The key goal is to generate CGPoint values for lat/long coordinates for a flat top-down view, but orient the points around the users current location similar to the orient map feature (the arrow) of Google Maps so you know where is what.
Are there frameworks which do the calculations?
first you have to transform lon/lat to cartesian x,y in meters.
next is the direction in degrees to your other players. the direction is dy/dx where dy = player2.y to me.y, same for dx. normalize dy and dx by this value by dividing by distance between playerv2 and me.
you receive
ny = dy / sqrt(dx*dx + dy*dy)
nx = dx / sqrt(dx*dx + dy*dy)
multipl with 50. now you have a point 50 m in direction of the player2:
comp2x = 50 * nx;
comp2y = 50 * ny;
now center the map on me.x/me.y. and apply the screen to meter scale
You want MKMapPointForCoordinate from MapKit. This converts from latitude-longitude pairs to a flat surface defined by an x and y. Take a look at the documentation for MKMapPoint which describes the projection. You can then scale and rotate those x,y pairs into CGPoints as needed for your display. (You'll have to experiment to see what scaling factors work for your game.)
To center the points around your user, just subtract the value of their x and y position (in MKMapPoints) from the points of all other objects. Something like:
MKMapPoint userPoint = MKMapPointForCoordinate(userCoordinate);
MKMapPoint otherObjectPoint = MKMapPointForCoordinate(otherCoordinate);
otherObjectPoint.x -= userPoint.x; // center around your user
otherObjectPoint.y -= userPoint.y;
CGPoint otherObjectCenter = CGPointMake(otherObjectPoint.x * 0.001, otherObjectPoint.y * 0.001);
// Using (50, 50) as an example for where your user view is placed.
userView.center = CGPointMake(50, 50);
otherView.center = CGPointMake(50 + otherObjectCenter.x, 50 + otherObjectCenter.y);