Cesium terrain z values incorrect over large distance

Whilst doing a Line trace down to the Cesium terrain (on its own channel), z values seem to be inconsistent over a large area (I think I read like 1m every 100km away from the Georeference anchor or something like that). I’m a bit confused as to how to use cameras to force tilesets to load in detail to ensure these linetraces are correct, I’d prefer to not use them at all if possible as im placing them using the z values found by this line trace (as such around half are under the surface and wouldn’t load the tileset anyway). Is there a way to force load a tileset given unreal (or Lat/Long) coordinates - maybe seaching for those coordinates and getting the triangle it belongs to - and then unload them again to save memory? I understand this will have performace issues, however I only have to run this on a large dataset once and then it should only be about 100ish each time I have to run it.

Many thanks.

Hi @Alec_Denny_Craine,

This sounds like a case for the SampleHeightMostDetailed function on Cesium3DTileset, which is available in the latest release. This allows you to sample the height of terrain (where height is relative to the WGS84 Ellipsoid) and clamp objects to those heights, either through use of Cesium Globe Anchor, or by converting to Unreal coordinates.

There’s a sample Blueprint in this Github comment, if you’d like it for reference:

Let us know if you have any follow-up questions! :smile:

Hi Janine, Is there any documentation on how to correctly use Asynchronous functions? I’m calling them from a python script so I may need a way to wait for them to finish in some cases. Also it would be nice to know simply when it does finish so I can gauge how long it will take.

Hi @Alec_Denny_Craine,
We’re not too familiar with automating Unreal from Python, so can’t really help there. But Janine shared a link above with a link to blueprintue.com demonstrating how this can be done from Blueprints. This follows a fairly normal pattern for async functions in Unreal, so hopefully you’ll be able to work out how to use it from Python.

Thanks Kevin, I’ve been looking through this and believe I’ve managed to find a somewhat workable solution, I’ve currently got one up for testing however it seems to not be executing whatsoever (I’ve added a print statement right after On Heights Sampled and nothing is printing, the input array has a length of 4). Do you know if there is anything else I need to be doing that isnt within that blueprint?

The blueprintue looks ok to me. I can’t see anything missing. But sometimes something gets lost when going to and from blueprintue. So if it’s not working for you, it may help to share a screenshot of your Blueprint and maybe we can spot the problem.

This should be the link to the blueprint im using Finding Z Value Using Cesiums Sample Height Most Detailed posted by anonymous | blueprintUE | PasteBin For Unreal Engine .
The input Vectors come in (and out of the convert function) correctly. The print string “Task Started” always prints but “Function Completed” Never does.

That’s very strange @Alec_Denny_Craine, I can’t explain why that would happen. Can you try passing just a single set of coordinates, such as (0,0,0), to the SampleHeightMostDetailed function and see if you get a response that way? It’s also worth checking the Output Log to see if there are any messages there that look possibly relevant. And finally, can you tell us what tileset you’re referring to when you say “Cesium terrain”? Is it Cesium World Terrain, asset ID 1 from Cesium ion?

I’ve just changed the blueprint to only have (0,0,0) as an input. It still doesn’t run. Heres my output log after about 20 minutes


I don’t see anything out of the ordinary in it. I’ve also changed the python script to only spawn a single Blueprint instead of 4 but the result seems the same.

When I’m referring to cesium terrain I am on about Cesium World Terrain, asset ID 1, with Bing Maps Road. Heres how I set it CesiumConstructionScript posted by anonymous | blueprintUE | PasteBin For Unreal Engine . It’s uploaded strangely for some reason (the random horizontal white lines have just appeared, and the tag is being compared to a Name of MainTileset) but doesn’t cause an error.

Can you try adding the Cesium3DTileset to your level directly in the Editor (instead of creating it at runtime). And then try doing a height query within the normal level blueprint attached to the BeginPlay event? That much certainly is working fine here. Depending on whether or not that also works for you, we can work on the next steps to diagnose what’s going wrong. But I think taking Python and the runtime creation out of the equation will be helpful for figuring this out.

Hi Kevin, I’m not sure I understand, how am I creating it at runtime? I thought that blueprint was just finding the Tileset I’ve already got loaded into the level. The Cesium 3D Tileset Valid Function is for a variable - maybe I’m misunderstanding.

That being said doing this within the level blueprint works, do asynchronous functions only work during runtime and not in the editor? I’ve only being testing this within the editor which may be the problem.

