Cesium convert lat lon to x and y - cesiumjs

I am looking to turn 2 lat/lon positions into an x and y distance of the canvas, then apply the distance formula to it.
Right now I have:
const leftPoint = new LatLon(center.lat, center.lon).destinationPoint(semiMajorAxis, 270);
const rightPoint = new LatLon(center.lat, center.lon).destinationPoint(semiMajorAxis, 90);
const leftXY = Cartographic.toCartesian(Cartographic.fromDegrees(leftPoint.lon, leftPoint.lat));
const rightXY = Cartographic.toCartesian(Cartographic.fromDegrees(rightPoint.lon, rightPoint.lat));
const diameter = distanceFormula(leftXY.x, leftXY.y, rightXY.x, rightXY.y);
But the result of diameter is 18,000, even though both points are on my screen!

Cesium's Cartographic.toCartesian function converts a Cartographic (lon/lat/alt) type of coordinate to a full 3D Cartesian position. Imagine X, Y, Z with zero being the center of the Earth itself, with the Earth's surface being approximately 6.3 million meters in any direction.
If you're looking for 2D canvas / screen coordinates, you must follow this call with another function, Cesium.SceneTransforms.wgs84ToWindowCoordinates. That function converts the 3D WGS84 (Cartesian3) Earth position into a 2D (Cartesian2) screen position. There's a demo of wgs84ToWindowCoordinates being used in the Sandcastle Star Burst Example around line 287.
Also it looks like you've rolled your own LatLon class, not specified above, that appears to have similar functions to Cesium's Cartographic class. You might be able to make the code a little cleaner by using Cartographic directly instead of a homebrew class there. Likewise you don't need to roll your own distanceFormula on the last line. Once you have 2D Cartesian2 window coordinates, call Cesium.Cartesian2.distance to get the distance.

I can't understand your saying 'x and y distance of the canvas'.
Generally, for calculate distance between two point on CesiumJS follow below steps.
1.Define two points
//Define x,y coordinate and convert to radian
const longitudeRadian_1 = Cesium.Math.toRadians(longitudeDegree_1)
const latitudeRadian_1 = Cesium.Math.toRadians(latitudeDegree_1)
const longitudeRadian_2 = Cesium.Math.toRadians(longitudeDegree_2)
const latitudeRadian_2 = Cesium.Math.toRadians(latitudeDegree_2)
//Get cartographic from degrees
const Carto_Point_1 = new Cesium.Cartographic(longitudeRadian_1 , latitudeRadian_1 )
const Carto_Point_2 = new Cesium.Cartographic(longitudeRadian_2 , latitudeRadian_2)
//Get cartesian from cartographic
const Cartesian_Point_1 = Cesium.Cartographic.toCartesian(Carto_Point_1)
const Cartesian_Point_2 = Cesium.Cartographic.toCartesian(Carto_Point_2)
2.Calculate distance between two points
const distance = Cesium.Cartesian3.distance(Cartesian_Point_1, Cartesian_Point_2)
console.log(distance)
I hope this would help

Related

How to move the camera in a forge viewer to face the true north direction, using the euler angles

