Skip to content

Scenarios, Regions & Playback ​

Scenarios ​

Scenarios are named graph snapshots. Each captures the full graph state (nodes + edges), viewport, and region boundaries. Saved scenarios are deep-cloned — mutations to the live graph never leak into saved state.

js
sys.saveScenario('baseline');

sys.updateAsset('V1', { status: 'closed' });
sys.removeConnection('V1', 'H1', 'feeds');
sys.saveScenario('v1-shutdown');

sys.loadScenario('baseline');
// graph, viewport, and regions restored to baseline state

Diff ​

Compare two saved scenarios to see what changed:

js
const diff = sys.diffScenarios('baseline', 'v1-shutdown');
// → {
//     assets:      { added: [], removed: [], modified: [...] },
//     connections:  { added: [], removed: [...], modified: [] },
//     regions:     { added: [], removed: [], modified: [] },
// }

Each category contains:

  • added — items present in the second scenario but not the first
  • removed — items present in the first but not the second
  • modified — items in both but with different data. Returns { uid, before, after }

CRUD ​

js
sys.saveScenario('name');
sys.loadScenario('name');
sys.deleteScenario('name');
sys.getScenarioNames();      // → ['baseline', 'v1-shutdown']

Regions ​

Regions are polygon-bounded spatial annotations that overlay the graph without participating in topology. They provide bounded-region semantics for any domain: distribution zones, coverage areas, administrative boundaries, operational sectors.

js
sys.addRegion('zone-north', {
    label: 'North Service Zone',
    polygon: [[12.95, 77.55], [12.95, 77.65], [13.05, 77.65], [13.05, 77.55]],
    meta: { fill: '#3b82f620', stroke: '#3b82f6', population: 1200000 },
});

Containment queries ​

Point-in-polygon queries determine which assets fall inside a region, and which regions contain a given asset:

js
sys.getAssetsInRegion('zone-north');
// → ['T1', 'P3', 'H12'] — assets whose coordinates fall inside the polygon

sys.getRegionsOfAsset('T1');
// → ['zone-north'] — which regions contain this asset

Containment reads coordinate fields the same way as reproject() and computeExtent(): per-type coordFields if registered, otherwise the global spatial coordFields. Assets without coordinate fields are skipped.

CRUD ​

js
sys.addRegion('zone-south', {
    label: 'South Service Zone',
    polygon: [[12.85, 77.55], [12.85, 77.65], [12.95, 77.65], [12.95, 77.55]],
    meta: { fill: '#10b98120', stroke: '#10b981' },
});

sys.updateRegion('zone-north', {
    label: 'North Zone (Expanded)',
    meta: { population: 1400000 },
});

sys.getRegion('zone-north');   // → { label, polygon, meta }
sys.getRegions();              // → [{ uid, label, polygon, meta }, ...]
sys.removeRegion('zone-south');
sys.regionCount;               // → 1

Regions are included in scenario snapshots. Different scenarios can have different region boundaries — saving a scenario captures the current region state, and loading one restores it.

The meta field is an open bag (same convention as type meta) — wity-system stores it, L3 interprets it for rendering.

Playback ​

Playback defines an ordered sequence of presentation steps for stepping through a system graph. Pure data — wity-system stores and serializes the sequence. L3 interprets step semantics (animation, pan/zoom, reveal timing).

js
sys.setPlayback([
    { type: 'focus', center: { a: 12.42, b: 76.57 }, zoom: 12 },
    { type: 'reveal-assets', uids: ['KRS', 'TKH-1'] },
    { type: 'reveal-connections', uids: ['trunk:KRS→TKH-1'] },
    { type: 'focus', uids: ['HRH', 'TAT'] },
    { type: 'reveal-regions', uids: ['zone-north'] },
    { type: 'annotate', text: 'Pipeline corridor', position: { a: 12.7, b: 77.1 } },
    { type: 'scenario', name: 'drought-2024' },
]);

sys.getPlayback();
// → [{ type: 'focus', ... }, { type: 'reveal-assets', ... }, ...]

Step types are open-ended — L3 defines and interprets them. Common conventions:

