<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Azeria</title>
    <description>The latest articles on DEV Community by Azeria (@azeria).</description>
    <link>https://dev.to/azeria</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4047667%2F937aa897-9417-49c4-acb0-0a749f1cbd41.png</url>
      <title>DEV Community: Azeria</title>
      <link>https://dev.to/azeria</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/azeria"/>
    <language>en</language>
    <item>
      <title>Angle of Ascent — Dating a Takeoff Photo to the Second</title>
      <dc:creator>Azeria</dc:creator>
      <pubDate>Fri, 21 Aug 2026 18:14:25 +0000</pubDate>
      <link>https://dev.to/azeria/angle-of-ascent-dating-a-takeoff-photo-to-the-second-22o3</link>
      <guid>https://dev.to/azeria/angle-of-ascent-dating-a-takeoff-photo-to-the-second-22o3</guid>
      <description>&lt;h2&gt;
  
  
  The task
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;This aircraft was captured moments after takeoff. Identify the airport and the plane, then trace its exact flight and determine its arrival gate at its destination.&lt;/p&gt;

&lt;p&gt;Flag format: &lt;code&gt;Hacktoria{ICAO_CALLSIGN_ARRIVAL_GATE}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One image. A light blue airliner over grass, its shadow on the ground beside it, a runway below and a taxiway above. No landmarks, no signage, no horizon. Nothing that a reverse image search will help with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F99372r5gtemmsdaw2sfb.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F99372r5gtemmsdaw2sfb.webp" alt="Aerial view of a light blue airliner climbing over a grass strip between a runway and a taxiway" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Everything you get: one runway, one parallel taxiway, a wide grass strip, a service vehicle, and an aircraft too small to read a registration off. The shadow ends up mattering twice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two things need to come out of this: &lt;strong&gt;where&lt;/strong&gt; and &lt;strong&gt;when&lt;/strong&gt;. The where is a geometry problem. The when turned out to be the interesting part.&lt;/p&gt;

&lt;p&gt;The answer, up front: &lt;strong&gt;Billund Airport&lt;/strong&gt;, Denmark — KLM Cityhopper flight &lt;strong&gt;KL1292&lt;/strong&gt; to Amsterdam, caught mid-rotation by a Danish survey aircraft on 13 May 2025 at 08:24:15 UTC. Callsign KLM46Z, arrival gate B20, flag &lt;code&gt;Hacktoria{KLM46Z_B20}&lt;/code&gt;. The rest of this post is how to get there without guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — What is the aircraft?
&lt;/h2&gt;

&lt;p&gt;Upscaling the aircraft to 6× answers the first question immediately: the engines are mounted &lt;strong&gt;under the wings&lt;/strong&gt;, not on the rear fuselage. That rules out the Fokker 70/100, which is otherwise the obvious guess for a light blue narrowbody in Northwest Europe.&lt;/p&gt;

&lt;p&gt;Light blue fuselage, white wings and tail, regional-jet proportions, wing-mounted engines: &lt;strong&gt;KLM Cityhopper, Embraer E-Jet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuhje7pi5tqdlgqu4z8kb.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuhje7pi5tqdlgqu4z8kb.webp" alt="Google Lens results for the cropped aircraft, all pointing at KLM Cityhopper Embraer E-Jets" width="800" height="727"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Google Lens on the aircraft crop needs no help — every visual match is a KLM Cityhopper E-Jet. Reverse image search fails on the scene, not on the aircraft.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The registration on the rear fuselage is below the resolution limit. Even with a whiteness-isolation filter (suppressing the blue channel to make white text pop against the light blue paint) it stays unreadable. That's fine — it isn't needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Narrow the field before measuring anything
&lt;/h2&gt;

