Skip to content

Symbol Layer

A symbol layer renders icons, text labels, or both at point locations from a GeoJSON source. It's the style-layer equivalent of addSymbol(), but scales to any number of features and supports data-driven styling.

Ten European cities with custom marker icons and text labels, rendered from a GeoJSON source.

Basic setup

// 1. Register a custom icon
await addImageFromAsset(controller, 'my-marker', 'assets/marker.png');

// 2. Add a GeoJSON source with point features
await controller.addGeoJsonSource('cities', {
  'type': 'FeatureCollection',
  'features': [
    {
      'type': 'Feature',
      'properties': {'name': 'Paris'},
      'geometry': {'type': 'Point', 'coordinates': [2.3522, 48.8566]},
    },
  ],
});

// 3. Add a symbol layer referencing the source
await controller.addSymbolLayer(
  'cities',
  'cities-layer',
  const SymbolLayerProperties(
    iconImage: 'my-marker',
    iconSize: 1.0,
    iconAnchor: 'bottom',
    iconAllowOverlap: true,
    textField: [Expressions.get, 'name'],
    textSize: 13,
    textOffset: [0, 1.2],
    textAnchor: 'top',
    textHaloColor: '#ffffff',
    textHaloWidth: 2,
  ),
);

Data-driven icon and text

SymbolLayerProperties(
  // Different icon per category
  iconImage: [
    Expressions.match,
    [Expressions.get, 'type'],
    'restaurant', 'restaurant-icon',
    'hotel', 'hotel-icon',
    'default-icon', // fallback
  ],

  // Text from a property
  textField: [Expressions.get, 'name'],

  // Text size driven by a rank property
  textSize: [
    Expressions.interpolate, ['linear'],
    [Expressions.get, 'rank'],
    1, 16.0,   // rank 1 -> 16px
    5, 10.0,   // rank 5 -> 10px
  ],
)

Collision behavior

Symbols automatically avoid each other. Control this:

SymbolLayerProperties(
  iconAllowOverlap: true,        // icons always show, even if overlapping
  iconIgnorePlacement: false,    // icons consider others' space
  textAllowOverlap: false,       // labels hide if they overlap
  symbolAvoidEdges: true,        // keep symbols away from map edges
)

Text layout

SymbolLayerProperties(
  textField: [Expressions.get, 'name'],
  textSize: 13,
  textAnchor: 'top',             // relative to the symbol point
  textJustify: 'center',         // left, center, right
  textOffset: [0, 1.5],          // [x, y] in ems
  textRotate: 0,                 // degrees
  textMaxWidth: 8,               // wrap at 8 ems
  textLetterSpacing: 0.05,
)

Update layer properties

await controller.setLayerProperties(
  'cities-layer',
  const SymbolLayerProperties(
    textSize: 16,
    iconSize: 1.5,
  ),
);

Filter by property

await controller.addSymbolLayer(
  'cities', 'major-cities',
  SymbolLayerProperties(...),
  filter: ['>=', ['get', 'population'], 1000000], // only cities with pop >= 1M
);

Key SymbolLayerProperties fields

Property Description
iconImage Icon name (registered via addImage)
iconSize Scale factor (1.0 = original)
iconAnchor Which part of icon touches the point
iconOffset [x, y] pixel offset
iconColor Tint color (SDF icons only)
iconAllowOverlap Show even if overlapping other icons
textField Label text (string or expression)
textSize Font size in pixels
textColor Label color
textHaloColor / textHaloWidth Outline around text
textAnchor Anchor relative to point
textOffset [x, y] offset in ems
symbolPlacement point or line

Key APIs