How to convert x,y,z to longitude, latitude, altitude in Cesium?

Using three.js library I have assembled a design(plant). That design is contains of so many smaller models which have the reference of position in (x,y,z) from origin (0,0,0). Attached the sample screenshot in the below link

Assembled Model Ref Image

Now I wanted to load the individual model with its own position into Cesium. When I try to load the directly converting the position (x,y,z) to (north, east, up) the result is not as expected. All the models are scattered.

The functionality which I am trying to achieve is, based on some origin (lon, lat, alt) point, I should position the model into cesium with reference of (x,y,z) relative to cesium coordinates (lon, lat, alt)

E.g.

origin geo-coordinates (ori_lon, ori_lat, ori_alt) => (-106.690647, 36.806761, 0)

Model coordinates (m_x, m_y, m_z) => (-150.9, 126.26, 217.7)

Expecting Coordinates for Cesium : (ori_lon + m_x, ori_lat + m_y, ori_alt + m_z)

or some algorithm to achieve this.

I have tried with the below article to convert the (x,y,z) to the (long, lat, alt) with some origin (long, lat, alt), but no luck :frowning:

(x, y, z) coordinates >> geo-coordinates

Advice/help to fix the issue.

This is easy to do, but you need to know what reference frame the XYZ offsets are in, Earth Fixed, NorthEastDown, EastNorthUp or something else entirely. That’s the crucial piece of information you’re missing. Ultimately, all positions get converted to Earth Fixed but you need to know the starting reference frames.

If they are in Earth Fixed, the code would just be simple edition:

//Convert origin to earth-fixed Cartesian

var position = Cesium.Cartesian3.fromDegrees(oLon, oLat, oAlt);

Then for each offset you just add the values

var finalPos = Cesium.Cartesian3.add(position, offset, new Cesium.Cartesian3());

If the code is in another reference frame, it needs to be transformed, but you need to know what frame that is. For example, if it’s eastNorthUP

//Convert origin to earth-fixed

var position = Cesium.Cartesian3.fromDegrees(oLon, oLat, oAlt);

//Compute a transform that converts ENU offsets to Earth-fixed

var transform = Cesium.Transforms.eastNorthUpToFixedFrame(position);

Then for each offset you just apply the transform.

var finalPos = Cesium.Matrix4.multiplyByPointAsVector(transform, offset, new Cesium.Cartesian3());

Hi Matthew,

Thank you so much for the explanation. I’m working on to implement the solution. I’ve a bit challenge to understanding some of the concepts.

· Reference frame the XYZ offset are in EarthFixed or NorthEastDown or EastNorthUp. How to find this? I’ve created these models with the reference of 0,0,0 in 3D plane. I’m not sure what exactly I need to convert or find out. Can you please send me some reference article on this?

· Screenshot :

Above screenshot will give the reference of model designed in three.js and the reference point on cesiumjs. The model which I’ve build is based in (0,0,0) axis point of 3D plane. And I want to build the same model in Cesium in the selected geo-coordinate point (32.0621175298, -28.3045768893, 0) as origin.

So, from the explanation, “convert origin to earth-fixed Cartesian” will refer to convert geo-coordinate point *(32.0621175298, -28.3045768893, 0) *to origin to earth-fixed Cartesian

i.e. var position = Cesium.Cartesian3.fromDegrees **(oLon, oLat, oAlt) => **(32.0621175298, -28.3045768893, 0); ??

Also, required little explanation on this “Then for each offset you just add the values.

It will be very much helpful, If I get some reference on this.

Note: If required, I shall share the source code.

Thanks,Premkumar

A good way to figure out how the coordinate system of your model corresponds to the world is by placing it on 3 key spots on Earth without performing any transformations.
At (0,0) local up is world +x, so the part of the model facing local up corresponds to world +x

at (90,0) local up is world +y, so the part of the model facing local up corresponds to world +y

at (0,90) local up is world +z, so the part of the model facing local up corresponds to world +z

With the rotation of the modelMatrix set to Matrix3.IDENTITY.