&lt;p&gt;The temptation is to start measuring. Resist it — measuring only helps once you have candidates to measure against. Three cheap observations cut the field down first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The airline gives you a route map.&lt;/strong&gt; KLM Cityhopper flies short-haul out of Amsterdam. That's a finite destination list, maybe fifty airports across Northwest Europe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The imagery type gives you a country group.&lt;/strong&gt; This is not satellite. The detail is too fine and the perspective is too steep — it's a survey aircraft. Which means someone flew a mapping mission and, in all likelihood, published it. The countries with genuinely good open aerial imagery portals are a short list: the Netherlands (&lt;a href="https://www.pdok.nl/" rel="noopener noreferrer"&gt;PDOK&lt;/a&gt;), Denmark (&lt;a href="https://skraafoto.dataforsyningen.dk/" rel="noopener noreferrer"&gt;Skråfoto&lt;/a&gt;), Norway (&lt;a href="https://norgeibilder.no/" rel="noopener noreferrer"&gt;Norge i bilder&lt;/a&gt;), Sweden. That overlaps heavily with the Cityhopper network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The author gives you a prior.&lt;/strong&gt; &lt;a href="https://hacktoria.com/" rel="noopener noreferrer"&gt;Hacktoria&lt;/a&gt; challenges have a Nordic lean (see the note further down). Denmark and Norway first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The layout gives you a filter.&lt;/strong&gt; One runway, one parallel taxiway, a wide grass strip between them, no second runway anywhere in frame. Copenhagen is out — two runways. Aalborg and Aarhus don't match the taxiway geometry. &lt;strong&gt;Billund&lt;/strong&gt; does: a single runway, 09/27, with a parallel taxiway to the north.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxe8jguwk8fwupj93778o.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxe8jguwk8fwupj93778o.webp" alt="Challenge image with three features circled: a vehicle on the taxiway, a bare patch in the grass, and a dirt track at the top right" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Three anchors picked before any measuring: a vehicle on the taxiway, a bare patch in the grass strip, a dirt track cutting the corner at top right. Whichever airport this is has to show all three in the right places.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's ten minutes of work with no arithmetic in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Confirm with the runway geometry
&lt;/h2&gt;

&lt;p&gt;Now measuring is worth doing, because there's a specific claim to test rather than an open-ended search.&lt;/p&gt;

&lt;p&gt;The cheap check comes first. Google's satellite view of Billund shows the same bare patch in the grass and the same dirt track cutting the corner northeast of the taxiway — the anchors from the challenge image, in the same relative positions along runway 09/27.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuppfo4c1gbbwq0as040f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuppfo4c1gbbwq0as040f.webp" alt="Google Maps satellite view of Billund with the matching bare patch and dirt track circled" width="799" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Same patch, same track, same corner — on Google's imagery of Billund. The layout match is settled before the first measurement; the geometry below is confirmation, not discovery.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Billund_Airport" rel="noopener noreferrer"&gt;Published aerodrome data&lt;/a&gt; gives Billund's runway as &lt;strong&gt;3101 × 45 m&lt;/strong&gt;. In the image, the distance between the two runway edge lines is 149 px, so if the airport is right, the scale is 45/149 = &lt;strong&gt;0.302 m/px&lt;/strong&gt;. Everything else in frame should then come out sensible:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measurement&lt;/th&gt;
&lt;th&gt;In pixels&lt;/th&gt;
&lt;th&gt;At 0.302 m/px&lt;/th&gt;
&lt;th&gt;Plausible?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runway edge line to edge line&lt;/td&gt;
&lt;td&gt;149&lt;/td&gt;
&lt;td&gt;45 m&lt;/td&gt;
&lt;td&gt;matches published width&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runway CL ↔ taxiway CL&lt;/td&gt;
&lt;td&gt;~574&lt;/td&gt;
&lt;td&gt;~173 m&lt;/td&gt;
&lt;td&gt;standard ICAO Code E separation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image footprint (width)&lt;/td&gt;
&lt;td&gt;1690&lt;/td&gt;
&lt;td&gt;~510 m&lt;/td&gt;
&lt;td&gt;reasonable survey frame&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There's also a free internal check available. &lt;a href="https://en.wikipedia.org/wiki/Runway#Runway_markings" rel="noopener noreferrer"&gt;ICAO standardises touchdown zone markings&lt;/a&gt;: the bars are 3 m wide with 18 m between their inner edges, putting bar centres at ±10.5 m from the centreline. Measured in the image they sit at ±36 px, which at 0.302 m/px is &lt;strong&gt;10.9 m&lt;/strong&gt;. Two independent features on the same runway agreeing on the same scale means the scale is right — and by extension the airport is right.&lt;/p&gt;

