Advanced Observable Plot Marks Showcase
This document demonstrates newly implemented Observable Plot mark types with climate and environmental science examples.
1. Density Mark - Temperature Distribution
Visualizing temperature measurement density across geographic locations.
View Source
data:
source: |
[
{"longitude": -122.4, "latitude": 37.8, "temperature": 18.5, "station_id": "CA001"},
{"longitude": -122.3, "latitude": 37.7, "temperature": 19.2, "station_id": "CA002"},
{"longitude": -122.5, "latitude": 37.9, "temperature": 17.8, "station_id": "CA003"},
{"longitude": -122.4, "latitude": 37.8, "temperature": 18.9, "station_id": "CA004"},
{"longitude": -122.2, "latitude": 37.6, "temperature": 20.1, "station_id": "CA005"},
{"longitude": -122.6, "latitude": 38.0, "temperature": 17.2, "station_id": "CA006"},
{"longitude": -122.3, "latitude": 37.7, "temperature": 19.5, "station_id": "CA007"},
{"longitude": -122.5, "latitude": 37.9, "temperature": 18.1, "station_id": "CA008"},
{"longitude": -122.4, "latitude": 37.8, "temperature": 18.7, "station_id": "CA009"},
{"longitude": -122.2, "latitude": 37.6, "temperature": 20.3, "station_id": "CA010"},
{"longitude": -122.3, "latitude": 37.7, "temperature": 19.0, "station_id": "CA011"},
{"longitude": -122.5, "latitude": 37.9, "temperature": 17.5, "station_id": "CA012"},
{"longitude": -122.4, "latitude": 37.8, "temperature": 18.6, "station_id": "CA013"},
{"longitude": -122.6, "latitude": 38.0, "temperature": 16.9, "station_id": "CA014"},
{"longitude": -122.2, "latitude": 37.6, "temperature": 20.5, "station_id": "CA015"}
]
mark: density
x: longitude
y: latitude
bandwidth: 15
fill: density
fillOpacity: 0.7
width: 800
height: 500
marginLeft: 50
marginRight: 502. Contour Mark - Elevation Contours
Creating contour lines from elevation data.
View Source
data:
source: |
[
{"x": 0, "y": 0, "elevation": 100},
{"x": 1, "y": 0, "elevation": 150},
{"x": 2, "y": 0, "elevation": 200},
{"x": 0, "y": 1, "elevation": 120},
{"x": 1, "y": 1, "elevation": 180},
{"x": 2, "y": 1, "elevation": 250},
{"x": 0, "y": 2, "elevation": 140},
{"x": 1, "y": 2, "elevation": 220},
{"x": 2, "y": 2, "elevation": 300},
{"x": 0, "y": 3, "elevation": 160},
{"x": 1, "y": 3, "elevation": 260},
{"x": 2, "y": 3, "elevation": 350},
{"x": 0, "y": 4, "elevation": 180},
{"x": 1, "y": 4, "elevation": 300},
{"x": 2, "y": 4, "elevation": 400}
]
mark: contour
x: x
y: y
fill: elevation
stroke: black
strokeWidth: 1
thresholds: 10
width: 800
height: 500
margin:
left: 50
right: 503. Hexbin Mark - Pollution Hotspots
Hexagonal binning to identify pollution concentration areas.
View Source
data:
source: |
[
{"longitude": -118.2, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 35.2},
{"longitude": -118.3, "latitude": 34.1, "pollutant": "PM2.5", "concentration": 42.1},
{"longitude": -118.2, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 38.5},
{"longitude": -118.1, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 31.8},
{"longitude": -118.3, "latitude": 34.1, "pollutant": "PM2.5", "concentration": 45.3},
{"longitude": -118.2, "latitude": 34.1, "pollutant": "PM2.5", "concentration": 39.7},
{"longitude": -118.2, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 36.4},
{"longitude": -118.3, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 41.2},
{"longitude": -118.1, "latitude": 34.1, "pollutant": "PM2.5", "concentration": 33.6},
{"longitude": -118.2, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 37.9},
{"longitude": -118.3, "latitude": 34.1, "pollutant": "PM2.5", "concentration": 43.8},
{"longitude": -118.2, "latitude": 34.1, "pollutant": "PM2.5", "concentration": 40.1},
{"longitude": -118.1, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 32.5},
{"longitude": -118.3, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 44.6},
{"longitude": -118.2, "latitude": 34.0, "pollutant": "PM2.5", "concentration": 38.2}
]
mark: hexbin
x: longitude
y: latitude
fill: count
r: 15
fillOpacity: 0.8
stroke: white
strokeWidth: 0.5
width: 800
height: 500
marginLeft: 50
marginRight: 504. Delaunay Link - Weather Station Network
Delaunay triangulation showing weather station connectivity.
View Source
data:
source: |
[
{"longitude": -95.3, "latitude": 29.7, "station_name": "Houston Central", "operational": true},
{"longitude": -97.7, "latitude": 30.2, "station_name": "Austin North", "operational": true},
{"longitude": -96.8, "latitude": 32.7, "station_name": "Dallas Metro", "operational": true},
{"longitude": -98.5, "latitude": 29.4, "station_name": "San Antonio West", "operational": true},
{"longitude": -100.0, "latitude": 31.5, "station_name": "Midland East", "operational": true},
{"longitude": -94.1, "latitude": 30.0, "station_name": "Beaumont Coast", "operational": true},
{"longitude": -99.9, "latitude": 27.5, "station_name": "Corpus South", "operational": true},
{"longitude": -106.5, "latitude": 31.8, "station_name": "El Paso Border", "operational": true}
]
mark: delaunaylink
x: longitude
y: latitude
stroke: steelblue
strokeOpacity: 0.3
strokeWidth: 1
curve: linear
width: 800
height: 500
marginLeft: 50
marginRight: 505. Delaunay Mesh - Weather Station Coverage
Delaunay mesh visualization of coverage areas.
View Source
data:
source: |
[
{"longitude": -95.3, "latitude": 29.7, "coverage_radius": 50, "station_type": "primary"},
{"longitude": -97.7, "latitude": 30.2, "coverage_radius": 45, "station_type": "primary"},
{"longitude": -96.8, "latitude": 32.7, "coverage_radius": 55, "station_type": "primary"},
{"longitude": -98.5, "latitude": 29.4, "coverage_radius": 40, "station_type": "secondary"},
{"longitude": -100.0, "latitude": 31.5, "coverage_radius": 35, "station_type": "secondary"},
{"longitude": -94.1, "latitude": 30.0, "coverage_radius": 48, "station_type": "primary"},
{"longitude": -99.9, "latitude": 27.5, "coverage_radius": 42, "station_type": "secondary"},
{"longitude": -106.5, "latitude": 31.8, "coverage_radius": 38, "station_type": "secondary"}
]
mark: delaunaymesh
x: longitude
y: latitude
fill: lightblue
fillOpacity: 0.2
stroke: navy
strokeWidth: 1.5
width: 800
height: 500
marginLeft: 50
marginRight: 506. Tree Mark - Climate Classification
Hierarchical climate zone classification.
View Source
data:
source: |
[
{"zone_path": "Climate", "area_km2": 510000, "avg_temp": 14.0},
{"zone_path": "Climate/Tropical", "area_km2": 120000, "avg_temp": 25.5},
{"zone_path": "Climate/Tropical/Rainforest", "area_km2": 45000, "avg_temp": 26.8},
{"zone_path": "Climate/Tropical/Monsoon", "area_km2": 35000, "avg_temp": 25.2},
{"zone_path": "Climate/Tropical/Savanna", "area_km2": 40000, "avg_temp": 24.6},
{"zone_path": "Climate/Temperate", "area_km2": 180000, "avg_temp": 12.3},
{"zone_path": "Climate/Temperate/Mediterranean", "area_km2": 55000, "avg_temp": 15.8},
{"zone_path": "Climate/Temperate/Oceanic", "area_km2": 70000, "avg_temp": 10.5},
{"zone_path": "Climate/Temperate/Continental", "area_km2": 55000, "avg_temp": 11.2},
{"zone_path": "Climate/Cold", "area_km2": 150000, "avg_temp": 2.5},
{"zone_path": "Climate/Cold/Tundra", "area_km2": 60000, "avg_temp": -3.2},
{"zone_path": "Climate/Cold/Subarctic", "area_km2": 90000, "avg_temp": 5.8},
{"zone_path": "Climate/Polar", "area_km2": 60000, "avg_temp": -15.0},
{"zone_path": "Climate/Polar/Ice Cap", "area_km2": 28000, "avg_temp": -28.5},
{"zone_path": "Climate/Polar/Ice Sheet", "area_km2": 32000, "avg_temp": -18.2}
]
mark: tree
path: zone_path
stroke: darkgreen
strokeWidth: 2
r: 4
fill: lightgreen
treeLayout: tidy
width: 800
height: 600
marginLeft: 50
marginRight: 507. Difference Mark - Temperature Forecast Comparison
Comparing actual vs predicted temperatures.
View Source
data:
source: |
[
{"day": 1, "actual_temp": 18.2, "forecast_temp": 18.5, "accuracy": 98.4},
{"day": 2, "actual_temp": 19.1, "forecast_temp": 18.8, "accuracy": 98.4},
{"day": 3, "actual_temp": 17.5, "forecast_temp": 18.2, "accuracy": 96.2},
{"day": 4, "actual_temp": 20.3, "forecast_temp": 19.5, "accuracy": 96.1},
{"day": 5, "actual_temp": 21.2, "forecast_temp": 20.8, "accuracy": 98.1},
{"day": 6, "actual_temp": 19.8, "forecast_temp": 20.5, "accuracy": 96.6},
{"day": 7, "actual_temp": 18.5, "forecast_temp": 19.2, "accuracy": 96.4},
{"day": 8, "actual_temp": 17.9, "forecast_temp": 18.0, "accuracy": 99.4},
{"day": 9, "actual_temp": 19.5, "forecast_temp": 19.1, "accuracy": 97.9},
{"day": 10, "actual_temp": 20.8, "forecast_temp": 20.2, "accuracy": 97.1},
{"day": 11, "actual_temp": 22.1, "forecast_temp": 21.5, "accuracy": 97.3},
{"day": 12, "actual_temp": 21.5, "forecast_temp": 21.8, "accuracy": 98.6},
{"day": 13, "actual_temp": 20.2, "forecast_temp": 20.5, "accuracy": 98.5},
{"day": 14, "actual_temp": 19.1, "forecast_temp": 19.8, "accuracy": 96.5}
]
mark: difference
x: day
y1: actual_temp
y2: forecast_temp
positiveFill: steelblue
negativeFill: coral
fillOpacity: 0.6
curve: natural
width: 800
height: 400
marginLeft: 50
marginRight: 508. Raster Mark - Satellite Temperature Grid
Rasterized satellite temperature measurements.
View Source
data:
source: |
[
{"x": 0, "y": 0, "temperature": 15.2, "cloud_cover": 20},
{"x": 1, "y": 0, "temperature": 16.1, "cloud_cover": 15},
{"x": 2, "y": 0, "temperature": 17.5, "cloud_cover": 10},
{"x": 3, "y": 0, "temperature": 18.2, "cloud_cover": 5},
{"x": 0, "y": 1, "temperature": 16.8, "cloud_cover": 25},
{"x": 1, "y": 1, "temperature": 17.9, "cloud_cover": 18},
{"x": 2, "y": 1, "temperature": 19.2, "cloud_cover": 12},
{"x": 3, "y": 1, "temperature": 20.1, "cloud_cover": 8},
{"x": 0, "y": 2, "temperature": 18.5, "cloud_cover": 30},
{"x": 1, "y": 2, "temperature": 19.8, "cloud_cover": 22},
{"x": 2, "y": 2, "temperature": 21.3, "cloud_cover": 15},
{"x": 3, "y": 2, "temperature": 22.5, "cloud_cover": 10},
{"x": 0, "y": 3, "temperature": 20.1, "cloud_cover": 35},
{"x": 1, "y": 3, "temperature": 21.5, "cloud_cover": 28},
{"x": 2, "y": 3, "temperature": 23.2, "cloud_cover": 20},
{"x": 3, "y": 3, "temperature": 24.8, "cloud_cover": 12}
]
mark: raster
fill: temperature
imageRendering: pixelated
interpolate: nearest
fillOpacity: 0.9
width: 800
height: 500
marginLeft: 50
marginRight: 509. Image Mark - Wildfire Risk Assessment
Image marks showing wildfire risk levels with custom icons (Note: Image mark requires valid image URLs).
View Source
data:
source: |
[
{"longitude": -120.5, "latitude": 39.5, "risk_level": "High", "icon_url": "https://example.com/fire-high.png", "affected_area": 1200},
{"longitude": -121.2, "latitude": 38.8, "risk_level": "Medium", "icon_url": "https://example.com/fire-med.png", "affected_area": 800},
{"longitude": -119.8, "latitude": 40.2, "risk_level": "High", "icon_url": "https://example.com/fire-high.png", "affected_area": 1500},
{"longitude": -122.1, "latitude": 39.0, "risk_level": "Low", "icon_url": "https://example.com/fire-low.png", "affected_area": 400},
{"longitude": -120.9, "latitude": 38.5, "risk_level": "Medium", "icon_url": "https://example.com/fire-med.png", "affected_area": 900},
{"longitude": -121.5, "latitude": 40.5, "risk_level": "High", "icon_url": "https://example.com/fire-high.png", "affected_area": 18},
{"longitude": -119.5, "latitude": 39.8, "risk_level": "Low", "icon_url": "https://example.com/fire-low.png", "affected_area": 350},
{"longitude": -120.2, "latitude": 38.2, "risk_level": "Medium", "icon_url": "https://example.com/fire-med.png", "affected_area": 750}
]
mark: image
x: longitude
y: latitude
src: icon_url
opacity: 0.9
width: 800
height: 500
marginLeft: 50
marginRight: 50Mark Features Summary
| Mark | Purpose | Key Features |
|---|---|---|
| density | 2D density estimation | Bandwidth control, thresholds |
| contour | Contour lines | Multiple threshold levels |
| hexbin | Hexagonal binning | Aggregation, variable bin size |
| delaunaylink | Point connections | Curve interpolation |
| delaunaymesh | Triangulation mesh | Fill/stroke styling |
| tree | Hierarchical visualization | Tidy/cluster layouts |
| difference | Series comparison | Positive/negative fills |
| raster | Rasterized data | Pixelated/smooth rendering |
| image | Image-based scatter | Custom icons, rotation |
Tips for Using These Marks
- Density & Contour: Best for large point datasets (100+ points)
- Hexbin: Excellent alternative to scatter plots with overlapping points
- Delaunay: Useful for network analysis and proximity visualization
- Tree: Requires hierarchical path structure in data
- Difference: Ideal for comparing two time series or forecasts
- Image: Requires valid image URLs (http/https or data URIs)
- Raster: Best for gridded/matrix data
Performance Considerations
- Density/Contour: Can be computationally expensive with >10,000 points
- Hexbin: More efficient than density for very large datasets
- Delaunay: O(n log n) complexity, reasonable for <5,000 points
- Tree: Performance depends on tree depth and node count
- Raster: Efficient for gridded data
Common Issues
Data Not Found
If you see bar charts instead of the expected visualization:
- Ensure your data is correctly formatted as inline JSON
- Use
data:\n source: |followed by JSON array on new lines - Verify JSON syntax is valid
Example - Correct Format:
```dg
data:
source: |
[
{"x": 1, "y": 2, "value": 10}
]
mark: density
x: x
y: y
---
## Further Reading
- [Observable Plot Documentation](https://observablehq.com/plot/)
- [Observable Plot Marks](https://observablehq.com/plot/features/marks)
- [Data Transformations](https://observablehq.com/plot/features/transforms)