I thought I read something in your last message that suggested you might be spawning the Cesium3DTileset at runtime. But if you’re not, it’s all good! Sorry for the misdirection.

Ok, so the level blueprint works fine. That’s good. We’ve never tried using SampleHeightMostDetailed other than in-game, so you could be on to something there. The callback will be driven by the Cesium3DTileset’s Tick, so if that’s not happening for some reason, then the callback won’t happen, either. I can’t think of any obvious reason that the tileset wouldn’t tick, though. In fact, if the same tileset is showing in the Editor viewport, then that’s a sure sign that it is ticking.

Another possibility is that the process by which you’re spawning the Blueprint from Python is somehow the issue. To try to tell the difference, I just tried creating a simple “Editor Utility Widget” with a clickable button. When the button is clicked, it runs this Blueprint:

This seems to work fine. When I click the button, both “After” and “Done” are printed to the Output Log.

It may be worth mentioning, though, that that “Get All Actors of Class” node was necessary. I wasn’t able to drag a Cesium3DTileset instance from my Outliner into this Editor Blueprint in order to use it directly. Which makes sense, really.

So I guess I’m coming around to the conclusion that there’s something about your Python-spawned Blueprint specifically that is problematic?

I ran a copy of the blueprint you made and that worked. I then made the same thing within my blueprint and set the function to be callable in editor, which did not work (Prints “After”, not “Done”). I’ve been using a blueprint of class Cesium3DTileset, and I’ve just noticed that its parent class is CesiumRuntime.Cesium3DTileset, which I’m assuming is causing the error? The python script is just spawning the blueprint and running custom events & functions inside it as you would in the editor.

So I’ve remade this as a Blutility.EditorUtilityActor and it isn’t workin on this either. Do you have any idea why it seems to only be working in the Editor Utility Widget/ During Runtime?

So I’ve remade this as a Blutility.EditorUtilityActor and it isn’t workin on this either.

Can you walk me through what you did? I don’t have any solid guesses as to why it wouldn’t work in some contexts. But if I can reproduce the problem myself, then I can debug it and see where things are going wrong.

So heres the full blueprint:

And the necissary parts from the python script:

geom = wkb.loads(df['geom'][i], hex=True)

    for index, poly in enumerate(geom.geoms):
        holeList = []
        for holeNum in range(shapely.get_num_interior_rings(poly)):
            holeList.append(EnsureOrientation(shapely.get_coordinates(shapely.get_interior_ring(poly,holeNum)))) # Interior Rings (Holes)
        latLong = EnsureOrientation(shapely.get_coordinates(shapely.get_exterior_ring(poly))) # Exterior Ring (Main Polygon)

This seperates the a Multipolygon into singular polygons, ensuring they are rotated counterclockwise for the “Append Simple Extrude Polygon” To function as expected.

        polyCoords = []

        for point in latLong:
            polyCoords.append(unreal.Vector2D(point[0],point[1])) # Convert points to unreal vector
        blueprint = EAL.load_asset('/Game/StartUpBlueprints/Test.Test')

EAL (Editor Asset Library)
This loads the blueprint we want

        spawnedBlueprint = ELL.spawn_actor_from_object(blueprint, (0,0,0))
        spawnedBlueprint.set_folder_path('/Temp/')
        spawnedBlueprint.set_actor_label(str(int(Group)) + "_" + str(df['Identifier'][i])+"_Temp")

This spawns the blueprint in the world

        spawnedBlueprint.call_method("SpawnPolygon",
                                      tuple([polyCoords, # Lat Long Coordinates
                                      df['height'][i]*330, # Height; Multiply Stories by 3.3m (Unreal uses centimeters)
                                      colour[1:], # Colour; Remove Hashtag from colour
                                      str(int(Group)) + "_" + str(df['Indetifier'][i]) + "_" + str(index), # Name; Index value included for MultiPolygons with more than one Polygon
                                      str(int(Group))])) 

This calls the Custom Event " Spawn Polygon" Within the blueprint

        for hole in holeList:
            holeCoords =[]
            for point in hole:
                holeCoords.append(unreal.Vector2D(point[0],point[1])) # Convert points to unreal vector
            spawnedBlueprint.call_method("Add Hole",tuple([holeCoords]))

This calls the custom event “Add Hole” within the blueprint

        spawnedBlueprint.call_method("FindZ")

