google-site-verification: google61fe8ba583a51912.html
CesiumJS 3D Globes Integration with Java Backend

CesiumJS is a powerful JavaScript library for creating 3D globes and maps in the browser. This guide covers integrating CesiumJS with Java backend services to create dynamic, data-driven 3D geographic applications.


Architecture Overview

Frontend (CesiumJS):

  • 3D globe rendering and visualization
  • User interaction and camera control
  • Data visualization (points, lines, polygons, 3D models)
  • Time-dynamic data handling

Backend (Java/Spring Boot):

  • Data processing and business logic
  • GeoJSON/CZML generation
  • Spatial database operations
  • REST API endpoints
  • Authentication and authorization

Dependencies and Setup

1. Frontend Dependencies
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>CesiumJS Globe with Java Backend</title> <!-- CesiumJS CSS --> <script src="https://cesium.com/downloads/cesiumjs/releases/1.107/Build/Cesium/Cesium.js"></script> <link href="https://cesium.com/downloads/cesiumjs/releases/1.107/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> <!-- Bootstrap for UI components --> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> <style> html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; } .control-panel { position: absolute; top: 10px; left: 10px; background: rgba(42, 42, 42, 0.9); padding: 15px; border-radius: 5px; color: white; z-index: 1000; min-width: 300px; } </style> </head> <body> <div id="cesiumContainer"></div> <!-- Control Panel --> <div class="control-panel"> <h5>Globe Controls</h5> <div class="mb-3"> <label class="form-label">Data Layer</label> <select id="dataLayer" class="form-select"> <option value="earthquakes">Earthquakes</option> <option value="satellites">Satellites</option> <option value="aircraft">Aircraft</option> </select> </div> <button id="loadData" class="btn btn-primary btn-sm">Load Data</button> <button id="clearData" class="btn btn-secondary btn-sm">Clear</button> </div> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script> <script src="js/app.js"></script> </body> </html>
2. Java Backend Dependencies
<!-- pom.xml --> <properties> <spring-boot.version>3.1.0</spring-boot.version> <jackson.version>2.15.2</jackson.version> <jts.version>1.19.0</jts.version> </properties> <dependencies> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>${spring-boot.version}</version> </dependency> <!-- JSON Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <!-- Spatial --> <dependency> <groupId>org.locationtech.jts</groupId> <artifactId>jts-core</artifactId> <version>${jts.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-spatial</artifactId> <version>6.3.0.Final</version> </dependency> <!-- Database --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <!-- Testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring-boot.version}</version> <scope>test</scope> </dependency> </dependencies>
3. Spring Boot Configuration
// application.yml server: port: 8080 spring: datasource: url: jdbc:postgresql://localhost:5432/cesium_db username: postgres password: password driver-class-name: org.postgresql.Driver jpa: hibernate: ddl-auto: update properties: hibernate: dialect: org.hibernate.dialect.PostgreSQLDialect format_sql: true show-sql: true servlet: multipart: max-file-size: 10MB max-request-size: 10MB cesium: ion-token: YOUR_CESIUM_ION_TOKEN terrain-url: https://assets.cesium.com/1/

Java Backend Implementation

