zip-codes-pl 0.1.1
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/LICENSE.txt +27 -0
- data/README.md +204 -0
- data/README.pl.md +179 -0
- data/data/zip-codes-pl.manifest.json +10 -0
- data/data/zip-codes-pl.tsv +72900 -0
- data/lib/zip-codes-pl.rb +3 -0
- data/lib/zip_codes/pl/builder.rb +113 -0
- data/lib/zip_codes/pl/city.rb +66 -0
- data/lib/zip_codes/pl/configuration.rb +46 -0
- data/lib/zip_codes/pl/errors.rb +11 -0
- data/lib/zip_codes/pl/manifest.rb +39 -0
- data/lib/zip_codes/pl/normalize.rb +30 -0
- data/lib/zip_codes/pl/railtie.rb +11 -0
- data/lib/zip_codes/pl/reader.rb +116 -0
- data/lib/zip_codes/pl/record.rb +50 -0
- data/lib/zip_codes/pl/sources/geonames.rb +127 -0
- data/lib/zip_codes/pl/sources/poczta_polska/client.rb +91 -0
- data/lib/zip_codes/pl/sources/poczta_polska.rb +115 -0
- data/lib/zip_codes/pl/tasks/zip_codes_pl.rake +64 -0
- data/lib/zip_codes/pl/version.rb +7 -0
- data/lib/zip_codes/pl/voivodeship.rb +48 -0
- data/lib/zip_codes/pl.rb +61 -0
- metadata +86 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9f093d90a449c54edbdfe6298dd0d3d480ac666eaefb25daad255d9ce6f86877
|
|
4
|
+
data.tar.gz: 0b3ccc8540f608eb8f8c6cce891dc0d75fa62cf52e11ee2f166984d53760f7d1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 07ebb11afbc3b7a95663a5fabc6e23946c31862e3441aa3d0ab2122e67ce4527c90c79f88549bb9a2ceb453d254c21720fbfcf0f5d56e34db65148ee068706ef
|
|
7
|
+
data.tar.gz: 89ffefe6ec7f2c9e41061b5960e71b5cfda91b2484aae3e92eeb0547a34ce0bb2a2583d8c9991519625711acedbaf8aa38bb9c5372055ba189fa25839e4d334b
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Łukasz Kerl
|
|
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 dataset this gem builds is derived from GeoNames (https://www.geonames.org),
|
|
26
|
+
licensed under Creative Commons Attribution 4.0. Redistributing a built dataset
|
|
27
|
+
requires crediting GeoNames.
|
data/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# zip-codes-pl
|
|
2
|
+
|
|
3
|
+
Polish postal codes, places and voivodeships **with coordinates**, built and
|
|
4
|
+
refreshed by a single rake task.
|
|
5
|
+
|
|
6
|
+
*(Dokumentacja po polsku: [README.pl.md](README.pl.md))*
|
|
7
|
+
|
|
8
|
+
**The dataset ships with the gem, so there is nothing to build before first
|
|
9
|
+
use.** A rake task refreshes it into a directory of your choosing, asking the
|
|
10
|
+
server with `If-None-Match` so a repeated run downloads nothing when the source
|
|
11
|
+
has not changed. Your refreshed copy takes precedence over the bundled one.
|
|
12
|
+
|
|
13
|
+
The dataset holds **72,899 rows**, **20,299 postal codes** and **52,325 places**
|
|
14
|
+
across 16 voivodeships, as a 6.6 MB tab separated file. Lookups scan the file
|
|
15
|
+
without building an index or retaining the whole dataset in memory.
|
|
16
|
+
|
|
17
|
+
## Why this exists
|
|
18
|
+
|
|
19
|
+
GeoNames is an excellent source of Polish postal codes and coordinates, and a
|
|
20
|
+
poor source of Polish *names*. It labels all sixteen voivodeships in English and
|
|
21
|
+
inconsistently - `Lower Silesia`, `Warmia-Masuria`, `Łódź Voivodeship` - which is
|
|
22
|
+
not something you can put in front of a Polish user.
|
|
23
|
+
|
|
24
|
+
This gem carries its own verified table of the sixteen voivodeships, mapped onto
|
|
25
|
+
the official **TERYT** codes used by Polish public administration, and replaces
|
|
26
|
+
the upstream labels with it. The TERYT county and commune codes that GeoNames
|
|
27
|
+
does carry are passed through, so the result joins cleanly against any other
|
|
28
|
+
Polish register.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
gem "zip-codes-pl"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Refreshing the dataset
|
|
37
|
+
|
|
38
|
+
GeoNames republishes daily. To pick up a newer extract than the one bundled with
|
|
39
|
+
your installed version:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
rake zip_codes:pl:update # into the default directory (data/)
|
|
43
|
+
rake zip_codes:pl:update[db/pna] # into a directory you choose
|
|
44
|
+
rake zip_codes:pl:info[db/pna] # what is built, and when
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
When the GeoNames extract has changed, the build fetches county and commune
|
|
48
|
+
names by TERYT code from the public Poczta Polska search.
|
|
49
|
+
Requests are sequential and spaced by at least 0.25 seconds by default; a `429`
|
|
50
|
+
response delays the next request according to `Retry-After`. A full enrichment
|
|
51
|
+
currently takes about five minutes. Poczta Polska is not queried after a
|
|
52
|
+
`304 Not Modified` response from GeoNames.
|
|
53
|
+
|
|
54
|
+
In a Rails application a railtie loads the tasks for you. Elsewhere, add one
|
|
55
|
+
line to your `Rakefile`:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
load Gem::Specification.find_by_name("zip-codes-pl").gem_dir + "/lib/zip_codes/pl/tasks/zip_codes_pl.rake"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The output directory can also be set once:
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
ZipCodes::PL.configure do |config|
|
|
65
|
+
config.output_dir = Rails.root.join("db/pna").to_s
|
|
66
|
+
config.poczta_request_interval = 0.5 # optionally go even slower
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
or through the `ZIP_CODES_PL_DIR` environment variable. Reads look there first
|
|
71
|
+
and fall back to the bundled dataset, so configuring a directory that has not
|
|
72
|
+
been refreshed yet changes nothing.
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
ZipCodes::PL.find_by_postal_code("86-010") # "86010" works too
|
|
78
|
+
# => [#<data ZipCodes::PL::Record postal_code="86-010", city="Koronowo", ...>, ...]
|
|
79
|
+
|
|
80
|
+
ZipCodes::PL.find_by_city("zlotow") # case and diacritics insensitive
|
|
81
|
+
ZipCodes::PL.find_by_city("Koronowo", voivodeship: "kujawsko-pomorskie")
|
|
82
|
+
ZipCodes::PL.search_by_city("wie") # name fragment, e.g. "Nowa Wieś"
|
|
83
|
+
ZipCodes::PL.search_by_city("OS") # exactly "Oś"
|
|
84
|
+
|
|
85
|
+
ZipCodes::PL.voivodeships
|
|
86
|
+
# => 16 records: TERYT code, Polish name, ASCII slug
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Fragment searches ignore case and Polish diacritics. Queries of three or more
|
|
90
|
+
characters behave like `%LIKE%`; a two-character query only matches a complete
|
|
91
|
+
name, so the shortest place, `Oś`, remains searchable. One-character queries
|
|
92
|
+
return an empty result without scanning the file.
|
|
93
|
+
|
|
94
|
+
One record is one postal code in one place. A town with several codes has
|
|
95
|
+
several records, and so does a code shared by several villages - `86-010` covers
|
|
96
|
+
42 places around Koronowo.
|
|
97
|
+
|
|
98
|
+
When you want **places rather than codes**, there is a ready aggregation:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
ZipCodes::PL.cities.first
|
|
102
|
+
# => #<data ZipCodes::PL::City
|
|
103
|
+
# name="Abisynia", voivodeship="pomorskie", voivodeship_teryt="22",
|
|
104
|
+
# commune="Karsin", commune_teryt="220603",
|
|
105
|
+
# latitude=53.9244, longitude=17.9337, postal_codes=["83-440"]>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
A place's coordinate is the mean of its rows, because the source gives one point
|
|
109
|
+
per postal code and Bydgoszcz has 679 of them.
|
|
110
|
+
|
|
111
|
+
Places are identified by name **and commune**, not by name alone. Poland has 119
|
|
112
|
+
villages called `Nowa Wieś`, 28 of them in a single voivodeship; collapsing them
|
|
113
|
+
by name puts the resulting point in a field up to 158 km from the farthest one.
|
|
114
|
+
|
|
115
|
+
### Importing into a database
|
|
116
|
+
|
|
117
|
+
Raw records can be imported in batches without loading the entire file:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
ZipCodes::PL.each_record.each_slice(1000) do |batch|
|
|
121
|
+
PostalCode.upsert_all(batch.map(&:to_h))
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Or import the aggregated places:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
ZipCodes::PL.cities.each_slice(1000) do |batch|
|
|
129
|
+
City.upsert_all(
|
|
130
|
+
batch.map do |city|
|
|
131
|
+
{ name: city.name, province: city.voivodeship,
|
|
132
|
+
latitude: city.latitude, longitude: city.longitude }
|
|
133
|
+
end,
|
|
134
|
+
unique_by: %i[name province]
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## File format
|
|
140
|
+
|
|
141
|
+
`zip-codes-pl.tsv` - tab separated with a header row, next to
|
|
142
|
+
`zip-codes-pl.manifest.json` holding the source ETag, build time and row count.
|
|
143
|
+
It is left uncompressed on purpose: the file is committed, and a refresh should
|
|
144
|
+
produce a reviewable diff rather than a fresh opaque blob.
|
|
145
|
+
|
|
146
|
+
| column | example | note |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| `postal_code` | `86-010` | |
|
|
149
|
+
| `city` | `Nowa Wieś Wielka` | |
|
|
150
|
+
| `voivodeship` | `kujawsko-pomorskie` | from this gem, not from the source |
|
|
151
|
+
| `voivodeship_teryt` | `04` | official TERYT code |
|
|
152
|
+
| `county` / `county_teryt` | `powiat bydgoski` / `0403` | Poczta Polska name joined by TERYT code |
|
|
153
|
+
| `commune` / `commune_teryt` | `Koronowo` / `040304` | Poczta Polska name joined by TERYT code |
|
|
154
|
+
| `latitude` / `longitude` | `53.3123` / `17.9539` | WGS84 |
|
|
155
|
+
| `accuracy` | `6` | as reported by the source - see below |
|
|
156
|
+
|
|
157
|
+
The file is written to a temporary path and renamed. A GeoNames download,
|
|
158
|
+
Poczta Polska response, parsing or validation failure leaves the previous TSV
|
|
159
|
+
and manifest unchanged.
|
|
160
|
+
|
|
161
|
+
## What this gem does not pretend to do
|
|
162
|
+
|
|
163
|
+
**These are postal-code centroids, not surveyed points.** GeoNames derives many
|
|
164
|
+
of them algorithmically from place names, and falls back to an average of
|
|
165
|
+
neighbouring postal codes where no match is found. They are good enough to sort
|
|
166
|
+
by distance or draw on a map; they are not property-grade coordinates. The
|
|
167
|
+
`accuracy` column is a source field, and for Poland it is currently `6` on all
|
|
168
|
+
72,899 rows, so it carries no usable signal.
|
|
169
|
+
|
|
170
|
+
**This is a postal dataset, not the official place register.** It covers places
|
|
171
|
+
that have a postal code in the GeoNames extract, under GeoNames' spelling. The
|
|
172
|
+
official Polish register (TERYT SIMC) is larger and authoritative on names; the
|
|
173
|
+
gap between the two has not been measured here.
|
|
174
|
+
|
|
175
|
+
**Administrative names are normalized by TERYT code.** The gem carries its own
|
|
176
|
+
table for the 16 voivodeships and fetches current county and commune names from
|
|
177
|
+
Poczta Polska during a refresh. The one explicit exception is historical code
|
|
178
|
+
`320304` for the former Ostrowice commune, abolished on 1 January 2019, which is
|
|
179
|
+
still present in GeoNames. **The `*_teryt` columns remain the dependable
|
|
180
|
+
identifiers**, not the names.
|
|
181
|
+
|
|
182
|
+
There are no streets or building numbers here, and this is not an address
|
|
183
|
+
geocoder. For address-level lookups Poland has a free official service at
|
|
184
|
+
`services.gugik.gov.pl/uug`.
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
bin/setup # or: bundle install
|
|
190
|
+
bundle exec rake test
|
|
191
|
+
bundle exec rubocop
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The test suite never touches the network: the source archive is generated in
|
|
195
|
+
memory and the HTTP transport is exercised against stubbed responses.
|
|
196
|
+
|
|
197
|
+
## Source and licence
|
|
198
|
+
|
|
199
|
+
Postal data and coordinates come from [GeoNames](https://www.geonames.org)
|
|
200
|
+
under **CC BY 4.0**. County and commune names are fetched from the public
|
|
201
|
+
[Poczta Polska search](https://www.poczta-polska.pl/znajdz-kod-pocztowy/).
|
|
202
|
+
The manifest carries a ready attribution string.
|
|
203
|
+
|
|
204
|
+
The code is MIT licensed.
|
data/README.pl.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# zip-codes-pl
|
|
2
|
+
|
|
3
|
+
*(English documentation: [README.md](README.md))*
|
|
4
|
+
|
|
5
|
+
Polskie kody pocztowe, miejscowości i województwa **ze współrzędnymi**, budowane
|
|
6
|
+
i odświeżane jednym zadaniem rake.
|
|
7
|
+
|
|
8
|
+
**Zbiór jedzie razem z gemem, więc nie trzeba niczego budować przed pierwszym
|
|
9
|
+
użyciem.** Zadanie rake odświeża go do wskazanego katalogu, pytając serwer
|
|
10
|
+
nagłówkiem `If-None-Match`, więc powtórne uruchomienie nie pobiera ani bajta,
|
|
11
|
+
kiedy źródło się nie zmieniło. Twoja odświeżona kopia ma pierwszeństwo przed
|
|
12
|
+
tą z gema.
|
|
13
|
+
|
|
14
|
+
Zbiór to **72 899 wierszy**, **20 299 kodów pocztowych** i **52 325
|
|
15
|
+
miejscowości** w 16 województwach, w pliku TSV o rozmiarze 6,6 MB. Wyszukiwanie
|
|
16
|
+
skanuje plik bez budowania indeksu i bez trzymania całego zbioru w pamięci.
|
|
17
|
+
|
|
18
|
+
## Instalacja
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem "zip-codes-pl"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Odświeżanie zbioru
|
|
25
|
+
|
|
26
|
+
GeoNames publikuje codziennie. Żeby wziąć nowszy zrzut niż ten w twojej wersji:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
rake zip_codes:pl:update # do katalogu domyślnego (data/)
|
|
30
|
+
rake zip_codes:pl:update[db/pna] # do wskazanego katalogu
|
|
31
|
+
rake zip_codes:pl:info[db/pna] # co jest zbudowane i z kiedy
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Kiedy zrzut GeoNames się zmienił, build dociąga z publicznej wyszukiwarki
|
|
35
|
+
Poczty Polskiej nazwy powiatów i gmin po kodach TERYT.
|
|
36
|
+
Zapytania idą sekwencyjnie, domyślnie nie częściej niż co 0,25 s; odpowiedź
|
|
37
|
+
`429` wstrzymuje kolejne zapytanie zgodnie z `Retry-After`. Pełne wzbogacenie
|
|
38
|
+
zajmuje obecnie około pięciu minut. Przy odpowiedzi `304 Not Modified` Poczta
|
|
39
|
+
Polska nie jest odpytywana.
|
|
40
|
+
|
|
41
|
+
W aplikacji Rails zadanie podpina się samo przez railtie. Poza Railsami dodaj do
|
|
42
|
+
swojego `Rakefile`:
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
load Gem::Specification.find_by_name("zip-codes-pl").gem_dir + "/lib/zip_codes/pl/tasks/zip_codes_pl.rake"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Katalog wyjściowy ustawia się też na stałe:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
ZipCodes::PL.configure do |config|
|
|
52
|
+
config.output_dir = Rails.root.join("db/pna").to_s
|
|
53
|
+
config.poczta_request_interval = 0.5 # opcjonalnie jeszcze wolniej
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
albo zmienną `ZIP_CODES_PL_DIR`. Odczyt najpierw zagląda tam, a w razie braku
|
|
58
|
+
pliku sięga po zbiór z gema - ustawienie katalogu, którego jeszcze nie
|
|
59
|
+
odświeżyłeś, niczego nie psuje.
|
|
60
|
+
|
|
61
|
+
## Użycie
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
ZipCodes::PL.find_by_postal_code("86-010") # działa też "86010"
|
|
65
|
+
# => [#<data ZipCodes::PL::Record postal_code="86-010", city="Koronowo", ...>, ...]
|
|
66
|
+
|
|
67
|
+
ZipCodes::PL.find_by_city("zlotow") # bez ogonków i wielkości liter
|
|
68
|
+
ZipCodes::PL.find_by_city("Koronowo", voivodeship: "kujawsko-pomorskie")
|
|
69
|
+
ZipCodes::PL.search_by_city("wie") # fragment nazwy, np. "Nowa Wieś"
|
|
70
|
+
ZipCodes::PL.search_by_city("OS") # dokładnie "Oś"
|
|
71
|
+
|
|
72
|
+
ZipCodes::PL.voivodeships
|
|
73
|
+
# => 16 rekordów: kod TERYT, nazwa, slug bez polskich znaków
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Wyszukiwanie fragmentu ignoruje wielkość liter i polskie znaki. Od trzech
|
|
77
|
+
znaków działa jak `%LIKE%`; dwuznakowa fraza dopasowuje tylko całą nazwę, dzięki
|
|
78
|
+
czemu nadal można znaleźć najkrótszą miejscowość „Oś”. Jednoznakowe zapytania
|
|
79
|
+
zwracają pusty wynik bez skanowania pliku.
|
|
80
|
+
|
|
81
|
+
Jeden rekord to jeden kod pocztowy w jednej miejscowości. Miejscowość z kilkoma
|
|
82
|
+
kodami ma kilka rekordów, tak samo kod dzielony przez kilka wsi - `86-010` to
|
|
83
|
+
42 miejscowości wokół Koronowa.
|
|
84
|
+
|
|
85
|
+
Kiedy potrzebujesz **miejscowości, a nie kodów**, jest gotowa agregacja:
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
ZipCodes::PL.cities.first
|
|
89
|
+
# => #<data ZipCodes::PL::City
|
|
90
|
+
# name="Abisynia", voivodeship="pomorskie", voivodeship_teryt="22",
|
|
91
|
+
# commune="Karsin", commune_teryt="220603",
|
|
92
|
+
# latitude=53.9244, longitude=17.9337, postal_codes=["83-440"]>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Współrzędne miejscowości to średnia z jej wierszy, bo źródło daje punkt na kod
|
|
96
|
+
pocztowy, a Bydgoszcz ma ich 679.
|
|
97
|
+
|
|
98
|
+
Miejscowość jest identyfikowana nazwą **i gminą**, nie samą nazwą. W Polsce jest
|
|
99
|
+
119 wsi „Nowa Wieś", z czego 28 w jednym województwie; sklejenie ich po nazwie
|
|
100
|
+
wystawia punkt w polu, nawet 158 km od najdalszej z nich.
|
|
101
|
+
|
|
102
|
+
### Import do bazy
|
|
103
|
+
|
|
104
|
+
Surowe rekordy można importować partiami bez wczytywania całego pliku:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
ZipCodes::PL.each_record.each_slice(1000) do |batch|
|
|
108
|
+
PostalCode.upsert_all(batch.map(&:to_h))
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Albo zapisać zagregowane miejscowości:
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
ZipCodes::PL.cities.each_slice(1000) do |batch|
|
|
116
|
+
City.upsert_all(
|
|
117
|
+
batch.map do |city|
|
|
118
|
+
{ name: city.name, province: city.voivodeship,
|
|
119
|
+
latitude: city.latitude, longitude: city.longitude }
|
|
120
|
+
end,
|
|
121
|
+
unique_by: %i[name province]
|
|
122
|
+
)
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Format pliku
|
|
127
|
+
|
|
128
|
+
`zip-codes-pl.tsv` - TSV z nagłówkiem, obok `zip-codes-pl.manifest.json` z
|
|
129
|
+
ETagiem źródła, datą budowy i liczbą wierszy. Celowo nieskompresowany: plik jest
|
|
130
|
+
commitowany, więc odświeżenie ma dawać diff do przejrzenia, a nie nowy nieczytelny
|
|
131
|
+
blob. Kolumny:
|
|
132
|
+
|
|
133
|
+
| kolumna | przykład | uwagi |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `postal_code` | `86-010` | |
|
|
136
|
+
| `city` | `Nowa Wieś Wielka` | |
|
|
137
|
+
| `voivodeship` | `kujawsko-pomorskie` | nazwa z tego gema, nie ze źródła |
|
|
138
|
+
| `voivodeship_teryt` | `04` | urzędowy kod TERYT |
|
|
139
|
+
| `county` / `county_teryt` | `powiat bydgoski` / `0403` | nazwa z Poczty Polskiej po kodzie TERYT |
|
|
140
|
+
| `commune` / `commune_teryt` | `Koronowo` / `040304` | nazwa z Poczty Polskiej po kodzie TERYT |
|
|
141
|
+
| `latitude` / `longitude` | `53.3123` / `17.9539` | WGS84 |
|
|
142
|
+
| `accuracy` | `6` | pole źródła - patrz niżej |
|
|
143
|
+
|
|
144
|
+
Plik jest zapisywany przez plik tymczasowy i `rename`. Błąd pobierania GeoNames,
|
|
145
|
+
odpowiedzi Poczty Polskiej, parsowania albo walidacji zostawia poprzedni TSV i
|
|
146
|
+
manifest bez zmian.
|
|
147
|
+
|
|
148
|
+
## Czego ten gem nie udaje
|
|
149
|
+
|
|
150
|
+
**To są centroidy kodów pocztowych, a nie punkty z pomiaru.** GeoNames wylicza
|
|
151
|
+
wiele z nich algorytmicznie z nazw miejscowości, a tam gdzie nie znajdzie
|
|
152
|
+
dopasowania, bierze średnią sąsiednich kodów. Nadają się do sortowania po
|
|
153
|
+
odległości i do mapy; nie są współrzędnymi geodezyjnymi. Kolumna `accuracy`
|
|
154
|
+
pochodzi ze źródła i dla Polski ma obecnie wartość `6` we **wszystkich** 72 899
|
|
155
|
+
wierszach, więc nie niesie żadnej informacji.
|
|
156
|
+
|
|
157
|
+
**To jest zbiór pocztowy, nie urzędowy rejestr miejscowości.** Obejmuje
|
|
158
|
+
miejscowości, które mają kod pocztowy w zrzucie GeoNames, w pisowni GeoNames.
|
|
159
|
+
Urzędowy TERYT SIMC jest większy i to on rozstrzyga o nazwach; różnicy między
|
|
160
|
+
nimi tutaj nie zmierzyłem.
|
|
161
|
+
|
|
162
|
+
**Nazwy administracyjne są ujednolicone po kodach TERYT.** Gem ma własną tabelę
|
|
163
|
+
16 województw, a podczas odświeżenia pobiera z Poczty Polskiej aktualne nazwy
|
|
164
|
+
powiatów i gmin. Jedynym jawnym wyjątkiem jest historyczny kod `320304` dawnej
|
|
165
|
+
gminy Ostrowice, zniesionej 1 stycznia 2019, który nadal występuje w GeoNames.
|
|
166
|
+
**Pewnym identyfikatorem pozostają kolumny `*_teryt`**, nie nazwy.
|
|
167
|
+
|
|
168
|
+
Gem nie zawiera ulic ani numerów budynków i nie jest geokoderem adresów. Do
|
|
169
|
+
adresu z dokładnością do numeru służy darmowe UUG GUGiK
|
|
170
|
+
(`services.gugik.gov.pl/uug`).
|
|
171
|
+
|
|
172
|
+
## Źródło i licencja
|
|
173
|
+
|
|
174
|
+
Dane PNA i współrzędne pochodzą z [GeoNames](https://www.geonames.org) na
|
|
175
|
+
licencji **CC BY 4.0**. Nazwy powiatów i gmin są pobierane z publicznej
|
|
176
|
+
wyszukiwarki [Poczty Polskiej](https://www.poczta-polska.pl/znajdz-kod-pocztowy/).
|
|
177
|
+
Manifest niesie gotową formułkę atrybucji.
|
|
178
|
+
|
|
179
|
+
Sam kod jest na licencji MIT.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"source_url": "https://download.geonames.org/export/zip/PL.zip",
|
|
3
|
+
"attribution": "GeoNames (https://www.geonames.org), CC BY 4.0; county and commune names: Poczta Polska (https://www.poczta-polska.pl)",
|
|
4
|
+
"etag": "\"f1225-65b68b320a969\"",
|
|
5
|
+
"last_modified": "Mon, 14 Sep 2026 03:05:00 GMT",
|
|
6
|
+
"row_count": 72899,
|
|
7
|
+
"built_at": "2026-09-14T23:25:36Z",
|
|
8
|
+
"gem_version": "0.1.1",
|
|
9
|
+
"format_version": 1
|
|
10
|
+
}
|