3D Mesh Document

Preparation and Publishing Guide

Basic requirements, formats, CRS/CRC, scene/layer conventions, and GeoJSON metadata preparation for Monitored-AI / OCLI


Architecture & Lifecycle Overview

How a 3D document travels from raw pipeline output to interactive 3D web rendering:

[ 3D Asset (.glb) ]
        │
        ├── 1. Export & Validate (Meters, Y-up, Node Hierarchy)
        │
[ Metadata (.geojson) ] ◄── 2. ai makegeojson (CRS anchor, layers, roles)
        │
        ├── 3. ai upload (Upload .glb & .geojson to S3/COS with CRC check)
        │
[ Cloud Object Storage ]
        │
        ├── 4. publish post (Register Feature in PostGIS docs DB)
        │
[ Monitored-AI UI ] ── 5. Fetch Feature ──► Stream GLB ──► Render 3D Scene

3D Format Specifications

Parameter

Specification

Notes

Payload Format

Binary glTF (.glb)

Self-contained single file (geometry, buffers, textures embedded).

Units & Scale

Meters (1 unit = 1.0 m)

Crucial for correct positioning on digital elevation / globe.

Axis Orientation

glTF Standard: Y-Up, +Z Forward

Model roots must conform to glTF Y-up. Conversion from GIS Z-up must be baked into node transforms.

Geometry

Indexed triangle meshes

Keep clean normal vectors and UV coordinates.

Optimization

Draco compression optional

Recommended for models > 20 MB; ensure viewer supports extension.


Spatial Reference (CRS) & Georeferencing

3D models and metadata use EPSG:4979 (3D) and EPSG:4326 (2D) as standard across 3D AIKPs (e.g., drillholes):

  • Anchor Position CRS (EPSG:4979 — WGS 84 3D Geographic):

    • Official 3D CRS with coordinates: [longitude, latitude, altitude] (height in meters above ellipsoid).

    • Used in geoReference.position and 3D bounding calculations (as implemented in drillholes).

  • Footprint Geometry CRS (EPSG:4326 — WGS 84 2D Geographic):

    • Standard 2D [longitude, latitude] for the GeoJSON geometry (Polygon or Point).

    • Enables 2D spatial indexing and map queries in PostGIS.

  • Model Coordinate Frame (model_crs):

    • Set in geoReference.model_crs: EPSG:3857 (Web Mercator frame with height, centered at local origin) or EPSG:4978 (WGS 84 ECEF). Model vertices are shifted around the anchor centroid.

  • Reference Implementation:

    • See ocli.pro.aikp.drillholes (constants.WGS84_CRS = 'epsg:4979', ai_makegeojson.py, make.py).


Coordinate Pipeline: Web Mercator, Centroid & Shift

Model vertices are constructed in Web Mercator (EPSG:3857 + height), centered at (0, 0, 0), and anchored in EPSG:4979:

Source Data (Vector / Shapefile / 3D Points)
    │
    ▼
[ 1. Web Mercator Projection ] ──► Vertices P = [X_3857, Y_3857, Z_height]
                                              │
                                              ▼
[ 2. Calculate Centroid ]       ──► C = [ (Xmin+Xmax)/2, (Ymin+Ymax)/2, (Zmin+Zmax)/2 ]
                                              │
                    ┌─────────────────────────┴─────────────────────────┐
                    ▼                                                   ▼
[ 3. Shift Vertices (P - C) ]                         [ 4. Transform C (3857+Z -> 4979) ]
                    │                                                   │
  P_local = [ P.x - C.x, P.y - C.y, P.z - C.z ]         Anchor = [ lon, lat, altitude ]
                    │                                                   │
                    ▼                                                   ▼
[ 5. GLTF Model Generation ]                          [ 6. GeoJSON Metadata Generation ]
  Model Centroid is (0, 0, 0)                           geoReference: {
  No 32-bit GPU FP jitter                                 "position": Anchor,
                                                          "model_crs": "EPSG:3857" (or 4978)
                                                        }
  1. Web Mercator Projection: Vertices are converted to EPSG:3857 with metric height Z so distances/radii are in meters.

  2. Calculate Centroid (C): Center of the 3D bounding box is computed in EPSG:3857+height coordinates.

  3. Shift Vertices (P - C): All vertices are shifted relative to C, setting local model center to (0, 0, 0).

  4. Anchor Projection: Centroid C is transformed from EPSG:3857 (with height) into EPSG:4979 [longitude, latitude, altitude].

  5. GeoJSON Attachment: Anchor coordinates are written to geoReference.position, anchoring the local origin on the globe.

  6. Reference Implementation (drillholes/preview/make.py):

    stack.to_crs(3857, inplace=True)
    centroid = self._centroid(self._bbox(stack.geometry))
    line_start = np.array(line.coords[0]) - centroid
    line_end   = np.array(line.coords[-1]) - centroid
    transformer = Transformer.from_crs(3857, 4979, always_xy=True)
    centroid_4979 = transformer.transform(centroid[0], centroid[1], centroid[2])
    

