# 90 angle between coordinate breaks when applied to world

I have a point `A` and direction `DIR` and a distant `DIST`

Let's use

``````A: `longitude: 139.33435143, latitude: 35.5385006, altitude: 0`
DIR : `longitude: 139.33547146, latitude: 35.54034106, altitude: 0`
DIST : `500`
``````

I would like using those value, to, create two point B/C so that BAC is a 90degree angle. and that AB is giving the starting vector on which apply the rotation.

Basically like this :

Sandcastle

The problem I have is the following, event tho, my long/lat that I calculate are forming a 90 angle :

``````origin = A.longitude, A.latitude
xAxis = A.longitude - DIR.longitude, A.latitude - DIR.Latitude
yAxis = cos(Math.PI / 2) * xAxis.longitude - sin(Math.PI /2) * xAxis.latitude,
sin(Math.PI /2) * xAxis.longitude  - cos(Math.PI /2) * xAxis.latitude
``````

So this would give me the following which have a 90degee angle when drawn.

``````origin = 139.33435143,35.5385006
xAxis = 139.33547146,35.54034106
yAxis = 139.33251097,35.53962063000001
``````

When I apply those coordinate and convert them to Cartesian3 in cesium, the resulting angle is not 90degree anymore but something smaller (as you can see in the link)

Since I believe converting to Cartesian3 apply the circular shape of the globe to the coordinate is there some additional conversion I am missing ?

• Spherical coordinate trigonometry is tricky to start, and once you start geodesic math on a spheroid, it becomes quite unmanageable. Best practice would be to use a geodesic library, and let it do the heavy lifting. But any way you work it, most of the rules of cumulative angles are thrown out the window when your trig is on a sphere and not a plane. – Vince Dec 27 '19 at 2:09
• I see, I guess I need to use some Cesium built in method to do so. Thanks. – Bobby Dec 27 '19 at 2:17

Since you're dealing with a localized area, your best bet may be to transform your coordinates into an East-North-Up frame, with its origin at your origin. This way, you can use `sin` and `cos` just to deal with local compass headings, and let Cesium's WGS84 ellipsoid transformations handle the hard part.

Here's a new Sandcastle Demo.

The code for that demo looks like this:

``````var viewer = new Cesium.Viewer('cesiumContainer');

// This data block was copied from the original question.
var area = {
id: 106,
projects_id: 13,
name: "",
description: "",
kind: "area",
ll: {longitude: 139.33435143, latitude: 35.5385006, altitude: 0},
dir: {longitude: 139.33547146, latitude: 35.54034106, altitude: 0},
lllr: 500,
llul: 500,
context: null,
status: true,
deleted: false,
create_date: 1572573782135,
update_date: 1574224549679
};

// Get origin point and "direction" point in Fixed frame.
var originFixed = Cesium.Cartesian3.fromDegrees(area.ll.longitude, area.ll.latitude);
var directionFixed = Cesium.Cartesian3.fromDegrees(area.dir.longitude, area.dir.latitude);

// Obtain a local East-North-Up Cartesian coordinate system, with its origin at our origin.
var localFrame = Cesium.Transforms.eastNorthUpToFixedFrame(originFixed);
var localFrameInverse = Cesium.Matrix4.inverse(localFrame, new Cesium.Matrix4());

// Get the "direction" point expressed in local coordinates.
var directionLocal = Cesium.Matrix4.multiplyByPoint(localFrameInverse, directionFixed, new Cesium.Cartesian3());

// Get a compass heading from the shared origin to the local "direction" indicator point.
var startAngle = Math.atan2(directionLocal.y, directionLocal.x);
var endAngle = startAngle + Cesium.Math.toRadians(90);

// Get distances to the new points.
var distance1 = area.lllr;
var distance2 = area.llul;

// Recompute the "direction" point based on a new distance.
var movedLocalDirection = new Cesium.Cartesian3(Math.cos(startAngle) * distance1, Math.sin(startAngle) * distance1, 0.0);
var movedFixedDirection = Cesium.Matrix4.multiplyByPoint(localFrame, movedLocalDirection, new Cesium.Cartesian3());

// Compute a newly rotated "direction" point.
var rotatedLocalDirection = new Cesium.Cartesian3(Math.cos(endAngle) * distance2, Math.sin(endAngle) * distance2, 0.0);
var rotatedFixedDirection = Cesium.Matrix4.multiplyByPoint(localFrame, rotatedLocalDirection, new Cesium.Cartesian3());

// Compute the far corner as the sum of the near corners.
var farLocal = Cesium.Cartesian3.add(movedLocalDirection, rotatedLocalDirection, new Cesium.Cartesian3());
var farFixed = Cesium.Matrix4.multiplyByPoint(localFrame, farLocal, new Cesium.Cartesian3());

// Show the Origin point in yellow.
name : 'Origin Point',
position : originFixed,
point : {
pixelSize : 14,
color : Cesium.Color.YELLOW
}
});

// Show the original "direction" point in cyan.
name : 'Direction Point',
position : directionFixed,
point : {
pixelSize : 10,
color : Cesium.Color.CYAN
}
});

// Show the moved "direction" point (same angle, new distance) in orange.
name : 'Moved Direction Point',
position : movedFixedDirection,
point : {
pixelSize : 10,
color : Cesium.Color.ORANGE
}
});

// Show the rotated "Direction" point in green.
name : 'Rotated Direction Point',
position : rotatedFixedDirection,
point : {
pixelSize : 10,
color : Cesium.Color.LIME
}
});

// Show the far corner in red.
name : 'Far Corner',
position : farFixed,
point : {
pixelSize : 10,
color : Cesium.Color.RED
}
});

// Show the full area as a purple polygon.
var purplePolygonUsingRhumbLines = viewer.entities.add({
name : 'Purple polygon using rhumb lines with outline',
polygon : {
hierarchy : [originFixed, movedFixedDirection, farFixed, rotatedFixedDirection],
extrudedHeight: 0,
material : Cesium.Color.PURPLE,
outline : true,
outlineColor : Cesium.Color.MAGENTA,
arcType : Cesium.ArcType.RHUMB
}
});

viewer.zoomTo(viewer.entities);
``````
• Thank you so much, I tried to use NothEast etc... for a while, but I never managed to understand the rotation and how to manipulate those value correctly. I will take a deep look at your answer, but the result is exactly what I want. I owe you one – Bobby Dec 28 '19 at 6:37
• Hello, I had time to read after the holidays, I just have a question if I may. Why do we need to invert the `localFrame` and use the inverted one ? @emackey – Bobby Jan 6 '20 at 1:15
• The inverted one converts from Fixed frame to local coordinates, but the original one converts from local coordinates to Fixed frame. The inverted one is only used once (to get `directionLocal` from `directionFixed`), then the calculations are done in local space, and the non-inverted one converts everything from local back to Fixed. – emackey Jan 6 '20 at 16:20