1. Domain Models
@Entity @Table(name = "earthquakes") public class Earthquake { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String eventId; private String title; private Double magnitude; private Double depth; @Column(columnDefinition = "geometry(Point,4326)") private Point location; private LocalDateTime eventTime; private String region; // Constructors, getters, setters public Earthquake() {} public Earthquake(String eventId, String title, Double magnitude, Double depth, Point location, LocalDateTime eventTime, String region) { this.eventId = eventId; this.title = title; this.magnitude = magnitude; this.depth = depth; this.location = location; this.eventTime = eventTime; this.region = region; } // Getters and setters... } @Entity @Table(name = "satellites") public class Satellite { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String noradId; private String type; @Column(columnDefinition = "geometry(Point,4326)") private Point position; private Double altitude; private Double velocity; private LocalDateTime timestamp; // Constructors, getters, setters... } @Entity @Table(name = "aircraft") public class Aircraft { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String icao24; private String callsign; private String originCountry; @Column(columnDefinition = "geometry(Point,4326)") private Point position; private Double longitude; private Double latitude; private Double altitude; private Double velocity; private Double heading; private LocalDateTime timestamp; // Constructors, getters, setters... }
2. Repository Layer
@Repository public interface EarthquakeRepository extends JpaRepository<Earthquake, Long> { @Query("SELECT e FROM Earthquake e WHERE e.eventTime BETWEEN :startDate AND :endDate") List<Earthquake> findByEventTimeBetween(@Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate); @Query(value = "SELECT * FROM earthquakes WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(:lng, :lat), 4326), :radius)", nativeQuery = true) List<Earthquake> findNearLocation(@Param("lng") Double longitude, @Param("lat") Double latitude, @Param("radius") Double radiusKm); List<Earthquake> findByMagnitudeGreaterThanEqual(Double minMagnitude); } @Repository public interface SatelliteRepository extends JpaRepository<Satellite, Long> { @Query("SELECT s FROM Satellite s WHERE s.timestamp > :since") List<Satellite> findRecentSatellites(@Param("since") LocalDateTime since); List<Satellite> findByType(String type); } @Repository public interface AircraftRepository extends JpaRepository<Aircraft, Long> { @Query("SELECT a FROM Aircraft a WHERE a.timestamp > :since") List<Aircraft> findRecentAircraft(@Param("since") LocalDateTime since); List<Aircraft> findByOriginCountry(String country); }
3. GeoJSON Service
@Service public class GeoJsonService { private final EarthquakeRepository earthquakeRepository; private final SatelliteRepository satelliteRepository; private final AircraftRepository aircraftRepository; private final ObjectMapper objectMapper; public GeoJsonService(EarthquakeRepository earthquakeRepository, SatelliteRepository satelliteRepository, AircraftRepository aircraftRepository) { this.earthquakeRepository = earthquakeRepository; this.satelliteRepository = satelliteRepository; this.aircraftRepository = aircraftRepository; this.objectMapper = new ObjectMapper(); this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); } public String getEarthquakesGeoJson(LocalDateTime startDate, LocalDateTime endDate) throws IOException { List<Earthquake> earthquakes = earthquakeRepository.findByEventTimeBetween(startDate, endDate); FeatureCollection featureCollection = new FeatureCollection(); for (Earthquake earthquake : earthquakes) { Point point = new Point(new Coordinate( earthquake.getLocation().getX(), earthquake.getLocation().getY() )); Map<String, Object> properties = new HashMap<>(); properties.put("title", earthquake.getTitle()); properties.put("magnitude", earthquake.getMagnitude()); properties.put("depth", earthquake.getDepth()); properties.put("time", earthquake.getEventTime().toString()); properties.put("region", earthquake.getRegion()); // Color based on magnitude String color = getColorForMagnitude(earthquake.getMagnitude()); properties.put("color", color); Feature feature = new Feature(point, properties, earthquake.getEventId(), null); featureCollection.addFeature(feature); } return objectMapper.writeValueAsString(featureCollection); } public String getSatellitesGeoJson() throws IOException { List<Satellite> satellites = satelliteRepository.findRecentSatellites( LocalDateTime.now().minusHours(1)); FeatureCollection featureCollection = new FeatureCollection(); for (Satellite satellite : satellites) { Point point = new Point(new Coordinate( satellite.getPosition().getX(), satellite.getPosition().getY() )); Map<String, Object> properties = new HashMap<>(); properties.put("name", satellite.getName()); properties.put("noradId", satellite.getNoradId()); properties.put("type", satellite.getType()); properties.put("altitude", satellite.getAltitude()); properties.put("velocity", satellite.getVelocity()); properties.put("timestamp", satellite.getTimestamp().toString()); Feature feature = new Feature(point, properties, satellite.getNoradId(), null); featureCollection.addFeature(feature); } return objectMapper.writeValueAsString(featureCollection); } public String getAircraftGeoJson() throws IOException { List<Aircraft> aircraft = aircraftRepository.findRecentAircraft( LocalDateTime.now().minusMinutes(10)); FeatureCollection featureCollection = new FeatureCollection(); for (Aircraft aircraftItem : aircraft) { Point point = new Point(new Coordinate( aircraftItem.getLongitude(), aircraftItem.getLatitude() )); Map<String, Object> properties = new HashMap<>(); properties.put("callsign", aircraftItem.getCallsign()); properties.put("icao24", aircraftItem.getIcao24()); properties.put("country", aircraftItem.getOriginCountry()); properties.put("altitude", aircraftItem.getAltitude()); properties.put("velocity", aircraftItem.getVelocity()); properties.put("heading", aircraftItem.getHeading()); properties.put("timestamp", aircraftItem.getTimestamp().toString()); Feature feature = new Feature(point, properties, aircraftItem.getIcao24(), null); featureCollection.addFeature(feature); } return objectMapper.writeValueAsString(featureCollection); } private String getColorForMagnitude(Double magnitude) { if (magnitude >= 7.0) return "#FF0000"; // Red if (magnitude >= 5.0) return "#FFA500"; // Orange if (magnitude >= 3.0) return "#FFFF00"; // Yellow return "#00FF00"; // Green } }
4. CZML Service for Time-Dynamic Data
@Service public class CzmlService { public String createSatelliteCzml(List<Satellite> satellites) throws IOException { List<Object> czmlDocuments = new ArrayList<>(); // Document header Map<String, Object> document = new HashMap<>(); document.put("id", "document"); document.put("name", "Satellites"); document.put("version", "1.0"); czmlDocuments.add(document); for (Satellite satellite : satellites) { Map<String, Object> satellitePacket = createSatellitePacket(satellite); czmlDocuments.add(satellitePacket); } return new ObjectMapper().writeValueAsString(czmlDocuments); } private Map<String, Object> createSatellitePacket(Satellite satellite) { Map<String, Object> packet = new HashMap<>(); packet.put("id", satellite.getNoradId()); packet.put("name", satellite.getName()); // Position with sampled data Map<String, Object> position = new HashMap<>(); position.put("epoch", satellite.getTimestamp().toString()); position.put("cartographicDegrees", Arrays.asList( 0, satellite.getPosition().getX(), satellite.getPosition().getY(), satellite.getAltitude() )); Map<String, Object> model = new HashMap<>(); model.put("gltf", "https://cesium.com/downloads/cesiumjs/releases/1.107/Build/Cesium/Assets/Models/CesiumAir/CesiumAir.glb"); model.put("scale", 100.0); model.put("minimumPixelSize", 64); Map<String, Object> label = new HashMap<>(); label.put("text", satellite.getName()); label.put("fillColor", Map.of("rgba", Arrays.asList(255, 255, 255, 255))); label.put("pixelOffset", Map.of("cartesian2", Arrays.asList(0.0, 40.0))); packet.put("position", position); packet.put("model", model); packet.put("label", label); return packet; } public String createAircraftCzml(List<Aircraft> aircraftList) throws IOException { List<Object> czmlDocuments = new ArrayList<>(); Map<String, Object> document = new HashMap<>(); document.put("id", "document"); document.put("name", "Aircraft"); document.put("version", "1.0"); czmlDocuments.add(document); for (Aircraft aircraft : aircraftList) { Map<String, Object> aircraftPacket = createAircraftPacket(aircraft); czmlDocuments.add(aircraftPacket); } return new ObjectMapper().writeValueAsString(czmlDocuments); } private Map<String, Object> createAircraftPacket(Aircraft aircraft) { Map<String, Object> packet = new HashMap<>(); packet.put("id", aircraft.getIcao24()); packet.put("name", aircraft.getCallsign()); // Position Map<String, Object> position = new HashMap<>(); position.put("cartographicDegrees", Arrays.asList( aircraft.getLongitude(), aircraft.getLatitude(), aircraft.getAltitude() )); // Orientation based on heading Map<String, Object> orientation = new HashMap<>(); orientation.put("velocityReference", "#position"); Map<String, Object> model = new HashMap<>(); model.put("gltf", "https://cesium.com/downloads/cesiumjs/releases/1.107/Build/Cesium/Assets/Models/CesiumAir/CesiumAir.glb"); model.put("scale", 2.0); model.put("minimumPixelSize", 32); Map<String, Object> path = new HashMap<>(); path.put("material", Map.of("solidColor", Map.of("color", Map.of("rgba", Arrays.asList(255, 255, 0, 128))))); path.put("width", 2); path.put("leadTime", 300); path.put("trailTime", 300); packet.put("position", position); packet.put("orientation", orientation); packet.put("model", model); packet.put("path", path); return packet; } }
5. REST Controllers
@RestController @RequestMapping("/api/cesium") @CrossOrigin(origins = "*") public class CesiumDataController { private final GeoJsonService geoJsonService; private final CzmlService czmlService; private final EarthquakeRepository earthquakeRepository; private final SatelliteRepository satelliteRepository; private final AircraftRepository aircraftRepository; public CesiumDataController(GeoJsonService geoJsonService, CzmlService czmlService, EarthquakeRepository earthquakeRepository, SatelliteRepository satelliteRepository, AircraftRepository aircraftRepository) { this.geoJsonService = geoJsonService; this.czmlService = czmlService; this.earthquakeRepository = earthquakeRepository; this.satelliteRepository = satelliteRepository; this.aircraftRepository = aircraftRepository; } @GetMapping("/earthquakes/geojson") public ResponseEntity<String> getEarthquakesGeoJson( @RequestParam(defaultValue = "2023-01-01T00:00:00") String startDate, @RequestParam(defaultValue = "2023-12-31T23:59:59") String endDate) { try { LocalDateTime start = LocalDateTime.parse(startDate); LocalDateTime end = LocalDateTime.parse(endDate); String geoJson = geoJsonService.getEarthquakesGeoJson(start, end); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(geoJson); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Error generating GeoJSON: " + e.getMessage()); } } @GetMapping("/satellites/geojson") public ResponseEntity<String> getSatellitesGeoJson() { try { String geoJson = geoJsonService.getSatellitesGeoJson(); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(geoJson); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Error generating GeoJSON: " + e.getMessage()); } } @GetMapping("/satellites/czml") public ResponseEntity<String> getSatellitesCzml() { try { List<Satellite> satellites = satelliteRepository.findRecentSatellites( LocalDateTime.now().minusHours(1)); String czml = czmlService.createSatelliteCzml(satellites); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(czml); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Error generating CZML: " + e.getMessage()); } } @GetMapping("/aircraft/geojson") public ResponseEntity<String> getAircraftGeoJson() { try { String geoJson = geoJsonService.getAircraftGeoJson(); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(geoJson); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Error generating GeoJSON: " + e.getMessage()); } } @GetMapping("/aircraft/czml") public ResponseEntity<String> getAircraftCzml() { try { List<Aircraft> aircraft = aircraftRepository.findRecentAircraft( LocalDateTime.now().minusMinutes(10)); String czml = czmlService.createAircraftCzml(aircraft); return ResponseEntity.ok() .contentType(MediaType.APPLICATION_JSON) .body(czml); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Error generating CZML: " + e.getMessage()); } } @PostMapping("/earthquakes") public ResponseEntity<Earthquake> addEarthquake(@RequestBody Earthquake earthquake) { try { Earthquake saved = earthquakeRepository.save(earthquake); return ResponseEntity.ok(saved); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } @GetMapping("/statistics") public ResponseEntity<Map<String, Object>> getStatistics() { Map<String, Object> stats = new HashMap<>(); long earthquakeCount = earthquakeRepository.count(); long satelliteCount = satelliteRepository.count(); long aircraftCount = aircraftRepository.count(); stats.put("earthquakes", earthquakeCount); stats.put("satellites", satelliteCount); stats.put("aircraft", aircraftCount); stats.put("lastUpdated", LocalDateTime.now().toString()); return ResponseEntity.ok(stats); } }
6. Data Initialization Service
@Component public class DataInitializer { private final EarthquakeRepository earthquakeRepository; private final SatelliteRepository satelliteRepository; private final AircraftRepository aircraftRepository; public DataInitializer(EarthquakeRepository earthquakeRepository, SatelliteRepository satelliteRepository, AircraftRepository aircraftRepository) { this.earthquakeRepository = earthquakeRepository; this.satelliteRepository = satelliteRepository; this.aircraftRepository = aircraftRepository; } @PostConstruct public void initializeSampleData() { if (earthquakeRepository.count() == 0) { initializeEarthquakes(); } if (satelliteRepository.count() == 0) { initializeSatellites(); } if (aircraftRepository.count() == 0) { initializeAircraft(); } } private void initializeEarthquakes() { GeometryFactory geometryFactory = new GeometryFactory(); List<Earthquake> earthquakes = Arrays.asList( new Earthquake("us7000kq8p", "M 6.3 - 12 km SSW of Smith Valley, Nevada", 6.3, 10.5, geometryFactory.createPoint(new Coordinate(-119.333, 38.567)), LocalDateTime.of(2023, 7, 8, 15, 45), "Nevada"), new Earthquake("us7000kq9t", "M 5.8 - 8 km SE of Waikoloa, Hawaii", 5.8, 8.2, geometryFactory.createPoint(new Coordinate(-155.789, 19.923)), LocalDateTime.of(2023, 7, 9, 8, 30), "Hawaii") ); earthquakeRepository.saveAll(earthquakes); } private void initializeSatellites() { GeometryFactory geometryFactory = new GeometryFactory(); List<Satellite> satellites = Arrays.asList( new Satellite("ISS (ZARYA)", "25544", "Space Station", geometryFactory.createPoint(new Coordinate(-45.123, 25.456)), 408.0, 7.66, LocalDateTime.now()), new Satellite("Hubble Space Telescope", "20580", "Telescope", geometryFactory.createPoint(new Coordinate(-60.789, 30.123)), 547.0, 7.59, LocalDateTime.now()) ); satelliteRepository.saveAll(satellites); } private void initializeAircraft() { List<Aircraft> aircraft = Arrays.asList( new Aircraft("a0f2c5", "UAL123", "United States", null, -74.0059, 40.7128, 35000.0, 450.0, 85.5, LocalDateTime.now()), new Aircraft("c0123d", "BAW456", "United Kingdom", null, -0.1276, 51.5074, 38000.0, 480.0, 92.3, LocalDateTime.now()) ); aircraftRepository.saveAll(aircraft); } }

Frontend JavaScript Implementation

1. Main Cesium Application
// public/js/app.js class CesiumGlobe { constructor() { this.viewer = null; this.dataSources = new Map(); this.init(); } init() { // Configure Cesium Ion access token Cesium.Ion.defaultAccessToken = 'YOUR_CESIUM_ION_TOKEN'; // Initialize the Cesium Viewer this.viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain(), timeline: true, animation: true, homeButton: true, geocoder: true, baseLayerPicker: true, sceneModePicker: true, navigationHelpButton: true, fullscreenButton: true }); // Configure camera and scene this.configureScene(); // Set up event listeners this.setupEventListeners(); // Load initial data this.loadInitialData(); } configureScene() { // Enable lighting this.viewer.scene.globe.enableLighting = true; // Set maximum screen space error for better performance this.viewer.scene.globe.maximumScreenSpaceError = 2; // Add some default imagery layers this.addImageryLayers(); // Set initial view this.viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(-74.0059, 40.7128, 10000000), orientation: { heading: 0, pitch: -1.57, roll: 0 } }); } addImageryLayers() { // Add Bing Maps imagery this.viewer.imageryLayers.addImageryProvider(new Cesium.BingMapsImageryProvider({ url: 'https://dev.virtualearth.net', key: 'YOUR_BING_MAPS_KEY', mapStyle: Cesium.BingMapsStyle.AERIAL })); } setupEventListeners() { document.getElementById('loadData').addEventListener('click', () => { this.loadSelectedData(); }); document.getElementById('clearData').addEventListener('click', () => { this.clearAllData(); }); // Handle entity clicks this.viewer.screenSpaceEventHandler.setInputAction((click) => { const pickedObject = this.viewer.scene.pick(click.position); if (Cesium.defined(pickedObject) && pickedObject.id) { this.showEntityInfo(pickedObject.id); } }, Cesium.ScreenSpaceEventType.LEFT_CLICK); } async loadSelectedData() { const dataType = document.getElementById('dataLayer').value; try { switch (dataType) { case 'earthquakes': await this.loadEarthquakes(); break; case 'satellites': await this.loadSatellites(); break; case 'aircraft': await this.loadAircraft(); break; } } catch (error) { console.error('Error loading data:', error); this.showError('Failed to load data: ' + error.message); } } async loadEarthquakes() { const response = await fetch('/api/cesium/earthquakes/geojson'); const geoJson = await response.json(); // Remove existing earthquake data this.removeDataSource('earthquakes'); // Load GeoJSON data source const dataSource = await Cesium.GeoJsonDataSource.load(geoJson, { stroke: Cesium.Color.BLACK, fill: Cesium.Color.YELLOW, strokeWidth: 2, markerSymbol: '?' }); // Style the entities based on properties dataSource.entities.values.forEach(entity => { const properties = entity.properties; if (properties) { const magnitude = properties.magnitude.getValue(Cesium.JulianDate.now()); const color = this.getColorForMagnitude(magnitude); if (entity.billboard) { entity.billboard.color = color; entity.billboard.scale = magnitude / 2; } // Add description for popup entity.description = this.createEarthquakeDescription(properties); } }); this.viewer.dataSources.add(dataSource); this.dataSources.set('earthquakes', dataSource); } async loadSatellites() { // Load CZML for time-dynamic data const response = await fetch('/api/cesium/satellites/czml'); const czml = await response.json(); this.removeDataSource('satellites'); const dataSource = await Cesium.CzmlDataSource.load(czml); this.viewer.dataSources.add(dataSource); this.dataSources.set('satellites', dataSource); // Zoom to satellites this.viewer.zoomTo(dataSource); } async loadAircraft() { // Load CZML for time-dynamic aircraft data const response = await fetch('/api/cesium/aircraft/czml'); const czml = await response.json(); this.removeDataSource('aircraft'); const dataSource = await Cesium.CzmlDataSource.load(czml); this.viewer.dataSources.add(dataSource); this.dataSources.set('aircraft', dataSource); // Zoom to aircraft this.viewer.zoomTo(dataSource); } removeDataSource(key) { if (this.dataSources.has(key)) { this.viewer.dataSources.remove(this.dataSources.get(key)); this.dataSources.delete(key); } } clearAllData() { for (const [key] of this.dataSources) { this.removeDataSource(key); } } getColorForMagnitude(magnitude) { if (magnitude >= 7.0) return Cesium.Color.RED; if (magnitude >= 5.0) return Cesium.Color.ORANGE; if (magnitude >= 3.0) return Cesium.Color.YELLOW; return Cesium.Color.GREEN; } createEarthquakeDescription(properties) { return ` <table class="cesium-infoBox-defaultTable"> <tbody> <tr><th>Title</th><td>${properties.title.getValue(Cesium.JulianDate.now())}</td></tr> <tr><th>Magnitude</th><td>${properties.magnitude.getValue(Cesium.JulianDate.now())}</td></tr> <tr><th>Depth</th><td>${properties.depth.getValue(Cesium.JulianDate.now())} km</td></tr> <tr><th>Time</th><td>${properties.time.getValue(Cesium.JulianDate.now())}</td></tr> <tr><th>Region</th><td>${properties.region.getValue(Cesium.JulianDate.now())}</td></tr> </tbody> </table> `; } showEntityInfo(entity) { // The info box will automatically show when an entity is clicked // due to Cesium's built-in functionality } showError(message) { // Simple error display - in production, use a proper notification system alert(message); } loadInitialData() { // Load some default data on startup this.loadEarthquakes(); } } // Initialize the application when the page loads document.addEventListener('DOMContentLoaded', () => { window.cesiumGlobe = new CesiumGlobe(); });
2. Advanced Visualization Features
// public/js/advanced-visualization.js class AdvancedVisualization { constructor(viewer) { this.viewer = viewer; this.heatmapDataSource = null; } // Create heatmap from earthquake data async createEarthquakeHeatmap() { const response = await fetch('/api/cesium/earthquakes/geojson'); const geoJson = await response.json(); // Convert to heatmap points const points = geoJson.features.map(feature => { const coords = feature.geometry.coordinates; const magnitude = feature.properties.magnitude; return { position: Cesium.Cartesian3.fromDegrees(coords[0], coords[1]), intensity: magnitude / 10 // Normalize intensity }; }); // Create heatmap imagery provider const heatmapProvider = new Cesium.HeatmapImageryProvider({ points: points, radius: 100000, // 100km radius minValue: 0, maxValue: 1 }); // Add to imagery layers this.viewer.imageryLayers.addImageryProvider(heatmapProvider); } // Add 3D terrain analysis addTerrainAnalysis() { // Enable terrain clipping this.viewer.scene.globe.depthTestAgainstTerrain = true; // Add contour lines this.addContourLines(); } addContourLines() { // This would require a custom terrain analysis service console.log('Contour lines feature would be implemented here'); } // Create time animation for historical data async createTimeAnimation() { // Set up time dynamic visualization this.viewer.clock.shouldAnimate = true; this.viewer.clock.multiplier = 3600; // Speed up time // Load time-series data await this.loadHistoricalEarthquakes(); } async loadHistoricalEarthquakes() { // Implementation for loading historical time-series data const startDate = '2023-01-01T00:00:00'; const endDate = '2023-12-31T23:59:59'; const response = await fetch(`/api/cesium/earthquakes/geojson?startDate=${startDate}&endDate=${endDate}`); const geoJson = await response.json(); // Process time-series data for animation this.animateTimeSeriesData(geoJson); } animateTimeSeriesData(geoJson) { // Implementation for animating features over time console.log('Time series animation would be implemented here'); } }

Spring Boot Application Class

@SpringBootApplication public class CesiumBackendApplication { public static void main(String[] args) { SpringApplication.run(CesiumBackendApplication.class, args); } @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:3000", "http://127.0.0.1:5500") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true); } }; } }

Testing

1. Service Tests
@SpringBootTest class GeoJsonServiceTest { @Autowired private GeoJsonService geoJsonService; @Autowired private EarthquakeRepository earthquakeRepository; @Test void testEarthquakeGeoJsonGeneration() throws IOException { // Given LocalDateTime startDate = LocalDateTime.of(2023, 1, 1, 0, 0); LocalDateTime endDate = LocalDateTime.of(2023, 12, 31, 23, 59); // When String geoJson = geoJsonService.getEarthquakesGeoJson(startDate, endDate); // Then assertThat(geoJson).isNotEmpty(); assertThat(geoJson).contains("FeatureCollection"); assertThat(geoJson).contains("Point"); } } @WebMvcTest(CesiumDataController.class) class CesiumDataControllerTest { @Autowired private MockMvc mockMvc; @MockBean private GeoJsonService geoJsonService; @Test void testGetEarthquakesGeoJson() throws Exception { // Given String sampleGeoJson = "{\"type\":\"FeatureCollection\",\"features\":[]}"; when(geoJsonService.getEarthquakesGeoJson(any(), any())).thenReturn(sampleGeoJson); // When & Then mockMvc.perform(get("/api/cesium/earthquakes/geojson")) .andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(content().json(sampleGeoJson)); } }

Deployment and Production Considerations

1. Docker Configuration
# Dockerfile FROM openjdk:17-jdk-slim WORKDIR /app COPY target/cesium-backend-1.0.0.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
2. Production Configuration
# application-prod.yml spring: datasource: url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:cesium_db} username: ${DB_USERNAME} password: ${DB_PASSWORD} jpa: hibernate: ddl-auto: validate show-sql: false server: compression: enabled: true mime-types: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json servlet: context-path: /api management: endpoints: web: exposure: include: health,metrics,info

Best Practices

