Datenmodelle
Die wichtigsten Objektformen, die die API zurückgibt. Zeitstempel sind Epoch-Millisekunden, sofern nicht anders vermerkt.
interface Marker { id: string; latitude: number; longitude: number; title: string; description?: string; // Markdown color: string; // hex, e.g. "#3B82F6" markerIconId?: string; // reference into the room icon registry markerIconSize?: number; // per-marker size override (px) tags?: string[]; createdBy: string; createdAt: number; eventDate?: number; // for the timeline imageKey?: string; // storage key — fetch via .../markers/image?key=… thumbnailKey?: string; imageBlob?: string; // legacy inline image, superseded by imageKey thumbnailBlob?: string; // legacy embeddedMediaUrl?: string; // YouTube / Vimeo / external mediaSize?: 'sm' | 'md' | 'lg'; // embedded-media popup width (default 'md') imageMetadata?: ImageMetadata; // EXIF read from the attached photo — read-only}
interface ImageMetadata { gpsSource: boolean; // true when the marker sits where the photo was taken cameraMake?: string; cameraModel?: string; dateTaken?: number; // epoch ms altitude?: number; orientation?: number;}imageMetadata entsteht, wenn in der App ein Foto angehängt wird — ein geotaggtes Foto verschiebt den
Marker an seine GPS-Position und setzt gpsSource. Über die API ist das Feld nicht setzbar, und ein
Austausch von imageKey/thumbnailKey löscht es, damit ein neues Foto nie die Herkunft des vorherigen
erbt.
Die Endpunkte für einzelne Marker (GET/PATCH /api/rooms/:roomId/markers/:markerId) lassen
createdByEmail und die Inline-Blob-Felder (imageBlob, thumbnailBlob, iconBlob) in ihren
Antworten weg; der Listen-Endpunkt liefert die gespeicherte Form unverändert.
PolygonLayer
Abschnitt betitelt „PolygonLayer“interface PolygonLayer { id: string; name: string; description?: string; geometryType?: string; // "Polygon" | "MultiPolygon" | "LineString" | ... color: string; opacity: number; // 0–1 strokeWeight: number; // px visible: boolean; totalFeatureCount?: number; createdBy: string; createdAt: number; eventDate?: number; bbox?: [number, number, number, number]; // [minLon, minLat, maxLon, maxLat]}RasterLayer
Abschnitt betitelt „RasterLayer“interface RasterLayer { id: string; name: string; description?: string; fileSize: number; // original TIFF size, bytes bounds: [number, number, number, number]; minZoom: number; maxZoom: number; opacity: number; // 0–1 visible: boolean; createdAt: number; eventDate?: number; processingStatus?: 'pending' | 'processing' | 'complete' | 'error'; // display (see the Raster Layers reference) colormap?: string; vmin?: number; vmax?: number; band?: number; // 0 = natural color / all bands renderMode?: 'tiles' | 'image_overlay' | 'rgb'; rgbBands?: [number, number, number]; // 1-based [R, G, B]}MarkerIcon
Abschnitt betitelt „MarkerIcon“interface MarkerIcon { id: string; name: string; iconBlob: string; // image data (base64) category?: string; size: number; // display height in px (default 27) createdAt: number;}Room & RoomMember
Abschnitt betitelt „Room & RoomMember“interface Room { roomId: string; createdBy: string; // owner's user ID createdAt: number; markerCount: number; commitCount: number; lastActivity?: number; isPublic?: boolean;}
interface RoomMember { userId: string; email: string; name?: string; role: 'owner' | 'editor' | 'viewer'; addedBy: string; addedAt: number;}