&lt;p&gt;In practice you'd do this with the ruler tool in Google Earth rather than by counting pixels. The point isn't the precision, it's that &lt;strong&gt;runway markings are standardised worldwide&lt;/strong&gt;, so any instrument runway in an overhead image is a calibrated ruler you already know the dimensions of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — The timestamp
&lt;/h2&gt;

&lt;p&gt;Here is where this challenge stops being a normal geolocation exercise.&lt;/p&gt;

&lt;p&gt;Denmark publishes &lt;strong&gt;every aerial photograph of the entire country as open data&lt;/strong&gt;, with a per-image capture timestamp accurate to the second, through the SDFI/Klimadatastyrelsen service &lt;strong&gt;Skråfoto&lt;/strong&gt;. Four oblique directions (north, south, east, west) plus nadir, roughly every two years since 2017, at about 10 cm GSD.&lt;/p&gt;

&lt;p&gt;There is a public web viewer at &lt;a href="https://skraafoto.dataforsyningen.dk/" rel="noopener noreferrer"&gt;skraafoto.dataforsyningen.dk&lt;/a&gt;, and behind it a STAC API. The API token is not a secret — it sits in the viewer's own config file at &lt;code&gt;/config.js&lt;/code&gt;, which the page loads client-side and which opens with a comment stating that everything in it is public.&lt;/p&gt;

&lt;p&gt;Querying every frame that covers a point on the runway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;T&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;token from config.js&amp;gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"https://api.dataforsyningen.dk/rest/skraafoto_api/v1.0/search?token=&lt;/span&gt;&lt;span class="nv"&gt;$T&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "collections":["skraafotos2017","skraafotos2019","skraafotos2021",
                   "skraafotos2023","skraafotos2025"],
    "intersects":{"type":"Point","coordinates":[9.15194,55.74028]},
    "limit":500}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.features[] | "\(.properties.datetime) \(.properties.direction) \(.id)"'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;sort&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns &lt;strong&gt;104 frames across 10 survey flights&lt;/strong&gt;, each one a timestamped candidate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2025-05-13T08:03:47Z west  2025_83_28_5_0009_00006036_100mm
2025-05-13T08:24:11Z north 2025_83_28_2_0008_00006280_100mm
2025-05-13T08:24:15Z north 2025_83_28_2_0008_00006281_100mm
2025-05-13T08:24:19Z north 2025_83_28_2_0008_00006282_100mm
2025-05-13T08:24:35Z nadir 2025_83_28_1_0008_00006286_100mm
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each STAC item also carries &lt;code&gt;asset:data&lt;/code&gt;, a Cloud-Optimised GeoTIFF that can be windowed directly over HTTP with &lt;code&gt;/vsicurl/&lt;/code&gt; — no need to pull 20544 × 14016 pixels to look at one corner.&lt;/p&gt;

&lt;p&gt;Cross-referencing each capture time against the &lt;a href="https://www.suncalc.org/" rel="noopener noreferrer"&gt;solar position at Billund&lt;/a&gt; narrows things considerably, because the shadow in the image encodes the sun elevation. But this is also where I made my first mistake — more on that below.&lt;/p&gt;

&lt;p&gt;The aircraft is in the &lt;strong&gt;north oblique&lt;/strong&gt; of the 2025 survey:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fotagvb3oieeq403isvba.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fotagvb3oieeq403isvba.webp" alt="Skråfoto viewer showing the north oblique of 13 May 2025 with the aircraft, its shadow, and the taxiway vehicle circled" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The challenge image, in situ: same aircraft, same shadow, even the same vehicle on the taxiway. Frame &lt;code&gt;2025_83_28_2_0008_00006281_100mm&lt;/code&gt;, north-facing oblique, survey pass 2/7 over Billund.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5yitnqan1gh2ecrurkyn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5yitnqan1gh2ecrurkyn.webp" alt="Skråfoto metadata panel for the frame, listing the capture time as 10:24:15 AM on 5/13/2025" width="800" height="819"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The frame's own metadata panel: Optagetidspunkt 10:24:15, direction north, collection skraafotos2025 — plus the full camera calibration, down to focal length.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13 May 2025, 10:24:15 local time.&lt;/strong&gt; Not an estimate. A logged camera trigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — The flight
&lt;/h2&gt;

