Aligning Non-Georeferenced Models Using Two Control Points in Cesium

Hello,
I would like to align non-georeferenced models (loaded as Cesium3DTileset) to real-world positions using two control points: two points in the model and two corresponding points in the real world.

My initial approach was as follows:

  1. I defined the real-world coordinates using Cartesian3.fromDegrees and the model coordinates using, for example:
const ModelPos1 = Cartesian3.fromElements(-10, -9, 0);  
const ModelPos2 = Cartesian3.fromElements(2, 9, -14);  
  1. I then translated the model so that ModelPos1 overlapped with WorldPos1.
  2. Next, I calculated the distance between ModelPos1 and ModelPos2 and the distance between WorldPos1 and WorldPos2 to determine the scaling factor.

Now, I need to rotate the model around ModelPos1 so that ModelPos2 aligns with WorldPos2. How can I achieve this? Alternatively, is there a better way to align the model using these four points?

Thank you in advance for your help!

I think that the constraints that you described so far are not enough. At least, if I understood the goal correctly.

You have two points of the model, and want these two points to match two selected world positions. In 2D, it would look like this:

In 2D, that transformation would be uniquely identified by the two source- and target points. But in 3D, there’s an additional degree of freedom: The model could be rotated around the line that connects these two points.

I assume that there is some concept of an “up-direction” that should be preserved, is that correct? (If not, it would be necessary to describe what the desired orientation should be…)

Yes, the “up-direction” should be preserved. No roll.

I have this example with a test house .The red box should be aligned with the green box and blue with yellow. red and blues positions are:

const ModelPos1 = Cartesian3.fromElements(-10, -9, 0);
const ModelPos2 = Cartesian3.fromElements(2, 9, -14);

I used this to display them:

const newP1 = new Cartesian3() 
Cartesian3.add(modelPosition,ModelPos1,newP1);
(redBox.position as any).setValue(newP1);

I expected that. But … technically, the problem is now overconstrained :slight_smile:

The point is that when the “height above the ground” of the target (world) points is “wrong”, then there are two options:

  • The up-axis (normal) has to change
  • or the points can not be “reached”

Or, as an attempt to illustrate:

However, I can roughly imagine what you are trying to achieve. There still are a few unknowns or details. And these details include the question about how the geo-position of the input model was determined to begin with (i.e. does the tileset have a tileset.root.transform that is not the identity matrix?)

But here is an example Sandcastle, with a few assumptions, but also with a few code snippets that may be helpful for the overall goal:

https://sandcastle.cesium.com/index.html#c=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

The sandcastle loads the same tileset twice (namely, the tileset from the 3D Tiles Photogrammetry Example Sandcastle).

Once, the tileset is shown in its original configuration. And once with a different “model matrix”. The sandcastle also shows a few spheres:

  • The red and green sphere are the minimum/maximum point of the original tileset
  • The blue and yellow spheres are the “world positions” of these points - i.e. the positions that these points should have

From these points, it computes a model matrix that is applied to the second tileset, so that its corner points end up at the desired positions.

You’re right about the overconstraint. But it is what I was asked to implement. The inaccuracies between your red and orange arrows should be negligible.
Thank you for thinking along, the pictures and the sandbox sample solution. This will help me a lot.
(Btw, the tileset has identity as root.transform)