I understand that forge viewer uses three.js extensively, I have a couple of questions
I want to point my forge viewer camera to the north direction (true north) and further synchronise the rotation based on the north values.
Also is it possible to set the bounds ?
I'm trying to synchronise forge viewer based on a set of euler angles (pitch, yaw and roll) available at my hand
I'm using the forge viewer version 7.
Fareed also asked this via email, so I'm copying & pasting my replies here.
Not sure which source model format you used, so suppose it's Revit (RVT).
In Revit model metadata, two attributes can help calculate the north rotation to the true north.
metadata['world north vector']['XYZ']: The project north vector of the Revit view.
metadata['custom values']['angleToTrueNorth']: The angel from the project north to true north of Revit view.
// Calculate project north angle
const projectNorthVector = new THREE.Vector3().fromArray( model.getData().metadata['world north vector']['XYZ'] );
const autoCam = viewer.autocam;
const frontDirection = autoCam.sceneFrontDirection.clone(); //!<<< viewer world north
const upVector = autoCam.sceneUpDirection.clone();
let crossVector = new THREE.Vector3();
crossVector.crossVectors( frontDirection, projectNorthVector );
const projectNorthAngle = projectNorthVector.angleTo( frontDirection ) * ( crossVector.dot( upVector ) < 0 ? -1 : 1 );
// Calculate true north angle
let trueNorthAngle = metadata['custom values']['angleToTrueNorth'] * (Math.PI / 180);
// Final rotation angle from viewer world north to true north
const finalRotationAngle = projectNorthAngle + trueNorthAngle;
// and then rotate your vector by Z.
Sorry, I'm not familiar with the Euler angles (pitch, yaw, and roll), but from three.js documentation, I can see it uses intrinsic Tait-Bryan angles.
Three.js uses intrinsic Tait-Bryan angles. This means that rotations are performed with respect to the local coordinate system. That is, for order 'XYZ', the rotation is first around the local-X axis (which is the same as the world-X axis), then around local-Y (which may now be different from the world Y-axis), then local-Z (which may be different from the world Z-axis).
So, probably, you can try to get that with the either way below:
Use three.js API to get Euler Tait-Bryan angles from quaternion
const quaternion = viewer.getCamera().quaternion.clone();
const rotation = new THREE.Euler().setFromQuaternion( quaternion, 'XYZ' );
Or get it from the camera's rotation
const { rotation } = viewer.getCamera();
const eulerOrder = rotation.order;
Or refer to the Navisworks approach: https://adndevblog.typepad.com/aec/2019/07/get-roll-value-of-edit-current-viewpoint.html
viewer.navigation.setCameraUpVector( new THREE.Vector3(0,1,0), true );
const quaternion = viewer.getCamera().quaternion.clone();
let { x, y, z, w } = quaternion;
let roll = Math.atan2(2*y*w - 2*x*z, 1 - 2*y*y - 2*z*z);
let pitch = Math.atan2(2*x*w - 2*y*z, 1 - 2*x*x - 2*z*z);
let yaw = Math.asin(2*x*y + 2*z*w);
To set Euler angles to camera, here is an approach, but I think you will need to change the Euler order if it's not XYZ.
const euler = new THREE.Euler(..., ..., ..., 'XYZ');
viewer.getCamera().quaternion..setFromEuler(euler);

Projection drift when rendering in WebGL over Google Map

I am trying to implement a WebGL-based rendering on Google Map (api3) as I want to render a massive amount of dynamic geometries.
Basically, I create a google.maps.OverlayView attached with a WebGL canvas into the map.
However, I encountered some problem with the mapping of the projection. Basically, I extracted the "fromLatLngToPoint" function from the googlemap api as follows:
function fromLatLngToPoint(a){
var c={x:0,y:0},
d=this.j;
c.x=d.x+a.lng*this.B;
var e=oe(m.sin(re(a.lat)),-(1-1E-15),1-1E-15);
c.y=d.y+.5*m.log((1+e)/(1-e))*-this.F;
return c
}
function oe(a,b,c){null!=b&&(a=m.max(a,b));null!=c&&(a=m.min(a,c));return a}
function re(a){return m.PI/180*a}
Then I implemented it in my vertex shader based on the documentation in Google Map Coordinates.
Basically, I have a event listener to send the updated projection constants, the viewport bounds, and the zoom level to my shader.
Then my shader will calculate the new screen coordinates based on these inputs.
highp float e, x, y, offsetY, offsetX;
// projection transformation for target points
e = sin(p.y* PI/180.0);
y = prj_y + 0.5 * log((1.0+e)/(1.0-e))*(-F);
x = prj_x + p.x*B;
// projection transformation for offset (bounds)
e = sin(bound_y*PI/180.0);
offsetY = prj_y + 0.5 * log((1.0+e)/(1.0-e))*(-F);
offsetX = prj_x + bound_x*B;
// calculate actual pixel coord wrt zoom/numTiles
x = (x* numTiles - offsetX* numTiles);
y = (y* numTiles - offsetY* numTiles);
gl_PointSize = 5.0;
gl_Position = projectionMatrix * modelViewMatrix * vec4(x,y,0.0,1.0);
However, as shown in the screenshot below, it seems there are some errors? The rendered geometries are distorted. (I used the google map polygon api to render some of the geometries as comparison)
Screenshot Here
I am totally at a loss, what might be the reason for this distortion?
I am suspecting that the single precision in the shader is giving rise to the error. So I am wondering if there is any workaround?
It is hard to debug this piece of code and diagnose the cause of the issue. I would suggest you using the CanvasLayer library that hides all these concrete details of specifying the coordinates you want to draw the polygon. Rather you would be able to focus on your app code and functionality. The performance will be better in terms of projected image.

Specify a Google Maps Static image with borders/viewport specified by lat and lon coordinates

