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