# Extension for automatic ENU registration

**URL:** <https://community.cesium.com/t/extension-for-automatic-enu-registration/40030>\
**Category:** 3D Tiles\
**Created:** [April 16, 2025, 6:23am UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030 "2025-04-16T06:23:14Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 16, 2025, 6:23am UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/1 "2025-04-16T06:23:14Z")

</div>

After studying the 3d tiles spec. and comments here, I think that 3d tiles as content by itself is CRS agnostic, eg. it is not geospatial. This is a bit surprising since Cesium created it and there a quite a few questions here about how to use 3d tiles geospatially.

In the context of Cesium, 3d tiles are expected to deliver (after all transforms) ECEF coordinates if they expect to be used as a geospatially registered dataset. Some 3d tiles are (like the Google Photorealistic tiles), some are not (many examples) and are then often ‘manually’ placed with an additional ENU to ECEF matrix, to a desired location on the globe.

Some users expect that this manual placement would occur automatically, either by consideration of the boundingVolume coordinates (region in latlong radians, box in ECEF meters) since that property appears to be the only one with a potential geospatial significance (other than rarely used metadata semantics). Or with a dedicated CRS property giving the projection. But this is not the case. Instead it is necessary to compute an appropriate transform matrix and add it to the tileset.json which is rather inconvenient or hard without tools.

Since this issue seems to come up regularly, is there a (proposed, vendor) 3d tiles extension (say 3D\_TILES\_BB\_ORIGIN) which places ENU tile content at the origin of the given boundingVolume (or at a dedicated extension property) ? This would cover an important use case, eg. small (building to City) ENU content with known geospatial registration at the center. It would just essentially add (multiply in) the ENU to ECEF matrix at the boundingVolume origin.

Any feedback or thoughts very welcome, Andreas

---

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 16, 2025, 1:46pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/2 "2025-04-16T13:46:58Z")

</div>

A similar feature is provided by RTC\_CENTER supported by B3DM. As pointed out in

> <https://github.com/CesiumGS/3d-tiles/blob/492adb06b00870d9ee99b8d97c261a466783034c/specification/TileFormats/glTF/MIGRATION.adoc>

RTC\_CENTER can be substituted by a additional translation transform at the glTF level, or the tile level.

But this is not the transform which would be needed for a ENU to ECEF transform since such involves also rotation to the ellipsoid/geoid tangential plane at the reference point, in addition to the translation.

Just pointing that out because RTC\_CENTER seems similar. I thought there must have been discussions or extension designs about such functionality but I could only find github issues about how to make that easy with Cesiumjs.

Given that 3d tiles is larger than Cesium I believe it would be beneficial to have a very short and simple extension which abstracts away implementation details.

The probably common use case would be:  
Content authors have glTF tiles (of say pipelines in city) with ENU, local coordinates. They want to geospatially register the tiles, and then use 3d tiles. They do not want to manually (and perhaps incorrectly) compute ECEF coordinates. They do not care about the curvature of the earth (since it is small model). The extension would enable construction of simple tileset json files which then can be used along side other geospatial content (with cesium or other viewers).

Have there been such discussions perhaps around RTC\_CENTER, perhaps some time ago ?

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [April 16, 2025, 4:51pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/3 "2025-04-16T16:51:37Z")

</div>

The underlying topic is very broad, and we are aware of some of the questions that users have. Some of these questions come up pretty frequently, and apply to differerent use-cases and steps in the toolchains.

