ProRail straatopnamen
Deliveries under ProRail's Vraagspecificatie Straatopnamen spooromgeving 2025: 360° panoramas and LiDAR captured from public roads, cycle paths and footpaths near the railway, for publication in SpoorInBeeld.
The scope is an interessegebied ProRail publishes as a map service — roads
within 100 m of the nearest track centreline, or within 50 m of a parcel
ProRail holds a right to (O-7). One dataset is one (deel)levering of an
opnameplan, and the delivery's YYYYMM_XXXX code becomes the
set name.
This is the same delivery format as
stationsopnamen, from the other tender. Two
differences are worth knowing before you export: photo filenames carry the
day (YYYYMMDD_XXXX_pppp, not YYYYMM_XXXX_pppp), and blurring is at your
discretion rather than ProRail's.
What we check, and what we do not. Most of what the specification asks for is measurement quality: 5 cm absolute accuracy, 1.5 cm relative precision, 50 m of cover either side of the drive line, freedom from outliers and moving objects. An independent party assesses those and advises ProRail on acceptance (O-19). Nothing here duplicates that judgement. What this page describes is the structural half: the things that are objectively true or false about the files you upload.
Folder layout#
<dataset bucket>/
├── panorama/
│ ├── 202603_A12.csv the pose CSV — exactly one
│ ├── 202603_A12_panoramaopnamesysteem1.txt one per capture system
│ └── jpg/
│ ├── 20260312_A12_000001.jpg
│ └── 20260312_A12_000002.jpg
├── pointcloud/
│ ├── LL136500_455800.laz
│ └── LL136500_455900.laz
└── Reports/delivery/ the PDF and GeoPackage deliverables
├── 20260312_Straatopnamerapport_DL1.pdf
├── 202603_straatopnamen_rijplan_A12.gpkg
└── 202603_A12_pano_verwerking.pdf
The panorama base folder is looked for under these names, in this order — the first base folder containing any object wins, and candidates below it are then dead:
panorama/
Once a base folder is chosen, an empty images folder inside it fails the run — it does not fall through to the next candidate.
The pointcloud folder is looked for under these names, in this order — the first containing at least one matching file wins:
pointcloud/
Note that the pose CSV carries the capture month while the photos carry the day — the specification asks for both (PF-15, PF-16), so the photo prefix is deliberately not the CSV's name. The code and the month must still agree between them.
Both halves are required. A straatopname is defined by the specification as the combination of panoramas and LiDAR, so a delivery missing one of them is incomplete rather than a smaller complete thing.
Panoramas#
- 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.
- Each photo's own frame header must describe the panorama the spec asks for: exactly twice as wide as it is high (PF-12), in colour at 8 bits per channel (PF-4). Both are read from the file itself rather than taken on trust, because a delivery of the wrong projection indexes and displays perfectly while pointing every viewer at the wrong part of the world.
- Photo filenames should be
YYYYMMDD_XXXX_pppp.jpg(PF-15) — capture day, opnameplan code, unique number — and the code and month should match the pose CSV's own name, which carries the month only. Note the difference from the station spec, which dates its photos by month rather than by day. Nothing breaks if they disagree, which is exactly why it is worth saying: the delivery is then labelled with a plan or a date it did not come from, and no later step can tell. (a warning, not an error) - The panoramaopnamesysteem files (PF-17) must describe the delivery they came with: their pixel dimensions agree with the photos, their pixel size in degrees is the 360° the width divides into, and between them they account for every photo in the pose CSV exactly once — each under the system name that photo's CSV row gives. These files are the delivery's only record of which camera took which photo, so a stale or mismatched one is a silent loss of provenance.
- Conventions in the pose CSV that we read either way but the spec fixes: capture time written as
YYYY:MM:DD-HH:MM:SS(PF-16), coordinates to at most three decimals, and photo names without their file extension. These are warnings rather than errors on purpose — the delivery is complete and correct, and re-exporting several hundred gigabytes to change a separator would help nobody. The independent checker works from the spec, though, so it is better to hear it here. (a warning, not an error) - Reported when the pose CSV carries the optional
pan,tiltandrollcolumns. Nothing is wrong with the delivery — the angles are read and applied — but the specification asks for panoramas that are already north-aligned and level, so a delivery supplying attitude instead is working to an agreement the specification does not describe. Whoever reads this report should know that from the report rather than discover it from a viewer. (a warning, not an error) - Panorama positions should be at most 20 m apart where the two points can see each other (PF-8). Visibility is not something a file can tell us — a bend, a bridge or a building legitimately breaks the line — so this reports the gaps rather than failing them. A cluster of them usually means a stretch of the rijplan was not driven, or the vehicle was moving too fast for its trigger interval. (a warning, not an error)
Panoramas must arrive already oriented. The specification requires the centre column of pixels to face grid north in RD, the first and last columns to face south, the top row to be the zenith, and the horizon to sit on the half-height row (PF-12, PF-13). Your capture system's three orientation angles have to be baked into the image by a transformation before delivery.
That is why the pose CSV has no orientation columns: a delivered panorama is north-aligned and level by contract, and its pose is a position only. Deliver unrotated images and nothing fails — every photo simply faces the wrong way in the viewer.
No-data pixels below the vertical range go at the bottom of the image and must be black.
Blurring is yours to decide (PF-14). If you deliver blurred photos, set
anonymisation to already_anonymised — otherwise they are blurred again
here, which costs time and softens the image twice.
Camera system files#
One plain-text file per capture system, next to the pose CSV, named
YYYYMM_XXXX_panoramaopnamesysteem#.txt (PF-17). Records are lines:
Ladybug6-01 record 1: the system name, as used in the CSV
8192 record 2: horizontal pixels
4096 record 3: vertical pixels
0.043945 record 4: degrees per pixel
20260312_A12_000001 records 5..n: the photos these parameters apply to,
20260312_A12_000002 without their extension
These files are the delivery's only record of which camera took which photo, so they are cross-checked against everything else: their dimensions against the photos, their pixel size against their own width (360° divided across it), and their photo lists against the pose CSV — every photo named exactly once, under the system its CSV row gives.
| Extensions | .txt, matched case-insensitively |
| Recursion | No — only files directly inside the folder |
At least one such file is required. A vehicle carrying two rigs delivers two.
The pose CSV#
| Separator | Comma |
| Preamble | A header row is optional — see below |
| Columns | Positional. A header row is optional: if the first row's numeric columns are not numbers, it is read as a header and discarded |
| Extra columns | Ignored |
| Decimals | . only |
| Photo names | Matched exactly as written, then with .jpg appended — first hit wins; case-sensitive |
| Which file | The alphabetically first .csv directly inside the folder itself |
| Column | Type | Meaning |
|---|---|---|
0 | text | Photo name without the .jpg extension |
1 | number | RD easting of the panorama's centre, metres |
2 | number | RD northing |
3 | number | NAP height |
4 | timestamp | Capture time in UTC |
5 | text | Must match record 1 of one of the panoramaopnamesysteem files |
6 | number, optional | Heading, degrees — only if attitude was agreed instead of a pre-rotated image |
7 | number, optional | Pitch, degrees |
8 | number, optional | Roll, degrees |
A header row is optional. The specification lists the fields of a record and never says whether to write one, so both readings are accepted: if the first row's coordinate columns are not numbers, it is a header and is discarded. A damaged first data row is still reported as a damaged row.
Timestamps#
timestamp accepts any of: YYYY:MM:DD-HH:MM:SS[.fff] (read as UTC); 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).
The specification writes the shape out in words rather than naming a
standard — het opnametijdstip in UTC (jaar:maand:dag-uur:minuut:seconde) —
which is 2026:03:12-09:18:00. Note the colons in the date and the dash
before the time.
Example#
20260312_A12_000001,136543.210,455812.345,4.120,2026:03:12-09:18:00,Ladybug6-01
20260312_A12_000002,136545.900,455814.010,4.135,2026:03:12-09:18:07,Ladybug6-01
Note: optional attitude columns#
Three further columns — pan, tilt and roll in degrees, at positions 6,
7 and 8 — are accepted after the six the specification lists.
They are not part of the specification, which asks for panoramas that arrive already north-aligned and level. They exist because levelling and northing the image is the part suppliers find hardest, and ProRail may agree with a supplier to receive the angles instead. If you have that agreement, put them here and the images may be delivered unrotated, in the same convention Mobile mapping generic uses.
Supplying them raises a warning on your validation report. Nothing is wrong — it is there so that whoever reads the report knows this delivery follows a separate agreement, rather than discovering it from a viewer.
Leave them out and no warning is raised. A row may also give some of the three and not others: the absent ones are read as zero, which is what the specification assumes anyway, and the report says how many rows did that.
Point cloud tiles#
| Extensions | .laz, matched case-insensitively |
| Recursion | Yes — subfolders under the chosen prefix are scanned |
| Minimum size | 10 bytes — smaller files fail the run |
- The LAS public header of each tile must parse (it is read from the first 64 KiB). A corrupt or truncated header fails the delivery, naming the file.
- Each tile's header must say what LP-9 and LP-11 require: LAS 1.4, a point format carrying both colour and GPS time (7 or 8), and GPS time recorded as Adjusted Standard GPS time rather than raw week seconds. The last one is a single flag in the header, and getting it wrong shifts every timestamp in the tile by roughly a billion seconds — about thirty years — which is why it is worth a check of its own.
- Tiles must be named
LLxxxxxx_yyyyyy.lazafter their own lower-left RD corner on the hundred-metre grid (LP-10, LP-12), and the points inside must actually lie in the 100 × 100 m square that name claims. The name is a statement about where the data is; if it disagrees with the header, the tile is mislabelled and everything built from it inherits the error.
Tiles are 100 × 100 m, cut on the hundred-metre lines of the RD grid, and each
is named after its own lower-left corner in whole metres: LL136500_455800.laz
is the square from 136500–136600 east and 455800–455900 north. Because the name
is a statement about where the data is, it is checked against the header.
Per point, format 7 or 8 carries what the specification asks for (LP-11):
| Point source ID | Which scanner recorded the point |
| GPS time | Adjusted Standard GPS time — the flag in the header, not raw week seconds |
| Colour | 16 bits per channel |
| Intensity | 16 bits, normalised to the scanner's range |
The GPS-time flag is worth double-checking. It is one bit in the header, and getting it wrong shifts every timestamp in the tile by about thirty years.
Not yet checked here: that those per-point fields are actually populated, and that the tiles reach 50 m either side of the drive line (LP-7). Reading point records is a different cost from reading headers, so those remain the independent checker's for now. Their absence from your report is not a pass.
Tolerance#
- Every CSV row must resolve to a delivered photo. One unresolvable row fails the run — but every row is checked first, so one pass reports them all.
- Photos in the delivery that the CSV does not reference are not indexed (noted, not an error).
There is no allowance for missing photos, unlike most templates. The specification grants none and the independent checker will not either, so a delivery with photos missing is one you would have to redo (O-25).
Ingest parameters#
| Key | Required | Type | Meaning |
|---|---|---|---|
anonymisation | no | string — recognised: auto_anonymise, already_anonymised | Defaults to auto_anonymise, because ProRail has faces and plates blurred here before publication (O-26). PF-14 leaves blurring to the supplier's discretion, so set already_anonymised if the delivered photos are already blurred — otherwise they are blurred again, which costs time and softens the image twice. Default: auto_anonymise. |
height_above_ground | yes | number | Height of the camera above the road surface, in metres. Used for viewer positioning, not stored per photo. |
set_name | no | string | Names the set the photos are grouped under. Leave it unset: the default is the pose CSV's own name (YYYYMM_XXXX), which is the opnameplan code and capture month, and is what a viewer should show. |
Coordinate reference system#
Nothing is reprojected. X and Y must already be RD (Rijksdriehoeksmeting) and Z must be NAP (O-4) — for a Dutch dataset, EPSG:7415 for positions and EPSG:28992 for footprints. See Coordinates are not reprojected.
Delivery documents#
The reports and plans the specification asks for go under Reports/delivery/,
and are published alongside the imagery on acceptance (O-27). They are kept,
not indexed, and their names are not currently checked.
| Document | Requirement |
|---|---|
YYYYMM_straatopnamen_opleveringsplan_XXXX.pdf | OP-7 |
YYYYMM_straatopnamen_rijplan_xxxx.gpkg | OP-8 |
YYYYMMDD_Straatopnamerapport_AAA.pdf | IS-5 |
YYYYMM_XXXX_pano_verwerking.pdf / _pano_controle.pdf | PF-18, PF-19 |
YYYYMMDD_XXXX_LiDAR-verwerking.pdf / _LiDAR-Controle.pdf | LP-14, LP-15 |
A delivery cannot be submitted without at least one document here.
What you get back#
- Indexed panoramas, positioned and oriented, in one set named after the delivery.
- Deep-zoom tiles per photo, so the viewer can stream them.
- Blurred derivatives, unless you delivered blurred photos and said so.
- Indexed point cloud tiles, plus a single merged COPC the web viewer streams.
Your tiles are indexed as delivered. Unlike some templates, nothing re-tiles them: they are already on the grid the specification asks for.
Common failures#
| Symptom | Cause |
|---|---|
| "the delivery has no panorama content" | The photos are not under panorama/jpg/, or are .jpeg rather than .jpg |
| Every photo reported missing | The pose CSV names photos that are not in jpg/ — check for a case mismatch |
| "is not a 8-digit date" | The photos were named with the station spec's month prefix rather than this one's day prefix |
| "is not the 2:1 equirectangular shape" | The panoramas were exported at another aspect, or are not equirectangular at all |
| Photos face the wrong way in the viewer | The images were delivered unrotated; the orientation must be baked in, not supplied as angles |
| "carry code …, expected …" | The photos and the pose CSV disagree about which opnameplan this delivery is |
| "is not the … square that its name claims" | Tiles were renamed, or cut on a different grid than the RD hundred-metre lines |
| "records GPS time as week seconds" | The GPS time type flag in the LAS header was not set |
| Photos blurred twice | anonymisation was left at its default on an already-blurred delivery |