This calls the custom event “FindZ” withing the blueprint

To take you through the blueprint:

When the blueprint is created “Set Tileset” is called to find the Cesium World Terrain from within the construction script

Then Spawn Polygon is called from Python, this sets all the input variables, goes through each point in the polygon and converts the points from Lat Long to Unreal. Once the points have been converted a Generated Dynamic Mesh actor is spawned in the world, and a simple extrude polygon is appended to the actor.

Afterwards, if neccisary, “Add Hole” is called from the python script, which finds a dynamic mesh in the world, converts the Lat Long coordinates to Unreal coordinates, appends a simple extrude polygon to it, sets the simple extrude polygon as the only mesh, and then applies a mesh Boolean to the Main Extruded Polygon created during “Spawn Polygon” to create a hole within it.

Finally “Call Z” is called from python. This takes the points from the main polygon (“Spawn Polygon”), adds a Z Value to them (999999, which is the inital value of both Lowest Ground Value and Inital Lowest Ground Value) and then calls Sample Height Most detalied. On completion of Sample Height Most Detalied, it should be going through each result, breaking up the CesiumSampleHeightResult, transforming the result to Unreal Coordinates, and then checking to see if that result is lower than the Lowest ground value we already have.

From my understanding of how Sample Height Most detailed words, the input z value is ignored and if it fails that input z value is passed back out. This should result in some massive Z value when converted into unreal coordinates and not impact the Lowest Ground Value check.

Once All results have been gone through, It checks to see whether a lowest ground value has been found, and moves (transforms) the main polygon to the ground. Then it checks to see there isnt a duplicate of the polygon we are adding, if there is, it deletes the one currently saved.

The Main polygon is then turned into a Static Mesh, a material is added to it and it is saved.

There isn’t anything wrong with how the python or blueprint is working (minus the obvious), as I’ve already ran this with a line trace instead of the Sample Heights Most Detailed and everything worked in that script as intended.

Please let me know whether there is anything else you need me to explain.

@Alec_Denny_Craine there’s a lot of complexity there. Can you try to boil it down to the simplest possible example that demonstrates the problem? If you can eliminate Python from the equation, that would be ideal (because as I mentioned, I’ve never used Python in Unreal before). In the current form, it will take me a lot of time to set this up.

Heres a simple version of it:

If you copy that into a Blutility.EditorUtilityActor and set both Set Tileset and FindZ as Callable in Editor. Place the blueprint into the world (anywhere) and call Set Tileset and then FindZ from its details panels you should see an output log of ‘started’ but not one of ‘finished’.

Thanks for that @Alec_Denny_Craine, but I’m having trouble getting it to work. I created an EditorUtilityActor in a Content Browser folder in my project:

And pasted your Blueprint into it its “Event Graph”. I had to recreate the Tileset variable nodes, but that was easy enough. It looks like this:

I made sure “Call in Editor” is ticked on both “Set Tileset” and “FindZ”:

Then I dragged this Blueprint from the Content Browser into the viewport in order to add it to the level. The problem is that there are no “Set Tileset” or “FindZ” buttons in the Details panel for the Actor:


What am I missing?

This page seems to suggest that such buttons shouldn’t exist in this case:

Editor Utility Blueprint classes that derive from the Actor base class don’t expose buttons in the Details panel for any Functions or Custom Events that are marked as callable in the Editor. If you absolutely need to use a button in the Details panel to drive your Blueprint logic, create your graphs in a normal Blueprint class and not in an Editor Utility Blueprint class. However, for a much more flexible and powerful approach to creating a custom UI to drive Blueprint logic in the Unreal Editor, consider using an Editor Utility Widget instead.

https://dev.epicgames.com/documentation/en-us/unreal-engine/calling-blueprints-in-the-unreal-editor

Hi Kevin, my best guess would be that I’ve got a plugin installed that allows this to happen, as everything basically looks the same. Aside from you not having buttons there are 3 differences:


Your editor Utility Blueprint has an option for “Has Virtualized Data”


I have an option for Level Instance which you don’t seem to have.


In both Custom Events I have an option for Access Specifier

I would assume its the Editor Scripting Utilities plugin that allows me to do this, if not here are all the editor plugins I currently have enabled:



I believe most of these are already enabled by default. Is there a way to see what plugins you’ve enabled that aren’t pre enabelled when creating a level?