Skip to content

GeoJSON Source

A GeoJSON source is the data container that style layers read from. It holds a FeatureCollection of points, lines, or polygons, either inline in your Dart code or fetched from a URL.

Capital cities across the Americas rendered from an inline FeatureCollection. Circle radius scales with population.

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.

The source + layer pattern

GeoJSON sources don't render anything by themselves. You always need at least one layer referencing the source:

// 1. Source: holds the data
await controller.addGeoJsonSource('capitals', featureCollection);

// 2. Layer: defines how to render the data
await controller.addCircleLayer(
  'capitals',         // source id
  'capitals-dots',   // layer id (must be unique)
  CircleLayerProperties(...),
);

// Multiple layers can reference the same source
await controller.addSymbolLayer(
  'capitals',
  'capitals-labels',
  SymbolLayerProperties(...),
);

Adding the source

Inline FeatureCollection

await controller.addGeoJsonSource('my-source', {
  'type': 'FeatureCollection',
  'features': [
    {
      'type': 'Feature',
      'id': 'f1',  // optional, needed for setGeoJsonFeature()
      'properties': {
        'name': 'Paris',
        'population': 2161000,
        'country': 'France',
      },
      'geometry': {
        'type': 'Point',
        'coordinates': [2.3522, 48.8566],  // [lng, lat]
      },
    },
  ],
});

Remote URL

await controller.addSource(
  'rivers',
  const GeojsonSourceProperties(
    data: 'https://example.com/data/rivers.geojson',
  ),
);

A URL goes through addSource rather than addGeoJsonSource, which takes the GeoJSON itself. MapLibre fetches and caches the URL. CORS headers must be present when running on web.

With clustering enabled

await controller.addSource(
  'events',
  GeojsonSourceProperties(
    data: featureCollection, // inline GeoJSON map, or a URL string
    cluster: true,
    clusterMaxZoom: 14, // stop clustering above zoom 14
    clusterRadius: 50, // pixel radius for clustering
  ),
);

Pass the data when you create the source: a geojson source needs its data up front, and on web a source added without it fails validation, so the layers that depend on it never attach.

See Cluster for the full clustering setup.

Updating data

Replace all features

await controller.setGeoJsonSource('my-source', newFeatureCollection);

Use this for bulk updates: filtering, time-series playback, real-time data refresh.

Update a single feature

await controller.setGeoJsonFeature('my-source', {
  'type': 'Feature',
  'id': 'f1',  // must match an existing feature id
  'properties': {'status': 'active', 'name': 'Paris'},
  'geometry': {
    'type': 'Point',
    'coordinates': [2.3522, 48.8566],
  },
});

Updating a single feature is more efficient than replacing the whole FeatureCollection when only one feature changes (e.g. tracking a moving vehicle).

Filtering layers

Add a filter to a layer to show only features matching a condition:

await controller.addCircleLayer(
  'events',
  'events-active',
  const CircleLayerProperties(circleColor: '#2ecc71'),
  filter: ['==', ['get', 'status'], 'active'],
);

await controller.addCircleLayer(
  'events',
  'events-inactive',
  const CircleLayerProperties(circleColor: '#95a5a6'),
  filter: ['==', ['get', 'status'], 'inactive'],
);

Both layers use the same source but each shows a subset of features.

Removing a source

// Remove all layers referencing the source first
await controller.removeLayer('my-layer');

// Then remove the source
await controller.removeSource('my-source');

Key APIs

Method Description
addGeoJsonSource(id, data) Add a source with inline GeoJSON data
addSource(id, GeojsonSourceProperties(...)) Add a source from a URL, or with clustering options
setGeoJsonSource(id, data) Replace all features
setGeoJsonFeature(id, feature) Update one feature by id
removeSource(id) Remove source (remove layers first)
getSourceIds() List all source ids in the current style