Skip to content

Data Sources

maplibre-yaml supports multiple ways to provide data to your layers: inline GeoJSON, remote URLs, and streaming connections.

For small, static datasets, embed the GeoJSON directly:

source:
type: geojson
data:
type: FeatureCollection
features:
- type: Feature
geometry:
type: Point
coordinates: [-74.006, 40.7128]
properties:
name: "New York"

Load GeoJSON from any URL:

source:
type: geojson
url: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson"
Live earthquake data from USGS

When loading from remote URLs, the server must allow cross-origin requests (CORS). If you see errors, the API may not support browser requests.

Solutions:

  • Use APIs that support CORS (like USGS earthquake feed)
  • Set up a proxy server
  • Host the data yourself

Control how fetched data is cached:

source:
type: geojson
url: "https://api.example.com/data.geojson"
cache:
enabled: true # Enable caching (default)
ttl: 300000 # Cache for 5 minutes (milliseconds)
ScenarioBehavior
First requestFetch from network, store in cache
Subsequent (within TTL)Return cached data immediately
After TTL expiresFetch fresh data, update cache
enabled: falseAlways fetch from network

Show feedback while data loads:

source:
type: geojson
url: "https://api.example.com/data.geojson"
loading:
enabled: true
message: "Loading earthquake data..."

When enabled, maplibre-yaml shows a spinner overlay while fetching data and an error message if the fetch fails.

const renderer = new MapRenderer(container, config);
renderer.on("layer:loading", ({ layerId }) => {
console.log(`Loading ${layerId}...`);
});
renderer.on("layer:loaded", ({ layerId, featureCount, fromCache }) => {
console.log(`Loaded ${featureCount} features (cached: ${fromCache})`);
});
renderer.on("layer:error", ({ layerId, error }) => {
console.error(`Failed to load ${layerId}:`, error.message);
});

For point datasets with many features, enable clustering:

source:
type: geojson
url: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson"
cluster: true
clusterRadius: 50 # Cluster points within 50px
clusterMaxZoom: 14 # Stop clustering at zoom 14

Then style clustered and unclustered points separately:

layers:
# Clustered points
- id: clusters
type: circle
source: earthquakes
filter: ["has", "point_count"]
paint:
circle-radius:
- step
- ["get", "point_count"]
- 20 # Default size
- 100
- 30 # Larger for 100+ points
- 750
- 40 # Even larger for 750+
circle-color: "#3b82f6"
# Cluster count labels
- id: cluster-count
type: symbol
source: earthquakes
filter: ["has", "point_count"]
layout:
text-field: "{point_count_abbreviated}"
text-size: 12
# Individual points
- id: unclustered
type: circle
source: earthquakes
filter: ["!", ["has", "point_count"]]
paint:
circle-radius: 6
circle-color: "#ef4444"
Clustered earthquake data - zoom in to see individual points

Define sources at the block level and reference them by name across multiple layers. This avoids duplicating source definitions and is the recommended approach when multiple layers share the same data.

type: map
id: earthquake-map
config:
center: [-120, 35]
zoom: 6
mapStyle: "https://demotiles.maplibre.org/style.json"
sources:
quakes:
type: geojson
url: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson"
cluster: true
clusterRadius: 50
clusterMaxZoom: 14
layers:
- id: clusters
type: circle
source: quakes
filter: ["has", "point_count"]
paint:
circle-radius:
- step
- ["get", "point_count"]
- 20
- 100
- 30
circle-color: "#3b82f6"
- id: cluster-count
type: symbol
source: quakes
filter: ["has", "point_count"]
layout:
text-field: "{point_count_abbreviated}"
text-size: 12
- id: unclustered
type: circle
source: quakes
filter: ["!", ["has", "point_count"]]
paint:
circle-radius: 6
circle-color: "#ef4444"

Named sources are added to the map before layers, so any layer can reference them by string ID. Inline sources on individual layers still work — the two approaches can be mixed freely.