&lt;p&gt;Departures from Billund that morning put exactly one KLM service in the window: &lt;strong&gt;KL1292&lt;/strong&gt; to Amsterdam, scheduled 10:15, operated by KLM Cityhopper on E190/E195 equipment. The livery already fixed the airline, and the timestamp allows only minutes of slack — there is no second candidate to rule out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.flightera.net/" rel="noopener noreferrer"&gt;Flightera's history&lt;/a&gt; for May 2025 closes it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff7yz01siradn7nt6iize.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff7yz01siradn7nt6iize.webp" alt="Flightera record for KL1292 on 13 May 2025: departure 10:24 CEST / 08:24 UTC, nine minutes late, arrival gate B20" width="800" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Flightera's record for 13 May 2025: departure 10:24 CEST, 08:24 UTC, nine minutes late. Everything the flag still needs is in this one header.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The photograph is stamped 08:24:15 UTC. The departure is logged at 08:24 UTC. Same minute. The survey aircraft caught KL1292 in the act of rotating.&lt;/p&gt;

&lt;p&gt;The flight detail page supplies the last two fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ICAO callsign: &lt;strong&gt;KLM46Z&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Arrival: Amsterdam, Terminal 2, &lt;strong&gt;Gate B20&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hacktoria{KLM46Z_B20}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where I went wrong
&lt;/h2&gt;

&lt;p&gt;Two errors, both instructive, both nearly fatal to the solve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I searched only the nadir frames.&lt;/strong&gt; My mental model of "aerial survey imagery" was straight-down orthophotos, so that's what I filtered for. I downloaded and inspected every nadir frame over the airport and found nothing, and briefly concluded the aircraft had been composited into a real photo by the challenge author. It was sitting in an oblique the whole time. When a survey publishes multiple camera directions, &lt;strong&gt;search all of them&lt;/strong&gt; — the interesting frame is often not the one pointing straight down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7budyqz5e6kd0l1yg1x0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7budyqz5e6kd0l1yg1x0.webp" alt="Skråfoto viewer with the direction thumbnails in the sidebar, the north oblique containing the aircraft circled" width="799" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The frame was in the sidebar the whole time — the viewer shows all five directions per date, and the 13 May north thumbnail already contains the aircraft. I was staring at the empty nadir on the left.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I mis-estimated the sun elevation because of that same assumption.&lt;/strong&gt; Measuring the shadow's stretch relative to the aircraft gave a factor of about 1.8, implying a sun elevation near 33°. The real value on 13 May at 10:24 is 40°. The error comes from the 45° oblique geometry: the ground is foreshortened along the viewing axis, but an aircraft several hundred metres above that ground is not foreshortened by the same amount. So the aircraft appears roughly 1.5× oversized relative to the terrain, and any shadow-length arithmetic that assumes a common scale comes out wrong.&lt;/p&gt;

&lt;p&gt;That discrepancy was actually visible early — the aircraft measured ~58 m long against a scale where an E190 should be 36 m — and I treated it as evidence of compositing rather than as evidence of an oblique view. &lt;strong&gt;An anomaly is a clue about the imaging geometry before it's a clue about tampering.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on the author
&lt;/h2&gt;

&lt;p&gt;Worth logging for future Hacktoria challenges: this one lands in Denmark, and &lt;a href="https://dev.to/writeups/geoclip-streetclip-hacktoria-norway/"&gt;an earlier geolocation challenge put me in Norway&lt;/a&gt; (a farmhouse brewery, Rodebak Gårdsbryggeri). That's a small sample, but the Nordic lean is there.&lt;/p&gt;

&lt;p&gt;It's a soft prior, not a shortcut — never let it override what the image actually shows. But it has practical value in how you order your work. When an image gives you a shape to match and no name, the question is which candidate list to walk first, and starting with Scandinavia would have saved me a pass here. It also pays off downstream: Denmark, Norway, Sweden and Finland all run national open geodata programmes with far better aerial coverage and metadata than most of Europe. If the challenge is set in the Nordics, the odds are good that a timestamped orthophoto or oblique exists for the exact scene, which is precisely what carried this solve.&lt;/p&gt;

