adressevaelger 0.1.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 +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +142 -0
- data/adressevaelger.gemspec +33 -0
- data/lib/adressevaelger/client.rb +223 -0
- data/lib/adressevaelger/error.rb +7 -0
- data/lib/adressevaelger/etrs89_to_wgs84.rb +38 -0
- data/lib/adressevaelger/resolved_address.rb +22 -0
- data/lib/adressevaelger/suggestion.rb +17 -0
- data/lib/adressevaelger/version.rb +5 -0
- data/lib/adressevaelger.rb +15 -0
- metadata +59 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 39ce3fca5b50b066546e6325522fb11677182fc4cf9a766a6baa138c937e0860
|
|
4
|
+
data.tar.gz: d01d7d4289c3a52997c744bade5d6c094f398b52547a7135964c06d87774dcc8
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 362d1a947156be0ecf9f8914596388136382e30748fd62289e13eaf05a836584092a9abd70bc86f9bb37a065451699e4ca310c8a0441f7fddcfdefc8d4528226
|
|
7
|
+
data.tar.gz: 875591926ea41fcfb4ada343652cd567c47a4d43e668f994e853318278a6641196daaececc21921c585fc23dbff357732139d98f6ff44d5d27931acb0c79975f
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial public release: Adressevælger client (`autocomplete` / `search`),
|
|
6
|
+
resolve by husnummer or adresse id, Adressevask `validate` / `vask`, and
|
|
7
|
+
optional ETRS89→WGS84 via soft-required `rgeo-proj4`.
|
|
8
|
+
- VCR cassettes for husnummer search and resolve (tokens redacted).
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rasmus Bergholdt
|
|
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.
|
data/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Adressevaelger
|
|
2
|
+
|
|
3
|
+
Ruby client for Klimadatastyrelsen **Adressevælger** and **Adressevask**
|
|
4
|
+
(Danmarks Adresseregister / DAR). Denmark only — no multi-country router,
|
|
5
|
+
manual entry adapter, or commercial geocoder fallback.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# Gemfile
|
|
11
|
+
gem "adressevaelger"
|
|
12
|
+
|
|
13
|
+
# Optional: fill ResolvedAddress#latitude / #longitude from ETRS89
|
|
14
|
+
gem "rgeo-proj4"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
bundle add adressevaelger
|
|
19
|
+
# or: gem install adressevaelger
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires Ruby 3.3+. No Rails dependency.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
require "adressevaelger"
|
|
28
|
+
|
|
29
|
+
client = Adressevaelger::Client.new(
|
|
30
|
+
token: ENV.fetch("ADRESSEVAELGER_TOKEN") # or rely on demo default locally
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Autocomplete / search (min 3 characters)
|
|
34
|
+
suggestions = client.autocomplete("Vestergade 1")
|
|
35
|
+
suggestions.first.id # => DAR husnummer id
|
|
36
|
+
suggestions.first.label # => "Vestergade 1, 1456 København K"
|
|
37
|
+
|
|
38
|
+
# Resolve husnummer or adresse by id
|
|
39
|
+
resolved = client.resolve(id: suggestions.first.id, type: "husnummer")
|
|
40
|
+
resolved.line1 # => "Vestergade 1"
|
|
41
|
+
resolved.postal_code # => "1456"
|
|
42
|
+
resolved.coord_easting # => ETRS89/UTM32N
|
|
43
|
+
resolved.latitude # => WGS84 if rgeo-proj4 is installed, else nil
|
|
44
|
+
|
|
45
|
+
# Adressevask (free-text wash)
|
|
46
|
+
washed = client.validate("vestergade 1 københavn")
|
|
47
|
+
# alias: client.vask(...)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Environment variables:
|
|
51
|
+
|
|
52
|
+
| Variable | Purpose |
|
|
53
|
+
| --- | --- |
|
|
54
|
+
| `ADRESSEVAELGER_TOKEN` | API token (preferred over the demo default) |
|
|
55
|
+
| `ADRESSEVAELGER_BASE_URL` | Override host (default `https://adressevaelger.dk`) |
|
|
56
|
+
|
|
57
|
+
## Optional ETRS89 → WGS84
|
|
58
|
+
|
|
59
|
+
`Adressevaelger::Etrs89ToWgs84` soft-requires [`rgeo-proj4`](https://github.com/rgeo/rgeo-proj4).
|
|
60
|
+
If PROJ is unavailable, `resolve` still returns ETRS89 easting/northing and leaves
|
|
61
|
+
WGS84 latitude/longitude `nil`.
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
Adressevaelger::Etrs89ToWgs84.available? # => true/false
|
|
65
|
+
lat, lon = Adressevaelger::Etrs89ToWgs84.call(easting: 724_533.07, northing: 6_176_026.84)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Token notes
|
|
69
|
+
|
|
70
|
+
The Adressevælger HTTP API requires a `token` query parameter. Until Brugerstyring
|
|
71
|
+
lands, Klimadatastyrelsen documents a shared demo token for exploration. The gem
|
|
72
|
+
falls back to that demo token when none is configured — **do not rely on it in
|
|
73
|
+
production**. Obtain a proper token via [Dataforsyningen](https://dataforsyningen.dk/).
|
|
74
|
+
|
|
75
|
+
## Testing
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
bundle exec rake test
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
HTTP integration tests use [VCR](https://github.com/vcr/vcr) cassettes recorded
|
|
82
|
+
against the real API (tokens redacted). CI uses `record: :none` (fail-closed).
|
|
83
|
+
To refresh cassettes locally:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
VCR_RECORD=all bundle exec rake test TEST=test/client_vcr_test.rb
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Never hand-write cassette response bodies.
|
|
90
|
+
|
|
91
|
+
## Scope
|
|
92
|
+
|
|
93
|
+
**In:** Adressevælger search/autocomplete, resolve by husnummer/adresse id,
|
|
94
|
+
Adressevask validate, optional PROJ transform.
|
|
95
|
+
|
|
96
|
+
**Out:** Multi-country routing, Manual adapters, commercial fallback geocoders,
|
|
97
|
+
Rails controllers/Stimulus, CRM persistence helpers.
|
|
98
|
+
|
|
99
|
+
## Attribution
|
|
100
|
+
|
|
101
|
+
Address data via [Dataforsyningen](https://dataforsyningen.dk/) /
|
|
102
|
+
Klimadatastyrelsen Adressevælger and Adressevask. This gem is an independent
|
|
103
|
+
open-source client and is not affiliated with or endorsed by Klimadatastyrelsen.
|
|
104
|
+
|
|
105
|
+
## Releasing
|
|
106
|
+
|
|
107
|
+
Version bumps are intentional — CI does not auto-bump on every `main` push.
|
|
108
|
+
|
|
109
|
+
1. Bump `Adressevaelger::VERSION` in `lib/adressevaelger/version.rb` (gemspec reads it).
|
|
110
|
+
2. Update `CHANGELOG.md` for that version.
|
|
111
|
+
3. Commit and push to `main`.
|
|
112
|
+
4. Tag and push: `git tag vX.Y.Z && git push origin vX.Y.Z`
|
|
113
|
+
(or create a GitHub Release for `vX.Y.Z` — that also pushes the tag).
|
|
114
|
+
5. Tag **must** match the gem version (`v0.1.0` ↔ `0.1.0`). The
|
|
115
|
+
[push_gem](.github/workflows/push_gem.yml) workflow verifies this, then
|
|
116
|
+
publishes via [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/)
|
|
117
|
+
(`rubygems/release-gem`, OIDC — no `RUBYGEMS_API_KEY` secret).
|
|
118
|
+
|
|
119
|
+
### One-time RubyGems Trusted Publisher setup
|
|
120
|
+
|
|
121
|
+
The gem is not on RubyGems yet, so use a **pending** trusted publisher
|
|
122
|
+
(Rasmus must click through the UI; no credentials belong in this repo):
|
|
123
|
+
|
|
124
|
+
1. Sign in at [rubygems.org](https://rubygems.org/) (MFA on).
|
|
125
|
+
2. Open [Pending trusted publishers](https://rubygems.org/profile/oidc/pending_trusted_publishers).
|
|
126
|
+
3. Click **Create**.
|
|
127
|
+
4. Fill in:
|
|
128
|
+
- **Gem name:** `adressevaelger`
|
|
129
|
+
- **Repository owner:** `bergholdt`
|
|
130
|
+
- **Repository name:** `adressevaelger`
|
|
131
|
+
- **Workflow filename:** `push_gem.yml`
|
|
132
|
+
- **Environment name:** `release`
|
|
133
|
+
5. Click **Create Pending trusted publisher**.
|
|
134
|
+
6. In GitHub → repo **Settings → Environments**, create an environment named
|
|
135
|
+
`release` (no required reviewers needed for a solo maintainer).
|
|
136
|
+
|
|
137
|
+
After the first successful tag-triggered publish, the pending publisher becomes
|
|
138
|
+
a normal trusted publisher and you own the gem on RubyGems.org.
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT. See [LICENSE.txt](LICENSE.txt).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/adressevaelger/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "adressevaelger"
|
|
7
|
+
spec.version = Adressevaelger::VERSION
|
|
8
|
+
spec.authors = ["Rasmus Bergholdt"]
|
|
9
|
+
spec.email = ["rasmus.bergholdt@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Ruby client for Klimadatastyrelsen Adressevælger and Adressevask (DAR)."
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
HTTP client for the Danish Adressevælger autocomplete/search and Adressevask
|
|
14
|
+
address-wash APIs (Danmarks Adresseregister / DAR), with an optional
|
|
15
|
+
ETRS89/UTM32N → WGS84 helper via PROJ.
|
|
16
|
+
DESC
|
|
17
|
+
spec.homepage = "https://github.com/bergholdt/adressevaelger"
|
|
18
|
+
spec.license = "MIT"
|
|
19
|
+
spec.required_ruby_version = ">= 3.3.0"
|
|
20
|
+
|
|
21
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
22
|
+
spec.metadata["source_code_uri"] = "https://github.com/bergholdt/adressevaelger/tree/v#{spec.version}"
|
|
23
|
+
spec.metadata["changelog_uri"] = "https://github.com/bergholdt/adressevaelger/blob/v#{spec.version}/CHANGELOG.md"
|
|
24
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/bergholdt/adressevaelger/issues"
|
|
25
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
26
|
+
|
|
27
|
+
spec.files = Dir.chdir(__dir__) do
|
|
28
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
29
|
+
f.start_with?(*%w[bin/ test/ .git .github Gemfile Rakefile])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
spec.require_paths = ["lib"]
|
|
33
|
+
end
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
require_relative "suggestion"
|
|
8
|
+
require_relative "resolved_address"
|
|
9
|
+
|
|
10
|
+
module Adressevaelger
|
|
11
|
+
# HTTP client for Klimadatastyrelsen Adressevælger (+ Adressevask).
|
|
12
|
+
#
|
|
13
|
+
# Official host is adressevaelger.dk (api.adressevaelger.dk does not resolve).
|
|
14
|
+
class Client
|
|
15
|
+
# Token is required by the API. Until Brugerstyring lands, KDS recommends
|
|
16
|
+
# this shared demo token for exploration — replace it in production.
|
|
17
|
+
DEFAULT_TOKEN = "adressevaelger123"
|
|
18
|
+
DEFAULT_BASE_URL = "https://adressevaelger.dk"
|
|
19
|
+
EPSG = 25832
|
|
20
|
+
|
|
21
|
+
attr_reader :base_url, :token
|
|
22
|
+
|
|
23
|
+
def initialize(token: nil, base_url: nil, http: nil)
|
|
24
|
+
@token = present_string(token) || present_string(ENV["ADRESSEVAELGER_TOKEN"]) || DEFAULT_TOKEN
|
|
25
|
+
@base_url = present_string(base_url) || present_string(ENV["ADRESSEVAELGER_BASE_URL"]) || DEFAULT_BASE_URL
|
|
26
|
+
@http = http
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Autocomplete husnumre by free-text query (min 3 characters).
|
|
30
|
+
def autocomplete(query)
|
|
31
|
+
return [] if blank?(query) || query.to_s.length < 3
|
|
32
|
+
|
|
33
|
+
payload = get_json("/husnumre/soeg", tekst: query)
|
|
34
|
+
Array(payload["fund"]).filter_map do |fund|
|
|
35
|
+
next unless present?(fund["id"]) && present?(fund["titel"])
|
|
36
|
+
|
|
37
|
+
Suggestion.from_fund(fund)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Alias used by callers that think in "search" terms.
|
|
42
|
+
alias search autocomplete
|
|
43
|
+
|
|
44
|
+
# Resolve a husnummer or adresse by DAR id.
|
|
45
|
+
def resolve(id:, type: "husnummer")
|
|
46
|
+
path = case type.to_s
|
|
47
|
+
when "adresse" then "/adresser/#{id}"
|
|
48
|
+
else "/husnumre/#{id}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
payload = get_json(path)
|
|
52
|
+
build_resolved(payload, type: type.to_s)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Wash / validate a free-text address via Adressevask (`/vask/`).
|
|
56
|
+
def validate(text)
|
|
57
|
+
return [] if blank?(text)
|
|
58
|
+
|
|
59
|
+
payload = get_json("/vask/", adresse: text)
|
|
60
|
+
results = Array(payload["resultater"] || payload["fund"] || payload["adresser"])
|
|
61
|
+
results.filter_map do |row|
|
|
62
|
+
id = row["adresse_id_lokalid"] || row["id"] || dig_hash(row, "adresse", "id")
|
|
63
|
+
label = row["betegnelse"] || row["titel"] || row["adressebetegnelse"]
|
|
64
|
+
next if blank?(id) || blank?(label)
|
|
65
|
+
|
|
66
|
+
Suggestion.new(
|
|
67
|
+
id: id,
|
|
68
|
+
type: present_string(row["type"]) || "adresse",
|
|
69
|
+
label: label,
|
|
70
|
+
provider: Adressevaelger::PROVIDER
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Alias matching the Adressevask product name.
|
|
76
|
+
alias vask validate
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
attr_reader :http
|
|
81
|
+
|
|
82
|
+
def build_resolved(payload, type:)
|
|
83
|
+
data = nested_entity(payload)
|
|
84
|
+
vejnavn = scalar(pick(data, "vejnavn")) || dig_hash(data, "vejstykke", "navn") || dig_hash(data, "navngivenvej", "vejnavn")
|
|
85
|
+
husnr = scalar(pick(data, "husnr", "husnummertekst", "husnummer"))
|
|
86
|
+
postal = scalar(pick(data, "postnr")) || dig_hash(data, "postnummer", "postnr") || dig_hash(data, "postnummer", "nr")
|
|
87
|
+
city = scalar(pick(data, "postnrnavn", "bynavn")) || dig_hash(data, "postnummer", "navn")
|
|
88
|
+
etage = scalar(pick(data, "etagebetegnelse", "etage"))
|
|
89
|
+
doer = scalar(pick(data, "doerbetegnelse", "dør", "doer"))
|
|
90
|
+
|
|
91
|
+
point = data["adgangspunkt"] || dig_hash(data, "adgangsadresse", "adgangspunkt") || data["position"]
|
|
92
|
+
easting, northing = extract_etrs89(point)
|
|
93
|
+
latitude, longitude = transform_etrs89(easting, northing)
|
|
94
|
+
|
|
95
|
+
id = pick(data, "id", "id_lokalid")
|
|
96
|
+
husnummer_id = pick(data, "husnummer_id", "adgangsadresseid") || (type == "husnummer" ? id : nil)
|
|
97
|
+
adresse_id = type == "adresse" ? id : pick(data, "adresse_id")
|
|
98
|
+
|
|
99
|
+
ResolvedAddress.new(
|
|
100
|
+
line1: [vejnavn, husnr].compact.reject { |part| blank?(part) }.join(" "),
|
|
101
|
+
line2: nil,
|
|
102
|
+
postal_code: postal.to_s,
|
|
103
|
+
city: city.to_s,
|
|
104
|
+
country: "DK",
|
|
105
|
+
etage: etage,
|
|
106
|
+
doer: doer,
|
|
107
|
+
address_provider: Adressevaelger::PROVIDER,
|
|
108
|
+
external_address_id: adresse_id || husnummer_id,
|
|
109
|
+
external_building_id: husnummer_id,
|
|
110
|
+
latitude: latitude,
|
|
111
|
+
longitude: longitude,
|
|
112
|
+
coord_easting: easting,
|
|
113
|
+
coord_northing: northing,
|
|
114
|
+
coord_epsg: easting ? EPSG : nil
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def nested_entity(payload)
|
|
119
|
+
%w[data husnummer adresse].each do |key|
|
|
120
|
+
value = payload[key]
|
|
121
|
+
return value if value.is_a?(Hash)
|
|
122
|
+
end
|
|
123
|
+
payload
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def pick(hash, *keys)
|
|
127
|
+
keys.each do |key|
|
|
128
|
+
value = hash[key]
|
|
129
|
+
return value if present?(value)
|
|
130
|
+
end
|
|
131
|
+
nil
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def dig_hash(hash, *keys)
|
|
135
|
+
return nil unless hash.is_a?(Hash)
|
|
136
|
+
|
|
137
|
+
hash.dig(*keys)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def scalar(value)
|
|
141
|
+
return nil if blank?(value) || value.is_a?(Hash) || value.is_a?(Array)
|
|
142
|
+
|
|
143
|
+
value
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def extract_etrs89(point)
|
|
147
|
+
return [nil, nil] if blank?(point)
|
|
148
|
+
|
|
149
|
+
if point.is_a?(Array) && point.size >= 2
|
|
150
|
+
return [point[0].to_f, point[1].to_f]
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
coords = point["koordinater"] || point["coordinates"] || point["position"] || dig_hash(point, "geometri", "coordinates")
|
|
154
|
+
if coords.is_a?(Array) && coords.size >= 2
|
|
155
|
+
return [coords[0].to_f, coords[1].to_f]
|
|
156
|
+
end
|
|
157
|
+
if coords.is_a?(Hash)
|
|
158
|
+
easting = coords["x"] || coords["øst"] || coords["oest"] || coords["easting"]
|
|
159
|
+
northing = coords["y"] || coords["nord"] || coords["northing"]
|
|
160
|
+
return [easting.to_f, northing.to_f] if present?(easting) && present?(northing)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
easting = point["øst"] || point["oest"] || point["easting"] || point["x"]
|
|
164
|
+
northing = point["nord"] || point["northing"] || point["y"]
|
|
165
|
+
return [nil, nil] if blank?(easting) || blank?(northing)
|
|
166
|
+
|
|
167
|
+
[easting.to_f, northing.to_f]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Wired when optional rgeo-proj4 / PROJ is available; otherwise leave WGS84 nil.
|
|
171
|
+
def transform_etrs89(easting, northing)
|
|
172
|
+
return [nil, nil] if easting.nil? || northing.nil?
|
|
173
|
+
|
|
174
|
+
Adressevaelger::Etrs89ToWgs84.call(easting: easting, northing: northing) || [nil, nil]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def get_json(path, params = {})
|
|
178
|
+
uri = URI.join("#{base_url}/", path.delete_prefix("/"))
|
|
179
|
+
query = params.merge(token: token).compact
|
|
180
|
+
uri.query = URI.encode_www_form(query)
|
|
181
|
+
|
|
182
|
+
request = Net::HTTP::Get.new(uri)
|
|
183
|
+
request["Accept"] = "application/json"
|
|
184
|
+
request["User-Agent"] = "Adressevaelger/#{VERSION}"
|
|
185
|
+
|
|
186
|
+
response = perform(uri, request)
|
|
187
|
+
unless response.is_a?(Net::HTTPSuccess)
|
|
188
|
+
raise ProviderError, "Adressevælger error (#{response.code})"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
JSON.parse(response.body)
|
|
192
|
+
rescue JSON::ParserError
|
|
193
|
+
raise ProviderError, "Adressevælger returned invalid JSON"
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def perform(uri, request)
|
|
197
|
+
if http
|
|
198
|
+
return http.call(uri, request)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", open_timeout: 5, read_timeout: 10) do |client|
|
|
202
|
+
client.request(request)
|
|
203
|
+
end
|
|
204
|
+
rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, SocketError, Socket::ResolutionError => e
|
|
205
|
+
raise ProviderError, "Adressevælger unreachable (#{e.class})"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def blank?(value)
|
|
209
|
+
value.nil? || (value.respond_to?(:empty?) && value.empty?) ||
|
|
210
|
+
(value.is_a?(String) && value.strip.empty?)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def present?(value)
|
|
214
|
+
!blank?(value)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def present_string(value)
|
|
218
|
+
return nil if blank?(value)
|
|
219
|
+
|
|
220
|
+
value.to_s
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Adressevaelger
|
|
4
|
+
# ETRS89/UTM32N (EPSG:25832) → WGS84 (EPSG:4326) via PROJ (`rgeo-proj4`).
|
|
5
|
+
#
|
|
6
|
+
# Soft-requires `rgeo/proj4`. Returns `nil` when the optional gem is not
|
|
7
|
+
# installed or PROJ cannot load; resolve still returns ETRS89 easting/northing.
|
|
8
|
+
module Etrs89ToWgs84
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def available?
|
|
12
|
+
return @available if defined?(@available)
|
|
13
|
+
|
|
14
|
+
begin
|
|
15
|
+
require "rgeo/proj4"
|
|
16
|
+
@source = RGeo::CoordSys::Proj4.create(25832)
|
|
17
|
+
@target = RGeo::CoordSys::Proj4.create(4326)
|
|
18
|
+
@available = true
|
|
19
|
+
rescue LoadError, StandardError
|
|
20
|
+
@available = false
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(easting:, northing:)
|
|
25
|
+
return nil unless available?
|
|
26
|
+
|
|
27
|
+
lon, lat = RGeo::CoordSys::Proj4.transform_coords(
|
|
28
|
+
@source,
|
|
29
|
+
@target,
|
|
30
|
+
easting.to_f,
|
|
31
|
+
northing.to_f,
|
|
32
|
+
nil
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
[lat.round(7), lon.round(7)]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Adressevaelger
|
|
4
|
+
# Provider-shaped resolved Danish address (no host-app persistence helpers).
|
|
5
|
+
ResolvedAddress = Data.define(
|
|
6
|
+
:line1,
|
|
7
|
+
:line2,
|
|
8
|
+
:postal_code,
|
|
9
|
+
:city,
|
|
10
|
+
:country,
|
|
11
|
+
:etage,
|
|
12
|
+
:doer,
|
|
13
|
+
:address_provider,
|
|
14
|
+
:external_address_id,
|
|
15
|
+
:external_building_id,
|
|
16
|
+
:latitude,
|
|
17
|
+
:longitude,
|
|
18
|
+
:coord_easting,
|
|
19
|
+
:coord_northing,
|
|
20
|
+
:coord_epsg
|
|
21
|
+
)
|
|
22
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Adressevaelger
|
|
4
|
+
PROVIDER = "adressevaelger"
|
|
5
|
+
|
|
6
|
+
# Autocomplete / vask hit from Adressevælger or Adressevask.
|
|
7
|
+
Suggestion = Data.define(:id, :type, :label, :provider) do
|
|
8
|
+
def self.from_fund(fund)
|
|
9
|
+
new(
|
|
10
|
+
id: fund.fetch("id"),
|
|
11
|
+
type: fund.fetch("type"),
|
|
12
|
+
label: fund.fetch("titel"),
|
|
13
|
+
provider: Adressevaelger::PROVIDER
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "adressevaelger/version"
|
|
4
|
+
require_relative "adressevaelger/error"
|
|
5
|
+
require_relative "adressevaelger/suggestion"
|
|
6
|
+
require_relative "adressevaelger/resolved_address"
|
|
7
|
+
require_relative "adressevaelger/etrs89_to_wgs84"
|
|
8
|
+
require_relative "adressevaelger/client"
|
|
9
|
+
|
|
10
|
+
# Ruby client for Klimadatastyrelsen Adressevælger + Adressevask (DAR).
|
|
11
|
+
#
|
|
12
|
+
# Denmark only. Country routing, manual entry, and commercial geocoders belong
|
|
13
|
+
# in the host application.
|
|
14
|
+
module Adressevaelger
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: adressevaelger
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Rasmus Bergholdt
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: |
|
|
13
|
+
HTTP client for the Danish Adressevælger autocomplete/search and Adressevask
|
|
14
|
+
address-wash APIs (Danmarks Adresseregister / DAR), with an optional
|
|
15
|
+
ETRS89/UTM32N → WGS84 helper via PROJ.
|
|
16
|
+
email:
|
|
17
|
+
- rasmus.bergholdt@gmail.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE.txt
|
|
24
|
+
- README.md
|
|
25
|
+
- adressevaelger.gemspec
|
|
26
|
+
- lib/adressevaelger.rb
|
|
27
|
+
- lib/adressevaelger/client.rb
|
|
28
|
+
- lib/adressevaelger/error.rb
|
|
29
|
+
- lib/adressevaelger/etrs89_to_wgs84.rb
|
|
30
|
+
- lib/adressevaelger/resolved_address.rb
|
|
31
|
+
- lib/adressevaelger/suggestion.rb
|
|
32
|
+
- lib/adressevaelger/version.rb
|
|
33
|
+
homepage: https://github.com/bergholdt/adressevaelger
|
|
34
|
+
licenses:
|
|
35
|
+
- MIT
|
|
36
|
+
metadata:
|
|
37
|
+
homepage_uri: https://github.com/bergholdt/adressevaelger
|
|
38
|
+
source_code_uri: https://github.com/bergholdt/adressevaelger/tree/v0.1.0
|
|
39
|
+
changelog_uri: https://github.com/bergholdt/adressevaelger/blob/v0.1.0/CHANGELOG.md
|
|
40
|
+
bug_tracker_uri: https://github.com/bergholdt/adressevaelger/issues
|
|
41
|
+
rubygems_mfa_required: 'true'
|
|
42
|
+
rdoc_options: []
|
|
43
|
+
require_paths:
|
|
44
|
+
- lib
|
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: 3.3.0
|
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
requirements: []
|
|
56
|
+
rubygems_version: 4.0.20
|
|
57
|
+
specification_version: 4
|
|
58
|
+
summary: Ruby client for Klimadatastyrelsen Adressevælger and Adressevask (DAR).
|
|
59
|
+
test_files: []
|