Skip to content

Circle Layer

A circle layer renders filled circles at point locations. It's ideal for data visualization: earthquakes sized by magnitude, events colored by category, density indicators.

Simulated earthquake data. Circle size and color driven by the magnitude property using interpolate expressions.

Add sources and layers after the style loads

Every call below needs a loaded style. Run them from onStyleLoadedCallback, not from onMapCreated, and run them there again after a style change: a new style discards every source and layer you added. See Constraints and gotchas.

Basic setup

await controller.addGeoJsonSource('points', featureCollection);

await controller.addCircleLayer(
  'points',
  'points-layer',
  const CircleLayerProperties(
    circleRadius: 8,
    circleColor: '#296CA8',
    circleOpacity: 0.8,
    circleStrokeWidth: 2,
    circleStrokeColor: '#ffffff',
  ),
);

Data-driven radius and color

This is the most common pattern: circle size and color both driven by a numeric property:

CircleLayerProperties(
  circleRadius: [
    Expressions.interpolate, ['linear'],
    [Expressions.get, 'magnitude'],
    2.0, 4.0,    // magnitude 2 -> 4px radius
    5.0, 14.0,
    8.0, 40.0,   // magnitude 8 -> 40px radius
  ],
  circleColor: [
    Expressions.interpolate, ['linear'],
    [Expressions.get, 'magnitude'],
    2.0, '#4CAF50',   // green
    5.0, '#FF9800',   // orange
    8.0, '#F44336',   // red
  ],
  circleOpacity: 0.75,
  circleStrokeWidth: 1.5,
  circleStrokeColor: '#ffffff',
)

Zoom-responsive circles

CircleLayerProperties(
  circleRadius: [
    Expressions.interpolate, ['linear'],
    [Expressions.zoom],
    5,  3.0,   // zoom 5  -> 3px
    12, 12.0,  // zoom 12 -> 12px
  ],
)

Handle taps

A circle layer is hit-tested on tap by default (enableInteraction: true), so onFeatureTapped fires with the feature id and the layer id. That payload carries no properties, so when you need those, let the tap reach onMapClick too and query the tapped point:

MapLibreMap(
  featureTapsTriggersMapClick: true, // otherwise circle taps never reach onMapClick
  onMapClick: (point, latLng) async {
    final features = await controller.queryRenderedFeatures(
      point,
      ['points-layer'],  // layer ids to query
      null,              // filter (optional)
    );
    if (features.isNotEmpty) {
      final props = features.first['properties'] as Map;
      print('Tapped: ${props['name']}');
    }
  },
)

Key CircleLayerProperties fields

Property Description
circleRadius Radius in pixels (or expression)
circleColor Fill color (or expression)
circleOpacity Fill opacity 0-1
circleStrokeWidth Outline width
circleStrokeColor Outline color
circleBlur Feather the edges
circlePitchAlignment map or viewport (3D perspective)
circleTranslate [x, y] pixel offset

Key APIs