Los mosaicos fotorrealistas en 3D están en el formato glTF estándar de OGC, lo que significa que puedes usar cualquier renderizador que admita la especificación de los mosaicos en 3D de OGC para compilar tus visualizaciones en 3D. Por ejemplo, Cesium es una biblioteca de código abierto fundamental para renderizar visualizaciones en 3D.
Trabaja con CesiumJS
CesiumJS es una biblioteca de JavaScript de código abierto para la visualización en 3D en la Web. Para obtener más información sobre el uso de CesiumJS, consulta Aprende a usar CesiumJS.
Controles de usuario
El renderizador de mosaicos de CesiumJS tiene un conjunto estándar de controles del usuario.
Acción | Descripción |
---|---|
Vista panorámica | Haz clic con el botón izquierdo del mouse y arrastra |
Vista de zoom | Haz clic con el botón derecho y arrastra el mouse, o bien desplaza la rueda del mouse. |
Rotar la vista | Ctrl + clic con el botón izquierdo o derecho y arrastrar, o clic con el botón central y arrastrar |
Prácticas recomendadas
Existen varios enfoques que puedes adoptar para reducir los tiempos de carga en 3D de CesiumJS. Por ejemplo:
Para habilitar las solicitudes simultáneas, agrega la siguiente instrucción a tu HTML de renderización:
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = <REQUEST_COUNT>
Cuanto mayor sea el valor de
REQUEST_COUNT
, más rápido se cargarán las tarjetas. Sin embargo, cuando se carga en un navegador Chrome conREQUEST_COUNT
mayor que 10 y la caché inhabilitada, es posible que se produzca un problema conocido de Chrome. Para la mayoría de los casos de uso, recomendamos unREQUEST_COUNT
de 18 para un rendimiento óptimo.Habilita el salto de niveles de detalle. Para obtener más información, consulta este problema de Cesium.
Habilita showCreditsOnScreen: true
para asegurarte de mostrar correctamente las atribuciones de datos. Para obtener más información, consulta Políticas.
Métricas de renderización
Para encontrar la velocidad de fotogramas, observa cuántas veces por segundo se llama al método requestAnimationFrame.
Para ver cómo se calcula la latencia de fotogramas, consulta la clase PerformanceDisplay.
Ejemplos de renderizador de CesiumJS
Puedes usar el renderizador de CesiumJS con los 3D Tiles de la API de Map Tiles. Para ello, solo debes proporcionar la URL del conjunto de mosaicos raíz.
Ejemplo sencillo
En el siguiente ejemplo, se inicializa el renderizador de CesiumJS y, luego, se carga el conjunto de mosaicos raíz.
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>CesiumJS 3D Tiles Simple Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer.
const viewer = new Cesium.Viewer('cesiumContainer', {
imageryProvider: false,
baseLayerPicker: false,
geocoder: false,
globe: false,
// https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/#enabling-request-render-mode
requestRenderMode: true,
});
// Add 3D Tiles tileset.
const tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is needed to appropriately display attributions
// as required.
showCreditsOnScreen: true,
}));
</script>
</body>
Para obtener información sobre requestRenderMode
, consulta Cómo habilitar el modo de renderización de solicitudes.
La página HTML se renderiza como se muestra aquí.
Integración de la API de Places
Puedes usar CesiumJS con la API de Places para recuperar más información. Puedes usar el widget de Autocomplete para volar al viewport de Places. En este ejemplo, se usan la API de Places Autocomplete, que se habilita siguiendo estas instrucciones, y la API de Maps JavaScript, que se habilita siguiendo estas instrucciones.
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>CesiumJS 3D Tiles Places API Integration Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<label for="pacViewPlace">Go to a place: </label>
<input
type="text"
id="pacViewPlace"
name="pacViewPlace"
placeholder="Enter a location..."
style="width: 300px"
/>
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer.
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: false,
baseLayerPicker: false,
requestRenderMode: true,
geocoder: false,
globe: false,
});
// Add 3D Tiles tileset.
const tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is required to display attributions as required.
showCreditsOnScreen: true,
})
);
const zoomToViewport = (viewport) => {
viewer.entities.add({
polyline: {
positions: Cesium.Cartesian3.fromDegreesArray([
viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
viewport.getSouthWest().lng(), viewport.getNorthEast().lat(),
viewport.getSouthWest().lng(), viewport.getSouthWest().lat(),
viewport.getNorthEast().lng(), viewport.getSouthWest().lat(),
viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
]),
width: 10,
clampToGround: true,
material: Cesium.Color.RED,
},
});
viewer.flyTo(viewer.entities);
};
function initAutocomplete() {
const autocomplete = new google.maps.places.Autocomplete(
document.getElementById("pacViewPlace"),
{
fields: [
"geometry",
"name",
],
}
);
autocomplete.addListener("place_changed", () => {
viewer.entities.removeAll();
const place = autocomplete.getPlace();
if (!place.geometry || !place.geometry.viewport) {
window.alert("No viewport for input: " + place.name);
return;
}
zoomToViewport(place.geometry.viewport);
});
}
</script>
<script
async=""
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"
></script>
</body>
Vista rotativa del dron
Puedes controlar la cámara para animar el conjunto de mosaicos. Cuando se combina con la API de Places y la API de Elevation, esta animación simula un sobrevuelo interactivo con dron de cualquier punto de interés.
Esta muestra de código te lleva a volar alrededor del lugar que seleccionaste en el widget de Autocomplete.
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>CesiumJS 3D Tiles Rotating Drone View Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<label for="pacViewPlace">Go to a place: </label>
<input type="text" id="pacViewPlace" name="pacViewPlace" placeholder="Enter a location..." style="width: 300px" />
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer and remove unneeded options.
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: false,
baseLayerPicker: false,
homeButton: false,
fullscreenButton: false,
navigationHelpButton: false,
vrButton: false,
sceneModePicker: false,
geocoder: false,
globe: false,
infobox: false,
selectionIndicator: false,
timeline: false,
projectionPicker: false,
clockViewModel: null,
animation: false,
requestRenderMode: true,
});
// Add 3D Tile set.
const tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is required to display attributions.
showCreditsOnScreen: true,
})
);
// Point the camera at a location and elevation, at a viewport-appropriate distance.
function pointCameraAt(location, viewport, elevation) {
const distance = Cesium.Cartesian3.distance(
Cesium.Cartesian3.fromDegrees(
viewport.getSouthWest().lng(), viewport.getSouthWest().lat(), elevation),
Cesium.Cartesian3.fromDegrees(
viewport.getNorthEast().lng(), viewport.getNorthEast().lat(), elevation)
) / 2;
const target = new Cesium.Cartesian3.fromDegrees(location.lng(), location.lat(), elevation);
const pitch = -Math.PI / 4;
const heading = 0;
viewer.camera.lookAt(target, new Cesium.HeadingPitchRange(heading, pitch, distance));
}
// Rotate the camera around a location and elevation, at a viewport-appropriate distance.
let unsubscribe = null;
function rotateCameraAround(location, viewport, elevation) {
if(unsubscribe) unsubscribe();
pointCameraAt(location, viewport, elevation);
unsubscribe = viewer.clock.onTick.addEventListener(() => {
viewer.camera.rotate(Cesium.Cartesian3.UNIT_Z);
});
}
function initAutocomplete() {
const autocomplete = new google.maps.places.Autocomplete(
document.getElementById("pacViewPlace"), {
fields: [
"geometry",
"name",
],
}
);
autocomplete.addListener("place_changed", async () => {
const place = autocomplete.getPlace();
if (!(place.geometry && place.geometry.viewport && place.geometry.location)) {
window.alert(`Insufficient geometry data for place: ${place.name}`);
return;
}
// Get place elevation using the ElevationService.
const elevatorService = new google.maps.ElevationService();
const elevationResponse = await elevatorService.getElevationForLocations({
locations: [place.geometry.location],
});
if(!(elevationResponse.results && elevationResponse.results.length)){
window.alert(`Insufficient elevation data for place: ${place.name}`);
return;
}
const elevation = elevationResponse.results[0].elevation || 10;
rotateCameraAround(
place.geometry.location,
place.geometry.viewport,
elevation
);
});
}
</script>
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"></script>
</body>
Cómo dibujar polilíneas y etiquetas
En este ejemplo de código, se muestra cómo agregar polilíneas y etiquetas a un mapa. Puedes agregar polilíneas a un mapa para mostrar instrucciones sobre cómo llegar a un lugar a pie o en automóvil, o bien para mostrar los límites de una propiedad o calcular las duraciones de los viajes a pie o en automóvil. También puedes obtener atributos sin renderizar la escena.
Podrías llevar a los usuarios a un recorrido guiado por un vecindario o mostrar las propiedades vecinas que están a la venta y, luego, agregar objetos 3D, como vallas publicitarias, a la escena.
Podrías resumir un viaje, enumerar las propiedades que viste y mostrar estos detalles en objetos virtuales.
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<title>CesiumJS 3D Tiles Polyline and Label Demo</title>
<script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
<link
href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"
/>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
// Enable simultaneous requests.
Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;
// Create the viewer.
const viewer = new Cesium.Viewer("cesiumContainer", {
imageryProvider: false,
baseLayerPicker: false,
requestRenderMode: true,
geocoder: false,
globe: false,
});
// Add 3D Tiles tileset.
const tileset = viewer.scene.primitives.add(
new Cesium.Cesium3DTileset({
url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
// This property is required to display attributions as required.
showCreditsOnScreen: true,
})
);
// Draws a circle at the position, and a line from the previous position.
const drawPointAndLine = (position, prevPosition) => {
viewer.entities.removeAll();
if (prevPosition) {
viewer.entities.add({
polyline: {
positions: [prevPosition, position],
width: 3,
material: Cesium.Color.WHITE,
clampToGround: true,
classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
},
});
}
viewer.entities.add({
position: position,
ellipsoid: {
radii: new Cesium.Cartesian3(1, 1, 1),
material: Cesium.Color.RED,
},
});
};
// Compute, draw, and display the position's height relative to the previous position.
var prevPosition;
const processHeights = (newPosition) => {
drawPointAndLine(newPosition, prevPosition);
const newHeight = Cesium.Cartographic.fromCartesian(newPosition).height;
let labelText = "Current altitude (meters above sea level):\n\t" + newHeight;
if (prevPosition) {
const prevHeight =
Cesium.Cartographic.fromCartesian(prevPosition).height;
labelText += "\nHeight from previous point (meters):\n\t" + Math.abs(newHeight - prevHeight);
}
viewer.entities.add({
position: newPosition,
label: {
text: labelText,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
pixelOffset: new Cesium.Cartesian2(0, -10),
showBackground: true,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
}
});
prevPosition = newPosition;
};
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(function (event) {
const earthPosition = viewer.scene.pickPosition(event.position);
if (Cesium.defined(earthPosition)) {
processHeights(earthPosition);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
</script>
</body>
Órbita de la cámara
En Cesium, puedes orbitar la cámara alrededor de un punto de interés y evitar colisiones con edificios. Como alternativa, puedes hacer que los edificios sean transparentes cuando la cámara los atraviese.
Primero, fija la cámara en un punto y, luego, podrás crear una órbita de cámara para mostrar tu recurso. Puedes hacerlo con la función lookAtTransform
de la cámara con un objeto de escucha de eventos, como se muestra en este ejemplo de código.
// Lock the camera onto a point.
const center = Cesium.Cartesian3.fromRadians(
2.4213211833389243,
0.6171926869414084,
3626.0426275055174
);
const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
viewer.scene.camera.lookAtTransform(
transform,
new Cesium.HeadingPitchRange(0, -Math.PI / 8, 2900)
);
// Orbit around this point.
viewer.clock.onTick.addEventListener(function (clock) {
viewer.scene.camera.rotateRight(0.005);
});
Para obtener más información sobre cómo controlar la cámara, consulta Cómo controlar la cámara.
Trabaja con Cesium para Unreal
Para usar el complemento de Cesium para Unreal con la API de 3D Tiles, sigue los pasos que se indican a continuación.
Instala el complemento de Cesium para Unreal.
Crea un proyecto nuevo de Unreal.
Conéctate a la API de Photorealistic 3D Tiles de Google.
Para abrir la ventana de Cesium, selecciona Cesium > Cesium en el menú.
Selecciona Blank 3D Tiles Tileset.
En World Outliner, selecciona este Cesium3DTileset para abrir el panel Details.
Cambia la fuente de From Cesium Ion a From URL.
Establece la URL como la URL de Google 3D Tiles.
https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
- Habilita Show Credits On Screen para mostrar las atribuciones correctamente.
Esto carga el mundo. Para moverte a cualquier LatLng, selecciona el elemento CesiumGeoreference en el panel Esquema y, luego, edita la Latitud/longitud/altura de origen en el panel Detalles.
Trabaja con Cesium for Unity
Para usar mosaicos fotorrealistas con Cesium for Unity, sigue los pasos que se indican a continuación.
Crea un proyecto nuevo de Unity.
Agrega un nuevo registro con alcance en la sección Package Manager (a través de Editor > Project Settings).
Nombre: Cesium
URL: https://unity.pkg.cesium.com
Permisos: com.cesium.unity
Instala el paquete de Cesium para Unity.
Conéctate a la API de Photorealistic 3D Tiles de Google.
Para abrir la ventana de Cesium, selecciona Cesium > Cesium en el menú.
Haz clic en Blank 3D Tiles Tileset.
En el panel lateral izquierdo, en la opción Tileset Source de Source, selecciona From URL (en lugar de From Cesium Ion).
Establece la URL en la URL de Google 3D Tiles.
https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
- Habilita Show Credits On Screen para mostrar las atribuciones correctamente.
Esto carga el mundo. Para moverte a cualquier LatLng, selecciona el elemento CesiumGeoreference en la Jerarquía de la escena y, luego, edita la latitud, la longitud y la altura del origen en el Inspector.
Trabaja con deck.gl
deck.gl, con tecnología de WebGL, es un framework de JavaScript de código abierto para visualizaciones de datos a gran escala y de alto rendimiento.
Atribución
Asegúrate de mostrar correctamente las atribuciones de datos extrayendo el campo copyright
de los archivos gltf de las tarjetas asset
y, luego, mostrándolo en la vista renderizada. Para obtener más información, consulta Cómo mostrar las atribuciones de datos.
Ejemplos de renderizador de deck.gl
Ejemplo sencillo
En el siguiente ejemplo, se inicializa el renderizador de deck.gl y, luego, se carga un lugar en 3D. En tu código, asegúrate de reemplazar YOUR_API_KEY por tu clave de API real.
<!DOCTYPE html>
<html>
<head>
<title>deck.gl Photorealistic 3D Tiles example</title>
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
<style>
body { margin: 0; padding: 0;}
#map { position: absolute; top: 0;bottom: 0;width: 100%;}
#credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
</style>
</head>
<body>
<div id="map"></div>
<div id="credits"></div>
<script>
const GOOGLE_API_KEY = YOUR_API_KEY;
const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
const creditsElement = document.getElementById('credits');
new deck.DeckGL({
container: 'map',
initialViewState: {
latitude: 50.0890,
longitude: 14.4196,
zoom: 16,
bearing: 90,
pitch: 60,
height: 200
},
controller: {minZoom: 8},
layers: [
new deck.Tile3DLayer({
id: 'google-3d-tiles',
data: TILESET_URL,
loadOptions: {
fetch: {
headers: {
'X-GOOG-API-KEY': GOOGLE_API_KEY
}
}
},
onTilesetLoad: tileset3d => {
tileset3d.options.onTraversalComplete = selectedTiles => {
const credits = new Set();
selectedTiles.forEach(tile => {
const {copyright} = tile.content.gltf.asset;
copyright.split(';').forEach(credits.add, credits);
creditsElement.innerHTML = [...credits].join('; ');
});
return selectedTiles;
}
}
})
]
});
</script>
</body>
</html>
Visualiza capas 2D sobre los mosaicos fotorrealistas en 3D de Google
La TerrainExtension de deck.gl renderiza datos que, de otro modo, serían en 2D en una superficie 3D. Por ejemplo, puedes superponer el GeoJSON de la huella de un edificio sobre la geometría de los mosaicos fotorrealistas en 3D.
En el siguiente ejemplo, se visualiza una capa de edificios con los polígonos adaptados a la superficie de los mosaicos fotorrealistas en 3D.
<!DOCTYPE html>
<html>
<head>
<title>Google 3D tiles example</title>
<script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
<style>
body { margin: 0; padding: 0;}
#map { position: absolute; top: 0;bottom: 0;width: 100%;}
#credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
</style>
</head>
<body>
<div id="map"></div>
<div id="credits"></div>
<script>
const GOOGLE_API_KEY = YOUR_API_KEY;
const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
const BUILDINGS_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson'
const creditsElement = document.getElementById('credits');
const deckgl = new deck.DeckGL({
container: 'map',
initialViewState: {
latitude: 50.0890,
longitude: 14.4196,
zoom: 16,
bearing: 90,
pitch: 60,
height: 200
},
controller: true,
layers: [
new deck.Tile3DLayer({
id: 'google-3d-tiles',
data: TILESET_URL,
loadOptions: {
fetch: {
headers: {
'X-GOOG-API-KEY': GOOGLE_API_KEY
}
}
},
onTilesetLoad: tileset3d => {
tileset3d.options.onTraversalComplete = selectedTiles => {
const credits = new Set();
selectedTiles.forEach(tile => {
const {copyright} = tile.content.gltf.asset;
copyright.split(';').forEach(credits.add, credits);
creditsElement.innerHTML = [...credits].join('; ');
});
return selectedTiles;
}
},
operation: 'terrain+draw'
}),
new deck.GeoJsonLayer({
id: 'buildings',
// This dataset is created by CARTO, using other Open Datasets available. More info at: https://3dtiles.carto.com/#about.
data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson',
stroked: false,
filled: true,
getFillColor: ({properties}) => {
const {tpp} = properties;
// quantiles break
if (tpp < 0.6249)
return [254, 246, 181]
else if (tpp < 0.6780)
return [255, 194, 133]
else if (tpp < 0.8594)
return [250, 138, 118]
return [225, 83, 131]
},
opacity: 0.2,
extensions: [new deck._TerrainExtension()]
})
]
});
</script>
</body>
</html>