Hi,

Thank you for taking time to respond to my post.

I’ve tried with your approach with keeping the models in all 3 key spots. The only problem I’m facing here is appending the value “X” or “Y” or “Z” with the world. Because, the value XYZ are quite big. e.g. (x,y,z) --> (0, 34.68, 0) or (10, 54.25, 5.2). Also, I’m having 50+ models. All the models got scattered in the world.

I’m missing the offset conversion mechanisa (i.e. XYZ value to the world coordinates). I’m still fighting with the conversion.

Let me know, if you have any clue on this.

Thank you!

Hi Matthew,

Greetings to you!

Thank you for taking time to respond. It was very much helpful. I’m completely new to Cesiumjs so taking a bit time to understand on the basics.

Now I got a better understand on the axis conventions. I’m going to have the starting reference frame as earth fixed Cartesian. Now I’m facing bit challenge on manipulate offset value. Can you please give your support on manipulate offset value.

//Convert origin to earth-fixed Cartesian

var position = Cesium.Cartesian3.fromDegrees(oLon, oLat, oAlt);

Then for each offset you just add the values

var finalPos = Cesium.Cartesian3.add(position, offset, new Cesium.Cartesian3());

Currently, I’m trying the offset

E.g.

“modelPosition”: {

“x”: 336.27653593294048,

“y”: -91.184997686408721,

“z”: -346.19807702970149

}

var oLat = -28.3045985862, oLon = 32.0621455464, oAlt = 0;

var lat = oLat + ((modelPosition.x) / 1000000), long = oLon + (modelPosition.z / 1000000), alt = oAlt + ((modelPosition.y + 100) / 10);

var position = Cesium.Cartesian3.fromDegrees(long, lat, alt);

Is’t the right way of implementing the offset value? Please advice.

Thank you!

I’ve made some SandCastle apps demonstrating transforms

var viewer = new Cesium.Viewer(‘cesiumContainer’);
var ellipsoid = viewer.scene.globe.ellipsoid;
var ENU = new Cesium.Matrix4();
var position = Cesium.Cartesian3.fromDegrees(0,0,0);
Cesium.Transforms.eastNorthUpToFixedFrame(position,ellipsoid,ENU);
console.log(ENU);

``

ENU is a local Cartesian (which itself is in terms of Earth-Fixed) from which you can use to offset the model

east is +x, north is +y, up is +z

For example, at 0lon,0lat

local East(1,0,0) is Earth Fixed (0,1,0) 1st column

local North(0,1,0) is Earth Fixed (0,0,1) 2nd column

local Up(0,0,1) is Earth Fixed (1,0,0) 3rd column

position in terms of Earth-Fixed is (6378137,0,0) 4th column

if you do

modelMatrix=ENU.clone();

Your model’s x will face east,y face north,z face up at that position.

//Transform a point
var viewer = new Cesium.Viewer(‘cesiumContainer’);
var ellipsoid = viewer.scene.globe.ellipsoid;
var ENU = new Cesium.Matrix4();
var position = Cesium.Cartesian3.fromDegrees(0,0,0);
Cesium.Transforms.eastNorthUpToFixedFrame(position,ellipsoid,ENU);
var offset=new Cesium.Cartesian3(2,0,0);
console.log(position);
var finalPos = Cesium.Matrix4.multiplyByPoint(ENU, offset, new Cesium.Cartesian3());
console.log(finalPos);

``

this moves 2 meters local east, which in Earth Fixed is 2 meters in the y direction

//Transform a vector
var viewer = new Cesium.Viewer(‘cesiumContainer’);
var ellipsoid = viewer.scene.globe.ellipsoid;
var ENU = new Cesium.Matrix4();
var position = Cesium.Cartesian3.fromDegrees(0,0,0);
Cesium.Transforms.eastNorthUpToFixedFrame(position,ellipsoid,ENU);
var localDir=new Cesium.Cartesian3(0,0,2);
console.log(localDir);
var EarthDir = Cesium.Matrix4.multiplyByPointAsVector(ENU, localDir, new Cesium.Cartesian3());
console.log(EarthDir);

