How to set initial camera view to fit all positions

So there are x amount of points I have on the Cesium map that I place doing positions.forEach((position) => { and const cartesian = new CesiumJs.Cartesian3( c[0], c[1], c[2] );.

Since map view initially loads the whole globe, user needs to zoom in each time. Is there a way that I can set initial view to show all points? I need all points visible on the map at the time of initial load and zoom to be set as zoomed in as possible to show all points. Few approaches I tried like below does the job but if points and their locations change, zoom level needs to be adjusted, which I cannot do dynamically. Please disregard var names since I put them randomly below so I don’t share what I actually use in my code in terms of variable names.

  const positionsList = Object.values(posList).map((pos, id) => {
    return new CesiumJs.Cartesian3(         ...        );
  });

  // Calculate the bounding sphere for all positions
  const boundingSphere = CesiumJs.BoundingSphere.fromPoints(positionsList);

  // Fly the camera to the bounding sphere
  viewer.scene.camera.flyToBoundingSphere(boundingSphere, {
    duration: 0, // Optional: Instant zoom
    offset: new CesiumJs.HeadingPitchRange(
      0.0, // Heading: Keep it 0 for no rotation
      -CesiumJs.Math.PI_OVER_TWO, // Pitch: -90 degrees for top-down view
      boundingSphere.radius * 2.5 // Range: Adjust to fit all points
    ),
  });

These may be two distinct goals:

Since map view initially loads the whole globe, user needs to zoom in each time. Is there a way that I can set initial view to show all points? I need all points visible on the map at the time of initial load and zoom to be set as zoomed in as possible to show all points.

This should be accomplished by the code snippet that you already showed.

Few approaches I tried like below does the job but if points and their locations change, zoom level needs to be adjusted, which I cannot do dynamically.

Dynamically changing the positions and adjusting the view based on that is far more subtle. One could easily come up with cases that raise a bunch of questions here.

(For example, consider a single point on one side of the earth, and adding another point on the other side of the earth. How should the camera behave here? And imagine the second point is then moving to the same position as the first point. What should happen in this case?)

Unless you can “constrain” the movement or make assumptions about the kind of updates for this point set, I think that the most “robust” (or at least, most versatile) solution would be to move the code snippet that you already showed into a function like updateCameraFor(positionsList) and call this whenever the positions change.

This will still raise questions for “large” changes, or when there are really many points in that list. But do you think that this could be a viable approach?

Well, positions do move but I am most concerned with initial load positioning. So at the time of the map loads showing all positions. If there are two possitions that cannot fit in a single view because distance is way too far (on the other part of the world, I guess it’d be safe to avoid trying to set initial camera view in that case. From the way I see, fitting all points into view and zoom are part of same scenario. For example, if we don’t zoom in, of course all points do fit no question about it, but for the user’s perspective if we can zoom in as much as possible, it’d be more useful because seeing dots on a very much zoomed out earth is not really useful, we need zoom to be as down as possible while fitting all points. For example if we do 4 zoom all points fit, but 5 zoom causes 1 point to be outside of view, well we need to be at 4 zoom. This is initial load time only though, once user loads, points get updated and they go outside perimeter of the screen that’s fine their positions got updated.

The code I have sort of fits some of them but doesn’t fit some of them. I tried few other ways like finding left-top right-top left-bottom and right-bottom points, these are specific points based on values I determine that there isn’t another point than the selected left-top one for example, then I set the camera view to that but that didn’t work either…

Essentially this idea stemmed from the situation where each time I refresh the page, I have to zoom in to view positions. I was like it’d be cool if view was perfectly set at initial load so i dont have to zoom in each time i refresh the page. But I cannot set specific spot on map because positions literally could be anywhere, so looking for a dynamic way.

Yes, many details may depend on the exact positions (and how they are distributed on the globe). When you say that the initial view does not contain all positions, then this might just be a small inaccuracy when zooming to the bounding sphere. (In this case, it might already be sufficient to simply enlarge the bounding sphere a bit, say, by scaling the radius with 1.1 or so). For further, more specific hints, posting a Sandcastle with example points (and solution attempts) is usually helpful.

Thank you @Marco13, zooming to boundingSphere was good only issue is if i set that ratio perfectly lets say 1.2, if i run a different scenario where points are elsewhere, now I need to adjust the ratio, so for same scenario ratio works great, when point locations change ratio needs to be updated which cannot work since user cannot update ratio each time they use different scenarios.

Sounds good, I’ll try to do a sandcastle.

Below does the job

  const zoomToExtent = () => {
    if (positions.length === 0) return;

    const cartesianPositions = positions.map(
      (position) => new Cartesian3(...Object.values(position)[0])
    );

    const boundingSphere = BoundingSphere.fromPoints(cartesianPositions);
    const padding = 1.2;

    const maxDist = cartesianPositions.reduce((max, p1) => {
      return cartesianPositions.reduce((innerMax, p2) => {
        const dist = Cartesian3.distance(p1, p2);
        return Math.max(innerMax, dist);
      }, max);
    }, 0);

    const radiusMultiplier = Math.max(3, maxDist / boundingSphere.radius);
    boundingSphere.radius *= padding;

    cesiumViewer.current?.camera.flyToBoundingSphere(boundingSphere, {
      duration: 1.5,
      offset: new HeadingPitchRange(
        0.0,
        -Math.PI / 2,
        boundingSphere.radius * radiusMultiplier
      ),
    });
  };
1 Like