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 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
+ [![CI](https://github.com/Trackberry/seaports/actions/workflows/ci.yml/badge.svg)](https://github.com/Trackberry/seaports/actions/workflows/ci.yml)
4
+ [![Gem](https://img.shields.io/gem/v/seaports)](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