Horus Vertex
Deliveries from the Horus Vertex mobile-mapping system, exported through Horus Movie Maker. One folder per recording, each carrying pre-stitched panoramas and the raw lidar capture — the coloured point cloud is produced here rather than delivered.
Folder layout#
<dataset bucket>/
├── 2026_05_07_10_34_40_Deventer_Deel1/ one folder per recording
│ ├── panorama/
│ │ ├── export.csv Movie Maker pose export
│ │ └── jpg/
│ │ ├── 0.jpeg pre-stitched equirectangular
│ │ └── 1.jpeg
│ └── lidar/
│ ├── capture.pcap Hesai XT32M2X capture
│ ├── trajectory.txt Applanix export
│ └── mission.log carries the mission date
└── 2026_05_07_11_02_48_Deventer_Deel2/
└── …
Recording folders sit at the bucket root — there is no ingest/
wrapper — and are recognised by their contents, not their name: any
root folder that directly contains a panorama/ or lidar/ subfolder is
a recording. Movie Maker names folders by the capture timestamp, and
keeping that is good practice (it sets the processing order), but nothing
depends on it. The folder name becomes the set
name, so it is what people will see in the viewer.
A root folder that is neither a recording nor a platform output
(pointcloud/, copc/) is reported and skipped. A delivery with no
recording folder at the root fails.
Inside a recording#
| Path | Required | Notes |
|---|---|---|
panorama/*.csv | for indexing | Exactly one is read: the alphabetically first CSV directly inside panorama/. A recording with none is skipped with a note and its photos are not indexed |
panorama/jpg/*.jpeg | yes | Pre-stitched equirectangular panoramas. .jpg is accepted too |
lidar/*.pcap | for the point cloud | One or more. A recording with none is panorama-only — it is not an error, and shows in the run as a skipped recording |
lidar/*.txt | with a .pcap | The Applanix trajectory export |
lidar/*.log | with a .pcap | Read for the mission date |
A delivery where no recording has a .pcap fails. A Vertex rig always
captures lidar, so that state means a broken delivery or the wrong bucket —
it is caught rather than quietly producing a panorama-only dataset.
Files we write back#
Two things appear in your bucket that you did not deliver, and neither should be removed by a subsequent upload:
<recording>/lidar/trajectory.360traj— the resolved trajectory.<recording>/recording_done— a marker written last, after every output for that recording is durable.
The marker is what makes re-running cheap: a recording that already has one
is skipped rather than reprocessed. To force a recording to be processed
again, delete its recording_done. See
Re-running is safe.
The pose CSV#
| Separator | Comma |
| Preamble | The first 2 lines are skipped, then a header row, then data |
| Columns | Addressed by name, case-insensitively. Order does not matter |
| Extra columns | Ignored |
| Decimals | Both . and , accepted |
| Photo names | Matched with .jpeg appended, then with .jpg appended — first hit wins; case-sensitive |
| Which file | The alphabetically first .csv directly inside panorama/ |
| Column | Type | Meaning |
|---|---|---|
Frame | text | Photo name without extension — resolves to panorama/jpg/<Frame>.jpeg, falling back to .jpg |
Longitude | number | Degrees — see CRS |
Latitude | number | Degrees |
Altitude | number | Ellipsoidal height in metres, not orthometric |
Heading | number | Compass heading, degrees |
Pitch | number | Degrees |
Roll | number | Degrees |
Stamp | timestamp | Capture time |
Ragged rows are tolerated — rows may have differing field counts. The two skipped preamble lines are part of the Movie Maker export — do not strip them, and do not add a third.
Stamp accepts any of: epoch seconds (whole or fractional); RFC 3339 (2026-05-07T09:18:00Z); YYYY-MM-DD HH:MM:SS[.fff] (read as UTC); YYYY-MM-DDTHH:MM:SS[.fff] (read as UTC); YYYY/MM/DD HH:MM:SS[.fff] (read as UTC); DD-MM-YYYY HH:MM:SS[.fff] (read as Dutch local time, Europe/Amsterdam); YYYY-MM-DD (UTC midnight).
These are the same formats Mobile mapping generic accepts; the unzoned shapes are read as UTC.
Coordinate reference system#
This template reprojects, unlike most. Positions are delivered as geographic coordinates in EPSG:4937 (ETRS89, 3D) and transformed into the dataset's configured CRS on ingest.
So Longitude / Latitude are degrees and Altitude is an ellipsoidal
height — not NAP, and not a projected easting/northing. Delivering RD
coordinates in these columns places the data far out to sea.
This is an exception to the general rule; see Coordinates are not reprojected.
See Rotation angles.
Tolerance#
Applied per recording, so one bad recording does not sink the rest:
- Up to 10 % of a recording's CSV rows may reference missing photos. Beyond that the run fails, naming the recording.
- Photos in the delivery that the CSV does not reference are not indexed (noted, not an error).
- A recording with no CSV under
panorama/→ not indexed, with a note. - A recording with no
.pcap→ panorama-only, no point cloud. Not an error. - A recording that already carries
recording_done→ skipped.
And the delivery as a whole must hold together:
- At least one recording must contain lidar captures. A Vertex rig always records lidar, so a delivery where every recording folder lacks a
.pcapis a broken delivery or the wrong bucket. - A recording with
.pcapfiles must also carry its trajectory: exactly one POSPac export.txtinlidar/(or an already-convertedtrajectory.360traj). Multiple.txtfiles are ambiguous and fail the recording. - A recording with
.pcapfiles must also carry the Movie Maker pose CSV — the point cloud is coloured from the panoramas, which need their poses. - Every delivered photo must start with a JPEG marker and end with an end-of-image marker. This catches a file that is not a JPEG whatever its extension says, and the truncated upload — which a size check cannot see, because a truncated file just looks smaller and nothing knows how big it should have been.
Ingest parameters#
| Key | Required | Type | Meaning |
|---|---|---|---|
anonymisation | yes | string — recognised: already_anonymised, auto_anonymise | Compared exactly: already_anonymised marks the photos as already blurred, auto_anonymise queues them for blurring, any other value does neither. |
height_above_ground | yes | number | Camera height above the road surface, in metres. |
vehicle_identifier | yes | string | The identifier of the capturing rig, as registered with the platform. |
All three are checked before any recording is processed, so a missing parameter fails immediately rather than part-way through a long run.
vehicle_identifier must name a rig we hold a record for under this ingest
profile, and that rig must have at least one calibration on file. Both are
verified up front, with a message saying which is missing. If you are
capturing with a rig for the first time, its calibration has to be
registered before the first delivery — talk to us before you upload.
What you get back#
- Indexed panoramas per recording, with positions and orientations.
- A tilepack (
.tpc) per photo. Omnibase tenants view the JPEG; DZP is not generated at ingest. - Blurred derivatives, if
anonymisationisauto_anonymise. - One coloured point cloud per recording at
pointcloud/<recording>.laz, built from the lidar capture, georeferenced against the trajectory and coloured from that recording's own panoramas. - A single merged COPC at
copc/pointcloud.copc.laz, which is what the web viewer streams.
You deliver raw captures; the coloured cloud is an output, not an input.
Common failures#
| Symptom | Cause |
|---|---|
| "no recording folders at the bucket root" | The recordings are nested under ingest/ or another wrapper — a recording folder must sit at the root with panorama/ or lidar/ directly inside it |
| "all N recording folders have no .pcap" | The lidar captures were not uploaded, or they are not under <recording>/lidar/ |
| A recording indexed with no point cloud | That recording has no .pcap — it was treated as panorama-only |
| A recording's photos missing entirely | No CSV directly inside its panorama/ folder |
| More than 10 % of photos missing | Frame values do not match the files in panorama/jpg/, or the photos are in a subfolder of it |
| "unknown vehicle_identifier" | The rig is not registered under this ingest profile |
| "has no calibration rows" | The rig is registered but not yet calibrated |
| Data far out to sea | Projected coordinates were delivered where degrees are expected |
| A recording not reprocessed after a re-upload | Its recording_done marker is still there — delete it |