I am trying to request an image from the Google Static Maps API with the borders of the map specified by a pair of latitude and longitude coordinates. I've tried centering on the center of the two coordinates, but there doesn't seem to be any parameter for doing this with the Static API. Does anyone know how to do this?
Note: this is for a desktop application, and I am not using the Javascript API.
The thing is that you cannot base the request on the map's corners because 1. zoom levels are discrete values and 2. the amount of latitude that a pixel represents varies with latitude. So, to display 2 degrees you'll need a given map height near the equator and a different height, (greater), near the poles. Are you willing to display maps of different heights in order to fit always 2 degrees?
If so, you can use the MercatorProjection object from my other post, and use the following function to calculate the necessary map size:
<script src="MercatorProjection.js"></script>
function getMapSize(center,zoom){
var proj = new MercatorProjection();
var scale = Math.pow(2,zoom);
var centerPx = proj.fromLatLngToPoint(center);
var SW = new google.maps.LatLng(center.lat()-1,center.lng()-1);
var southzWestPx = proj.fromLatLngToPoint(SW);
var NE = new google.maps.LatLng(center.lat()+1,center.lng()+1);
var northEastPx = proj.fromLatLngToPoint(NE);
// Then you can calculate the necessary width and height of the map:
var mapWidth = Math.round((northEastPx.x - southzWestPx.x) * scale);
var mapHeight = Math.round((southzWestPx.y - northEastPx.y) * scale);
}
With center = new google.maps.LatLng(49.141404, -121.960988) and zoom = 7 you get that you need a map of (W x H) 182 x 278 pixels in order to display 2 x 2 degrees.

How do I know if a Lat,Lng point is contained within a circle?