For the client-side, the goal is often to ~“just place some content at a specific location”, or (slightly more specific) to ~“modify the position of a tileset”. (Some [links in a recent thread](https://community.cesium.com/t/placing-a-tileset-at-the-camera-position-with-correct-rotation/39010/5)). But similar questions apply to earlier steps: People have some “raw/input” data (point clouds, CAD data, GeoJSON, GeoTIFF…). That data may or may not define a CRS in one form or another. And they want to convert this data to use a different CRS (or no CRS at all…). (There are many threads about this - not listing them here explicitly…)

> [@andreasplesch](#):
>
> Some users expect that this manual placement would occur automatically, either by consideration of the boundingVolume coordinates (region in latlong radians, box in ECEF meters) since that property appears to be the only one with a potential geospatial significance (other than rarely used metadata semantics). Or with a dedicated CRS property giving the projection. But this is not the case. Instead it is necessary to compute an appropriate transform matrix and add it to the tileset.json which is rather inconvenient or hard without tools.

The idea of using some extension may be viable. But there are several things that may warrant further discussion.

On the one hand, I see the relevance of the bounding box. The computation of the bounding box that is required in the tileset JSON can be challenging. (It’s not really “difficult” - the main point is that there is no sufficient tooling support). On the other hand, I don’t see why this information should be “associated” with the bounding volume in any way.

Maybe I misunderstood that point. But imagine that you have “some 3D model” (like or a single glTF file). And this should be placed at a certain position on the globe. I think that instead of using a bounding volume, you could just define the cartographic position of where the _coordinate system origin (0,0,0)_ of that object should be. Brainstorming: On the level of the tileset JSON, this could **roughly** be something like

```json
      "3DTILES_geolocation_DRAFT": {
        "longitudeDeg": -80,
        "latitudeDeg": 40,
        "height": 10
      }

```

that could **replace** the `tile.transform` (meaning that the `transform` matrix would just be computed from that information, using functions like the `Transforms.eastNorthUpToFixedFrame` in CesiumJS).

**But…** the potential benefit of that, compared to an actual `transform`, would be _relatively_ low: The `transform` can trivially be computed from that information. So this may rather be a matter of tooling (more on that below).

> [@andreasplesch](#):
>
> RTC\_CENTER can be substituted by a additional translation transform at the glTF level, or the tile level.
> 
> But this is not the transform which would be needed for a ENU to ECEF transform since such involves also rotation to the ellipsoid/geoid tangential plane at the reference point, in addition to the translation.

The `RTC_CENTER` was originally designed in order to avoid a _very_ low-level, technical issue, which is described in [Precisions, Precisions | DME Component Libraries for .NET 2025 r1](https://help.agi.com/STKComponents/html/BlogPrecisionsPrecisions.htm) . And this is indeed not sufficient for “arbitrary placement”. (I think it’s good that this can become obsolete with glTF. Let’s pretend [`CESIUM_RTC`](https://github.com/KhronosGroup/glTF/blob/main/extensions/1.0/Vendor/CESIUM_RTC/README.md) just didn’t exist 🙂 ) Using the glTF node `matrix` is _much_ more versatile.

But still, the question of “where _exactly_ should the geo-placement happen?” is not totally trivial to answer.

I did write a few thoughts and fragments of information, about the options and their pros/cons. For example, in [3DTileset rotation - #2 by Marco13](https://community.cesium.com/t/3dtileset-rotation/27554/2) or [From I3dm to EXT\_mesh\_gpu\_instancing? - #11 by Marco13](https://community.cesium.com/t/from-i3dm-to-ext-mesh-gpu-instancing/19731/11) . And the task to somehow “consolidate” that information and present it in a more approachable form is on my TODO list (albeit with relatively low priority…).

> [@andreasplesch](#):
>
> The probably common use case would be:  
> Content authors have glTF tiles (of say pipelines in city) with ENU, local coordinates. They want to geospatially register the tiles, and then use 3d tiles. They do not want to manually (and perhaps incorrectly) compute ECEF coordinates. They do not care about the curvature of the earth (since it is small model). The extension would enable construction of simple tileset json files which then can be used along side other geospatial content (with cesium or other viewers).

This comes back to the point of “tooling” above:

The use-case that you described is pretty common. And I’ve needed that functionality quite often: Given a glTF file, I wanted a `tileset.json` that just refers to that glTF (and maybe places it at some position on the globe). Therefore, the `3d-tiles-tools` offers the [`createTilesetJson`](https://github.com/CesiumGS/3d-tiles-tools?tab=readme-ov-file#createtilesetjson) command, which does exactly that.

There are **many** functionalities that could be offered, and that fall into a similar category. For example, [Extend tileset creation function to assign multiple positions · Issue #130 · CesiumGS/3d-tiles-tools · GitHub](https://github.com/CesiumGS/3d-tiles-tools/issues/130) , or [Consider a command to re-compute bounding volumes · Issue #106 · CesiumGS/3d-tiles-tools · GitHub](https://github.com/CesiumGS/3d-tiles-tools/issues/106) , and maybe [Consider convenience functions to create instanced models · Issue #84 · CesiumGS/3d-tiles-tools · GitHub](https://github.com/CesiumGS/3d-tiles-tools/issues/84) .

Another one would be a function to “reset” the location/rotation of a whole tileset. This would mean to “pull out” the `RTC_CENTER` of **all** B3DMs, and maybe reset their _orientation_ , with the goal to eventually create a tileset that is located at the origin, is oriented Z-up in ECEF, and that can then be placed on arbitrary points of the globe, by setting the `modelMatrix`, at runtime. Things like that would involve some guesses and heuristics, but could be useful in order to make the “placement of the tileset on the globe” easier for clients.

However, before tackling something like that, there should be a clear(er) idea about the goals, scope, and granularities of these functionalities.

---

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 16, 2025, 5:53pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/4 "2025-04-16T17:53:28Z")

</div>

Thank you much for the comprehensive response which addresses indeed the general issue I tried to raise. There is definitely more to discuss and think about but I do think the concept of shifting the geospatial placement of glTF content (since now preferred) into an extension (and into the client implementation of such) could be quite valuable.

> [@Marco13](#):
>
> On the one hand, I see the relevance of the bounding box. The computation of the bounding box that is required in the tileset JSON can be challenging. (It’s not really “difficult” - the main point is that there is no sufficient tooling support). On the other hand, I don’t see why this information should be “associated” with the bounding volume in any way.

Agreed. The boundingVolume (center) is just the main (and probably only) place where a geolocation center could live outside of a metadata semantic or extension property. It is cleaner and more versatile as you suggest to instead have such extension properties. For example, there are probably many cases where the center of the bounding volume does not naturally correspond to the origin of the ENU, local CRS.

> [@Marco13](#):
>
> Therefore, the `3d-tiles-tools` offers the [`createTilesetJson`](https://github.com/CesiumGS/3d-tiles-tools?tab=readme-ov-file#createtilesetjson) command, which does exactly that.

Yes, that is the equivalent tool which I was not aware of. This is cool and really helpful. There are some additional benefits of making this functionality declarative, in an extension:

- users do not have to know or use the tool or think too much about how this is done since it is now internal to the viewer implementation.
- implementations can approach this in the way they prefer, for example if there is already similar functionality outside of 3d tiles support.

I think focusing a potential extension on just one, limited, well defined target function is possible and would be the most effective. It should be small.

> [@Marco13](#):
>
> There are **many** functionalities that could be offered, and that fall into a similar category. For example, [Extend tileset creation function to assign multiple positions · Issue #130 · CesiumGS/3d-tiles-tools · GitHub](https://github.com/CesiumGS/3d-tiles-tools/issues/130)

This issue does apply but I did not understand why merging after creating Tilesetjsons for single glTFs is not a good option.

It may be insightful to use

> [@Placing multiple models using geographic coordinates](https://community.cesium.com/t/placing-multiple-models-using-geographic-coordinates/30090):
>
> Hello, I’m not quite sure if this is thread should be in the 3D Tiles or CesiumJS category, please move it if you think CesiumJS is more fitting. In our project we currently have a hard time to place multiple glTF models with geographic coordinates. Everything is fine when we have tileset with only one model, since we then use a transform which represents both the tileset’s and the buildings center. But we can not figure out the right way to place a model relative to a tileset’s center, which…

as an example to study if in this case such an extension would be helpful. Just an idea.

My background is that we are considering implementing (partial) 3d tiles support for [X3D](https://www.web3d.org) which has geospatial capabilities including GeoLocation (very similar) and glTF loading.

---

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 16, 2025, 8:52pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/5 "2025-04-16T20:52:32Z")

</div>

A couple more thoughts.

This was not explicitly mentioned but including the geospatial placement in the 3D Tileset via an extension makes it completely standalone and transportable. This of course is true also by just using ECEF coordinates or a matrix transform in the first place but then additional tagging/metadata is needed to confirm to a renderer that the CRS used is indeed ECEF.

One consideration is the case of a Tileset using such an extension within a parent Tileset which is also using the extension. This would be incorrect and should be left to the responsibilities of an author, similar to other transform hierarchies. If there are multiple Tilesets with the same local origin, a wrapping outer Tileset could use the extension to cover all.

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [April 16, 2025, 9:53pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/6 "2025-04-16T21:53:03Z")

</div>

I think that such an extension _could_ make sense. But extensions should be well thought through, considering the implications: Runtime engines must dedicatedly implement support for that extension. And it’s important to find the right balance between making it 1. simple enough to actually bring a benefit/simplifcation, and 2. still making it _powerful_ enough to offer what users _need_.

Some further thoughts:

> [@andreasplesch](#):
>
> There are some additional benefits of making this functionality declarative, in an extension:
> 
> - users do not have to know or use the tool or think too much about how this is done since it is now internal to the viewer implementation.
> - implementations can approach this in the way they prefer, for example if there is already similar functionality outside of 3d tiles support.
> 
> I think focusing a potential extension on just one, limited, well defined target function is possible and would be the most effective. It should be small.

Just to confirm my understanding: The _purpose_ of the extension would then really only be replace the `tile.transform` - is that correct?

You originally mentioned the bounding volume. And I now think that this was only intended as a possible way to transport the lat/lon/height information. But the bounding volume is still one of the requirements for integrating a glTF into a tileset JSON.

Would that extension include anything that is related to the _definition_ of the bounding volume of the glTF?

The point is: If someone has a glTF and wants to “wrap” that into a tileset JSON to place it somewhere on earth, then having such an extension like `{ lat: .. lon:... height:...}` could be simpler and more intuitive than the `transform: [...]`. But it will _still_ need a `boundingVolume`. And if the bounding volume has to be computed with an external tool, then it’s only a small step for that tool to _also_ compute the proper `transform`…

In fact, one of the most important parts of that `createTilesetJson` function is the computation of the bounding volume. This is also “simple”, in some way, because it really is just the bounding box of the glTF, converted into the tileset `boundingVolume.box` representation. But users would still need tooling support for that, i.e. for computing the bounding box of the glTF, converting it into the `boundingVolume.box` representation, and taking into account the up-axis-conversions, like shown in [this snippet](https://github.com/CesiumGS/3d-tiles-samples/tree/main/1.1/BoundingBoxTests#conversion).

Maybe the higher-level question is: Which tools would _produce_ tilesets that _contain_ this extension?

* * *

> [@andreasplesch](#):
>
> This issue does apply but I did not understand why merging after creating Tilesetjsons for single glTFs is not a good option.

There are _many_ options. And in fact, some of the challenges for the whole topic of “geo-placement” **is** exactly that there are so many ways of how it _could_ be represented.

(I wrote much, much more here, but that derailed a bit - trying to focus on your point: )

There are the two options:

1. One could create two `tileset.json` files, each containing one glTF and a geo-locating `transform`, and the merge them into one `tileset.json`
2. One could directly create _one_ `tileset.json`, with _two_ glTF files that should have certain geo-locations

An attempt to illustrate the difference:

 ![Cesium Geolocations](https://global.discourse-cdn.com/cesium/original/3X/b/1/b1b2caac62a21730d138a758ed0b1cb5833e1a4c.png)

I think that the latter is far more flexible and easier to handle. Storing the “geo-locating `transform`” _twice_ is not ideal. And more importantly: When someone wants to _modify_ this, it can become really challenging: There would be two children, and their `transform` arrays are

`-0.12062712460954786,0.9926978879842713,0,0,-0.7706863170115904,-0.09364951363581787,0.6302954619596158,0,0.6256929738933811,0.07603072923063509,0.7763553507467535,0,3998831.079016911,485915.70577487565,4928505.253901741,1`  
and  
`-0.12062712807471718,0.9926978875632031,0,0,-0.7706863166846918,-0.0936495163260206,0.6302954619596157,0,0.6256929736279837,0.07603073141471559,0.7763553507467534,0,3998831.0773207457,485915.71973342943,4928505.253901741,1`

What is that? 😵‍💫

Yeah, it’s just the same geo-position, twice, but a few meters apart. Having that geo-placement _once_, and letting the transform of one child just be  
`1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 1`  
makes it clear: This is moved 10 meters along x.

Another point is about modifications of these transforms _at runtime_ (in CesiumJS). You’re loading a `tileset.json`, and have a tile hierarchy like `root->children[0]->children[4]->glTF`, and the glTF appears at the right place. But you don’t know whether this is caused by one `transform` of the root, a `transform` of one of the children, or a combination thereof. This was one of the quirks that made that [sandcastle in the other post](https://community.cesium.com/t/3dtileset-rotation/27554/2) more complicated, and limitied the applicability of that approach at the same time.

I’m derailing again.  
But … maybe not completely:

> [@andreasplesch](#):
>
> It may be insightful to use  
> …  
> as an example to study if in this case such an extension would be helpful. Just an idea.

This is exactly the case that I described above: Someone knows the _relative_ locations of the buildings: “This building is 10 meters left of this one”. It’s more intuitive to describe these relations _locally_, and then have a _single_ place (at the root of the tileset) that provides the geolocation.

That’s another specific question regarding the extension that you suggested:

Should that extension be applicable to _every_ tile, or _only_ to the `root`?

(I may silently have assumed that it should only be in the `root`. But maybe you intended it to be applicable to _each_ tile individually…?)

* * *

> [@andreasplesch](#):
>
> This was not explicitly mentioned but including the geospatial placement in the 3D Tileset via an extension makes it completely standalone and transportable.

There are several ways of “representing a geo-location”. And I already summarized some of them elsewhere:

1. the actual geometry (vertex data) contains the position and orientation of the model on the globe (this is not recommended, because it leads to the common “Precisions, Precisions” jittering issues)
2. the model has a certain orientation, and the `CESIUM_RTC` extension (somehow deprecated…)
3. the glTF node hierarchy (usually the root node) contains the transform that puts the geometry at a certain position/orientation on the globe
4. the glTF model is referred to as tile content in 3D Tiles, and the tile transform contains the proper transform
5. the whole tileset is put to a certain position/orientation with a 1-liner at runtime, by setting its `modelMatrix`

They are basically sorted by “increasing flexibility”. And I think that the concept of “portability” is important. When you have a glTF that contains a `node.matrix` that places the geometry at a certain geolocation, then this 1. violates some conventions of glTF, and 2. makes it hard to place this glTF _elsewhere_.

Option 4. is the most flexibile one **if** the geolocation should still be encoded in the tileset itself. But this should probably be constrained even more: I think that the _actual_ geo-placement should preferably only happen via the `root.transform` (and the `children[i].transform`s should be the identity, or small, local transforms).

> [@andreasplesch](#):
>
> This of course is true also by just using ECEF coordinates or a matrix transform in the first place but then additional tagging/metadata is needed to confirm to a renderer that the CRS used is indeed ECEF.

Encoding that information _explicitly_ is certainly desirable. There already _is_ a [metadata semantic `TILESET_CRS_GEOCENTRIC`](https://github.com/CesiumGS/3d-tiles/blob/main/specification/Metadata/Semantics/README.adoc#tileset-semantics) for encoding that information. But one could make a case to store this in/as some extension as well.

> [@andreasplesch](#):
>
> One consideration is the case of a Tileset using such an extension within a parent Tileset which is also using the extension.

This is one of the things that has to be thought through. There already are a few aspects of “external tilesets” that are a bit vague and could be specified more clearly. But certain (invalid) combinations of features (in tilesets and their external ones) could probably be ruled out easily by the specification of such an extension.

---

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 17, 2025, 3:35am UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/7 "2025-04-17T03:35:56Z")

</div>

My sense of how extensions fill needs of smaller of wider sections of the community is developed from glTF which is adopted more widely in very diverse settings. So my first thought was to look for an existing, probably single engine, custom, non-ratified extension to adopt or be inspired by. But it looks like nobody tried it perhaps because it is challenging to implement 3d tiles in the first place and it would be time to design one.

> [@Marco13](#):
>
> Just to confirm my understanding: The _purpose_ of the extension would then really only be replace the `tile.transform` - is that correct?

The exact purpose is what we should attempt to delimit here but to me it looks like a replacement of a geospatial tile.transform is a good, easy to understand and limited target. I think it would really be an additional transform expressed as a geolocation, applied after a tile.transform which then could serve other purposes such as scaling.

> [@Marco13](#):
>
> Would that extension include anything that is related to the _definition_ of the bounding volume of the glTF?

I do not think so. The boundingVolume would still be the limits in local coordinates, except for .region which is always global. This means it is necessary to apply the implied ENU to ECEF transform to the boundingVolume to get the global, geospatial box but this is the implementations job to do where this would be needed.

> [@Marco13](#):
>
> And if the bounding volume has to be computed with an external tool, then it’s only a small step for that tool to _also_ compute the proper `transform`…

I am not sure I agree that this is small step for a new tool. These are very different computations, one is finding the min/max (after applying all internal transforms), the other involves WGS84, finding the tangent to an ellipsoid and rotations in a certain order.

> [@Marco13](#):
>
> Maybe the higher-level question is: Which tools would _produce_ tilesets that _contain_ this extension?

I think the point is that it would be pretty straightforward for existing and new tools, including humans, to generate tilesets using the extension.

> [@Marco13](#):
>
> There are _many_ options. And in fact, some of the challenges for the whole topic of “geo-placement” **is** exactly that there are so many ways of how it _could_ be represented.

Very true. That makes it pretty confusing. But in the end it is just a choice an author can make. Although a potential extension is yet another option, I think it could lead to less confusion because its function is almost self explanatory and much needed.

Thanks for explaining the options so well with the nice graphics of the earth.

> [@Marco13](#):
>
> This is exactly the case that I described above: Someone knows the _relative_ locations of the buildings: “This building is 10 meters left of this one”. It’s more intuitive to describe these relations _locally_, and then have a _single_ place (at the root of the tileset) that provides the geolocation.

Yes, exactly. This is well covered with a local transform hierachy, and a final geolocating transform, potentially expressed by an extension.

> [@Marco13](#):
>
> Should that extension be applicable to _every_ tile, or _only_ to the `root`?

I think the extension would only make sense as the final transformation in one of the root nodes.

> [@Marco13](#):
>
> Option 4. is the most flexibile one **if** the geolocation should still be encoded in the tileset itself. But this should probably be constrained even more: I think that the _actual_ geo-placement should preferably only happen via the `root.transform` (and the `children[i].transform`s should be the identity, or small, local transforms).

Yes, exactly. That would be the concept.

> [@Marco13](#):
>
> This is one of the things that has to be thought through.

True, there are probably other, unintended cases. But this is unavoidable since any feature can be misused. So a specification can only make recommendations (only intended for root tiles or highest level before only identity transforms). It will good to think through a few specific examples.

With the risk of creating some fog, let me bring up another, related idea. If the glTF or tileset you have is already in the ECEF frame, it is sometimes useful to transform that to a local ENU frame, for example to deal with smaller numbers. This requires the ECEF to ENU transform matrix which is mathematically the inverse of the ENU to ECEF geolocation transform matrix. So this could perhaps become a similar inverse geolocation extension or perhaps part of this here.

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [April 17, 2025, 3:05pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/8 "2025-04-17T15:05:17Z")

</div>

> [@andreasplesch](#):
>
> My sense of how extensions fill needs of smaller of wider sections of the community is developed from glTF which is adopted more widely in very diverse settings. So my first thought was to look for an existing, probably single engine, custom, non-ratified extension to adopt or be inspired by. But it looks like nobody tried it perhaps because it is challenging to implement 3d tiles in the first place and it would be time to design one.

The comparison to glTF makes sense, but the “variety of things that people want to do with that” is much larger there, and the number of extensions is already huge (and growing). This already _does_ raise the question of how to manage that growing complexity. (A bit of a tangent: I once started [Documenting the extension development process by javagl · Pull Request #2225 · KhronosGroup/glTF · GitHub](https://github.com/KhronosGroup/glTF/pull/2225) , but that has been buried under other tasks in the meantime…)

> [@andreasplesch](#):
>
> I think it would really be an additional transform expressed as a geolocation, applied after a tile.transform which then could serve other purposes such as scaling.

I didn’t consider that, but it makes perfect sense: You could just define the tileset (at the origin, with(!) or without some (local!) `root.transform`). And then you could just add that `{ lat: ..; lon:...; height: ... }`, to place that tileset at a certain position on the globe. (And this is easy - one could even do that with a text editor, or a trivial tool…)

> [@andreasplesch](#):
>
> I do not think so. The boundingVolume would still be the limits in local coordinates, except for .region which is always global. This means it is necessary to apply the implied ENU to ECEF transform to the boundingVolume to get the global, geospatial box but this is the implementations job to do where this would be needed.

The `tile.transform` is applied to the `boundingVolume` anyhow (except for `region`). So this would be “built-in”, and not put a burden on implementors.

My question mainly aimed at the _producing_ side:

> [@andreasplesch](#):
>
> I am not sure I agree that this is small step for a new tool.

Not for a “new” tool, that’s right. Things like `glTF-Transform` offer functions like [`getBounds`](https://gltf-transform.dev/modules/functions/functions/getBounds), and even implementing a basic version of that “manually” may take… maybe 100 lines of code. But computing the transform is _highly_ specific for the geospatial context, and requires a certain infrastructure. So the key is: It is not a small step for a “new” tool (meaning: if this has to be implemented from scratch), but it’s a small step for a tool that already has this infrastructure (i.e. a dependency to CesiumJS).

> [@andreasplesch](#):
>
> Although a potential extension is yet another option, I think it could lead to less confusion because its function is almost self explanatory and much needed.

One important point here is that it could be a _simplification_, and this could lead to some form of “normalization”. Right now, people can use _any_ of the ways that I listed in that bullet point list, in order to “place their data on the globe”. Many of these options have severe drawbacks, and limit the flexibility. If the _easiest_ solution was also the _best_ solution, then… people would just use that.

But for that, this constraint is important:

> [@andreasplesch](#):
>
> I think the extension would only make sense as the final transformation in one of the root nodes.

If people could put this extension _everywhere_, then we’d soon encounter tilesets where the `root` has two `children`, and they _both_ define that extension, with the intention to “place these children at different positions”. This would be… an absolute mess, or at the very least, be “very challenging” to support for runtime engines. Imagine one child has this extension, but the parent defines a `transform` - should that parent transform affect the placement that is determined by the extension? More generally: Many “rules” of the 3D Tiles transform hierarchy would no longer hold, with the most important one being “The global transform of a tile is the product of all transforms on the path from the root to that tile”…

> [@andreasplesch](#):
>
> If the glTF or tileset you have is already in the ECEF frame, it is sometimes useful to transform that to a local ENU frame, for example to deal with smaller numbers. This requires the ECEF to ENU transform matrix which is mathematically the inverse of the ENU to ECEF geolocation transform matrix. So this could perhaps become a similar inverse geolocation extension or perhaps part of this here.

I’d have to think that through. On a high level: One goal of the extension would be to _avoid_ the case where some global transform is baked into the tile `transform`s (thus, avoiding the case where the computation of that inverse is really required). Otherwise: I don’t know how that transform could/should convey that “inverse”. Similar to the extension and its implied transform _itself_: When you have the infrastructure to convert (lat,lon,height) into a 4x4 matrix, then you usually also have the infrastructure to _invert_ this matrix…

---

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 18, 2025, 3:27am UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/9 "2025-04-18T03:27:33Z")

</div>

Thanks for thinking this through. I agree with everything.

> [@Marco13](#):
>
> If people could put this extension _everywhere_, then we’d soon encounter tilesets where the `root` has two `children`, and they _both_ define that extension, with the intention to “place these children at different positions”. This would be… an absolute mess, or at the very least, be “very challenging” to support for runtime engines. Imagine one child has this extension, but the parent defines a `transform` - should that parent transform affect the placement that is determined by the extension? More generally: Many “rules” of the 3D Tiles transform hierarchy would no longer hold, with the most important one being “The global transform of a tile is the product of all transforms on the path from the root to that tile”…

All true but a valid use case may be that there are two siblings with different uses of the extension which have the same parent which only does grouping and has no additional transformation (unless perhaps to another planet, half joking).

That brings up that this extension would only apply to Earth, although it is thinkable to add another property to define the ellipsoid along with the lat, long, height location.

This all suggests that the extension may be best explained and thought of as another transform, just a simpler way to express what otherwise would be a complicated transform in matrix form. It is up to an author to use it correctly, otherwise results can be unexpected or undefined.

Let’s not get sidetracked with the inverse stuff, I apologize.

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [April 18, 2025, 11:35am UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/10 "2025-04-18T11:35:21Z")

</div>

So generally, the meaning of that extension would be: The matrix that is defined with this extension is pre-multiplied to the `transform` of that tile.

Beyond that, for the question of _where_ it can be added:

> [@andreasplesch](#):
>
> All true but a valid use case may be that there are two siblings with different uses of the extension which have the same parent which only does grouping and has no additional transformation

That’s somewhat similar to the case where one tileset refers to two _external_ tilesets that both use this extension. But for both (the “internal” and “external” cases) there could be different strategies:

- Just “allow it everywhere”. It would then be the responsibility of the _creator_ of the tileset to not mess this up. The creator has make sure that there is no parent tile that has a `transform` that contains non-uniform scaling factors or whatnot.
- Only allow it in the `root` of a standalone tileset. (And not even in the roots of _external_ tilesets)

While I do see the possible benefits of being able to add that extension _everywhere_, I’m concerned about the drawbacks:

- Establishing a mechanism that is (in some way) _equivalent_ to the `transform` could lead to a point where people do not use the `transform` any more. Instead, they may have a tileset with 100 buildings, and the position of each building is defined with its own instance of that extension…
- For the runtime engine (like CesiumJS), this increases the implementation effort considerably. (There are quite a few matrices floating around in the tile hierarchy in CesiumJS - I’d _really_ like to avoid increasing the complexity there…).
- One thing that I like about the idea of such an extension in general would then be “lost”. Specifically, I like the idea of having a clean, standard, standalone `tileset.json`, and then essentially “just add one line of JSON, at the top”, to place that whole thing at a specific point on the globe. When the extension may appear “everywhere”, then there is no longer a “single place that defines the geolocation”.

(That last point is roughly related to things like the [ion location editor](https://cesium.com/learn/3d-tiling/ion-tile-set-location/#step-3-fine-tune-the-position-rotation-and-scale-of-your-tileset). One could make a case that the information that is defined there could _directly_ be written into that extension. (Not the translation/rotation/scale, though, but that’s a different topic))

And finally, from a “specification/evolution perspective”: When the extension is allowed everywhere, and this turns out to be a bad idea, then there is zero chance to take it back. That is thinking a bit “far ahead”, very abstractly, and somewhat speculative, but I think that for a _reliable, stable_ standard/specification/file format, one has to take some care to not introduce something that causes fragmentation or inconistencies.

One example: What should clients do that do **not** support the extension? (I.e. technically: Will it be listed in the `extensionsUsed` or in the `extensionsRequired`?). When it is really only used for the geo-placement on the `root`, then clients that do not support the extension can ignore the extension and still load the tileset, and see it with the right structure. (It will be at the center of the earth, but clients can set the `modelMatrix` manually). When the extension is allowed _everywhere_, and a client does not support it, then the whole tileset may be “broken” (like all buildings collapsing at a single point or so…)

Note: All these things are considerations of which I think that they _have_ to be addressed at some point. But my general point of view is that I think that such an extension could be really useful (with all these details to be sorted out). So I think that it could make sense to open an issue (or maybe even a _draft_ PR) in [GitHub - CesiumGS/3d-tiles: Specification for streaming massive heterogeneous 3D geospatial datasets 🌎](https://github.com/CesiumGS/3d-tiles) about this. (I can try to allocate some time for that, but cannot make promises about a timeline right now).

---

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 19, 2025, 3:53am UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/11 "2025-04-19T03:53:11Z")

</div>

> [@Marco13](#):
>
> Only allow it in the `root` of a standalone tileset. (And not even in the roots of _external_ tilesets)

Sounds reasonable but I am bit doubtful that it is possible to have a standalone tileset which uses the extension at its root level but then not be able to have the same tileset as an ‘external’ tileset referenced from a parent tileset. Creators will probably do that anyways. The extension can say that this is not allowed or will lead to undefined behaviour but it might be ok to say in the spec. that this is not recommended and will rarely be useful (until it is).

To make it easy for creators to identify tilesets which require the extension, there could be a recommended convention for naming such json files (“geolocation\_tileset.json”).

> [@Marco13](#):
>
> For the runtime engine (like CesiumJS), this increases the implementation effort considerably.

I would have initially thought the reverse would be true because then all tilesets could be treated the same without exception but I trust your familiarity with the code base.

> [@Marco13](#):
>
> then there is zero chance to take it back.

Good point. It is possible to declare that an engine does not support a given extension anymore. Of course if there is critical mass this becomes difficult. There is probably a path to go from ‘beta’, ‘experimental’ to well supported or even ratified by a group.

> [@Marco13](#):
>
> What should clients do that do **not** support the extension?

The only option they have is to ignore it, perhaps provide a warning and not apply this additional transform matrix. Or, thinking about it, I think glTF requires clients to bail out of rendering if a required extension is not supported ? Perhaps 3d tiles has general extension rules ?

Creators will have to decide if their tileset requires the extension (probably most) or if it is optional (a nice enhancement). That is the choice for any extension.

[extensionUsed could mean that a client should use it only if it is in the actual root but ignore it if the tileset ended up referenced as a child. It might be better to have a dedicated property (boolean onlyAsRoot). Not sure, sounds overcomplicated.]

For me this was all exploratory but Cesiumjs may be in a good position to start to think about an actual implementation. Does Cesiumjs have a ‘plugin’ mechanism (maybe not) ? I will be happy to move further exploration to a github issue. There is no particular timeline.

With regards to performance, this will always require a bit more processing, for a static scene only once initially and for a dynamic scene the transform matrix math would have to happen somewhere in any case.

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [April 19, 2025, 12:34pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/12 "2025-04-19T12:34:57Z")

</div>

> [@andreasplesch](#):
>
> Sounds reasonable but I am bit doubtful that it is possible to have a standalone tileset which uses the extension at its root level but then not be able to have the same tileset as an ‘external’ tileset referenced from a parent tileset. Creators will probably do that anyways.

Yeah … this is a bit of a side-track, but I mentioned earlier:

> [@Marco13](#):
>
> There already are a few aspects of “external tilesets” that are a bit vague and could be specified more clearly.

On the one hand, I _really_ like (and think we should work towards) that idea of 1. each tileset is a standalone, self-contained thing and 2. these tilesets can arbitrarily be combined (and even nested!) as ‘external’ tilesets, to build larger structures.

But on the other hand, there _are_ subtle corner cases. For example, the “metadata schema”: This is defined for a _tileset_. And at runtime. one would expect some `tileset.getMetadataSchema()` function. But when there are multiple external tilesets, then it may be necessary to explicitly access these, to obain _their_ metadata, or … merge metadata schemas at runtime.

The extension that we’re currently considering could be another one where it could be tricky to think through the implications that it will have on the runtime engine.

* * *

The next point… may also be a side-track, but … I think that it’s worth thinking this through:

> [@andreasplesch](#):
>
> I would have initially thought the reverse would be true because then all tilesets could be treated the same without exception but I trust your familiarity with the code base.

I might also not be as familiar with the codebase as I should be (but I’m attributing that, among other things, to the fact that the codebase is _huge_). The point is that “usually” and in the most naive form, you’d have something like a `Tile` class that has a function that returns the “global transform” (i.e. the product of all transforms from the root to the tile). And this can be implemented trivially, as in pseudocode

```auto
getTransform() {
    if (parent == null) return this.transform;
    return parent.getTransform() * this.transform;
}

```

(Yes, the reality already **is** far more complicated!)

Now, with the extension, that `return this.transform` will have to turn into something that checks whether the extension is present, and eventually, goes down to details like whether it’s possible to do `tile.extensions["3DTILES_geolocation"].latitudeDeg = 34;` and runtime. (Note: All this could be fairly simply with certain abstractions, but JavaScript often does not foster certain abstractions…)

* * *

> [@andreasplesch](#):
>
> There is probably a path to go from ‘beta’, ‘experimental’ to well supported or even ratified by a group.

There is no predefined process for the extension development. But given that it could start as some sort of “draft/proposal”, and Cesium itself would implement the extension in multiple environments (CesiumJS and `cesium-native` are ‘multiple’ 🙂 ), I think that this could be a good basis for creating something that is robust, tested, and can be implemented “everywhere”.

* * *

> [@andreasplesch](#):
>
> The only option they have is to ignore it, perhaps provide a warning and not apply this additional transform matrix. Or, thinking about it, I think glTF requires clients to bail out of rendering if a required extension is not supported ? Perhaps 3d tiles has general extension rules ?
> 
> Creators will have to decide if their tileset requires the extension (probably most) or if it is optional (a nice enhancement). That is the choice for any extension.

In glTF, extensions usually say explicitly whether they are in `extensionsUsed` or `extensionsRequired`, and sometimes state _conditions_ for the latter (for example: When there is an explicit “fallback”, then they may not be ‘required’).

> [@andreasplesch](#):
>
> [extensionUsed could mean that a client should use it only if it is in the actual root but ignore it if the tileset ended up referenced as a child. It might be better to have a dedicated property (boolean onlyAsRoot). Not sure, sounds overcomplicated.]

Yeah, in this case, I’d **roughly** say that when the extension is only at the root tile, then it would be optional, but when it is at inner tiles, then it would be ‘required’, but that’s just a first shot for now.

> [@andreasplesch](#):
>
> For me this was all exploratory but Cesiumjs may be in a good position to start to think about an actual implementation. Does Cesiumjs have a ‘plugin’ mechanism (maybe not) ? I will be happy to move further exploration to a github issue. There is no particular timeline.

There is no explicit “plugin” mechanism for that. (One reason, roughly: Extensions could change nearly _everything_. In this example: You’d have that `tile.getTransform()` function, and without anticipating that there _might_ be an extension that affects the `transform`, you’d never add some sort of “plugin-hook” at _exactly_ this place…)

I think that starting an issue or a draft PR with a summary of this discussion here could be a good start.

(Yes, it’s a long discussion, with many details - maybe it’s evident from that: I think that the whole topic of ‘geoplacement (and how to make it easy for users)’ is important, and should get more attention. This extension could be a step in the right direction here).

---

<div class="post-metadata">

**Author:** ![andreasplesch](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/andreasplesch/32/29089_2.png) [@andreasplesch](https://community.cesium.com/u/andreasplesch)\
**Post date:** [April 19, 2025, 8:21pm UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/13 "2025-04-19T20:21:11Z")

</div>

> [@Marco13](#):
>
> In glTF, extensions usually say explicitly whether they are in `extensionsUsed` or `extensionsRequired`, and sometimes state _conditions_ for the latter (for example: When there is an explicit “fallback”, then they may not be ‘required’).

I think the logic is that creators are free to add used extensions to extensionsRequired but then the extension spells what consequences this has.

> <https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md#conformance>

is an example.

[drafting of 3DTILES\_geolocation extension · Issue #798 · CesiumGS/3d-tiles · GitHub](https://github.com/CesiumGS/3d-tiles/issues/798) is a new issue for more detailed drafting.

---

<div class="post-metadata">

**Author:** ![Marco13](https://sea2.discourse-cdn.com/cesium/user_avatar/community.cesium.com/marco13/32/2757_2.png) [@Marco13](https://community.cesium.com/u/Marco13)\
**Post date:** [April 20, 2025, 1:07am UTC](https://community.cesium.com/t/extension-for-automatic-enu-registration/40030/14 "2025-04-20T01:07:01Z")

</div>

Thanks for opening that issue. I’ll try to allocate some time to summarize the discussion from here (not sure when I can do this).