&lt;p&gt;So: keep a running note on where a given author sets their challenges. It won't identify anything on its own, but it tells you which archives to have open before you start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Runway markings are a calibrated ruler — but use them to confirm, not to search.&lt;/strong&gt; ICAO standardises edge line placement, centreline dimensions, touchdown zone bars and aiming point markings, so any overhead image of any instrument runway carries a known scale. What that gets you is verification: once you have a candidate airport, two independent measurements agreeing on one scale settles it. What it does not get you is the candidate. Narrow the field with cheap observations first — airline, imagery source, layout, author habits — then measure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Denmark timestamps its aerial imagery to the second, and gives it away.&lt;/strong&gt; For any GEOINT task on Danish soil, Skråfoto is not a nice-to-have — it converts "roughly what year" into "which second". Combined with ADS-B history, that's enough to identify individual aircraft movements. Similar national orthophoto programmes exist elsewhere (the Netherlands' PDOK, Norway's Norge i bilder), though not all expose per-frame capture times as cleanly — &lt;a href="https://dev.to/writeups/timestamping-open-aerial-archives/"&gt;the technique page on open aerial archives&lt;/a&gt; collects the portals and the query workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API tokens in client-side config are public by design.&lt;/strong&gt; The Skråfoto viewer's config file says so explicitly in a comment. Reading the front end of a public map viewer to find its data endpoint is standard practice, not a bypass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://skraafoto.dataforsyningen.dk/" rel="noopener noreferrer"&gt;Skråfoto viewer&lt;/a&gt; — Klimadatastyrelsen's oblique aerial imagery archive; the STAC API behind it is at &lt;code&gt;api.dataforsyningen.dk/rest/skraafoto_api/v1.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dataforsyningen.dk/" rel="noopener noreferrer"&gt;Dataforsyningen documentation&lt;/a&gt; — API reference for the Skråfoto STAC endpoints&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Billund_Airport" rel="noopener noreferrer"&gt;Billund Airport — Wikipedia&lt;/a&gt; — runway 09/27, 3101 × 45 m&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.faa.gov/air_traffic/publications/atpubs/aim_html/chap2_section_3.html" rel="noopener noreferrer"&gt;FAA AIM — Airport Marking Aids and Signs&lt;/a&gt; — standard dimensions for edge lines and touchdown zone bars (mirrors ICAO Annex 14)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/KLM_Cityhopper" rel="noopener noreferrer"&gt;KLM Cityhopper — Wikipedia&lt;/a&gt; — fleet and route network&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.flightera.net/" rel="noopener noreferrer"&gt;Flightera&lt;/a&gt; — KL1292 flight history for 13 May 2025, including callsign and arrival gate&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://suncalc.org/" rel="noopener noreferrer"&gt;SunCalc&lt;/a&gt; — solar elevation at Billund for the shadow cross-check&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hacktoria.com/" rel="noopener noreferrer"&gt;Hacktoria&lt;/a&gt; — the Angle of Ascent challenge&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Image credits
&lt;/h2&gt;

&lt;p&gt;Aerial imagery (challenge image, annotated crops, cover, and viewer screenshots):&lt;br&gt;
Skråfoto oblique photograph &lt;code&gt;2025_83_28_2_0008_00006281_100mm&lt;/code&gt; and surrounding&lt;br&gt;
frames © &lt;a href="https://skraafoto.dataforsyningen.dk/" rel="noopener noreferrer"&gt;Klimadatastyrelsen&lt;/a&gt; (Danish&lt;br&gt;
Climate Data Agency), licensed under&lt;br&gt;
&lt;a href="https://creativecommons.org/licenses/by/4.0/" rel="noopener noreferrer"&gt;CC BY 4.0&lt;/a&gt;.&lt;br&gt;
Modified: cropped, resized, and annotated for analysis.&lt;br&gt;
Layout-verification screenshot: map data © Google. Google Lens and Flightera&lt;br&gt;
screenshots reproduced for the purpose of analysis and commentary.&lt;/p&gt;

