Everything you need to integrate Kartograf.
From basic setup to custom plugin development. Covers the complete declarative Story API with feature config references and examples.
Introduction
Kartograf is a geospatial animation and storytelling library built on MapLibre GL JS. It extends standard GeoJSON with custom geometry types — such as animated line-point paths, bezier arcs, and time-series timelines.
It exposes a clean declarative Story API (createStory — play). Define scenes with camera settings, GeoJSON features, and transition delays — Kartograf normalizes the config into its internal rendering pipeline automatically.
Kartograf exposes a clean declarative Story API (createStory → play). Define scenes with camera settings, GeoJSON features, and transition delays — Kartograf normalizes the config into its internal rendering pipeline automatically.
Installation
Load Kartograf and MapLibre from CDN — no build tools required:
<script src="https://fikretanday.b-cdn.net/kartograf/latest/kartograf.min.js"></script>
MapLibre GL JS is also loaded via CDN:
<script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>
<link href="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css" rel="stylesheet" />
Dependencies bundled in the CDN build:
three— 3D model rendering via custom MapLibre layers@turf/turf— geospatial calculations (bounding boxes, buffers, bezier splines, distance)
Basic Setup
Initialize a MapLibre map, then create a Kartograf instance bound to it:
// Kartograf & MapLibre loaded via <script> tags
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [35, 39],
zoom: 5,
});
map.on('load', () => {
const kartograf = new Kartograf({
mapInstance: map,
});
// Ready to load data
});
The constructor takes a single options object:
| Option | Type | Required | Description |
|---|---|---|---|
| mapInstance | maplibregl.Map | Yes | MapLibre GL map instance with loaded style |
| libraryRef | any | No | Optional external library reference for custom rendering |
Scene Transitions
Transitions are configured at the scene level via transition:
| Field | Type | Default | Description |
|---|---|---|---|
| transition.delay | number | 0 | Seconds to wait after the scene finishes before advancing to the next scene |
Feature Properties
Features in a scene use a clean shorthand syntax. Kartograf normalizes this into the internal format automatically.
Scene Configuration
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | No | Scene identifier (used for branching) |
| camera | object | No | Camera behavior for this scene's features |
| features | array | Yes | Array of features (declarative or GeoJSON) |
| stagger | number | No | Milliseconds delay between each feature reveal. First feature appears immediately; subsequent features cascade. |
| transition | object | No | { delay: number } — seconds after scene ends |
| meta | object | No | { audioUrl: string } — narration file |
StoryPlayer
Created by kartograf.createStory(config). Manages playback.
| Method | Returns | Description |
|---|---|---|
| play() | Promise | Play the story from the first scene. Resolves when all scenes complete. |
| stop() | void | Stop playback immediately. |
| getProgress() | number | 0–1 progress ratio. |
| getCurrentScene() | Scene|null | Currently playing scene. |
Events on the Story
Attach listeners via story.story.on(event, handler):
story.story.on('sceneStart', (scene) => { /* ... */ });
story.story.on('sceneEnd', (scene) => { /* ... */ });
story.story.on('complete', () => { /* ... */ });
Geometry Types
Kartograf supports 25 geometry types: 7 standard GeoJSON and 18 custom patch types. The factory routes each type to the appropriate Layer class.
Point
StandardSingle location with itemDisplayImage configuration. Supports CIRCLE, PULSING_ANIMATED, STABLE_ANIMATED, and THREED display modes. Camera flies to the point coordinates.
{
geometry: { type: 'Point', coordinates: [28.978, 41.008] },
marker: { type: 'PULSING', popup: { title: 'Istanbul' } },
}
LineString
StandardPath with optional progressive draw animation. Camera fits bounds to the line with configurable buffer.
{
geometry: {
type: 'LineString',
coordinates: [[28.978, 41.008], [32.860, 39.933]],
},
line: { color: '#c9a87c', width: 3, animate: true },
}
Polygon
StandardFilled area with semi-transparent red fill. Camera fits to bounds. No marker or line config — display is purely geometric.
{
geometry: {
type: 'Polygon',
coordinates: [[[32.8, 39.8], [32.9, 39.9], [32.7, 39.9], [32.8, 39.8]]],
},
}
MultiPoint
StandardMultiple points, each with independent marker configuration. The markers array maps 1:1 to coordinates by position. Each point can have a different display type and popup. Supports 3D models per point.
{
geometry: {
type: 'MultiPoint',
coordinates: [
[28.978, 41.008],
[32.860, 39.933],
],
},
markers: [
{ type: 'PULSING', popup: { title: 'Istanbul' } },
{ type: 'CIRCLE' },
],
}
MultiLineString
StandardMultiple lines, each with independent styling via the lines array. Each line can have different color, width, animate settings. Animates all lines simultaneously in lockstep.
{
geometry: {
type: 'MultiLineString',
coordinates: [
[[28.978, 41.008], [30.520, 39.800]],
[[32.860, 39.933], [27.138, 38.423]],
],
},
lines: [
{ color: '#c9a87c', width: 3, animate: true },
{ color: '#88c0d0', width: 2, animate: true },
],
}
MultiPolygon
StandardMultiple polygons rendered as fills. Each polygon is drawn with a semi-transparent red fill. No per-polygon styling — all share the same paint properties.
{
geometry: {
type: 'MultiPolygon',
coordinates: [
[[[32.8, 39.8], [32.9, 39.9], [32.7, 39.9], [32.8, 39.8]]],
[[[33.0, 40.0], [33.2, 40.2], [33.0, 40.0]]],
],
},
}
LinePointString Custom
The signature Kartograf type. A LineString that draws progressively while a point marker moves along the path tip simultaneously. Internally, this is a cross-breed — the geometry type is LinePointString, which the parser converts to LineString with itemType: "LinePointString". The factory then routes it to LinePointLayer instead of LineLayer.
Both marker and line configs are applied. The point can be a pulsing dot, static marker, or THREED 3D model. The line can have its own styling independent of the point.
{
geometry: {
type: 'LinePointString',
coordinates: [[28.978, 41.008], [30.520, 39.800], [32.860, 39.933],
},
marker: { type: 'PULSING', popup: { title: 'Traveler' } },
line: { color: '#c9a87c', width: 3, animate: true },
}
MultiLinePointString Custom
Multiple lines with simultaneous point animation. Each line draws while its corresponding point moves along the path tip. Uses both markers and lines arrays. The point markers update their positions frame by frame, and 3D models move in real-time.
{
geometry: {
type: 'MultiLinePointString',
coordinates: [
[[28.978, 41.008], [30.520, 39.800]],
[[32.860, 39.933], [27.138, 38.423]],
],
},
markers: [
{ type: 'PULSING', popup: { title: 'Convoy A' } },
{ type: 'CIRCLE' },
],
lines: [
{ color: '#c9a87c', width: 3, animate: true },
{ color: '#88c0d0', width: 2, animate: true },
],
}
LineArc Custom
Takes exactly two coordinates (start and end) and generates a smooth quadratic bezier curve between them using Turf.js. The arc's control point is calculated as a perpendicular offset from the midpoint. Internally, the parser converts LineArc to LineString with computed bezier coordinates and routes it to LineLayer — there is no dedicated LineArcLayer.
{
geometry: {
type: 'LineArc',
coordinates: [[28.978, 41.008], [51.390, 35.689]],
},
line: { color: '#88c0d0', width: 3, animate: true },
}
Algorithm: turf.midpoint(start, end) → perpendicular offset by distance/4 → turf.bezierSpline([start, control, end]) → resulting bezier coordinates.
MultiLineArc Custom
Multiple arc pairs. Each pair [start, end] is independently converted to a bezier curve. The result is rendered as a MultiLineString and routed to MultiLineLayer — there is no dedicated MultiLineArcLayer.
{
geometry: {
type: 'MultiLineArc',
coordinates: [
[[28.978, 41.008], [32.860, 39.933]],
[[27.138, 38.423], [30.520, 39.800]],
],
},
lines: [
{ color: '#c9a87c', width: 3, animate: true },
{ color: '#88c0d0', width: 2, animate: true },
],
}
Timeline Custom
Time-series data visualization within a bounding polygon. Fetches external GeoJSON data and iterates through date or numeric values, rendering points frame by frame. The initialQuery is used for the first frame; subsequent frames use query with CURRV and PREVV keywords substituted at runtime.
{
geometry: {
type: 'Timeline',
coordinates: [[[26, 42], [45, 42], [45, 35], [26, 35], [26, 42]]],
},
timeline: {
url: 'https://api.example.com/events.geojson',
start: '1200-01-01',
end: '1453-05-29',
step: 30,
qualifier: 'DATE',
fps: 120,
paint: { color: '#c9a87c', radius: 5, opacity: 0.7 },
initialQuery: "['all',['==','$type','Point'],['==','date',CURRV]]",
query: "['all',['==','$type','Point'],['>=','date',PREVV],['<','date',CURRV]]",
},
}
Reserved keywords for queries:
| Keyword | Replaced With |
|---|---|
CURRV | Current iteration value (date or number) |
PREVV | Previous iteration value |
GeometryCollection Custom
Mixed geometry types in a single feature. Supports sequential display with per-sub-geometry display configs inlined directly in each geometry object. The destructGeometryCollection function destructures the GC into individual Feature objects, propagating parent properties to each sub-feature with itemLocalIndex for filtering.
Key behaviors:
- Without
sequence: true: all sub-geometries render at once, camera fits to all bounds. - With
sequence: true: sub-geometries render one at a time viaSequenceRunner. Each waits forITEM_ANIMATION_FINISHEDbefore advancing. focusedItemIndexes: [0, 2]: only these sub-items trigger camera flyTo. Others render but camera stays still.shouldShowInSequence: false: the sub-geometry renders but the camera does not move.- Cross-breed types (LinePointString, Timeline) inside GC are handled automatically.
{
geometry: {
type: 'GeometryCollection',
geometries: [
{
type: 'Point',
coordinates: [28.978, 41.008],
// Inline config for this sub-geometry:
marker: { type: 'PULSING', popup: { title: 'Start' } },
},
{
type: 'LineString',
coordinates: [[28.978, 41.008], [32.860, 39.933]],
line: { color: '#c9a87c', width: 3, animate: true },
},
{
type: 'LinePointString',
coordinates: [[32.860, 39.933], [27.138, 38.423]],
marker: { type: 'CIRCLE' },
line: { color: '#8fbcbb', width: 2, animate: true },
},
],
},
sequence: true,
delayDurationInSecond: 2,
focusedItemIndexes: [0, 2],
}
Circle New
CustomGenerates a polygon approximation of a circle from a center point and radius using Turf.js. Supports sweep, pulse, and breathe animations.
{
geometry: { type: 'Circle', coordinates: [28.978, 41.008] },
circle: { radius: 50, unit: 'km', color: '#c9a87c', opacity: 0.4, animate: 'sweep', speed: 1 },
}
Extrusion New
Custom3D polygon extrusion using MapLibre's fill-extrusion paint type. Supports rise (grow height), wave (oscillate), and color sweep animations.
{
geometry: { type: 'Extrusion', coordinates: [[[bounds...]]] },
extrusion: { height: 200, color: '#c9a87c', opacity: 0.7, base: 0, animate: 'rise', speed: 1 },
}
Donut New
CustomTwo concentric circles producing a ring polygon with a hole. Supports sweep (inner radius grows) and pulse (thickness oscillates) animations.
{
geometry: { type: 'Donut', coordinates: [28.978, 41.008] },
donut: { innerRadius: 10, outerRadius: 50, unit: 'km', color: '#c9a87c', opacity: 0.5, animate: 'sweep', speed: 1 },
}
FlowLine New
CustomLine with animated particles flowing continuously along the path. Two modes: particles (flowing dots) and stream (moving dash pattern). Supports glow and progressive draw animation before the flow begins.
{
geometry: { type: 'FlowLine', coordinates: [[lng, lat], ...] },
flow: {
type: 'particles',
color: '#c9a87c',
width: 3,
particleCount: 20,
speed: 0.5,
size: 4,
animationDuration: 3000,
glow: { color: '#c9a87c', width: 8, opacity: 0.3, blur: 4 },
},
}
Cluster New
CustomMapLibre-native point clustering with automatic cluster counts. Supports explode animation for interactive pulsing cluster/point radii.
{
geometry: { type: 'Cluster', coordinates: [[lng, lat], ...] },
cluster: { radius: 50, maxZoom: 14, color: '#c9a87c', animate: 'explode', speed: 1 },
}
IconPoint New
CustomImage-based point marker using an external image URL. The image is rendered inside a CSS shape mask — choose square, circle, or octagon. Supports optional border. Uses an HTML marker element instead of a MapLibre symbol layer for full CSS control.
IconPoint does not support popups or bounce animation. To use an image marker with a popup, set marker: { type: "ICON_POINT", iconPointConfig: { ... } } on a Point or LinePointString instead.{
geometry: { type: 'IconPoint', coordinates: [28.978, 41.008] },
iconPoint: {
imageUrl: 'https://example.com/icon.jpg',
shape: 'circle',
size: 48,
border: { color: '#ffffff', width: 2 },
},
}
Heatmap New
CustomDensity heatmap from point data using MapLibre's native heatmap layer type. Configurable radius, intensity, weight, and color stops. Supports pulse animation that oscillates intensity.
{
geometry: { type: 'Heatmap', coordinates: [[lng, lat], ...] },
heatmap: {
radius: 30,
intensity: 0.6,
weight: 1,
colorStops: [
[0, 'rgba(33,102,172,0)'],
[1, 'rgb(178,24,43)']
],
animate: 'pulse',
},
}
Arc New
CustomGreat-circle curved line between two coordinates using @turf/greatCircle. Renders a smooth flight-path arc. Supports sweep animation (line grows from origin to destination) and optional particles flowing along the arc.
{
geometry: { type: 'Arc', coordinates: [[28.978, 41.008], [51.390, 35.689]] },
arc: {
color: '#88c0d0',
width: 3,
npoints: 100,
animate: 'sweep',
particles: true,
particleCount: 15,
},
}
Label New
CustomText label placed on the map via MapLibre's symbol layer with text-field. Configurable font, size, color, halo, offset, and anchor. Works on Point geometry.
{
geometry: { type: 'Label', coordinates: [28.978, 41.008] },
label: {
text: 'Istanbul',
size: 18,
color: '#c9a87c',
haloColor: '#111d2b',
haloWidth: 2,
},
}
Choropleth New
CustomData-driven polygon fills using MapLibre step expressions. Maps numeric property values to colors using configurable stops with animated color transitions.
{
geometry: { type: 'Polygon', coordinates: [[[28.8, 40.9], ...]] },
choropleth: {
property: 'population',
value: 75,
stops: [[0, '#1b2838'], [50, '#c9a87c'], [100, '#f4f1ea']],
animate: true,
},
}
Works with: Polygon, MultiPolygon geometries. The value field supplies the data point for synthetic features; real GeoJSON datasets carry values in their properties natively.
Gradient Fill New
CustomCanvas-generated gradient texture applied as a MapLibre fill-pattern. Supports radial and linear gradient types with configurable color stops.
{
geometry: { type: 'Polygon', coordinates: [[[28.9, 41.0], ...]] },
gradient: {
type: 'radial',
stops: ['#c9a87c', '#1b2838', '#88c0d0'],
},
}
Works with: Polygon, MultiPolygon. The gradient texture stretches to fit the polygon bounds. Use type: 'linear' for diagonal gradient fill.
Fog of War New
CustomWorld-covering dark overlay with geographic clear-zone holes. Added as an additive layer in StoryPlayer — does not replace the main feature render.
{
geometry: { type: 'Point', coordinates: [28.978, 41.008] },
marker: { type: 'PULSING' },
fog: {
color: '#0a0a14',
opacity: 0.85,
clearZones: [{ center: [28.978, 41.008], radius: 30 }],
animate: { duration: 3000 },
},
}
Additive overlay: Declare fog on any feature alongside its normal styling. The fog renders above the basemap with circular holes cut out at the specified geographic centers.
Mask Reveal New
CustomCircular mask reveal that expands from a geographic center using CSS radial-gradient. Configurable feather for soft edges. Added as an additive overlay.
{
geometry: { type: 'Point', coordinates: [28.978, 41.008] },
marker: { type: 'PULSING' },
mask: {
type: 'circle',
center: [28.978, 41.008],
animate: { from: 0, to: 500, duration: 3000 },
feather: 20,
},
}
Screen-space effect: The mask center tracks the geographic point as the camera moves. When animation completes, the overlay is removed. Use feather: 0 for hard edges.
Feature Config Reference
Detailed reference for all declarative configuration fields. These are the clean-format keys that the normalizer maps to internal GeoJSON properties.
Marker
Controls how point-based geometries appear. Used by Point, LinePointString, and as entries in the markers array.
| Field | Type | Default | Description |
|---|---|---|---|
| type | string | "PULSING" | PULSING, STABLE, CIRCLE, THREED, ICON_POINT |
| popup.title | string | — | Popup header |
| popup.content | string | — | Popup body |
| popup.image | string | — | Popup background image URL |
| popup.size | number | — | Popup image container size in px |
| model.url | string | — | GLTF model URL (requires type: "THREED") |
| model.size | number | 1 | Model scale factor |
| model.angle | number | 0 | Initial rotation in degrees |
| bounce | boolean | false | Animate the marker bouncing up and down (currently disabled in code) |
| iconPointConfig | object | — | When type: "ICON_POINT", pass { imageUrl, shape, size, border } |
Display type mapping: PULSING → PULSING_ANIMATED, STABLE → STABLE_ANIMATED, CIRCLE → CIRCLE, THREED → THREED, ICON_POINT → ICON_POINT.
Line
Controls how line-based geometries render and animate. Used by LineString, LineArc, LinePointString, and as entries in lines.
| Field | Type | Default | Description |
|---|---|---|---|
| color | string | "#0f115e" | Hex color |
| width | number | 3 | Stroke width in pixels |
| opacity | number | 1 | Stroke opacity (0–1) |
| blur | number | 0 | Gaussian blur radius |
| animate | boolean | true | Progressive line draw animation |
| duration | number | 3000 | Animation duration in milliseconds (time-based, independent of line length) |
| dashed | boolean | false | Enable dashed stroke |
| dashArray | number[] | [2, 2] | Dash pattern [on, off] |
| ghost | boolean | false | Show a dashed preview line ahead of the drawing animation |
| ghostColor | string | "#ffffff" | Color of the ghost path line |
| ghostWidth | number | 2 | Width of the ghost path line |
| particles | object | — | Particle flow config: { count: 15, speed: 0.5, size: 3, color: '#c9a87c' } |
| glow | object | — | Glow trail behind the line: { width: 3, color: '#c9a87c', opacity: 0.3, blur: 3 } |
FlowLine
Controls the FlowLine layer behavior. Used by FlowLine geometry type.
| Field | Type | Default | Description |
|---|---|---|---|
| type | string | "particles" | particles (flowing dots) or stream (moving dash pattern) |
| color | string | "#c9a87c" | Line and particle color |
| width | number | 3 | Line width in pixels |
| size | number | 4 | Particle radius in pixels |
| particleCount | number | 20 | Number of flowing particles |
| speed | number | 0.5 | Animation speed multiplier |
| animationDuration | number | 3000 | Progressive draw duration in ms before flow begins |
| glow | object | — | Glow behind the line: { width: 8, color, opacity: 0.3, blur: 4 } |
IconPoint Config
Controls the image-based point marker.
| Field | Type | Default | Description |
|---|---|---|---|
| imageUrl | string | — | URL of the image to display |
| shape | string | "circle" | circle, square, or octagon |
| size | number | 32 | Marker size in pixels |
| border.color | string | "transparent" | Border color |
| border.width | number | 0 | Border width in pixels |
Note: The bounce property is documented but not currently implemented for standalone IconPoint or marker-based ICON_POINT.
Heatmap Config
Controls the Heatmap layer behavior.
| Field | Type | Default | Description |
|---|---|---|---|
| radius | number | 30 | Heatmap blur radius in pixels |
| intensity | number | 0.5 | Heatmap intensity multiplier (0–1) |
| weight | number | 1 | Per-point weight for density |
| opacity | number | 0.8 | Heatmap opacity |
| colorStops | array | — | Array of [stop, color] pairs for heatmap gradient |
| animate | string | — | pulse — oscillate intensity |
| speed | number | 1 | Animation speed multiplier |
Arc Config
Controls the Arc layer behavior.
| Field | Type | Default | Description |
|---|---|---|---|
| color | string | "#c9a87c" | Arc line color |
| width | number | 2 | Arc line width |
| opacity | number | 0.8 | Arc line opacity |
| npoints | number | 100 | Number of points on the great-circle arc |
| animate | string | — | sweep — grow arc from origin |
| speed | number | 1 | Animation speed multiplier |
| particles | boolean | false | Enable particle flow after sweep |
| particleCount | number | 15 | Number of flowing particles |
| size | number | 3 | Particle size in pixels |
Label Config
Controls the text label layer.
| Field | Type | Default | Description |
|---|---|---|---|
| text | string | — | Label text content |
| color | string | "#333333" | Text color |
| size | number | 12 | Font size in pixels |
| opacity | number | 1 | Text opacity |
| font | array | ["Open Sans Regular","Arial Unicode MS Regular"] | Font family stack |
| haloColor | string | "#ffffff" | Text halo/outline color |
| haloWidth | number | 1 | Text halo width |
| offset | array | [0, 0] | Text offset [x, y] in ems |
| anchor | string | "center" | Text anchor: center, left, right, etc. |
Choropleth
Data-driven polygon coloring using step expressions. Used on Polygon/MultiPolygon features.
| Field | Type | Default | Description |
|---|---|---|---|
| property | string | "value" | Name of the feature property to read |
| value | number | — | Data value for synthetic features (real GeoJSON carries values in properties natively) |
| stops | array | — | Array of [threshold, color] pairs for the step expression |
| defaultColor | string | "#1b2838" | Fallback color when property is missing or null |
| animate | boolean | false | Animate color transition from default to target colors over 2s |
Gradient Fill
Canvas-based gradient texture applied via MapLibre fill-pattern. Used on Polygon/MultiPolygon features.
| Field | Type | Default | Description |
|---|---|---|---|
| type | string | "radial" | radial or linear gradient type |
| stops | string[] | ["#c9a87c","#1b2838"] | Array of hex colors evenly distributed across the gradient |
| center | [number, number] | — | Optional radial gradient center [lng, lat] |
Fog of War
World-covering dark overlay with clear-zone holes. Additive overlay — declared on any feature alongside its normal styling.
| Field | Type | Default | Description |
|---|---|---|---|
| color | string | "#0a0a14" | Fog fill color |
| opacity | number | 0.85 | Fog opacity (0–1) |
| clearZones | object[] | [] | Array of { center: [lng,lat], radius: km } clear zones cut as polygon holes |
| animate.duration | number | — | Fade-in duration in ms (fades opacity from 0 to target) |
Mask Reveal
Circular mask reveal expanding from a geographic center. Screen-space CSS effect — additive overlay. The center tracks the geographic point during camera movement.
| Field | Type | Default | Description |
|---|---|---|---|
| type | string | "circle" | Mask shape (circle supported) |
| center | [number, number] | — | Geographic center [lng, lat] of the mask reveal |
| animate.from | number | — | Starting mask radius in screen pixels |
| animate.to | number | — | Ending mask radius in screen pixels |
| animate.duration | number | 2000 | Animation duration in ms |
| feather | number | 0 | Soft edge width in pixels (0 = hard edge) |
Polygon
Controls polygon-specific display and animation behaviors.
| Field | Type | Default | Description |
|---|---|---|---|
| breathe | boolean | false | Oscillate fill opacity between 0.2 and 0.8 |
| progressiveFill | boolean | false | Grow polygon from centroid to full size |
| speed | number | 1 | Animation speed multiplier |
Circle
Controls the Circle layer behavior. Used by Circle geometry type.
| Field | Type | Default | Description |
|---|---|---|---|
| radius | number | 50 | Circle radius |
| unit | string | "kilometers" | Radius unit (kilometers, miles, etc.) |
| color | string | "#c9a87c" | Fill color |
| opacity | number | 0.4 | Fill opacity |
| animate | string | — | sweep, pulse, breathe |
| speed | number | 1 | Animation speed multiplier |
Extrusion
Controls the Extrusion layer behavior. Used by Extrusion geometry type.
| Field | Type | Default | Description |
|---|---|---|---|
| height | number | 200 | Extrusion height in meters |
| color | string | "#c9a87c" | Extrusion color |
| opacity | number | 0.7 | Extrusion opacity |
| base | number | 0 | Base height in meters |
| animate | string | — | rise, wave, color |
| speed | number | 1 | Animation speed multiplier |
Donut
Controls the Donut layer behavior. Used by Donut geometry type.
| Field | Type | Default | Description |
|---|---|---|---|
| innerRadius | number | 10 | Inner ring radius |
| outerRadius | number | 50 | Outer ring radius |
| unit | string | "kilometers" | Radius unit |
| color | string | "#c9a87c" | Fill color |
| opacity | number | 0.5 | Fill opacity |
| animate | string | — | sweep, pulse, breathe |
| speed | number | 1 | Animation speed multiplier |
Cluster
Controls the Cluster layer behavior. Used by Cluster geometry type.
| Field | Type | Default | Description |
|---|---|---|---|
| radius | number | 50 | Cluster radius in pixels |
| maxZoom | number | 14 | Maximum zoom level for clustering |
| color | string | "#c9a87c" | Cluster color |
| animate | string | — | explode |
| speed | number | 1 | Animation speed multiplier |
Buffer
Controls camera bounds padding. Higher values = more zoomed out. A buffer of 25 km is applied if not specified. The buffer is computed using @turf/buffer and the resulting bounding box is passed to map.fitBounds.
| Declarative | Internal | Description |
|---|---|---|
buffer: 15 | itemBufferSize: [{ index: 0, buffer: 15 }] | Buffer in kilometers |
Timeline Config
| Declarative | Internal | Description |
|---|---|---|
url | url | GeoJSON endpoint URL |
start | startValue | Start date "2024-01-01" or number 0 |
end | endValue | End date or number |
step | stepSize | Increment per frame (days for DATE, number for NUMBER) |
qualifier | qualifier | "DATE" or "NUMBER" |
displayType | displayType | "CIRCLE" or "ANIMATED_POINT" |
fps | fps | Frame rate in ms (lower = faster) |
paint | paint | Style: { color, radius, opacity } |
initialQuery | initialQuery | Filter for the first frame |
query | query | Filter for subsequent frames. Keywords: CURRV, PREVV |
Image Overlay
Raster image overlaid on the map with 0.7 opacity. Four-corner coordinate system (top-left, top-right, bottom-right, bottom-left).
| Declarative | Internal | Description |
|---|---|---|
overlay.url | mapOverlayImage[].imageUrl | Image URL |
overlay.coordinates | mapOverlayImage[].coordinates | Four-corner lon/lat array |
{
geometry: { type: 'Polygon', coordinates: [bounds] },
overlay: {
url: 'https://example.com/map.jpg',
coordinates: [
[29.03, 42.92],
[50.01, 42.92],
[50.01, 33.01],
[29.03, 33.01],
],
},
}
Video Overlay
Video overlaid on the map using four-corner coordinate bounding. Supports autoplay, looping, fade-in/fade-out, and seeking.
| Declarative | Internal | Description |
|---|---|---|
overlay.type | type | Set to "video" for video overlay |
overlay.url | imageUrl | Video URL (MP4/WebM) |
overlay.coordinates | coordinates | Four-corner lon/lat array |
overlay.muted | muted | Start muted |
overlay.loop | loop | Loop playback |
overlay.play | play | Autoplay on add |
overlay.fadeIn | fadeIn | Fade-in duration in ms |
overlay.fadeOut | fadeOut | Fade-out duration in ms |
overlay.seek | seek | Initial seek time in seconds |
overlay.duration | duration | Maximum playback duration in ms |
{
geometry: { type: 'Polygon', coordinates: [bounds] },
overlay: {
type: 'video',
url: 'https://example.com/video.mp4',
coordinates: [
[29.03, 42.92],
[50.01, 42.92],
[50.01, 33.01],
[29.03, 33.01],
],
muted: true,
loop: true,
play: true,
fadeIn: 500,
},
}
Camera Options
Camera is configured at the scene level via scene.camera.
| Field | Type | Default | Description |
|---|---|---|---|
| zoomLevel | number | map default | Target zoom level |
| pitch | number | 0 | Camera pitch (0–60) |
| bearing | number | 0 | Camera rotation (0–360) |
| duration | number | 3000 | flyTo / fitBounds duration in milliseconds |
| shouldRotate | boolean | false | Enable continuous map rotation (0.1° per 10ms) |
| orbit | boolean / number | false | Auto-orbit camera around the scene center. true = 30s full rotation, or specify seconds: orbit: 60. |
GeometryCollection-only camera options:
| Field | Type | Default | Description |
|---|---|---|---|
| shouldShowInSequence | boolean | true | If false, the sub-geometry renders but the camera does not move. |
| focusedItemIndexes | number[] | — | Array of sub-item indexes that trigger camera movement. Others render silently. |
| delayDurationInSecond | number | 0 | Seconds to wait before advancing to the next sub-item in a sequence. |
Camera behavior per geometry:
- Point:
flyTothe coordinates at the specifiedzoomLevel. - LineString/Polygon:
fitBoundsto the feature's buffered bounding box. - GeometryCollection: Fits to the collective bounds. With
focusedItemIndexes, only specified sub-items trigger the camera.
Camera Sequence New
Multi-stage camera choreography defined via scene.camera.sequence — an array of timed actions. Runs independently of feature-driven camera, after all features finish rendering.
| Action | Params | Description |
|---|---|---|
flyTo | t, target, zoom, pitch?, bearing?, duration, easing? | Animate camera to a new center with optional pitch/bearing |
fitBounds | t, bbox, pitch?, bearing?, duration | Fit camera to a bounding box |
orbit | t, angle?, duration | Orbit camera by specified degrees (default 360) |
rotateTo | t, bearing, duration, easing? | Rotate camera to a specific bearing |
wait | t, duration | Pause at current position |
shake | t, intensity?, duration | Camera shake with configurable intensity and decay |
{
camera: {
sequence: [
{ t: 0, action: 'flyTo', target: [28.978, 41.008], zoom: 10, duration: 3000, easing: 'easeOut' },
{ t: 3, action: 'orbit', angle: 180, duration: 4000 },
{ t: 8, action: 'flyTo', target: [32.860, 39.933], zoom: 12, pitch: 60, duration: 4000, easing: 'easeInOutCubic' },
],
},
}
Each action has a t (start time in seconds from feature render completion). Actions are sorted by t and scheduled independently. Overlapping actions (e.g., orbit + flyTo at the same t) may conflict — stagger them.
Easing Curves New
28 easing functions available for camera animations. Pass the name string to any camera action that accepts easing.
| Category | Available Functions |
|---|---|
| Linear | linear |
| Quad | easeInQuad, easeOutQuad, easeInOutQuad |
| Cubic | easeIn (alias), easeOut (alias), easeInOut (alias), easeInCubic, easeOutCubic, easeInOutCubic |
| Quart | easeInQuart, easeOutQuart, easeInOutQuart |
| Quint | easeInQuint, easeOutQuint, easeInOutQuint |
| Sine | easeInSine, easeOutSine, easeInOutSine |
| Expo | easeInExpo, easeOutExpo, easeInOutExpo |
| Circ | easeInCirc, easeOutCirc, easeInOutCirc |
| Back | easeInBack, easeOutBack, easeInOutBack |
| Elastic | easeInElastic, easeOutElastic |
| Bounce | easeOutBounce |
| Spring | { type: 'spring', stiffness: 0.3, damping: 0.7 } |
| Custom | { type: 'cubicBezier', values: [x1, y1, x2, y2] } |
Usage: Set easing: 'easeOutCubic' on any camera action. For spring physics, pass easing: { type: 'spring', stiffness: 0.3, damping: 0.7 }. For custom beziers, easing: { type: 'cubicBezier', values: [0.42, 0, 0.58, 1] }.
Branching
Stories can have non-linear paths. Branches are evaluated when a scene ends. If the condition passes, playback jumps to the target scene instead of continuing sequentially.
| Method | Arguments | Description |
|---|---|---|
| story.story.addBranch(fromId, condition, toId) | string, Function|Object, string | Add a branch rule. condition can be a function (sceneData) → boolean or an object { key, value } for property matching. |
// Condition as function
story.story.addBranch(
'crossroads',
(sceneData) => sceneData.sceneIndex === 0,
'northern_route'
);
// Condition as key-value match
story.story.addBranch(
'northern_route',
{ key: 'completed', value: true },
'finale'
);
Plugin System
Extend Kartograf with custom geometry types by implementing the Layer interface and registering it:
// CDN: Kartograf.Layer is the base classspan>
const { Layer } = Kartograf;
class HeatmapLayer extends Layer {
// Called to add sources and layers to the map
add() {
const { map, element, index, state, camera } = this;
// Camera is handled automatically by the caller
}
// Called to clean up
remove() {
// Remove sources, layers, markers
}
// Optional: per-frame animation
animate() {
// Return when animation is complete
}
}
kartograf.registerLayerType('Heatmap', HeatmapLayer);
After registration, use the type name in your geometry:
{
geometry: { type: 'Heatmap', coordinates: [...] },
// Your custom config fields
}
Layer class interface:
| Method | Required | Description |
|---|---|---|
constructor(map, element, index, state, camera) | — | Called by the factory. Store references. |
add() | Yes | Add sources, layers, markers. Camera is handled before this is called. |
remove() | Yes | Remove all sources and layers this layer added. |
animate() | No | Start any animation. Fire ITEM_ANIMATION_FINISHED when done. |
Events Reference
Kartograf fires custom events on the map instance. Listen to them to synchronize external UI, control navigation, or track state.
| Event Name | Source | Fired When |
|---|---|---|
ITEM_IN_ANIMATION | map | A feature animation starts (line draw, point move, camera flight). Sets state.isMapFlyToInProgress = true. |
ITEM_ANIMATION_FINISHED | map | Animation completes. Fires state.mapIsReadyForNextItemDraw() which advances the SequenceRunner. |
map.on('ITEM_IN_ANIMATION', () => {
// Disable UI, show loading
});
map.on('ITEM_ANIMATION_FINISHED', () => {
// Re-enable UI, advance navigation
});
Animation Manager API:
// CDN: getAnimationManager is on the Kartograf global
const { getAnimationManager } = Kartograf;
const manager = getAnimationManager();
manager.getActiveAnimations(); // string[] of active IDs
manager.cancelAllAnimations(); // stop everything
manager.getStats(); // debug info
Error Handling
Common errors and their causes
| Error | Cause | Solution |
|---|---|---|
Kartograf: mapInstance is required | No mapInstance passed to constructor | Pass a valid MapLibre GL map instance |
mapInstance is required | No map instance passed to constructor | Pass a valid MapLibre GL map instance |
Failed to render geometry | Invalid feature in a story scene | Check feature geometry and properties |
Graceful degradation
- Missing
dateOrder→ defaults may be applied (behavior depends on layer type) - Invalid or empty coordinates → defensive checks skip them with console warnings
- Failed 3D model load → warning logged, feature renders without the model
- Animation conflicts →
cancelAllAnimationsis called before each new feature
Performance
- Coordinate interpolation: Line animations pre-compute interpolated coordinates to avoid per-frame calculation overhead.
- Animation lifecycle: The
AnimationManagertracks all active requestAnimationFrame IDs and cancels them on cleanup — no orphaned frames. - Buffer size: Larger buffer values produce more zoomed-out camera views, which reduces rendering load. Use
buffer: 5for detailed views,buffer: 50for overviews. - 3D models: Keep GLTF files under 5 MB. Models are loaded once per feature — avoid re-creating models unnecessarily.
- Timeline FPS: Higher
fpsvalues (e.g., 200+) mean faster frame advancement. Lower values (e.g., 50) mean slower, smoother progression. - Memory: Each
StoryPlayerscene cleans up previous feature sources, layers, markers, and animation frames before rendering. Callingdestroy()on the Kartograf instance cleans up everything including the camera controller.
kartograf.destroy() when removing a Kartograf instance (e.g., in framework beforeUnmount / useEffect cleanup). This prevents event listener leaks and orphaned animation frames.