Skip to content

Annotation Contexts

An annotation context defines a scope where annotations exist — for example, multiple annotation and labelling tasks. Each context specifies which drawing tools are available and optionally limits how many annotations of each type can be created.

Annotation contexts are an extension of the core Annotation model, and are defined in the @osdlabel/annotation-context package.

Only one context is active at a time.

import { createAnnotationContextId } from '@osdlabel/annotation-context';
import type { AnnotationContext } from '@osdlabel/annotation-context';
const contexts: AnnotationContext[] = [
{
id: createAnnotationContextId('buildings'),
label: 'Buildings',
// Only annotate buildings in specific regions
imageIds: [createImageId('sample-1'), createImageId('sample-2')],
tools: [
// Up to 10 building outlines per image
{ type: 'rectangle', maxCount: 10, countScope: 'per-image' },
// Up to 5 freehand boundaries total for irregular shapes
{ type: 'polyline', maxCount: 5 },
],
},
{
id: createAnnotationContextId('roads'),
label: 'Roads',
tools: [
// Trace road segments with lines
{ type: 'line', maxCount: 20, countScope: 'per-image' },
// Mark intersections
{ type: 'point', maxCount: 15 },
],
},
{
id: createAnnotationContextId('landmarks'),
label: 'Landmarks',
tools: [
{ type: 'rectangle' },
{ type: 'circle' },
{ type: 'line' },
{ type: 'point' },
{ type: 'polyline' },
{ type: 'freeHandPath' },
],
},
];

Each tool in a context can have:

PropertyTypeDefaultDescription
typeToolType(required)'rectangle' | 'circle' | 'line' | 'point' | 'polyline' | 'freeHandPath'
maxCountnumberunlimitedMaximum number of annotations of this type
countScopeCountScope'global'Whether maxCount applies per-image or globally across all images
defaultStylePartial<AnnotationStyle>default styleOverride the default stroke/fill for this tool

When a tool’s maxCount is reached, it is automatically disabled in the toolbar and via keyboard shortcuts.

The countScope property controls how annotations are counted against maxCount:

  • 'global' (default) — Counts all annotations of this type across all images
  • 'per-image' — Counts annotations per image independently
{
type: 'line',
maxCount: 3,
countScope: 'per-image', // Each image can have up to 3 lines
}

A context can be restricted to specific images using the imageIds property:

{
id: createAnnotationContextId('context1'),
label: 'My context',
imageIds: [createImageId('img-1'), createImageId('img-2')],
tools: [{ type: 'line', maxCount: 3 }],
}

When the active cell shows an image not in the context’s imageIds, all tools are disabled. If imageIds is omitted, the context applies to all images.

Pass contexts to the Annotator component or set them programmatically:

// Via Annotator component
<Annotator images={images} contexts={contexts} />;
// Via actions (when using AnnotatorProvider)
const { actions } = useAnnotator();
actions.setContexts(contexts);
actions.setActiveContext(contexts[0].id);

You can switch contexts programmatically using actions:

const { actions } = useAnnotator();
actions.setActiveContext(contextId);
// Clear the active tool when switching contexts
actions.setActiveTool(null);

Or enable the built-in UI in the Annotator component:

<Annotator images={images} contexts={contexts} showContextSwitcher={true} />

The ContextSwitcher component can also be used independently in custom layouts. See Components.

By default, only the active context’s annotations are visible on the canvas. You can display annotations from additional contexts as a read-only overlay — visible but not selectable or editable.

<Annotator
images={images}
contexts={contexts}
displayedContextIds={[createAnnotationContextId('ctx1'), createAnnotationContextId('ctx2')]}
/>

Via actions (when using AnnotatorProvider)

Section titled “Via actions (when using AnnotatorProvider)”
const { actions } = useAnnotator();
actions.setDisplayedContexts([
createAnnotationContextId('ctx1'),
createAnnotationContextId('ctx2'),
]);

The active context is always displayed regardless of displayedContextIds. Annotations from non-active displayed contexts are rendered on the canvas but cannot be selected, moved, or modified.

Use the constraintStatus accessor to check which tools are enabled:

const { constraintStatus } = useAnnotator();
const status = constraintStatus();
// status.rectangle.enabled — boolean
// status.rectangle.currentCount — number
// status.rectangle.maxCount — number | null

Or use the useConstraints hook for convenience:

import { useConstraints } from 'osdlabel/hooks';
const { isToolEnabled, canAddAnnotation } = useConstraints();
if (isToolEnabled('rectangle')) {
// Rectangle tool is available
}