Hi,
I’m generating a grid of points based on where the user clicks on the globe. I can create the gridpoints fine, and they display on the ground properly, but I’m also trying to get the cartesian data associated with these points. Everything I’ve tried doesn’t seem to clamp the points to the ground. Here’s the code (I don’t know how to link a sandcastle project):
const viewer = new Cesium.Viewer("cesiumContainer", {
terrain: Cesium.Terrain.fromWorldTerrain(),
});
const numRowsCols = 10;
const gridSize = 50;
const rectangle = Cesium.Rectangle.fromDegrees(-110.0, 38.0, -105.0, 41.0);
const targetHeight = 3000; // meters above ground
flyToRectangleWithHeight(viewer, rectangle, targetHeight, () => {
waitForTerrainToLoad(viewer).then(() => {
console.log("✅ Terrain loaded at desired height.");
});
});
function generateClampedGrid(centerLat, centerLon, rows, cols, spacingMeters, viewer) {
const metersPerDegreeLat = 111320;
const metersPerDegreeLon = 111320 * Math.cos(Cesium.Math.toRadians(centerLat));
const rowOffset = -(rows - 1) / 2;
const colOffset = -(cols - 1) / 2;
const halfSpacingDegLat = (spacingMeters / 2) / metersPerDegreeLat;
const halfSpacingDegLon = (spacingMeters / 2) / metersPerDegreeLon;
viewer.entities.add({
position: Cesium.Cartesian3.fromRadians(centerLat, centerLon),
point: {
pixelSize: 10,
color: Cesium.Color.RED,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
}
});
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const deltaLat = (r + rowOffset) * spacingMeters / metersPerDegreeLat;
const deltaLon = (c + colOffset) * spacingMeters / metersPerDegreeLon;
const lat = centerLat + deltaLat;
const lon = centerLon + deltaLon;
// console.log(`Row: ${r}, Col: ${c}, Location: ${lat},${lon}`);
// Clamp point to ground
viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(lon, lat),
point: {
pixelSize: 8,
color: Cesium.Color.WHITE,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
}
});
const corners = Cesium.Cartesian3.fromDegreesArray([
lon - halfSpacingDegLon, lat - halfSpacingDegLat,
lon + halfSpacingDegLon, lat - halfSpacingDegLat,
lon + halfSpacingDegLon, lat + halfSpacingDegLat,
lon - halfSpacingDegLon, lat + halfSpacingDegLat
]);
viewer.entities.add({
polygon: {
hierarchy: new Cesium.PolygonHierarchy(corners),
material: Cesium.Color.YELLOW.withAlpha(0.3),
perPositionHeight: false
}
});
}
}
}
function waitForTerrainToLoad(viewer, timeoutMs = 10000) {
return new Promise((resolve, reject) => {
const start = Date.now();
function checkReady() {
const globe = viewer.scene.globe;
const tilesLoaded = globe._surface._tilesToRender.length === 0;
if (tilesLoaded || (Date.now() - start > timeoutMs)) {
resolve(); // resolve after timeout or when terrain is done
} else {
requestAnimationFrame(checkReady);
}
}
checkReady();
});
}
function flyToRectangleWithHeight(viewer, rectangle, height, callback) {
const center = Cesium.Rectangle.center(rectangle);
const destination = Cesium.Cartesian3.fromRadians(center.longitude, center.latitude, height);
viewer.camera.flyTo({
destination: destination,
complete: () => {
console.log("Camera reached target at specified height:", height);
if (typeof callback === "function") {
callback();
}
}
});
}
// A handler hat will respond to left mouse clicks, be performing
// a visibility computation based on a point slightly above the
// clicked point, and visualize the result
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (action) {
const cartesian = viewer.scene.pickPosition(action.position);
if (!cartesian) {
return;
}
const cartographic = Cesium.Cartographic.fromCartesian(
cartesian,
Cesium.Ellipsoid.WGS84,
new Cesium.Cartographic()
);
cartographic.height += 10;
const centerPoint = Cesium.Cartographic.toCartesian(
cartographic,
Cesium.Ellipsoid.WGS84,
new Cesium.Cartesian3()
);
const longitudeDeg = Cesium.Math.toDegrees(cartographic.longitude);
const latitudeDeg = Cesium.Math.toDegrees(cartographic.latitude);
generateClampedGrid(latitudeDeg, longitudeDeg, numRowsCols, numRowsCols, gridSize, viewer);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);