A Jupyter widget for CesiumJS 3D globe visualization using anywidget
Project description
CesiumJS Anywidget
A Jupyter widget for interactive 3D globe visualization using CesiumJS and anywidget.
Features
- 🌍 Interactive 3D Globe: Full CesiumJS viewer integration
- 🎯 Camera Control: Fly to locations with smooth animations
- 🔄 Bidirectional Sync: Camera state syncs between Python and JavaScript
- 🗺️ GeoJSON Support: Load and visualize GeoJSON data
- 🏔️ Terrain & Imagery: World terrain and satellite imagery
- 🏙️ Photorealistic 3D Tiles: Google's photorealistic global 3D cities and landscapes
- 📏 Measurement Tools: Built-in distance, multi-point, and height measurement tools
- ⚙️ Highly Configurable: Customize viewer options and UI elements
Installation
Using uv (recommended):
uv pip install cesiumjs-anywidget
Or for development:
git clone https://github.com/Alex-PLACET/cesiumjs_anywidget.git
cd cesiumjs_anywidget
uv pip install -e ".[dev]"
Quick Start
from cesiumjs_anywidget import CesiumWidget
# Create and display the widget
widget = CesiumWidget(height="700px")
widget
Usage Examples
Fly to a Location
# Fly to New York City
widget.fly_to(latitude=40.7128, longitude=-74.0060, altitude=50000)
# Fly to Mount Everest
widget.fly_to(latitude=27.9881, longitude=86.9250, altitude=20000)
Advanced Camera Control
# Set camera with custom orientation
widget.set_view(
latitude=40.7128,
longitude=-74.0060,
altitude=5000,
heading=45.0, # Rotate view 45 degrees
pitch=-45.0, # Look at angle instead of straight down
roll=0.0
)
Read Camera State
# Camera position is synchronized bidirectionally
print(f"Latitude: {widget.latitude:.4f}°")
print(f"Longitude: {widget.longitude:.4f}°")
print(f"Altitude: {widget.altitude:.2f} meters")
Visualize GeoJSON Data
geojson_data = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-74.0060, 40.7128]
},
"properties": {
"name": "New York City"
}
}
]
}
widget.load_geojson(geojson_data)
Visualize CZML Data
CZML (Cesium Language) is a JSON format for describing time-dynamic graphical scenes. It's particularly useful for animations and complex visualizations.
# Simple CZML example - as Python list
czml_data = [
{
"id": "document",
"name": "Simple CZML",
"version": "1.0"
},
{
"id": "point",
"name": "Location",
"position": {
"cartographicDegrees": [-74.0060, 40.7128, 0]
},
"point": {
"pixelSize": 10,
"color": {
"rgba": [255, 0, 0, 255]
}
}
}
]
widget.load_czml(czml_data)
Or from a JSON string:
import json
# CZML as JSON string
czml_json = json.dumps([
{"id": "document", "version": "1.0"},
{
"id": "satellite",
"position": {
"cartographicDegrees": [-75, 40, 500000]
},
"point": {"pixelSize": 8, "color": {"rgba": [0, 255, 0, 255]}}
}
])
widget.load_czml(czml_json)
You can use the czml3 library to generate CZML more easily, then pass the output as a string or list.
Configure Viewer Options
# Create widget with custom configuration
widget = CesiumWidget(
height="700px",
enable_terrain=True,
enable_lighting=True,
show_timeline=True,
show_animation=True,
request_render_mode=True, # Better idle CPU usage
maximum_render_time_change=None, # Uses Infinity (best performance when time-based updates are not needed)
latitude=27.9881,
longitude=86.9250,
altitude=30000
)
With explicit rendering enabled, if you change scene properties through Python and want an immediate frame, call:
widget.request_render()
For time-dynamic scenes (animations/CZML), set a finite value instead of None, for example:
widget.maximum_render_time_change = 0.0
widget.should_animate = True
Google Photorealistic 3D Tiles
Visualize cities and landscapes in stunning photorealistic detail using Google's Photorealistic 3D Tiles:
# Enable photorealistic tiles (recommended method)
widget = CesiumWidget()
widget.enable_photorealistic_3d_tiles(True)
# Fly to a city to see the tiles
widget.fly_to(latitude=40.7128, longitude=-74.0060, altitude=2000, pitch=-45)
# Or configure manually during creation
widget = CesiumWidget(
enable_photorealistic_tiles=True,
show_globe=False, # Disable base globe (recommended)
enable_terrain=False, # Disable terrain (tiles include terrain)
height="700px"
)
Features:
- Global coverage of major cities and populated areas
- Photorealistic imagery with 3D buildings and structures
- Automatic terrain integration
- Works with all camera controls and measurement tools
See PHOTOREALISTIC_TILES.md for detailed documentation and examples/photorealistic_tiles_demo.ipynb for interactive examples.
Development
Enable hot module replacement for live updates during development:
export ANYWIDGET_HMR=1
jupyter lab
Running Tests
# Install with dev dependencies
uv pip install -e ".[dev]"
# Run all tests
pytest
# Run with coverage
pytest --cov=cesiumjs_anywidget --cov-report=html
# Or use make
make test
make test-cov
See tests/README.md for detailed testing documentation.
Measurement Tools
The widget includes built-in measurement tools for spatial analysis:
Distance Measurement
Measure the distance between two points:
# Enable distance measurement mode
widget.enable_measurement(mode="distance")
# Click two points in the viewer to measure distance
# Results are automatically synced to Python
measurements = widget.get_measurements()
print(f"Distance: {measurements[0]['value']:.2f} meters")
Multi-Point Distance Measurement
Measure distances along a polyline with multiple points:
# Enable multi-point distance measurement
widget.enable_measurement(mode="multi-distance")
# Click multiple points to create a polyline
# Right-click to finish the measurement
# Total distance is calculated automatically
Height Measurement
Measure vertical height from ground to a point (useful for buildings):
# Enable height measurement
widget.enable_measurement(mode="height")
# Click on a point to measure its height above ground
measurements = widget.get_measurements()
print(f"Height: {measurements[0]['value']:.2f} meters")
Managing Measurements
# Get all measurement results
measurements = widget.get_measurements()
for m in measurements:
print(f"Type: {m['type']}, Value: {m['value']:.2f}m")
# Clear all measurements
widget.clear_measurements()
# Disable measurement mode
widget.disable_measurement()
Loading Measurements from Python
You can programmatically add measurements with specific coordinates:
# Load a distance measurement between two points
measurements_data = [
{
"type": "distance",
"name": "Bridge Length",
"points": [
{"coordinates": [-74.0445, 40.6892, 10]}, # [lon, lat, alt]
{"coordinates": [-73.9626, 40.8075, 10]}
]
}
]
widget.load_measurements(measurements_data)
# Load an area measurement (polygon)
area_data = [
{
"type": "area",
"name": "Central Park",
"points": [
{"coordinates": [-73.9812, 40.7681, 0]},
{"coordinates": [-73.9581, 40.7681, 0]},
{"coordinates": [-73.9581, 40.8005, 0]},
{"coordinates": [-73.9812, 40.8005, 0]}
]
}
]
widget.load_measurements(area_data)
# Focus camera on a specific measurement
widget.focus_on_measurement(0) # Focus on first measurement
Controlling Measurement UI Visibility
You can show/hide the measurement tools and list panel:
# Hide measurement tools (toolbar)
widget.hide_tools()
# Or: widget.show_measurement_tools = False
# Show tools again
widget.show_tools()
# Or: widget.show_measurement_tools = True
# Hide the measurements list panel
widget.hide_list()
# Or: widget.show_measurements_list = False
# Show list again
widget.show_list()
# Or: widget.show_measurements_list = True
# Create a clean viewer without measurement tools
widget = CesiumWidget(
show_measurement_tools=False,
show_measurements_list=False
)
Each measurement includes:
type: Measurement type ('distance','multi-distance','height', or'area')value: Measured value in meters (distance) or square meters (area)points: List of coordinates withlat,lon,altpropertiesname: Optional name for the measurement (auto-generated if not provided)
API Reference
CesiumWidget
Parameters:
latitude(float): Camera latitude in degrees (default: 0.0)longitude(float): Camera longitude in degrees (default: 0.0)altitude(float): Camera altitude in meters (default: 20000000.0)heading(float): Camera heading in degrees (default: 0.0)pitch(float): Camera pitch in degrees (default: -90.0)roll(float): Camera roll in degrees (default: 0.0)height(str): Widget height CSS value (default: "600px")enable_terrain(bool): Enable terrain visualization (default: True)enable_lighting(bool): Enable scene lighting (default: False)show_timeline(bool): Show timeline widget (default: False)show_animation(bool): Show animation widget (default: False)ion_access_token(str): Cesium Ion access token (optional)geojson_data(dict): GeoJSON data to display (optional)czml_data(list): CZML data to display (optional)measurement_mode(str): Active measurement mode (default: "")measurement_results(list): List of measurement results (default: [])show_measurement_tools(bool): Show measurement toolbar (default: True)show_measurements_list(bool): Show measurements list panel (default: True)
Methods:
fly_to(latitude, longitude, altitude=10000, duration=3.0): Fly camera to locationset_view(latitude, longitude, altitude=10000, heading=0.0, pitch=-90.0, roll=0.0): Set camera view instantlyload_geojson(geojson): Load GeoJSON data for visualizationload_czml(czml): Load CZML data for time-dynamic visualizationenable_measurement(mode="distance"): Enable measurement tool (modes: 'distance', 'multi-distance', 'height', 'area')disable_measurement(): Disable measurement toolget_measurements(): Get all measurement resultsclear_measurements(): Clear all measurements from viewerload_measurements(measurements): Load measurements from Python datafocus_on_measurement(index): Fly camera to specific measurementshow_tools(): Show the measurement tools toolbarhide_tools(): Hide the measurement tools toolbarshow_list(): Show the measurements list panelhide_list(): Hide the measurements list panel
Examples
See the examples directory for Jupyter notebook demonstrations.
Troubleshooting
If you encounter issues with widget initialization:
from cesiumjs_anywidget import CesiumWidget
widget = CesiumWidget()
widget.debug_info() # Show debug information
Common fixes:
- Open browser DevTools (F12) and check the Console tab for errors
- Try without terrain:
widget = CesiumWidget(enable_terrain=False) - Ensure you're using JupyterLab 4.0+ or Jupyter Notebook 7.0+
- Check internet connection (CesiumJS loads from CDN)
See TROUBLESHOOTING.md for detailed debugging guide.
License
Apache License 2.0 - see LICENSE file for details.
Acknowledgments
- Built with anywidget
- Powered by CesiumJS
- Uses Cesium Ion for terrain and imagery
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cesiumjs_anywidget-1.2.0.tar.gz.
File metadata
- Download URL: cesiumjs_anywidget-1.2.0.tar.gz
- Upload date:
- Size: 5.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a2a62340c4db9eea65d7a6ed666dfbe7f9e1ddf4ca2c9564f45566f4bcd82f6
|
|
| MD5 |
8ee161ac0067ac81f47dbdec764258f0
|
|
| BLAKE2b-256 |
ebf02eb3b276ae051cb947e4f127be1130b6af9f9ec35229435ac564d4dff0b6
|
Provenance
The following attestation bundles were made for cesiumjs_anywidget-1.2.0.tar.gz:
Publisher:
release_and_publish.yml on Alex-PLACET/cesiumjs_anywidget
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cesiumjs_anywidget-1.2.0.tar.gz -
Subject digest:
1a2a62340c4db9eea65d7a6ed666dfbe7f9e1ddf4ca2c9564f45566f4bcd82f6 - Sigstore transparency entry: 1026080683
- Sigstore integration time:
-
Permalink:
Alex-PLACET/cesiumjs_anywidget@4ffa7a168b3b6ae18e73ad09eda5e951a47cf7dc -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Alex-PLACET
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release_and_publish.yml@4ffa7a168b3b6ae18e73ad09eda5e951a47cf7dc -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file cesiumjs_anywidget-1.2.0-py3-none-any.whl.
File metadata
- Download URL: cesiumjs_anywidget-1.2.0-py3-none-any.whl
- Upload date:
- Size: 5.1 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b887fa15415ef9a282e5df48c57bfb45a1aa7c16e7367336fab00ea99a19acc0
|
|
| MD5 |
30bb2765bf4fd64d69279666a653f6ac
|
|
| BLAKE2b-256 |
0738ebc2d6fd6eb13f38b85b357efcc7457595e4d2ac6cdbfd25ee7a4f76dd8a
|
Provenance
The following attestation bundles were made for cesiumjs_anywidget-1.2.0-py3-none-any.whl:
Publisher:
release_and_publish.yml on Alex-PLACET/cesiumjs_anywidget
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cesiumjs_anywidget-1.2.0-py3-none-any.whl -
Subject digest:
b887fa15415ef9a282e5df48c57bfb45a1aa7c16e7367336fab00ea99a19acc0 - Sigstore transparency entry: 1026080800
- Sigstore integration time:
-
Permalink:
Alex-PLACET/cesiumjs_anywidget@4ffa7a168b3b6ae18e73ad09eda5e951a47cf7dc -
Branch / Tag:
refs/heads/main - Owner: https://github.com/Alex-PLACET
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release_and_publish.yml@4ffa7a168b3b6ae18e73ad09eda5e951a47cf7dc -
Trigger Event:
workflow_dispatch
-
Statement type: