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 ( |
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.positionand 3D bounding calculations (as implemented indrillholes).
Footprint Geometry CRS (
EPSG:4326— WGS 84 2D Geographic):Standard 2D
[longitude, latitude]for the GeoJSONgeometry(PolygonorPoint).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) orEPSG: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)
}
Web Mercator Projection: Vertices are converted to EPSG:3857 with metric height
Zso distances/radii are in meters.Calculate Centroid (
C): Center of the 3D bounding box is computed in EPSG:3857+height coordinates.Shift Vertices (
P - C): All vertices are shifted relative toC, setting local model center to(0, 0, 0).Anchor Projection: Centroid
Cis transformed fromEPSG:3857(with height) intoEPSG:4979[longitude, latitude, altitude].GeoJSON Attachment: Anchor coordinates are written to
geoReference.position, anchoring the local origin on the globe.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_crc32orsha256).
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.positioncontains accurate[lon, lat, elevation].[ ] Integrity: CRC32 or ETag verified against source file.
[ ] Naming: Internal glTF node names follow
layer_<category>_<id>convention.[ ] Metadata:
properties.typeis"Mesh"andsource.urlfollows/s3/<bucket>/<key>.glb.[ ] Access Roles: Appropriate access roles set (e.g.
["public_read"]).
Summary
Standardized format: Single
.glbbinary file with meter scaling.Deterministic georeferencing: WGS84 anchor with
EPSG:4978model CRS.Structured layer mapping: glTF nodes map directly to GeoJSON UI controls.
Standard OCLI pipeline:
makegeojson$\to$upload$\to$publish post.