``

A vector facing 2 meters local up is a vector facing 2 meters Earth Fixed +x direction

Hi,

Thank you so much for the guidance.

I’ve tried your solution. I’m half way through to meet my goal. I’m facing some challenges, when perform transform (point or vector). The place of the models is going wrong.

Here is the code and demo site.

var viewer = new Cesium.Viewer(“globe-Container”, {

infoBox: false,

selectionIndicator: false

});

var globeScene = viewer.scene;

var ellipsoid = viewer.scene.globe.ellipsoid;

function addMesh(fileName, modelPosition, modelRotation) {

var oLat = -28.3045985862,

    oLon = 32.0621455464,

    oAlt = 0,

lat = oLat + (modelPosition.x / 1000000),

long = oLon + (modelPosition.z / 1000000),

alt = 0 + ((modelPosition.y) / 10),

heading = Cesium.Math.toRadians(modelRotation.y),

pitch = Cesium.Math.toRadians(modelRotation.z),

roll = Cesium.Math.toRadians(modelRotation.x),

offsetLat = (modelPosition.x / 1000000), // Adjusting the position to geo position. 

offsetLong = (modelPosition.z / 1000000),

offsetAlt = (modelPosition.y / 10);

if (model1Flag) {

    alt = 0 + ((modelPosition.y) / 10);

} else {

    /* The models are loading below the ground level, So added +100 to the altitude */

    alt = 0 + ((modelPosition.y + 100) / 10);

}

/* var ENU = new Cesium.Matrix4();

   var position = Cesium.Cartesian3.fromDegrees(oLon, oLat, oAlt);

   Cesium.Transforms.eastNorthUpToFixedFrame(position, ellipsoid, ENU);

   var offset = new Cesium.Cartesian3(offsetLat, offsetLong, offsetAlt);

   console.log(position);

   var finalPos = Cesium.Matrix4.multiplyByPointAsVector(ENU, offset, new Cesium.Cartesian3());

   console.log(finalPos);*/

var finalPos = Cesium.Cartesian3.fromDegrees(long, lat, alt);

var orientation = Cesium.Transforms.headingPitchRollQuaternion(finalPos, heading, pitch, roll);

var entity = viewer.entities.add({

    name: "gltf/" + fileName,

    position: finalPos,

    orientation: orientation,

    model: {

        uri: "gltf/" + fileName,

        minimumPixelSize: 0

    }

});

viewer.trackedEntity = entity;

}

function removeGlobeMesh() {

viewer.entities.removeAll();

}

var models = [
{gltffilename:“productsizingcrusher.gltf”,position:{x:0,y:34.6866455477384,z:0},rotation:{x:0,y:1.5707963267948966,z:0}},
{gltffilename:“smallpiller.gltf”,position:{x:0,y:0,z:0},rotation:{x:0,y:1.5707963267948966,z:0}},
{gltffilename:“basicironbeam.gltf”,position:{x:1.8474245779789382,y:56.978831129127812,z:.436160116231305},rotation:{x:0,y:1.5707963267948966,z:0}},
{gltffilename:“roof05.gltf”,position:{x:0,y:32.993313735315141,z:10.52622915735363},rotation:{x:0,y:0,z:0}}]

/* On click */
$("#loadModel").click(function () {

/* remove the models */

removeGlobeMesh();

/* To load the model into cesiumjs */

for (var j = 0; j < models.length; j++) {

if (models[j].gltffilename !== “”) {

addMesh(models[j].gltffilename, models[j].position, models[j].rotation);

}

}

});

``

The complete source code can be downloaded from **here. **

I’m totally struck with this problem. Please advice to fix it.

Thank you!

I’m a bit confused by these lines, they seem to be mixing Cartographic and Cartesian components

lat = oLat + (modelPosition.x / 1000000),
long = oLon + (modelPosition.z / 1000000),
alt = 0 + ((modelPosition.y) / 10),