</description>
      <category>geolocation</category>
      <category>aviation</category>
      <category>adsb</category>
      <category>shadowanalysis</category>
    </item>
    <item>
      <title>From a Blurry Container Photo to an IMO Number | Diver 2026</title>
      <dc:creator>Azeria</dc:creator>
      <pubDate>Sun, 26 Jul 2026 08:01:34 +0000</pubDate>
      <link>https://dev.to/azeria/from-a-blurry-container-photo-to-an-imo-number-3p6a</link>
      <guid>https://dev.to/azeria/from-a-blurry-container-photo-to-an-imo-number-3p6a</guid>
      <description>&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;A single photograph: a quayside, a gantry crane, three stacked containers in front of a warehouse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;この黄色いコンテナを最近まで運んでいた船のIMO番号を答えよ。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Answer the IMO number of the ship that was transporting this yellow container until recently.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Flag format: &lt;code&gt;Diver26{IMO}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No stego, no crypto — read the image, extract an identifier, resolve it, validate.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fixzgqukt2cw0i79jo9w6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fixzgqukt2cw0i79jo9w6.jpeg" alt="Quayside with a yellow MSC container, a red container beside it, and a Kamigumi gantry crane in front of a warehouse" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Reading the Photo
&lt;/h2&gt;

&lt;p&gt;Two things stand out before you go anywhere near the container number.&lt;/p&gt;

&lt;p&gt;The yellow container carries the &lt;code&gt;MSC&lt;/code&gt; logo and a vertical &lt;code&gt;MEDITERRANEAN SHIPPING COMPANY&lt;/code&gt; legend, so we know the carrier immediately. The red container next to it (&lt;code&gt;TWCU&lt;/code&gt; prefix, Chinese characters) is a distractor.&lt;/p&gt;

&lt;p&gt;More useful: the gantry crane in the foreground has &lt;strong&gt;KAMIGUMI&lt;/strong&gt; painted on its counterweight. Kamigumi Co., Ltd. is a Japanese port and terminal operator — Osaka, Kobe, Nagoya, Yokohama, Tokyo, Shimizu. That pins the location to a Japanese port before we've read a single digit.&lt;/p&gt;

&lt;p&gt;Standard reflex worth repeating: equipment markings beat cargo markings for location. Containers travel, cranes don't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting the Container Number
&lt;/h2&gt;

&lt;p&gt;The number runs vertically down the left edge of the container.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fit5g16c4o90cvxwow9jr.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fit5g16c4o90cvxwow9jr.webp" alt="Cropped and sharpened close-up of the container number reading MSMU 145296 9" width="120" height="346"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Upscaling adds no detail — Lanczos just interpolates. What it buys you is working surface for the unsharp mask, which lifts contrast at the glyph edges. Unreadable becomes barely readable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MSMU 145296 9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this resolution you will misread characters. Verify before you spend time on a wrong number.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validating with ISO 6346
&lt;/h2&gt;

