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
npm install kartograf
Kartograf requires MapLibre GL JS as a peer dependency:
npm install maplibre-gl
Peer dependencies (installed automatically):
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:
import Kartograf from 'kartograf';
import maplibregl from 'maplibre-gl';
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) |
| 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 21 geometry types: 7 standard GeoJSON and 14 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,
},
}
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. |
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. Unlike the imperative API, camera does not live inside feature properties — it is a scene-level concern. (The normalizer preserves any cameraOptions if you pass the old format.)
| 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) |
| 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.
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:
import { Layer } from '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:
import { getAnimationManager } from '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.