Skip to content

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: 50

2. 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: 50

3. 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: 50

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: 50

5. 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: 50

6. 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: 50

7. 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: 50

8. 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: 50

9. 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: 50

Mark Features Summary

MarkPurposeKey Features
density2D density estimationBandwidth control, thresholds
contourContour linesMultiple threshold levels
hexbinHexagonal binningAggregation, variable bin size
delaunaylinkPoint connectionsCurve interpolation
delaunaymeshTriangulation meshFill/stroke styling
treeHierarchical visualizationTidy/cluster layouts
differenceSeries comparisonPositive/negative fills
rasterRasterized dataPixelated/smooth rendering
imageImage-based scatterCustom icons, rotation

Tips for Using These Marks

  1. Density & Contour: Best for large point datasets (100+ points)
  2. Hexbin: Excellent alternative to scatter plots with overlapping points
  3. Delaunay: Useful for network analysis and proximity visualization
  4. Tree: Requires hierarchical path structure in data
  5. Difference: Ideal for comparing two time series or forecasts
  6. Image: Requires valid image URLs (http/https or data URIs)
  7. 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)

Released under the MIT License. Built by Boundary Lab.