Step typeFieldsL3 interpretation
focuscenter, zoom or uidsPan/zoom camera to area or to fit named assets
reveal-assetsuidsMake named assets visible
reveal-connectionsuidsMake named connections visible
reveal-regionsuidsMake named regions visible
annotatetext, positionShow text overlay at position
scenarionameSwitch to named scenario

Selector resolution ​

For structured L2-level step resolution, SystemGraph provides resolveSelector and resolvePlaybackStep:

js
sys.resolveSelector({ type: 'region', uid: 'zone-north' });
// → ['T1', 'P3', 'H12'] — assets inside the region

sys.resolveSelector({ type: 'assetType', name: 'plant' });
// → ['WTP', 'WTP2'] — all assets of that type

sys.resolveSelector({ type: 'path', from: 'WTP', to: 'H1' });
// → ['WTP', 'V1', 'H1'] — shortest path

sys.resolveSelector({ type: 'uids', uids: ['WTP', 'V1'] });
// → ['WTP', 'V1'] — explicit list (validates existence)

sys.resolveSelector({ type: 'all' });
// → all asset UIDs

sys.resolveSelector({ type: 'union', selectors: [
    { type: 'region', uid: 'zone-north' },
    { type: 'assetType', name: 'plant' },
]});
// → deduplicated union of both queries

resolvePlaybackStep resolves a full step definition into concrete UIDs and viewport bounds:

js
const resolved = sys.resolvePlaybackStep({
    label: 'Show plants in north zone',
    select: [
        { type: 'region', uid: 'zone-north' },
        { type: 'assetType', name: 'plant' },
    ],
    connections: 'related',
    regions: ['zone-north'],
    viewport: 'fit',
    cumulative: true,
}, prevAssets);

// → { assets: [...], connections: [...], regions: [...], viewport: { bounds } }

Step schema:

FieldTypeDefaultDescription
labelstring—Human-readable step name
selectSelector | Selector[]—What assets to show
connections'related' | 'all' | 'none''related'Connection inclusion mode
regionsstring[][]Region UIDs to highlight
viewport'fit' | { center, zoom }'fit'Camera positioning
cumulativebooleantrueWhether to include previous step's assets

Spatial projection ​

SystemGraph supports optional spatial projection with pluggable coordinate adapters:

js
const sys = new SystemGraph({
    GraphAbstract,
    spatial: {
        project:     (lat, lon) => mapLibrary.project([lon, lat]),
        unproject:   (x, y) => mapLibrary.unproject([x, y]),
        coordFields: ['lat', 'lon'],
    },
});

Per-type coordFields override the global default:

js
sys.registerAssetType('tower', {
    coordFields: ['easting', 'northing'],
});

sys.addAsset('T1', 'tower', { easting: 500000, northing: 7800000 });
sys.reproject();  // writes x/y into asset data — reads correct fields per type

Methods ​

js
sys.isSpatial;              // → boolean
sys.project(lat, lon);      // → { x, y }
sys.unproject(x, y);        // → { a, b }
sys.reproject();            // writes x/y into all assets from their coord fields
sys.computeExtent();        // → { sw: {a,b}, ne: {a,b} } from all asset positions
sys.getExtentOfAssets(uids); // → extent for a subset of assets
sys.setSpatial({ project, unproject, coordFields });
sys.clearSpatial();

Serialization as <wity-knowledge> ​

System documents serialize to <wity-knowledge> XML via graph-headless. The graph provider must be injected once at startup:

js
import * as graph from '@wity/graph-headless';
import { setGraphProvider, serialize, parse } from 'wity-system';

setGraphProvider(graph);

const xml = serialize(sys.snapshot());   // → <wity-knowledge> XML string
const snapshot = parse(xml);             // → SystemGraph-compatible snapshot
sys.restore(snapshot);

The serializer maps the snapshot to a GraphAbstract:

  • Root node ($sys, type system-document) — carries version, viewport, spatial config, and all top-level fields
  • Type registry node ($sys/types, type system-types) — serialized TypeRegistry
  • Scenario nodes ($sys/scenario/<name>, type system-scenario) — each saved scenario
  • Asset nodes — directly from the system graph
  • Connection edges — directly from the system graph

Metadata nodes use the $sys UID prefix to avoid collision with user-created asset UIDs.