offsetLat = (modelPosition.x / 1000000), // Adjusting the position to geo position.
offsetLong = (modelPosition.z / 1000000),
offsetAlt = (modelPosition.y / 10);

I’d comment those out and comment in this to determine finalPos, experiment with offsets set to 0, then start changing those later. Use multiplyByPoint instead of multiplyByPointAsVector for position.

offsetEast= 0;
offsetNorth = 0;
offsetUp = 0;

var ENU = new Cesium.Matrix4();
var position = Cesium.Cartesian3.fromDegrees(oLon, oLat, oAlt);
Cesium.Transforms.eastNorthUpToFixedFrame(position, ellipsoid, ENU);//return result via parameter
var offset = new Cesium.Cartesian3(offsetEast, offsetNorth, offsetUp);
var finalPos = Cesium.Matrix4.multiplyByPoint(ENU, offset, new Cesium.Cartesian3());

``

Neat app BTW! So the Three viewer is on the left and Cesium on the right. Can the Three viewer show x,y,z labels by chance?

Thank you so much for your support!

I’ve updated the demo link and the project to show the XYZ on mouse over and show the XYZ of the model on selection (mouse click on the model). The updated demo and source code.

I’ve converted my position transformation to “multiplyByPoint” and trying to adjust the offset. All the models are loading in the origin point.

Now, experimenting with giving my modelposition to offset values. But it’s not exactly placing in the right place.

Can you help me on this.

Thank you!

Thanks, I’ll take a look at it. Before worrying about offsets, getting the models in one piece in Cesium would be nice, like it is in threejs. On model 2 I’m seeing at least one part of the model buried deep under the ellipsoid, and many components have dropped to the ground. Each of the 2 model arrays (models1 models2) have many gltf models.

I’m not sure if this has anything to do with up in threejs being y and up in Cesium being z. Maybe in function addMesh start with

//swap y and z
var temp = modelPosition.z;
modelPosition.z=modelPosition.y;
modelPosition.y=temp;

``

and see what that does.

While testing the swap be sure to comment this out

/* altitude adjustment /
if (model1Flag) { alt = 0 + ((modelPosition.y) / 10); }
else { /
The models are loading below the ground level, So added +100 to the altitude */ alt = 0 + ((modelPosition.y + 100) / 10); }

Also I think you’ve inadvertently commented this out

 var orientation = Cesium.Transforms.headingPitchRollQuaternion(finalPos, heading, pitch, roll);

Hi,

Thank you for your kind help.

I’ve tried with your suggestions, by swapping the y and z and comment out the altitude adjustment. Still the same problem. If I swap y & z them I need to adjust offsetUp and offsetEast. there is nothing much difference on swap.