Integrity & Checksum Verification (CRC)

Ensuring binary asset integrity between pipeline output and cloud storage:

  • CRC32 / Checksum Integrity:

    • Pre-compute CRC32 checksum or SHA-256 hash prior to upload.

    • Store hash in GeoJSON metadata (properties.metadata.checksum_crc32 or sha256).

  • S3/COS ETag Verification:

    • Verify payload against S3 ETag post-upload (ai upload).

  • Protection against silent corruption:

    • Guards against partial uploads, proxy truncations, or corrupted 3D model buffers.


Scene & Node Hierarchy Conventions

Internal glTF node hierarchy determines how parts are identified:

Scene Root
├── layer_terrain              (Ground surface / foundation mesh)
├── layer_structures_main      (Primary buildings / architectural elements)
├── layer_infrastructure_pipe  (Utilities, pipelines, subterranean)
└── layer_sensors_active       (Instrument locations / sensor equipment)
  • Prefix Convention: layer_<category>_<identifier>

  • Rules:

    • Node names must be unique within the glTF scene graph.

    • Avoid DCC default names (e.g., Cube.001, Mesh_Group).

    • Group static meshes logically to minimize draw calls while maintaining layer toggleability.


UI Layer Mapping (properties.layers)

Connects internal glTF nodes to user-toggleable layers in the UI layer tree:

"layers": [
  {
    "id": "terrain",
    "node_name": "layer_terrain",
    "name": "Ground Terrain",
    "visible": true,
    "opacity": 1.0,
    "category": "base"
  },
  {
    "id": "structures",
    "node_name": "layer_structures_main",
    "name": "Primary Structures",
    "color": "#3b82f6",
    "visible": true,
    "opacity": 0.9,
    "category": "assets"
  }
]

GeoJSON Document Preparation (DB Metadata)

{
  "type": "Feature",
  "geometry": {
    "type": "Polygon",
    "coordinates": [[[4.891, 52.372], [4.896, 52.372], [4.896, 52.375], [4.891, 52.375], [4.891, 52.372]]]
  },
  "properties": {
    "type": "Mesh",
    "friendly_name": "Industrial Facility 3D Model",
    "Bucket": "staging",
    "Endpoint": "https://s3.optoss.net",
    "ResultKey": "projects/site_a/facility_3d",
    "roles": ["public_read", "draft_read"],
    "source": {
      "url": "/s3/staging/projects/site_a/facility_3d/model.glb",
      "type": "Mesh"
    },
    "geoReference": {
      "position": [4.8935, 52.3735, 12.5],
      "model_crs": "EPSG:4978"
    },
    "metadata": {
      "crs": "EPSG:4979",
      "units": "meters",
      "crc32": "a4f108d2"
    },
    "style": { "scale": 1.0 }
  }
}

Step-by-Step Publishing Workflow

1. Generate & Validate Model

Export your model to binary .glb with meter units, embedded textures, and named nodes.

2. Generate GeoJSON Metadata (makegeojson)

ai makegeojson --friendly-name "Facility 3D Mesh" --cos-key "facility_3d"

Or execute custom make_geojson.py populating properties.type = "Mesh", source, and geoReference.

3. Upload Artifacts to Cloud Object Storage (upload)

ai upload

Uploads model.glb and the accompanying metadata .geojson to target S3/COS bucket.

4. Register Document in DB (publish post)

publish post

Pre-Flight Quality Checklist

Before running publish post, verify:

  • [ ] Format: File is valid .glb (self-contained, valid JSON header + binary chunk).

  • [ ] Units: Real-world scale (1 unit = 1 meter).

  • [ ] Orientation: glTF Y-up axis correctly oriented to local zenith.

  • [ ] CRS: geoReference.position contains accurate [lon, lat, elevation].

  • [ ] Integrity: CRC32 or ETag verified against source file.

  • [ ] Naming: Internal glTF node names follow layer_<category>_<id> convention.

  • [ ] Metadata: properties.type is "Mesh" and source.url follows /s3/<bucket>/<key>.glb.

  • [ ] Access Roles: Appropriate access roles set (e.g. ["public_read"]).


Summary

  1. Standardized format: Single .glb binary file with meter scaling.

  2. Deterministic georeferencing: WGS84 anchor with EPSG:4978 model CRS.

  3. Structured layer mapping: glTF nodes map directly to GeoJSON UI controls.

  4. Standard OCLI pipeline: makegeojson $\to$ upload $\to$ publish post.