Skip to content

Instantly share code, notes, and snippets.

@datadavev
Last active August 4, 2024 18:57
Show Gist options
  • Save datadavev/5579bc1a30b2c569a5e7e944f7564aeb to your computer and use it in GitHub Desktop.
Save datadavev/5579bc1a30b2c569a5e7e944f7564aeb to your computer and use it in GitHub Desktop.
Cesium computeViewRectangle heading south
/**************************************************************************************************
* "Patch" for Cesium camera computeViewRectangle.
*
* This path provides a modified implementation of computeViewRectangle that
* returns the correct view rectangle when the camera is oriented towards the south
* and the horizon is visible. The default implementation truncates the view rectangle
* at a lower corner of the view which can significantly reduce the reported view
* rectangle.
*
* Load this in sandcastle.
*/
const VIEWER = new Cesium.Viewer(
'cesiumContainer', {
timeline: false,
animation: false,
baseLayerPicker: false,
});
let worldTerrain;
try {
worldTerrain = await Cesium.createWorldTerrainAsync();
VIEWER.scene.terrainProvider = worldTerrain;
// Turn off the default globe so we can get to bathymetry view
VIEWER.scene.globe.show = false;
} catch (error) {
window.alert(`There was an error creating world terrain. ${error}`);
}
let worldTileset;
try {
worldTileset = await Cesium.createGooglePhotorealistic3DTileset();
VIEWER.scene.primitives.add(worldTileset);
} catch (error) {
console.log(`Error loading Photorealistic 3D Tiles tileset.
${error}`
);
}
VIEWER.scene.globe.depthTestAgainstTerrain = true;
const TOOLBAR = document.getElementById('toolbar');
// The global bounding box latitude and longitude values.
const GLOBAL_RECT = new Cesium.Rectangle(-Cesium.Math.PI, -Cesium.Math.PI_OVER_TWO, Cesium.Math.PI, Cesium.Math.PI_OVER_TWO);
let bbEntity = null;
/**
* Scans vertically at x pixels min_y to max_y to find a pixel that intersect
* the ellipsoid.
*
* In screen space canvas, 0,0 is the top left corner.
* Returns the cartographic coordinates of the picked point or null if
* there is no intersection.
*/
function computeHorizonPointY(camera, ellips, x, min_y, max_y) {
for (let j=min_y; j < max_y; j += 5) {
const cp = camera.pickEllipsoid(new Cesium.Cartesian2(x, j), ellips);
if (cp) {
return ellips.cartesianToCartographic(cp);
}
}
return null;
}
/**
* Compute if point is visible in current view
* p: Cartographic3
*/
function isPositionVisible(camera, ellips, cartesian) {
const frustum = camera.frustum;
const cullingVolume = frustum.computeCullingVolume(
camera.position,
camera.direction,
camera.up
);
const intersection = cullingVolume.computeVisibility(new Cesium.BoundingSphere(cartesian, 0.0));
if (intersection === Cesium.Intersect.INSIDE) {
const globeBoundingSphere = new Cesium.BoundingSphere(
Cesium.Cartesian3.ZERO,
ellips.minimumRadius
);
const occluder = new Cesium.Occluder(
globeBoundingSphere,
camera.position
)
return occluder.isPointVisible(cartesian);
}
return false;
}
/**
* Return 0 if neither visible, -1 for south, 1 for north
*/
function isPoleVisible(camera, ellips) {
const NORTH_POLE = Cesium.Cartographic.toCartesian(
Cesium.Cartographic.fromDegrees(0.0, 90.0, 1.0, new Cesium.Cartographic()),
ellips,
new Cesium.Cartesian3()
);
if (isPositionVisible(camera, ellips, NORTH_POLE)) {
return 1;
}
const SOUTH_POLE = Cesium.Cartographic.toCartesian(
Cesium.Cartographic.fromDegrees(0.0, -90.0, 1.0, new Cesium.Cartographic()),
ellips,
new Cesium.Cartesian3()
);
if (isPositionVisible(camera, ellips, SOUTH_POLE)) {
return -1;
}
return 0;
}
/**
* Computes five cartographic points corresponding with the canvas top left,
* top middle, top right, lower right, and lower left.
*
* The top three points are computed at the intersection of that column of pixels
* with the ellpsoid.
*
* null is returned for any point that does not intersect with the ellpsoid.
*/
function computeViewHorizonPoints(canvas, camera, ellips) {
const xs = [0, Math.floor(canvas.width/2), canvas.width];
const points = [null, null, null, null, null];
for (let i=0; i<3; i++) {
points[i] = computeHorizonPointY(camera, ellips, xs[i], 0, canvas.height);
}
points[3] = computeHorizonPointY(camera, ellips, xs[0], canvas.height-1, canvas.height);
points[4] = computeHorizonPointY(camera, ellips, xs[2], canvas.height-1, canvas.height);
return points;
}
/**
* Given a list of cartographic points, compute
* the bounding rectangle for the points.
*/
function pointsToBoundingRectangle(camera, ellips, points, result) {
for (let i=0; i < points.length; i++) {
if (!points[i]) {
return GLOBAL_RECT;
}
}
result = Cesium.Rectangle.fromCartographicArray(points, result);
const pvisible = isPoleVisible(camera, ellips);
if (pvisible === 1) {
result.west = -Cesium.Math.PI;
result.east = Cesium.Math.PI;
result.north = Cesium.Math.PI_OVER_TWO;
} else if (pvisible === -1) {
result.west = -Cesium.Math.PI;
result.east = Cesium.Math.PI;
result.south = -Cesium.Math.PI_OVER_TWO;
}
return result;
}
/**
* Compute the (approximate) bounding rectangle of the camera view.
*
* returns Cesium.Rectangle
*/
Cesium.Camera.prototype.computeViewRectangle2 = function(ellips, result) {
const points = computeViewHorizonPoints(this._scene.canvas, this, ellips);
return pointsToBoundingRectangle(this, ellips, points, result);
}
const handler = new Cesium.ScreenSpaceEventHandler(VIEWER.canvas);
handler.setInputAction((click) => {
const bb = VIEWER.camera.computeViewRectangle2(VIEWER.scene.globe.ellipsoid, new Cesium.Rectangle());
TOOLBAR.innerHTML = `<pre>
west: ${Cesium.Math.toDegrees(bb.west).toFixed(2)}
south:${Cesium.Math.toDegrees(bb.south).toFixed(2)}
east:${Cesium.Math.toDegrees(bb.east).toFixed(2)}
north:${Cesium.Math.toDegrees(bb.north).toFixed(2)}</pre>`;
if (bb === GLOBAL_RECT) {
if (bbEntity) {
bbEntity.display = false;
}
return;
};
if (bbEntity) {
bbEntity.rectangle.coordinates = bb;
bbEntity.display = true;
} else {
bbEntity = VIEWER.entities.add({
rectangle:{
coordinates: bb,
material: Cesium.Color.RED.withAlpha(0.3),
clampToGround: true
// Can't draw an outline when clamped to the ground an showing terrain.
//outline: true,
//outlineColor: Cesium.Color.YELLOW,
//outlineWidth: 5.0,
//height: 0
}
});
}
}, Cesium.ScreenSpaceEventType.LEFT_UP);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment