seaports 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE.txt +28 -0
- data/README.md +132 -0
- data/data/un_locode_seaports.csv +17521 -0
- data/lib/seaports/builder.rb +114 -0
- data/lib/seaports/diff.rb +174 -0
- data/lib/seaports/version.rb +11 -0
- data/lib/seaports.rb +121 -0
- metadata +70 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 58b616c89654cb06c7c095c1106a16bc7164835cd6cedeaf40e10f22b23089be
|
|
4
|
+
data.tar.gz: 9366dc14c643f638d73d6a288c2b10442698a03219afa0bc241a41e2e672b7f9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ae4bd49fb6adad08534857a84df567724347dc21d7d3ef7163f3760f9e0ac470b78e15db27ae35fbe60bdd8d63a25a1ab3d12da5cedbfdeb827ca6de45f8b5cc
|
|
7
|
+
data.tar.gz: 134cc3f0980934cc76e251719a1ea47f0c142975c792485c4355e8ced35f25f3f9b47298c321c20f80e15ddbe80b22af0592ef212b6aaa32f0980c59c2b048e5
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every entry below the first is a UN/LOCODE refresh, opened as a pull request by
|
|
4
|
+
`.github/workflows/refresh.yml` and merged by a person.
|
|
5
|
+
|
|
6
|
+
## 1.0.0 — 2026-08-21
|
|
7
|
+
|
|
8
|
+
First release. Extracted from the Trackberry application, where this table
|
|
9
|
+
answered port calls from AIS tracking feeds. Built from UN/LOCODE 2024-2:
|
|
10
|
+
17,520 sea ports, 11,762 of them with published coordinates.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Édouard Brière
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
The sea-port table in data/ is derived from the UN/LOCODE code list published
|
|
26
|
+
by the United Nations Economic Commission for Europe, which is public domain,
|
|
27
|
+
by way of the datasets/un-locode distribution released under the Open Data
|
|
28
|
+
Commons Public Domain Dedication and Licence (PDDL-1.0).
|
data/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Seaports
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Trackberry/seaports/actions/workflows/ci.yml)
|
|
4
|
+
[](https://rubygems.org/gems/seaports)
|
|
5
|
+
|
|
6
|
+
A locode in, a port name and a position out.
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
Seaports.name("CNXMG") # => "Xiamen Pt"
|
|
10
|
+
Seaports.coordinates("AUBNE") # => { "lat" => -27.4667, "lng" => 153.0167 }
|
|
11
|
+
Seaports.find("KRPUS") # => #<data Seaports::Port locode="KRPUS", name="Busan", ...>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Tracking feeds speak in codes. An AIS provider names some port calls and sends
|
|
15
|
+
only a code for the rest, so a stop that mattered arrives as a bare `XMG` — a
|
|
16
|
+
real, load-bearing fact about a voyage, written in a vocabulary nobody reads.
|
|
17
|
+
UNECE publishes the table that turns it back into `Xiamen Pt`, and publishes
|
|
18
|
+
coordinates alongside, so one dataset answers both *where is this?* and *where
|
|
19
|
+
do I draw it?*
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
gem "seaports"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Ruby 3.2 or newer. One dependency, `csv`, and only because Ruby 3.4 demoted it
|
|
28
|
+
out of the default gems.
|
|
29
|
+
|
|
30
|
+
## What is in it
|
|
31
|
+
|
|
32
|
+
Every entry UN/LOCODE classifies as a sea port: **17,520 ports**, 11,762 of them
|
|
33
|
+
with published coordinates, built from **UN/LOCODE 2024-2**.
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
Seaports.count # => 17520
|
|
37
|
+
Seaports.all # => [#<data Seaports::Port ...>, ...]
|
|
38
|
+
Seaports.data_release # => "2024-2"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The table ships inside the gem as a CSV. It is reference data that changes
|
|
42
|
+
twice a year, and a port name should not depend on a network. Nothing is
|
|
43
|
+
fetched at runtime, and the file is parsed once, on the first lookup rather
|
|
44
|
+
than at require time.
|
|
45
|
+
|
|
46
|
+
Lookups are case- and whitespace-insensitive, and anything that is not shaped
|
|
47
|
+
like a locode — two letters and three alphanumerics — resolves to `nil` rather
|
|
48
|
+
than being guessed at. `nil` is also the answer for a code the table does not
|
|
49
|
+
hold: seventeen thousand ports is most of the world's, not all of it, so an
|
|
50
|
+
unknown code is the ordinary case rather than an error.
|
|
51
|
+
|
|
52
|
+
Roughly a third of the table has no position published. That is a fact about
|
|
53
|
+
the source, not a failure — the name is still worth having, and a map simply
|
|
54
|
+
draws no marker.
|
|
55
|
+
|
|
56
|
+
### What is deliberately not in it
|
|
57
|
+
|
|
58
|
+
Only entries UNECE marks with function `1`, "port", are kept. A road terminal,
|
|
59
|
+
an airport or a postal exchange sharing a city with a port must never answer a
|
|
60
|
+
lookup for a vessel's port call: `CNSHA` is Shanghai Hongqiao airport, and the
|
|
61
|
+
port of Shanghai is `CNSGH`.
|
|
62
|
+
|
|
63
|
+
The notable absence is `PAPCN`, "Panama Canal", which some feeds do send. UNECE
|
|
64
|
+
files it as a road terminal with status `RL` — "recognised location", its
|
|
65
|
+
weakest, meaning no national authority approved it and its functions were never
|
|
66
|
+
verified. That is the default bucket for an unchecked entry rather than a
|
|
67
|
+
ruling, but the substance holds anyway: a canal is a waterway, not a port. The
|
|
68
|
+
ports at either end are coded properly, `PABLB` Balboa and `PACTB` Cristóbal, so
|
|
69
|
+
a canal transit is better rendered from the country than from a port lookup.
|
|
70
|
+
|
|
71
|
+
Keeping the filter narrow also guards anyone rebuilding a five-character locode
|
|
72
|
+
from a three-character carrier code, which is a heuristic: a code that is not a
|
|
73
|
+
locode tail could pair with a country to form a valid-looking locode for some
|
|
74
|
+
inland village, and a table of ports is far less likely to name it.
|
|
75
|
+
|
|
76
|
+
## How the table stays current
|
|
77
|
+
|
|
78
|
+
UNECE republishes twice a year and does not announce it. A scheduled workflow
|
|
79
|
+
rebuilds the table weekly and, on the rare week something moved, opens a pull
|
|
80
|
+
request for a person to merge:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
rake seaports:refresh # fetch the current release, rebuild, report
|
|
84
|
+
rake seaports:refresh[path/to/code-list.csv,2025-1]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The source is the [datasets/un-locode][mirror] distribution, which republishes
|
|
88
|
+
UNECE's list with headers, UTF-8 and a machine-readable release number; UNECE's
|
|
89
|
+
own download is a zip of headerless latin-1 files.
|
|
90
|
+
|
|
91
|
+
That mirror is a third party, which is the whole reason the refresh does not
|
|
92
|
+
simply commit what it downloads. Seventeen thousand rows are not reviewable by
|
|
93
|
+
eye, so `Seaports::Diff` reads them instead, and refuses the rebuild outright
|
|
94
|
+
if:
|
|
95
|
+
|
|
96
|
+
- fewer than 15,000 ports survive, or the table shrank by more than 2% in one
|
|
97
|
+
release — a truncated download rather than a busy half-year;
|
|
98
|
+
- any of sixteen anchor ports spread across the continents has vanished, which
|
|
99
|
+
catches a source that dropped one region while keeping its row count healthy;
|
|
100
|
+
- `PAPCN` has been reclassified as a port upstream, which should be a decision
|
|
101
|
+
someone makes rather than a row that appears quietly.
|
|
102
|
+
|
|
103
|
+
A refusal leaves the working tree exactly as it was and opens an issue. A pass
|
|
104
|
+
bumps the minor version, records the new UN/LOCODE release, writes the
|
|
105
|
+
changelog entry, and puts the whole diff in the pull request body — every
|
|
106
|
+
removal listed in full, because those are the changes that break a caller, and
|
|
107
|
+
position changes called out separately, because they move markers on a map.
|
|
108
|
+
|
|
109
|
+
Merging that pull request changes `lib/seaports/version.rb`, which is the only
|
|
110
|
+
thing that triggers a release. The gem is published by RubyGems trusted
|
|
111
|
+
publishing, so there is no API key in this repository.
|
|
112
|
+
|
|
113
|
+
## Versioning
|
|
114
|
+
|
|
115
|
+
Semver, where the data is the product:
|
|
116
|
+
|
|
117
|
+
| | |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| **patch** | code fixes, table unchanged |
|
|
120
|
+
| **minor** | a UN/LOCODE refresh — ports added, renamed, repositioned |
|
|
121
|
+
| **major** | an API change, or a refresh that removes ports known to be load-bearing |
|
|
122
|
+
|
|
123
|
+
`Seaports.data_release` tells you which UN/LOCODE edition your copy carries,
|
|
124
|
+
which is the question a gem version cannot answer.
|
|
125
|
+
|
|
126
|
+
## Licence
|
|
127
|
+
|
|
128
|
+
The code is MIT. The table is derived from the UN/LOCODE code list published by
|
|
129
|
+
UNECE, which is public domain, by way of the [datasets/un-locode][mirror]
|
|
130
|
+
distribution released under PDDL-1.0. See [LICENSE.txt](LICENSE.txt).
|
|
131
|
+
|
|
132
|
+
[mirror]: https://github.com/datasets/un-locode
|