Repository navigation
Verifying the Orto Data Page
The test flow for gis.digiproject.uk/ortodata, the page on which a signed-in reviewer looks at a building's historical orthophotos year by year and records the first year the building is present. It is the check to run after every deployment of the page or of the services behind it, and it needs nothing beyond a browser, a DigiProject account and a terminal.
Feature umbrella: DiGi.GIS.WebAPI.UI#46. Deployment checklist for the whole feature: DiGi.GIS.WebAPI#37.
Important
Sign in again after every deployment. The token signing key is generated in memory when the API service starts (DiGi.User.WebAPI/Modify/InitializeAsync.cs), so a restart of api.digiproject.uk invalidates every session issued before it. The page then answers Nothing left to verify … or the service could not be reached or could not be drawn (status 401) with a perfectly healthy service. Tokens also expire after one hour.
| Mode | How it opens | What it shows |
|---|---|---|
| Random (default) | /ortodata |
A building that has orthophoto coverage and no user answer yet, drawn at random from all covered counties (weighted by their size); Next moves to another one |
| Direct | /ortodata?countyid=<part id>&reference=<reference> |
That building, with its existing answer pre-selected and a who / when line |
Each card is one archive year in which a photo of the building exists — only those years, labelled with the year, never a neighbouring year's photo. The toolbar shows County ID and, on the line below it, Reference.
An answer is one of:
| Action | Stored as | Meaning |
|---|---|---|
| click a year card |
Year = <that year>, YearBuiltRelation = 0
|
the first photo in which the building is present — a lower bound on the construction year |
| Existed in oldest photo |
Year = <oldest listed year>, YearBuiltRelation = 1
|
already there in the oldest photo; construction is earlier than the archive |
| Not visible in newest photo |
Year = <newest listed year>, YearBuiltRelation = 2
|
not there even in the newest photo |
Single-select everywhere: a click on any card or bound button unselects the others; a click on the selected one unselects it. Clear resets the on-screen selection only. Next with a selection saves it and moves on; Next without a selection skips — no write. A save replaces the building's previous user answer (exactly one UserYearBuilt entry ever exists) and records the reviewer's e-mail and a UTC timestamp. Predicted years are never shown and never affect which buildings are drawn.
Open https://gis.digiproject.uk/login and sign in. The Orto Data entry appears in the navigation only when signed in; /ortodata redirects anonymous visitors to the login page.
Open /ortodata. Note the County ID and Reference from the toolbar (you need them for step 3), pick a card or a bound button, press Next. The page moves to the next building without a reload.
gis/yearbuiltdata/itemsbyreference is an open read, so this needs no token:
Invoke-RestMethod "https://api.digiproject.uk/gis/yearbuiltdata/itemsbyreference?reference=<reference>&countyid=<county id>" | ConvertTo-Json -Depth 5or
curl -s "https://api.digiproject.uk/gis/yearbuiltdata/itemsbyreference?reference=<reference>&countyid=<county id>"Expected — one YearBuiltData row with exactly one UserYearBuilt entry:
[{"_type":"DiGi.GIS.Classes.YearBuiltData,DiGi.GIS","Guid":"…","YearBuilts":[
{"_type":"DiGi.GIS.Classes.UserYearBuilt,DiGi.GIS","Year":2012,"YearBuiltRelation":0,
"DateTime":"2026-09-22T05:42:42.9872406+00:00","UserName":"user@digiproject.uk"}],
"Reference":"314DE397-0220-2130-E053-CC2BA8C06DD0"}]Check: Year is the year you clicked (or the oldest / newest listed year for a bound button), YearBuiltRelation matches the table in §1, UserName is your account, DateTime is now in UTC. A PredictedYearBuilt entry may sit beside it — that is the ML pipeline's, not yours.
Press Next without selecting anything. The building changes and nothing is written: re-reading the skipped building's reference answers 204 No Content (no year-built row at all) or its previous entries unchanged.
Open /ortodata?countyid=<county id>&reference=<reference> for the building of step 2. The card or button you chose is pre-selected and the toolbar shows who recorded it and when. Choose a different year and press Next: step 3 now shows the new year and timestamp, still as the single entry, and the page continues in random mode with the query cleared from the address bar.
A run on 2026-09-22 (the first reviewer session after the feature went live): two buildings answered — one bound (2008, relation 1) and one exact (2012, relation 0) — each read back within a minute, one entry apiece, stamped with the reviewer's e-mail.
| Symptom | Look at | Usually |
|---|---|---|
could not be drawn (status 401) right after a deployment |
— | stale session (see the note at the top); sign in again |
Nothing left to verify in random mode |
the API log, lines RandomBuilding2DReferenceWithoutUserYearBuilt: … references drawn, … without a user entry
|
a genuinely empty pool is one thing; an answer within ~20 ms is an early return, not an empty pool (DiGi.GIS.PostgreSQL#91) |
| Cards listed but none rendered |
GET gis/ortodatas/imagebyreference?reference=&countyid=&year= directly |
it must answer 200 image/jpeg, or 404 for a year without a photo — never 406 (DiGi.GIS.WebAPI#38, DiGi.GIS.PostgreSQL#90) |
| The API log says a building was drawn but the page shows the empty state | the raw upstream body (§4) | the answer is not in the DiGi wire shape (_type, declared names) — DiGi.GIS.WebAPI#39
|
The API log is Serilog's daily file on the API machine; every request logs OrtoDatasController:<Action> started and the draw logs one line per stage.
The session token lives in an HttpOnly cookie and never reaches page scripts, so a direct call needs its own token. In PowerShell (the curl alias and escaped quotes mangle JSON bodies there — use the cmdlets):
$token = (Invoke-RestMethod -Method Post -Uri "https://api.digiproject.uk/user/login" -ContentType "application/json" -Body '{"Email":"<e-mail>","Password":"<password>"}').Token
Invoke-WebRequest -Uri "https://api.digiproject.uk/gis/ortodatas/randombuilding2dreference?countyids=<part id>" -Headers @{ Authorization = "Bearer $token" } -SkipHttpErrorCheck | Select-Object StatusCode, Content | Format-ListExpected: 200 and {"_type":"DiGi.GIS.PostgreSQL.Classes.Building2DReference,DiGi.GIS.PostgreSQL","CountyId":…,"Reference":"…","Id":…,"SubdivisionId":…} in well under a second (about 130–300 ms; up to ~0.9 s cold on the largest county part). countyids are building_2d part ids, not county codes, repeated for a multi-part county; omit it to draw from every covered part.
The other gated routes: GET gis/ortodatas/yearsbyreference?reference=&countyid=&fallbackbyreference=true (the years that have a card) and POST gis/yearbuiltdata/setuseryearbuilt with body {"CountyId":…,"Reference":"…","Year":…,"Relation":0} (what Next sends through the page's relay). Anonymous calls to any of them answer 401.