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

Reference the same source from multiple layers:

layers:
- id: earthquakes-source
type: circle
source:
id: quakes # Named source
type: geojson
url: "https://..."
paint: { ... }
- id: earthquake-labels
type: symbol
source: quakes # Reference by ID
layout:
text-field: "{mag}"