Ok pretty self explanatory. I'm using google maps and I'm trying to find out if a lat,long point is within a circle of radius say x (x is chosen by the user).
Bounding box will not work for this. I have already tried using the following code:
distlatLng = new google.maps.LatLng(dist.latlng[0],dist.latlng[1]);
var latLngBounds = circle.getBounds();
if(latLngBounds.contains(distlatLng)){
dropPins(distlatLng,dist.f_addr);
}
This still results in markers being places outside the circle.
I'm guess this is some simple maths requiring the calculation of the curvature or an area but I'm not sure where to begin. Any suggestions?
Unfortunately Pythagoras is no help on a sphere. Thus Stuart Beard's answer is incorrect; longitude differences don't have a fixed ratio to metres but depend on the latitude.
The correct way is to use the formula for great circle distances. A good approximation, assuming a spherical earth, is this (in C++):
/** Find the great-circle distance in metres, assuming a spherical earth, between two lat-long points in degrees. */
inline double GreatCircleDistanceInMeters(double aLong1,double aLat1,double aLong2,double aLat2)
{
aLong1 *= KDegreesToRadiansDouble;
aLat1 *= KDegreesToRadiansDouble;
aLong2 *= KDegreesToRadiansDouble;
aLat2 *= KDegreesToRadiansDouble;
double cos_angle = sin(aLat1) * sin(aLat2) + cos(aLat1) * cos(aLat2) * cos(aLong2 - aLong1);
/*
Inaccurate trig functions can cause cos_angle to be a tiny amount
greater than 1 if the two positions are very close. That in turn causes
acos to give a domain error and return the special floating point value
-1.#IND000000000000, meaning 'indefinite'. Observed on VS2008 on 64-bit Windows.
*/
if (cos_angle >= 1)
return 0;
double angle = acos(cos_angle);
return angle * KEquatorialRadiusInMetres;
}
where
const double KPiDouble = 3.141592654;
const double KDegreesToRadiansDouble = KPiDouble / 180.0;
and
/**
A constant to convert radians to metres for the Mercator and other projections.
It is the semi-major axis (equatorial radius) used by the WGS 84 datum (see http://en.wikipedia.org/wiki/WGS84).
*/
const int32 KEquatorialRadiusInMetres = 6378137;
Use Google Maps API geometry library to calculate distance between circle's center and your marker, and then compare it with your radius.
var pointIsInsideCircle = google.maps.geometry.spherical.computeDistanceBetween(circle.getCenter(), point) <= circle.getRadius();
It's very simple. You just have to calculate distance between centre and given point and compare it to radius. You can Get Help to calculate distance between two lat lang from here
The following code works for me: my marker cannot be dragged outside the circle, instead it just hangs at its edge (in any direction) and the last valid position is preserved.
The function is the eventhandler for the markers 'drag' event.
_markerDragged : function() {
var latLng = this.marker.getPosition();
var center = this.circle.getCenter();
var radius = this.circle.getRadius();
if (this.circleBounds.contains(latLng) &&
(google.maps.geometry.spherical.computeDistanceBetween(latLng, center) <= radius)) {
this.lastMarkerPos = latLng;
this._geocodePosition(latLng);
} else {
// Prevent dragging marker outside circle
// see (comments of) http://unserkaiser.com/code/google-maps-marker-check-if-in-circle/
// see http://www.mvjantzen.com/blog/?p=3190 and source code of http://mvjantzen.com/cabi/trips4q2012.html
this.marker.setPosition(this.lastMarkerPos);
}
},
Thanks to http://unserkaiser.com/code/google-maps-marker-check-if-in-circle/
and http://www.mvjantzen.com/blog/?p=3190 .
I've been a bit silly really. Thinking about it we can use Pythagorus' theorem.
We have a maximum distance away from a point (X miles), and two latitudes and two longitudes. If we form a triangle using these then we can solve for the distance from the point.
So say we know point1 with coordinates lat1,lng1 is the center of the circle and point2 with coordinates lat2,lng2 is the point we are trying to decide is in the circle or not.
We form a right angled triangle using a point determined by point1 and point2. This, point3 would have coordinates lat1,lng2 or lat2,lng1 (it doesn't matter which). We then calculate the differences (or if you prefer) distances - latDiff = lat2-lat1 and lngDiff = lng2-lng1
we then calculate the distance from the center using Pythagorus - dist=sqrt(lngDiff^2+latDiff^2).
We have to translate everything into meters so that it works correctly with google maps so miles are multiplied by 1609 (approx) and degrees of latitude/longitude by 111000 (approx). This isn't exactly accurate but it does an adequate job.
Hope that all makes sense.

Constructing a triangle based on Coordinates on a map

I'm constructing a geolocation based application and I'm trying to figure out a way to make my application realise when a user is facing the direction of the given location (a particular long / lat co-ord). I've got the math figured, I just have the triangle to construct.
//UPDATE
So I've figured out a good bit of this...
Below is a method which takes in a long / lat value and attempts to compute a triangle finding a point 700 meters away and one to its left + right. It'd then use these to construct the triangle. It computes the correct longitude but the latitude ends up somewhere off the coast of east Africa. (I'm in Ireland!).
public void drawtri(double currlng,double currlat, double bearing){
bearing = (bearing < 0 ? -bearing : bearing);
System.out.println("RUNNING THE DRAW TRIANGLE METHOD!!!!!");
System.out.println("CURRENT LNG" + currlng);
System.out.println("CURRENT LAT" + currlat);
System.out.println("CURRENT BEARING" + bearing);
//Find point X(x,y)
double distance = 0.7; //700 meters.
double R = 6371.0; //The radius of the earth.
//Finding X's y value.
Math.toRadians(currlng);
Math.toRadians(currlat);
Math.toRadians(bearing);
distance = distance/R;
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
System.out.println("CURRENT ALAT!!: " + Global.Alat);
//Finding X's x value.
Global.Alng = currlng + Math.atan2(Math.sin(bearing)*Math.sin(distance)
*Math.cos(currlat), Math.cos(distance)-Math.sin(currlat)*Math.sin(Global.Alat));
Math.toDegrees(Global.Alat);
Math.toDegrees(Global.Alng);
//Co-ord of Point B(x,y)
// Note: Lng = X axis, Lat = Y axis.
Global.Blat = Global.Alat+ 00.007931;
Global.Blng = Global.Alng;
//Co-ord of Point C(x,y)
Global.Clat = Global.Alat - 00.007931;
Global.Clng = Global.Alng;
}
From debugging I've determined the problem lies with the computation of the latitude done here..
Global.Alat = Math.asin(Math.sin(currlat)*Math.cos(distance)+
Math.cos(currlat)*Math.sin(distance)*Math.cos(bearing));
I have no idea why though and don't know how to fix it. I got the formula from this site..
http://www.movable-type.co.uk/scripts/latlong.html
It appears correct and I've tested multiple things...
I've tried converting to Radians then post computations back to degrees, etc. etc.
Anyone got any ideas how to fix this method so that it will map the triangle ONLY 700 meters in from my current location in the direction that I am facing?
Thanks,
for long distance: http://www.dtcenter.org/met/users/docs/write_ups/gc_simple.pdf
but for short distance You can try simple 2d math to simulate "classic" compass using: http://en.wikipedia.org/wiki/Compass#Using_a_compass. For example you can get pixel coordinates from points A and B and find angle between line connecting those points and vertical line.
also You probably should consider magnetic declination: http://www.ngdc.noaa.gov/geomagmodels/Declination.jsp
//edit:
I was trying to give intuitive solution. However calculating screen coordinates from long/lat wouldn't be easy so You probably should use formulas provided in links.
Maybe its because I don't know javascript, but don't you have to do something like
currlat = Math.toRadians(currlat);
to actually change the currlat value to be radians.
Problem was no matter what I piped in java would output in Radians, Trick was to change everything to Radians and then output came in radians, convert to degrees.