Marker Details
Marker Details Guide
Section titled “Marker Details Guide”Learn how to work with marker details - the measurement and positioning metrics calculated from marker corner points.
Overview
Section titled “Overview”Marker Details is a one-to-one entity that stores calculated spatial measurements for markers. While markers store the raw 3D corner points, marker details provide:
- Center Location: Average position in longitudinal (Z) and cross (X) directions
- Distance Metrics: Distances to reference planes/axes
- Size Measurements: Physical dimensions (width and length)
- Custom Properties: Additional measurement metadata
Data Structure
Section titled “Data Structure”interface MarkerDetails { marker_id: UUID center_location_long: number // Center Z coordinate center_location_cross: number // Center X coordinate x_negative: number // Left distance (signed) x_positive: number // Right distance (signed) z_negative: number // Near distance z_positive: number // Far distance long_size: number // Length along Z axis cross_size: number // Width along X axis custom_props: object // Custom measurements created_at: timestamp updated_at: timestamp}Metric Calculations
Section titled “Metric Calculations”All metrics are calculated from the four marker corner points: p1, p2, p3, p4.
Center Location (Longitudinal)
Section titled “Center Location (Longitudinal)”The average Z coordinate of all four corners.
Formula:
center_location_long = (p1[2] + p2[2] + p3[2] + p4[2]) / 4Example:
const p1 = [-0.230, 0.060, 0.030]const p2 = [-0.165, 0.057, 0.037]const p3 = [-0.167, 0.050, 0.262]const p4 = [-0.232, 0.053, 0.255]
const centerZ = (0.030 + 0.037 + 0.262 + 0.255) / 4// Result: 0.146 metersPurpose: Indicates the marker’s position along the longitudinal axis (forward/backward direction).
Center Location (Cross)
Section titled “Center Location (Cross)”The average X coordinate of all four corners.
Formula:
center_location_cross = (p1[0] + p2[0] + p3[0] + p4[0]) / 4Example:
const centerX = (-0.230 + -0.165 + -0.167 + -0.232) / 4// Result: -0.198 metersPurpose: Indicates the marker’s position along the cross axis (left/right direction).
X Negative / X Positive
Section titled “X Negative / X Positive”The X coordinate that is closest to zero (preserving sign), indicating which side of the centerline the marker is on.
Formula:
// Find X with minimum absolute value, keep signx_negative = X_value where |X_value| = min(|p1[0]|, |p2[0]|, |p3[0]|, |p4[0]|)
// For x_positive, you could use max, or store both extremesExample:
const xValues = [-0.230, -0.165, -0.167, -0.232]const absValues = [0.230, 0.165, 0.167, 0.232]const minIndex = absValues.indexOf(Math.min(...absValues)) // index 1const xNegative = xValues[minIndex] // -0.165Interpretation:
- Negative value: Marker is on the left side
- Positive value: Marker is on the right side
- Magnitude: Distance from centerline (X=0)
Z Negative / Z Positive
Section titled “Z Negative / Z Positive”The minimum and maximum Z coordinates, indicating the span along the longitudinal axis.
Formula:
z_negative = min(p1[2], p2[2], p3[2], p4[2])z_positive = max(p1[2], p2[2], p3[2], p4[2])Example:
const zValues = [0.030, 0.037, 0.262, 0.255]const zNegative = Math.min(...zValues) // 0.030const zPositive = Math.max(...zValues) // 0.262Purpose:
z_negative: Closest edge to the starting point (Z=0)z_positive: Farthest edge from the starting point
Long Size (Length)
Section titled “Long Size (Length)”The extent of the marker along the Z axis (longitudinal direction).
Formula:
long_size = z_positive - z_negativeExample:
const longSize = 0.262 - 0.030// Result: 0.232 meters (23.2 cm)Purpose: Physical length of the marker along the movement direction.
Cross Size (Width)
Section titled “Cross Size (Width)”The extent of the marker along the X axis (cross direction).
Formula:
cross_size = max(p1[0], p2[0], p3[0], p4[0]) - min(p1[0], p2[0], p3[0], p4[0])Example:
const xValues = [-0.230, -0.165, -0.167, -0.232]const crossSize = Math.max(...xValues) - Math.min(...xValues)// Result: -0.165 - (-0.232) = 0.067 meters (6.7 cm)Purpose: Physical width of the marker perpendicular to movement direction.
Coordinate System
Section titled “Coordinate System”Understanding the coordinate system is essential for interpreting metrics:
+Y (Up) │ │ └─────────── +X (Right) ╱ ╱ ╱ +Z (Forward)Axis Directions:
-
X-axis: Cross direction (perpendicular to movement)
- Negative: Left side
- Positive: Right side
- Zero: Centerline
-
Y-axis: Vertical direction (height)
- Zero: Reference plane (e.g., floor)
- Positive: Upward
-
Z-axis: Longitudinal direction (along movement)
- Zero: Starting edge/origin
- Positive: Forward direction
Working with Marker Details
Section titled “Working with Marker Details”Creating Marker Details (API)
Section titled “Creating Marker Details (API)”curl -X PUT http://localhost:8080/api/v1/markers/{marker_id}/details \ -H "Content-Type: application/json" \ -d '{ "center_location_long": 0.146, "center_location_cross": -0.198, "x_negative": -0.232, "x_positive": -0.165, "z_negative": 0.030, "z_positive": 0.262, "long_size": 0.232, "cross_size": 0.067, "custom_props": { "measurement_method": "automatic", "confidence": 0.95 } }'Auto-Calculate from Marker Points
Section titled “Auto-Calculate from Marker Points”# Calculate for single markercurl -X POST http://localhost:8080/api/v1/markers/{marker_id}/details/calculate
# Calculate for all markers in a sessioncurl -X POST http://localhost:8080/api/v1/work-sessions/{session_id}/markers/calculate-detailsiOS Implementation
Section titled “iOS Implementation”struct MarkerDetailsCalculator { static func calculate(from marker: Marker) -> UpsertMarkerDetails { let points = [marker.p1, marker.p2, marker.p3, marker.p4]
// Extract coordinates let xs = points.map { $0[0] } let ys = points.map { $0[1] } let zs = points.map { $0[2] }
// Calculate center let centerX = Float(xs.reduce(0, +) / 4.0) let centerZ = Float(zs.reduce(0, +) / 4.0)
// Find X closest to zero (signed) let xAbs = xs.map { abs($0) } let minIndex = xAbs.enumerated().min(by: { $0.element < $1.element })!.offset let xNegative = Float(xs[minIndex])
// Z range let zNegative = Float(zs.min()!) let zPositive = Float(zs.max()!)
// Sizes let longSize = zPositive - zNegative let crossSize = Float(xs.max()! - xs.min()!)
return UpsertMarkerDetails( centerLocationLong: centerZ, centerLocationCross: centerX, xNegative: xNegative, xPositive: Float(xs.max()!), // or calculate differently zNegative: zNegative, zPositive: zPositive, longSize: longSize, crossSize: crossSize, customProps: [:] ) }}Calibration Impact
Section titled “Calibration Impact”When a space has a calibration vector, markers have both raw and calibrated coordinates. Marker details can be calculated from either:
Raw Metrics
Section titled “Raw Metrics”Calculated from p1, p2, p3, p4 (original AR coordinates)
Calibrated Metrics
Section titled “Calibrated Metrics”Calculated from calibrated_data.p1, p2, p3, p4 (transformed coordinates)
Important:
- Center locations will differ between raw and calibrated (translation applied)
- Sizes should remain identical (only translation, no rotation/scaling)
Detecting Calibration:
func calibratedDiffers(raw: Double, calibrated: Double) -> Bool { return abs(calibrated - raw) > 1e-6}
// Check if center Z differslet centerZDiffers = calibratedDiffers( raw: rawCenterZ, calibrated: calibratedCenterZ)Custom Properties
Section titled “Custom Properties”Store additional measurement data in custom_props:
{ "measurement_method": "automatic", "confidence": 0.95, "inspector": "John Doe", "notes": "Measured during routine inspection", "quality_score": 4.5, "verification_required": false, "tags": ["verified", "high-accuracy"], "environmental_conditions": { "lighting": "good", "temperature_celsius": 22, "humidity_percent": 45 }}Use Cases
Section titled “Use Cases”1. Finding Markers Near Centerline
Section titled “1. Finding Markers Near Centerline”-- Find markers within 0.1m of centerlineSELECT m.*, md.x_negativeFROM markers mJOIN marker_details md ON m.id = md.marker_idWHERE ABS(md.x_negative) < 0.1ORDER BY ABS(md.x_negative);2. Sorting by Position
Section titled “2. Sorting by Position”-- Sort markers by longitudinal positionSELECT m.*, md.center_location_longFROM markers mJOIN marker_details md ON m.id = md.marker_idORDER BY md.center_location_long;3. Size-based Filtering
Section titled “3. Size-based Filtering”-- Find large markers (>0.2m in either dimension)SELECT m.*, md.long_size, md.cross_sizeFROM markers mJOIN marker_details md ON m.id = md.marker_idWHERE md.long_size > 0.2 OR md.cross_size > 0.2;4. Quality Control
Section titled “4. Quality Control”func validateMarkerDetails(_ details: MarkerDetails) -> [String] { var issues: [String] = []
// Check if size is too small if details.longSize < 0.01 { issues.append("Length too small: \(details.longSize)m") }
if details.crossSize < 0.01 { issues.append("Width too small: \(details.crossSize)m") }
// Check if position is within expected bounds if abs(details.centerLocationCross) > 2.0 { issues.append("Center too far from centerline") }
return issues}Visualization
Section titled “Visualization”Display in UI
Section titled “Display in UI”struct MarkerDetailsView: View { let details: MarkerDetails
var body: some View { VStack(alignment: .leading, spacing: 8) { Section("Position") { HStack { Text("Longitudinal:") Spacer() Text("\(details.centerLocationLong, specifier: "%.3f") m") .monospaced() }
HStack { Text("Cross:") Spacer() Text("\(details.centerLocationCross, specifier: "%.3f") m") .monospaced() } }
Section("Dimensions") { HStack { Text("Length:") Spacer() Text("\(details.longSize, specifier: "%.3f") m") .monospaced() }
HStack { Text("Width:") Spacer() Text("\(details.crossSize, specifier: "%.3f") m") .monospaced() } }
Section("Distance Metrics") { HStack { Text("Z Near:") Spacer() Text("\(details.zNegative, specifier: "%.3f") m") .monospaced() }
HStack { Text("Z Far:") Spacer() Text("\(details.zPositive, specifier: "%.3f") m") .monospaced() } } } .padding() }}Best Practices
Section titled “Best Practices”1. Auto-Calculate in Batch
Section titled “1. Auto-Calculate in Batch”# When completing a session, calculate all details at oncecurl -X POST http://localhost:8080/api/v1/work-sessions/{session_id}/markers/calculate-details2. Validate Before Saving
Section titled “2. Validate Before Saving”func createMarkerWithDetails(points: [[Double]], label: String) async throws { // Create marker let marker = try await MarkerService.shared.createMarker( CreateMarker( workSessionId: sessionId, label: label, p1: points[0], p2: points[1], p3: points[2], p4: points[3], color: "#FF0000" ) )
// Auto-calculate details try await MarkerDetailsService.shared.calculateDetails(markerId: marker.id)}3. Use Custom Props for Metadata
Section titled “3. Use Custom Props for Metadata”let details = UpsertMarkerDetails( centerLocationLong: 1.5, centerLocationCross: -0.2, xNegative: -0.25, xPositive: -0.15, zNegative: 1.4, zPositive: 1.6, longSize: 0.2, crossSize: 0.1, customProps: [ "calculated_at": AnyCodable(Date()), "ar_confidence": AnyCodable(0.95), "manual_verification": AnyCodable(false) ])4. Compare Raw vs Calibrated
Section titled “4. Compare Raw vs Calibrated”func compareMetrics(marker: Marker) { let rawDetails = MarkerDetailsCalculator.calculate(from: marker)
if let calibrated = marker.calibratedData { let calibratedMarker = Marker( /* copy marker with calibrated points */ ) let calibratedDetails = MarkerDetailsCalculator.calculate(from: calibratedMarker)
print("Center Z difference: \(calibratedDetails.centerLocationLong - rawDetails.centerLocationLong)") print("Size difference: \(calibratedDetails.longSize - rawDetails.longSize)") // Should be ~0 }}Troubleshooting
Section titled “Troubleshooting”Problem: Width or length differs between raw and calibrated
Cause: Calibration included rotation or scaling (unexpected)
Solution: Review space calibration vector - should only be translation [x, y, z]
Problem: Center coordinates seem incorrect
Cause: One or more corner points are outliers
Solution: Inspect individual point coordinates, re-capture marker if needed
Problem: Negative sizes or extreme values
Cause: Invalid point order or coordinate system mismatch
Solution: Validate point ordering, ensure consistent coordinate system