Height position confusion, seems to be wrong

Hi,
I have noticed an issue, and I am wondering if someone could explain what I am missing to make it work and being accurate. Cesium for Unity V 1.6.4

The coordinates I use are : Lat: 44.173590 Lon: 5.278830 Height:1895
I confirmed the height in meters from different sources.

First I was testing by code using the following to commands to find the Unity World position:
CesiumWgs84Ellipsoid.LongitudeLatitudeHeightToEarthCenteredEarthFixed(lonlatheight);
Unity.Mathematics.double3 pos = instance.TransformEarthCenteredEarthFixedPositionToUnity(ecef);

This gave me a position x, z visibly ok, but the y position was like 50 meters below the real point.

I then made a new clean scene, I added a CesiumGeoreference object and I added a simple “Cesium world Terrain + Bing textures”. Next I entered in the CesiumGeoreference the Lon,Lat,Height position as “Origin” in the inspector parameters.

Instead of showing the position on the top of the mountain (on the road) the center in Unity is shown inside the mountain 50meters below the point.

First I thought about a shift in the Y coordinate, but then I imported a GPX which was shown, again, below the surface but visually I could notice that the slope of the GPX positions was not the same as the slope of the hill. Which makes me think of some “scaling factor” problem.

I tried google the issue and chatgpt but nothing useful, so I thought I would ask here.

So the calculation by script as well as when using the predefined function it seems that the LonLatHeight to Unity coordinates conversion has a problem.

Thanks for your help.

Hi @Claude_V,

If I’m understanding your issue correctly, you’re expecting the Height property to mean the height above terrain. In Cesium for Unity, the Height of the CesiumGeoreference refers to the height above the WGS84 ellipsoid model. This is often unequal to the actual height of the terrain above mean sea level.

If you want the height above the terrain, you’ll have to use Physics.Raycast and raycast towards the center of the globe. If you want the height above mean sea level, see Kevin’s post on this thread: The height of the sea is not 0 - #8 by Kevin_Ring

So height does not refer to “elevation” ? That’s what I could make up out of your answer :slight_smile:

So if I load a GPX I am not able to convert the positions from GeoPos (Lat,Lon,Elevation) to Unity 3D coordinates ? Only by using a Recast ?
There are a couple of issues in this scenario.

  • If the GPX is defined on a larger area than what is displayed we don’t have the tiles loaded, how can I raycast in nonleaded tiles ?
  • And during the time the tiles are loading I also can not raycast
  • I see the tiles MeshCollider is also not tagged Convex which makes the raycast not stop
  • It would be good to be able to get a specific tile object with the given Lon,Lat so we can raycast with only one mesh collider

Any hints on how to “quickly” handle these elevation calculations to place objects are welcome.

Thanks,

Claude

There is no single definition of “elevation”. It’s an ambiguous term. As Janine mentioned, Cesium for Unity uses the height above the WGS84 ellipsoid. This is exactly the same height you’ll get out of a GPS receiver, at least before it’s transformed to some other height reference. EGM96 or EGM2008 are commonly used “mean sea level-like” height references, but there are many others. Here in Australia, for instance, the official reference for elevation is the Australian Height Datum. You can use a library like PROJ to transform between height references if necessary. WGS84 ellipsoidal heights are
precise and easy to work with mathematically - and also the standard choice in many environments - which is why Cesium for Unity uses it by default.

Thanks Kevin for the precisions.

However I would like to come back to the “raycast” solution you put forward.

During my tests I read a series of Lat,Lon positions and calculate the X,Z unity world position. Then I raycast with the physical meshes of the terrain.

The problem I see is that certain tiles have the terrain mapping on the top polygons of the tiles physics mesh and others on the bottom polygons.

This results in my calculated Unity Y positions being sometimes above and sometimes under the terrain.

As I don’t know how I should decide to raycast from above or from beneath.

This brings me to my initial question… how can I find the Unity Y position with a certain Lon,Lat that matches the visual terrains ?

I can supply you very precise tiles where you can see the raycast issue clearly.

Thanks.

I have found my problem. I was using Physics.RayCast(), and when I first used it, it didn’t work, so I googled the issue and they came up with "tag your physics mesh as convex, which I did and then it worked:

if (Physics.Raycast(position, Vector3.up, out hit, 10000.0f))

I didn’t go deeper into the subject as it “worked”.

But it seems that switching the convex collision mesh will create some 3D mesh with polygons that do not reflect the surface, so will give you false hit points.

I actually can’t tell why it didn’t work without the convex tag on at start but apparently there is absolutely no need for this flag to be tagged.

For those that did not find a conversion tool. I prepare my conversion server side with GeoidEval which is a tool that runs on linux/ubuntu. The installation needs a dataset that you better download and manually install on your server. The results are pretty close to the Cesium display, only 1 meter off, which is pretty good.

apt-get install geographiclib-tools

Check this for dataset install : Fail to install Geographiclib dataset · Issue #963 · mavlink/mavros · GitHub

I un-tarred these:
Source tar files here : GeographicLib - Browse /geoids-distrib at SourceForge.net

cd /usr/share/GeographicLib
tar xofjC /tmp/egm96-5.tar.bz2 .
sudo tar xofjC /tmp/egm96-5.tar.bz2 .

Using the command as follows : echo lat lon msl | GeoidEval --msltohae
and you will get : lat lon wgs84

I did not test different regions or zones, the result looked quite accurate.

Now for display reasons the LineCast seems a better approach.