  1. Cesium Ion Token: Store Cesium Ion token in environment variables
  2. CORS Configuration: Configure CORS properly for production
  3. Data Pagination: Implement pagination for large datasets
  4. Caching: Use Redis or similar for caching frequently accessed data
  5. Error Handling: Implement comprehensive error handling and logging
  6. Performance: Use spatial indexes in database for geographic queries
  7. Security: Implement proper authentication and authorization
// Example of paginated response public class PaginatedResponse<T> { private List<T> content; private int page; private int size; private long totalElements; private int totalPages; // Constructors, getters, setters } // Example of cached service method @Cacheable(value = "earthquakes", key = "#startDate + '-' + #endDate") public String getEarthquakesGeoJson(LocalDateTime startDate, LocalDateTime endDate) throws IOException { // Implementation... }

Conclusion

This integration provides:

  • Rich 3D Visualization with CesiumJS frontend
  • Robust Backend with Java/Spring Boot
  • Spatial Data Management with PostGIS and JTS
  • RESTful APIs for data serving
  • Time-Dynamic Visualizations with CZML
  • Scalable Architecture ready for production

The combination of CesiumJS for frontend visualization and Java for backend processing creates a powerful platform for building sophisticated 3D geographic applications that can handle complex spatial data and provide interactive user experiences.

Java Observability, Logging Intelligence & AI-Driven Monitoring (APM, Tracing, Logs & Anomaly Detection)

https://macronepal.com/blog/beyond-metrics-observing-serverless-and-traditional-java-applications-with-thundra-apm/
Explains using Thundra APM to observe both serverless and traditional Java applications by combining tracing, metrics, and logs into a unified observability platform for faster debugging and performance insights.

https://macronepal.com/blog/dynatrace-oneagent-in-java-2/
Explains Dynatrace OneAgent for Java, which automatically instruments JVM applications to capture metrics, traces, and logs, enabling full-stack monitoring and root-cause analysis with minimal configuration.

https://macronepal.com/blog/lightstep-java-sdk-distributed-tracing-and-observability-implementation/
Explains Lightstep Java SDK for distributed tracing, helping developers track requests across microservices and identify latency issues using OpenTelemetry-based observability.

https://macronepal.com/blog/honeycomb-io-beeline-for-java-complete-guide-2/
Explains Honeycomb Beeline for Java, which provides high-cardinality observability and deep query capabilities to understand complex system behavior and debug distributed systems efficiently.

https://macronepal.com/blog/lumigo-for-serverless-in-java-complete-distributed-tracing-guide-2/
Explains Lumigo for Java serverless applications, offering automatic distributed tracing, log correlation, and error tracking to simplify debugging in cloud-native environments. (Lumigo Docs)

https://macronepal.com/blog/from-noise-to-signals-implementing-log-anomaly-detection-in-java-applications/
Explains how to detect anomalies in Java logs using behavioral patterns and machine learning techniques to separate meaningful incidents from noisy log data and improve incident response.

https://macronepal.com/blog/ai-powered-log-analysis-in-java-from-reactive-debugging-to-proactive-insights/
Explains AI-driven log analysis for Java applications, shifting from manual debugging to predictive insights that identify issues early and improve system reliability using intelligent log processing.

https://macronepal.com/blog/titliel-java-logging-best-practices/
Explains best practices for Java logging, focusing on structured logs, proper log levels, performance optimization, and ensuring logs are useful for debugging and observability systems.

https://macronepal.com/blog/seeking-a-loguru-for-java-the-quest-for-elegant-and-simple-logging/
Explains the search for simpler, more elegant logging frameworks in Java, comparing modern logging approaches that aim to reduce complexity while improving readability and developer experience.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper