Skip to content

About

An interactive map of a live AIS snapshot of 4,993 vessels in and around Iranian waters: the Persian Gulf, Strait of Hormuz, Gulf of Oman, northern Arabian Sea and the Caspian Sea.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

iran-map-vessel

An interactive map of a live AIS snapshot of 4,993 vessels in and around Iranian waters: the Persian Gulf, Strait of Hormuz, Gulf of Oman, northern Arabian Sea and the Caspian Sea.

The repository contains the raw snapshot (JSON + CSV) and a single self-contained HTML page that renders, filters and audits it. There is no build step and no server-side code.

iran_ships.json   1.8 MB   4,993 vessel records (source of truth, read by map.html)
iran_ships.csv    0.4 MB   the same records flattened to 21 columns
map.html          31 KB    Leaflet viewer: filters, course vectors, coverage grid

Quick start

map.html fetches iran_ships.json from the same folder, and browsers block fetch() on file:// URLs. Serve the folder instead:

py -m http.server 8000        # or: python3 -m http.server 8000
                              # or: npx serve .

Then open http://localhost:8000/map.html.

If you do open the file directly, the page detects the failed fetch and falls back to a file picker. Choose iran_ships.json manually and everything works the same.

Leaflet 1.9.4 and Leaflet.markercluster 1.5.3 are loaded from unpkg, so the first load needs a network connection (as do the OpenStreetMap tiles).

What the map does

Ship type. One colour per MarineTraffic SHIPTYPE, each with a checkbox and a live count. Tugs & special craft (1,463) and fishing vessels (1,070) dominate the snapshot; tankers (660) and cargo (706) are the commercially interesting ones.

Speed & motion. A dual-handle range slider over 0-55 knots, plus All / Stopped / Moving / Fast presets. Vessels reporting no speed at all (288 records) are toggled separately so they are never silently dropped. Enabling course vectors draws a schematic arrow per moving vessel, length scaled by speed and angle taken from COURSE (falling back to HEADING).

Destination. The reported destination field is messy, so entries are folded to a canonical key: AE FJR, AEFJR and AE-FJR all collapse into one bucket, and the dropdown shows the most common spelling with a [n spellings] marker. Known placeholders (CLASS B, 0, N/A, UNKNOWN, and so on) are treated as no destination. Optional on-map labels are capped at 350 in-view labels, and the panel tells you how many were suppressed.

Flag. 111 distinct flags, sorted by frequency. Iran (657), Saudi Arabia (543), Kuwait (466), Qatar (400) and the UAE (377) lead, followed by the usual open registries (Panama, Liberia, Marshall Islands).

Search. Free-text match across ship name, SHIP_ID and destination.

Coverage check. The panel's last section is about the data, not the ships. It draws the scrape bounding box and, optionally, the XYZ tile grid the snapshot was collected on, with a per-tile vessel count. Tiles are coloured grey when they returned nothing and red at or above 500 vessels, which is the point where a tile response is likely truncated by an upstream cap rather than genuinely empty of traffic. The hint line summarises tiles requested, empty tiles, saturated tiles and vessels that landed outside the tile range.

Clicking any marker opens a detail table (type, flag, position, speed, course, heading, destination, status, dimensions, DWT, report age) with a link through to the vessel's MarineTraffic page.

The data

A single point-in-time snapshot collected on 2026-08-15. ELAPSED (minutes since each vessel's last AIS report) has a median of 16 minutes, so most positions were fresh at collection time; the tail runs to about 24 hours for sporadic reporters.

Extent of the records:

min max
latitude 21.90 40.67
longitude 46.93 65.43

The scrape bounding box declared in map.html is 23.0-40.5 N, 43.0-64.5 E. A handful of records fall outside it, because the tile that covers a corner of the box extends past its edge.

Fields

Every record carries LAT, LON, SPEED, COURSE, HEADING, ELAPSED, SHIPNAME, SHIPTYPE and SHIP_ID. The rest depend on what the upstream tile returned, so JSON objects have varying key sets; the CSV pads the missing ones with empty strings.

Field Present Meaning
LAT, LON 4,993 Position, decimal degrees, as strings
SPEED 4,705 Tenths of a knot, so 524 is 52.4 kn. null where the tile carried no speed
COURSE 4,222 Course over ground, degrees
HEADING 2,286 Compass heading, degrees; often null for small craft
ELAPSED 4,993 Minutes since the vessel's last AIS report
SHIPNAME 4,993 Name, or an MMSI-like number for Class B transponders, or [SAT-AIS]
SHIPTYPE 4,993 Coarse type code, 0-9 (see below)
SHIP_ID 4,993 MarineTraffic vessel id; unique across all 4,993 records
FLAG 4,843 ISO 3166-1 alpha-2 country code
LENGTH, WIDTH 4,842 Overall dimensions in metres; 0 when unreported
DESTINATION 4,836 Free text, frequently a placeholder
L_FORE, W_LEFT 4,835 Antenna offset from bow / port side, metres
GT_SHIPTYPE 2,510 Fine-grained type code
DWT 2,488 Deadweight tonnage
ROT 1,517 Rate of turn
TYPE_IMG, TYPE_NAME, STATUS_NAME 150 Only on satellite-AIS records

SHIPTYPE codes and their counts in this snapshot:

Code Type Count
3 Tugs & Special Craft 1,463
2 Fishing 1,070
7 Cargo Vessel 706
8 Tanker 660
0 Unspecified 507
9 Pleasure Craft 313
1 Navigation Aids 201
6 Passenger Vessel 63
4 High Speed Craft 10
5 Military 0

Things to know before analysing it

  • Speed is in tenths of a knot. Forgetting to divide by 10 is the single most common mistake with this data. map.html does it in knots().
  • 1,806 records report CLASS B as their destination. That is not a place; it is what the source puts in the field for Class B transponders. Another 746 report an empty destination. Treat both as unknown, which is what the viewer does.
  • 150 records are satellite-AIS (SHIPNAME is [SAT-AIS]). These have a base64 blob instead of a numeric SHIP_ID, carry TYPE_NAME/STATUS_NAME that terrestrial records lack, and are missing FLAG and dimensions. They are the reason the FLAG count stops at 4,843.
  • All values are strings, or null. Nothing is numeric in the JSON. Cast before comparing.
  • Class B / small-craft names are often bare MMSI-like numbers (447804000, sometimes with a trailing number). Don't assume SHIPNAME is human-readable.
  • SHIP_ID is unique here, so it works as a primary key for this snapshot, but it is a MarineTraffic-internal id, not an IMO or MMSI number.
  • The CSV is UTF-8 with a BOM and lists the 21 columns alphabetically. Read it with encoding='utf-8-sig' in Python, or the first column name comes back as COURSE.

Collection

The snapshot was scraped tile-by-tile over an XYZ grid covering the bounding box, at MarineTraffic zoom 9 (XYZ zoom 8). map.html hard-codes the same constants at the top of its script block so the coverage overlay reproduces the exact grid that was requested:

var BBOX = { north: 40.5, south: 23.0, west: 43.0, east: 64.5 };
var XYZ_ZOOM = 8;            // MARINE_Z (9) - 1
var TILE_SATURATION = 500;   // per-tile count that hints at an API cap

The scraper itself (update.py) is not part of this repository, only its output and the constants needed to interpret that output. If you regenerate the data with a different bounding box or zoom, update those three constants to match, or the coverage overlay will describe a grid that was never requested.

To refresh the map with your own snapshot, replace iran_ships.json with a JSON array of objects using the field names above. Records missing usable coordinates are filtered out at load and reported in the panel subtitle.

Caveats

This is one snapshot of self-reported AIS data, not a surveillance product. AIS can be switched off, misconfigured or spoofed; coverage is uneven, and an empty tile in the coverage grid means nothing was returned, which is not the same as nothing was there. Vessel type, destination and dimensions are entered by crews and are frequently stale or wrong. Anything derived from this data deserves a second source.

License

GPL-3.0, see LICENSE. Ship data © MarineTraffic; base map tiles © OpenStreetMap contributors.

About

An interactive map of a live AIS snapshot of 4,993 vessels in and around Iranian waters: the Persian Gulf, Strait of Hormuz, Gulf of Oman, northern Arabian Sea and the Caspian Sea.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages