CesiumJS es una biblioteca de JavaScript de código abierto y de alto rendimiento diseñada para la creación de aplicaciones de visualización geoespacial 3D basadas en la web. Su motor WebGL permite la representación de grandes conjuntos de datos geográficos, modelos terrestres de alta precisión y la visualización dinámica de datos.
Este artículo se basa en la versión 1.119 de CesiumJS.
Preparación en un entorno Vue 3 con Vite
Para integrar Cesium en un proyecto Vue 3, instale las dependencias necesarias y configure el complemento de Vite.
npm install cesium@1.119
npm install vite-plugin-cesium -D
La configuración en vite.config.js debe incluir el plugin de Cesium.
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue';
import cesium from 'vite-plugin-cesium';
export default defineConfig({
plugins: [vue(), cesium()]
})
Inicialiazción del Visor Cesium
El componente principal es el Viewer. Su inicialización requiere un token de acceso y un contenedor DOM.
<template>
<div id="cesiumViewport"></div>
</template>
<script setup>
import { onMounted } from 'vue';
import * as Cesium from 'cesium';
import 'cesium/Build/Cesium/Cesium';
Cesium.Ion.defaultAccessToken = 'TU_CESIUM_ION_TOKEN';
const initializeViewer = () => {
const viewerInstance = new Cesium.Viewer('cesiumViewport', {
baseLayerPicker: false,
geocoder: false,
sceneModePicker: true,
animation: false,
timeline: false,
vrButton: false,
infoBox: false,
shadows: true,
});
// Ocultar la atribución de Cesium en la esquina
viewerInstance.cesiumWidget.creditContainer.style.display = 'none';
};
onMounted(initializeViewer);
</script>
Carga de Capas de Imágenes
Cesium soporta múltiples proveedores de imágenes, como ArcGIS, Bing Maps y OpenStreetMap. Por defecto, carga Bing Maps si no se especifica otro.
const imageryProvider = new Cesium.OpenStreetMapImageryProvider({
url: 'https://a.tile.openstreetmap.org/'
});
const viewer = new Cesium.Viewer('cesiumViewport', {
imageryProvider: imageryProvider,
});
Integración de Terreno 3D
El terreno mundial de Cesium proporciona un modelo terrestre tridimensional detallado, incluyendo relieve y cuerpos de agua.
async function setupViewerWithTerrain() {
const viewer = new Cesium.Viewer('cesiumViewport', {
terrainProvider: await Cesium.createWorldTerrainAsync({
requestWaterMask: true, // Habilita la detección de cuerpos de agua
requestVertexNormals: true, // Habilita sombreado basado en normales
}),
});
return viewer;
}
Representación de Datos 3D Tiles
3D Tiles es un formato para la transmisión eficiente de grandes modelos 3D, como ciudades enteras. Los datos se dividen en teselas para una carga y renderizado óptimos.
Carga de un conjunto de tiles desde Ion
Los recursos de Cesium Ion se pueden cargar usando su ID de activo.
async function loadCesiumOsmBuildings(viewer) {
const osmBuildingsTileset = await Cesium.Cesium3DTileset.fromIonAssetId(96188);
viewer.scene.primitives.add(osmBuildingsTileset);
// Enfocar la vista en una ubicación específica
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(-74.006, 40.7128, 500),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-45),
roll: 0.0
}
});
}
Estilizado condicional de entidades 3D Tiles
Se pueden aplicar estilos basados en propiedadse como la altura o la ubicación geográfica.
function styleBuildingsByHeight(tileset) {
tileset.style = new Cesium.Cesium3DTileStyle({
color: {
conditions: [
['${Height} >= 200', "color('#FF5733')"], // Color para edificios altos
['${Height} >= 100', "color('#FFC300')"], // Color para edificios medianos
['${Height} >= 50', "color('#DAF7A6')"], // Color para edificios bajos
['true', "color('#900C3F')"] // Color por defecto
]
}
});
}
Manejo de interacción con el ratón
Se pueden detectar eventos como pasar el ratón sobre un edificio para resaltarlo o mostrar información.
function enableBuildingHoverInteraction(viewer, tileset) {
const screenEventHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
let highlightedEntity = null;
const originalColor = new Cesium.Color();
screenEventHandler.setInputAction(function onMouseMove(movement) {
if (highlightedEntity) {
highlightedEntity.color = Cesium.Color.clone(originalColor);
}
const pickedFeature = viewer.scene.pick(movement.endPosition);
if (Cesium.defined(pickedFeature) && pickedFeature instanceof Cesium.Cesium3DTileFeature) {
highlightedEntity = pickedFeature;
Cesium.Color.clone(pickedFeature.color, originalColor);
pickedFeature.color = Cesium.Color.YELLOW;
// Mostrar tooltip con propiedades, ej: pickedFeature.getProperty('name')
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
Visualización de Datos Vectoriales
GeoJSON
GeoJSON es un formato común para intercambiar datos geográficos simples.
async function loadGeoJSONDataSource(viewer, geojsonUrl) {
const geoJsonDataSource = await Cesium.GeoJsonDataSource.load(geojsonUrl, {
stroke: Cesium.Color.GREENYELLOW,
fill: Cesium.Color.GREEN.withAlpha(0.3),
strokeWidth: 2
});
viewer.dataSources.add(geoJsonDataSource);
viewer.zoomTo(geoJsonDataSource);
}
KML y CZML
KML es un formato XML para datos geográficos, mientras que CZML es un formato JSON específico de Cesium para describir escenas dinámicas.
// Ejemplo de carga de KML
async function loadKmlData(viewer, kmlUrl) {
const kmlDataSource = await Cesium.KmlDataSource.load(kmlUrl, {
camera: viewer.scene.camera,
canvas: viewer.scene.canvas
});
viewer.dataSources.add(kmlDataSource);
}
// Ejemplo de carga de CZML
async function loadCzmlData(viewer, czmlUrl) {
const czmlDataSource = await Cesium.CzmlDataSource.load(czmlUrl);
viewer.dataSources.add(czmlDataSource);
}
Importación de Modelos glTF
glTF es un formato eficiente para modelos 3D. Se pueden cargar usando entidades.
function loadGltfModel(viewer, modelUrl, positionDegrees) {
const position = Cesium.Cartesian3.fromDegrees(...positionDegrees);
const modelEntity = viewer.entities.add({
position: position,
model: {
uri: modelUrl,
scale: 1.0,
minimumPixelSize: 64
}
});
viewer.trackedEntity = modelEntity;
}
Configuración de la Escena
El objeto scene controla la apariencia visual global, como el fondo, la atmósfera y el sol.
function configureSceneAppearance(viewer) {
viewer.scene.backgroundColor = Cesium.Color.fromCssColorString('#1a1a2e');
viewer.scene.fog.enabled = true;
viewer.scene.fog.density = 0.0002;
viewer.scene.globe.enableLighting = true;
viewer.scene.sun.show = true;
viewer.scene.moon.show = true;
}
Control de la Cámara
La cámara se puede manipular para establecer vistas iniciales o animaciones de vuelo.
function flyToLocation(viewer, longitude, latitude, height, heading, pitch) {
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
orientation: {
heading: Cesium.Math.toRadians(heading),
pitch: Cesium.Math.toRadians(pitch),
roll: 0.0
},
duration: 3 // Duración de la animación en segundos
});
}
Gestión del Tiempo y Animación
El reloj (clock) gestiona la simulación del tiempo para animaciones.
function setupClockAnimation(viewer, start, stop, speedMultiplier) {
viewer.clock.startTime = Cesium.JulianDate.fromDate(start);
viewer.clock.stopTime = Cesium.JulianDate.fromDate(stop);
viewer.clock.currentTime = Cesium.JulianDate.fromDate(start);
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // Bucle
viewer.clock.multiplier = speedMultiplier;
viewer.clock.shouldAnimate = true;
// Ejemplo de reacción al tick del reloj
viewer.clock.onTick.addEventListener((clock) => {
// Actualizar lógica basada en el tiempo de la simulación
});
}
Creación y Manipulación de Entidades
Las entidades representan objetos en la escena (puntos, líneas, modelos, etc.).
function addComplexEntity(viewer, positionDegrees) {
const position = Cesium.Cartesian3.fromDegrees(...positionDegrees);
const entity = viewer.entities.add({
name: 'Punto de Interés',
position: position,
point: {
pixelSize: 10,
color: Cesium.Color.SKYBLUE,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2
},
label: {
text: 'POI',
font: '14pt sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -9)
},
billboard: {
image: 'ruta/a/icono.png',
width: 32,
height: 32
}
});
return entity;
}
// Eliminar una entidad específica
function removeEntityById(viewer, entityId) {
const entity = viewer.entities.getById(entityId);
if (entity) viewer.entities.remove(entity);
}
// Eliminar todas las entidades
function clearAllEntities(viewer) {
viewer.entities.removeAll();
}