&lt;p&gt;Container numbers are four letters plus seven digits, where the last digit is a check digit computed from the preceding ten characters. Letter values start at &lt;code&gt;A = 10&lt;/code&gt; and skip every multiple of 11 — which is why &lt;code&gt;K = 21&lt;/code&gt; but &lt;code&gt;L = 23&lt;/code&gt;. Each character is weighted by &lt;code&gt;2^position&lt;/code&gt;, the sum taken modulo 11, and a result of 10 written as 0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;VALUES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABCDEFGHIJKLMNOPQRSTUVWXYZ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;38&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALUES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isalpha&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;check_digit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MSMU1452969&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# -&amp;gt; True
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One caveat on how you use this. Every ten-character string has exactly one valid check digit, so computing the eleventh character from your own reading proves nothing. It only works if you read all eleven independently and check whether they agree. Where it genuinely earns its keep is as a filter: enumerate the plausible readings of each ambiguous position and discard the combinations that don't match the check digit you actually see.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tracking the Container
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;MSMU1452969&lt;/code&gt; into a tracking portal. MSC's own search is a JavaScript SPA behind a captcha and awkward to script; the aggregators are easier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.msc.com/en/track-a-shipment" rel="noopener noreferrer"&gt;MSC Track a Shipment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.searates.com/container/tracking/" rel="noopener noreferrer"&gt;SeaRates&lt;/a&gt; · &lt;a href="https://shipsgo.com/" rel="noopener noreferrer"&gt;ShipsGo&lt;/a&gt; · &lt;a href="https://www.visiwise.co/tracking/container/msc/" rel="noopener noreferrer"&gt;Visiwise&lt;/a&gt; · &lt;a href="https://www.tracecontainer.com/msc/" rel="noopener noreferrer"&gt;TraceContainer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2026-04-03&lt;/td&gt;
&lt;td&gt;Osaka, Japan&lt;/td&gt;
&lt;td&gt;Gate out empty by truck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04-06&lt;/td&gt;
&lt;td&gt;Osaka, Japan&lt;/td&gt;
&lt;td&gt;Gate in full by truck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04-09&lt;/td&gt;
&lt;td&gt;Osaka, Japan&lt;/td&gt;
&lt;td&gt;Loaded on vessel &lt;strong&gt;JULIE&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04-12&lt;/td&gt;
&lt;td&gt;Busan, South Korea&lt;/td&gt;
&lt;td&gt;Discharged from vessel JULIE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04-17&lt;/td&gt;
&lt;td&gt;Busan, South Korea&lt;/td&gt;
&lt;td&gt;Loaded on vessel &lt;strong&gt;MSC RAYA&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04-19&lt;/td&gt;
&lt;td&gt;Ningbo, China&lt;/td&gt;
&lt;td&gt;Discharged from vessel MSC RAYA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-04-24&lt;/td&gt;
&lt;td&gt;Ningbo, China&lt;/td&gt;
&lt;td&gt;Loaded on vessel &lt;strong&gt;MSC ROME&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06-10&lt;/td&gt;
&lt;td&gt;Le Havre, France&lt;/td&gt;
&lt;td&gt;Discharged from vessel MSC ROME&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06-13&lt;/td&gt;
&lt;td&gt;Le Havre, France&lt;/td&gt;
&lt;td&gt;Loaded on vessel &lt;strong&gt;MSC BENIN&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06-16&lt;/td&gt;
&lt;td&gt;London Gateway, UK&lt;/td&gt;
&lt;td&gt;Discharged from vessel MSC BENIN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06-18&lt;/td&gt;
&lt;td&gt;London Gateway, UK&lt;/td&gt;
&lt;td&gt;Delivered by truck&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026-06-22&lt;/td&gt;
&lt;td&gt;London Gateway, UK&lt;/td&gt;
&lt;td&gt;Empty returned by truck&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Container type: &lt;strong&gt;20'DV&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Osaka confirms the Kamigumi read, and dates the photo to the window between gate-in and loading — &lt;strong&gt;2026-04-06 to 2026-04-09&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The rest is textbook transshipment: feeder legs into the Asian hubs, the long ocean leg Ningbo → Le Havre (six weeks, so Cape of Good Hope rather than Suez), then a short hop across to London Gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Four vessels, and the wording matters.&lt;/strong&gt; "Until recently" wants the last ocean vessel, not the one with the longest leg. Grab the ocean crossing and you answer MSC ROME. Read to the end of the chain and you get &lt;strong&gt;MSC BENIN&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resolving the IMO
&lt;/h2&gt;

&lt;p&gt;The IMO number is permanently bound to the hull and survives renaming, sale and reflagging — unlike the MMSI, which changes with the flag. Any AIS database resolves it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.vesselfinder.com/vessels/details/9974565" rel="noopener noreferrer"&gt;VesselFinder&lt;/a&gt; · &lt;a href="https://www.marinetraffic.com/en/ais/details/ships/shipid:9542393/mmsi:636025252/imo:9974565/vessel:MSC%20BENIN" rel="noopener noreferrer"&gt;MarineTraffic&lt;/a&gt; · &lt;a href="https://atlas.flexport.com/vessel/imo:9974565/mmsi:636025252/name:msc-benin" rel="noopener noreferrer"&gt;Flexport Atlas&lt;/a&gt; · &lt;a href="https://www.equasis.org/" rel="noopener noreferrer"&gt;Equasis&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;MSC BENIN&lt;/strong&gt; — IMO &lt;strong&gt;9974565&lt;/strong&gt;, MMSI 636025252, call sign 5LWN7. Neo-Panamax container ship, built 2025, Liberia flag, 260 m LOA, 8,496 TEU.&lt;/p&gt;