I’m currently experimenting by adjusting the offset value. So far no success. so still exploring… You can find that page here (http://creative-labs.in/webglv2/)

Have you get some time to spend on this issue? any good news?

Hi,

Thank you so much for the suggestion.

I’ve examined your approach by swap y & z and commenting the altitude adjustment. Also, I made the change on offsetSouth and offsetUp. But I got the same result which I get it before.

I’m currently experimenting on adjusting the offset position. So far no luck :frowning: You can see those changes here.

Have you get some time to spend on this issue? Any luck?

Thank you!

I was busy most of yesterday, I’ll definitely take a look at it today.

I just found out how to get the modelMatrix of each model, via viewer.scene.primitives._primitives array

such as (can also type these in the console to get values)

viewer.scene.primitives._primitives[0].modelMatrix //primitive 0 modelMatrix

viewer.scene.primitives._primitives[17].modelMatrix //primitive 17 modelMatrix

Maybe just like you can select and move models around in ThreeJS, the same can be done in Cesium. Similar to the app I wrote here

https://groups.google.com/d/msg/cesium-dev/imIpoZHvKrM/Xyx4c-zEuQsJ

However include orientation adjustments as well.

//to get model position
viewer.scene.primitives._primitives[i].modelMatrix[12] //position x of primitive 0
viewer.scene.primitives._primitives[i].modelMatrix[13] //position y of primitive 1
viewer.scene.primitives._primitives[i].modelMatrix[14] //position z of primitive 2

``

//to get model EAST NORTH UP orientation
var myMatrix4 = new Cesium.Matrix4();
var myMatrix3 = new Cesium.Matrix3();
var EAST = new Cesium.Cartesian3();
var NORTH = new Cesium.Cartesian3();
var UP = new Cesium.Cartesian3();
myMatrix4 = viewer.scene.primitives._primitives[i].modelMatrix.clone();
Cesium.Matrix4.getRotation(myMatrix4,myMatrix3);
Cesium.Matrix3.getColumn(myMatrix3,0,EAST);
Cesium.Matrix3.getColumn(myMatrix3,0,NORTH);
Cesium.Matrix3.getColumn(myMatrix3,0,UP);

``

Currently the models are scattered all over the place in Cesium, collecting this data should help figure out what is going on.

A few typos due to sloppy copy/pasting
-where it says //position of x of primitive 0 it should read of primitive i, same with the other 2 comments

-also I forgot to alter the 2nd parameter

Cesium.Matrix3.getColumn(myMatrix3,0,EAST);
Cesium.Matrix3.getColumn(myMatrix3,1,NORTH);
Cesium.Matrix3.getColumn(myMatrix3,2,UP);

``

Just realized you already made a LEFT DOUBLE CLICK event to get position data of a primitive.

Hi,

Thank you so much for spending time on this.

Today I also spend some time on dynamically change the translation value from modelMatrix. On LEFT_DOUBLE_CLICK, I’m picking the model and getting the position value from Cesium.Matrix4.getTranslation and try to change it in console. But the value is not updating in the model.

I believe we should render the canvas or model again after changing the position. Any clue how to re-render the canvas after update any value?

By the way, I want to load the model from threejs to cesiumjs with out changing the position and orientation. I’ve tried this approach to get to know about the position value so that I can adjust the offset value based on that.

It would be of great help if, you can help to position the models in exact place as we can see it in threejs. As mentioned, Orientation also I should look into… But if we fix the position then I believe changing the orientation won’t take much time.

Currently I’m experimenting on changing the offset values as shown below. Is this the right approach to find the exact offset value or I’m travelling in wrong direction.

offsetNorth = modelPosition.x **+** adjustment value; // trying with different operators (+, -, * , /)
offsetEast = modelPosition.y **+** adjustment value;
offsetUp = modelPosition.z **/** adjustment value;

``

Please guide me. Also, it would be of great help if I got some solution for this.

Thank you!

I’d just comment out everything dealing with offset for now, including the Cesium.Matrix4.multiplyByPoint , basically offset is already ‘built into’ the varying model positions relative to eachother.

Both ThreeJS and CesiumJS are fed the same position data, yet ThreeJS is also fed scale information, which may or may not be a factor. Maybe scale in ThreeJS not only determines size of the model for each dimension, but also determines scaling of position. For example say position data is 10. 10 what? Meters, Milli-Meters? I believe Cesium always assumes meters for position, so maybe you need to convert position to meters based on scale. I’ll look into this some more to see if this is the case or not, it’s just a guess.

Taking a look at the data inputted into function addMesh for model 2 without swapping (y is up, z is the direction you face when walking the stairs)
x varies between 325 to 401

y varies between -27 to -91

z varies between -301 to -348

Wait a minute, model.position IS offset. Sorry, I wasn’t thinking straight!

Just looked at http://creative-labs.in/webglv2/js/custom/config.js

There’s a geopositon property on models1 but not models2. I suppose that’s not important as ThreeJS doesn’t use it and it renders just fine.

ThreeJS is using json models while CesiumJS is using gltf. I noticed that the json models are easily readable in a text editor while gltf compresses some data into base64, I suppose for better bandwidth usage. Can CesiumJS use those json models, or do they have to be converted to gltf?