&lt;p&gt;Worth a cross-check, since ship names repeat and MSC runs over 700 hulls. Flexport lists MSC BENIN on service &lt;code&gt;NWCIPAK&lt;/code&gt; (North West Continent ↔ India/Pakistan) — Le Havre → London Gateway is exactly one leg of that rotation, and a three-day hop between two ports on the same service is precisely what the tracking shows. AIS put the vessel in the North Sea heading for Antwerp, and later southbound for Nhava Sheva. Built 2025, so active in June 2026. Consistent on all three counts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Flag
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Diver26{9974565}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Notes
&lt;/h2&gt;

&lt;p&gt;Read the crane, not the container. Place-bound equipment gives you location for free.&lt;/p&gt;

&lt;p&gt;Read the task wording carefully — with four vessels in the chain, "until recently" is doing real work.&lt;/p&gt;

&lt;p&gt;And be aware this challenge has a shelf life. Carriers only retain tracking data for completed shipments for a limited window, and this container was returned empty on 2026-06-22. Anyone attempting it from the archive in a year's time will find the chain gone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tracking&lt;/strong&gt; — &lt;a href="https://www.msc.com/en/track-a-shipment" rel="noopener noreferrer"&gt;MSC&lt;/a&gt; · &lt;a href="https://www.searates.com/container/tracking/" rel="noopener noreferrer"&gt;SeaRates&lt;/a&gt; · &lt;a href="https://shipsgo.com/" rel="noopener noreferrer"&gt;ShipsGo&lt;/a&gt; · &lt;a href="https://www.visiwise.co/tracking/container/msc/" rel="noopener noreferrer"&gt;Visiwise&lt;/a&gt; · &lt;a href="https://www.tracecontainer.com/msc/" rel="noopener noreferrer"&gt;TraceContainer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Vessel data&lt;/strong&gt; — &lt;a href="https://www.vesselfinder.com/vessels/details/9974565" rel="noopener noreferrer"&gt;VesselFinder&lt;/a&gt; · &lt;a href="https://www.marinetraffic.com/en/ais/details/ships/shipid:9542393/mmsi:636025252/imo:9974565/vessel:MSC%20BENIN" rel="noopener noreferrer"&gt;MarineTraffic&lt;/a&gt; · &lt;a href="https://atlas.flexport.com/vessel/imo:9974565/mmsi:636025252/name:msc-benin" rel="noopener noreferrer"&gt;Flexport Atlas&lt;/a&gt; · &lt;a href="https://maritimeoptima.com/public/vessels/pages/imo:9974565/mmsi:636025252/MSC_BENIN.html" rel="noopener noreferrer"&gt;MaritimeOptima&lt;/a&gt; · &lt;a href="https://shipnext.com/vessel/9974565-msc-benin" rel="noopener noreferrer"&gt;Shipnext&lt;/a&gt; · &lt;a href="https://www.equasis.org/" rel="noopener noreferrer"&gt;Equasis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt; — &lt;a href="https://www.bic-code.org/" rel="noopener noreferrer"&gt;BIC&lt;/a&gt; · &lt;a href="https://www.imo.org/en/OurWork/MSAS/Pages/IMO-identification-number-scheme.aspx" rel="noopener noreferrer"&gt;IMO Ship Identification Number Scheme&lt;/a&gt; · &lt;a href="https://www.kamigumi.co.jp/english/" rel="noopener noreferrer"&gt;Kamigumi&lt;/a&gt; · &lt;a href="https://diverctf.org/en.html" rel="noopener noreferrer"&gt;DIVER OSINT CTF&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>osint</category>
      <category>writeup</category>
      <category>maritimeosint</category>
    </item>
  </channel>
</rss>
