swissmatch-location 0.0.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.
- data/LICENSE.txt +8 -0
- data/README.markdown +96 -0
- data/Rakefile +10 -0
- data/data/swissmatch/plz_c_20120723.txt +2498 -0
- data/data/swissmatch/plz_p1_20120723.txt +5331 -0
- data/data/swissmatch/plz_p2_20120723.txt +1828 -0
- data/lib/swissmatch/canton.rb +84 -0
- data/lib/swissmatch/cantons.rb +121 -0
- data/lib/swissmatch/communities.rb +126 -0
- data/lib/swissmatch/community.rb +88 -0
- data/lib/swissmatch/datafiles.rb +333 -0
- data/lib/swissmatch/loaderror.rb +26 -0
- data/lib/swissmatch/location/autoload.rb +8 -0
- data/lib/swissmatch/location/ruby.rb +43 -0
- data/lib/swissmatch/location/version.rb +15 -0
- data/lib/swissmatch/location.rb +249 -0
- data/lib/swissmatch/name.rb +47 -0
- data/lib/swissmatch/zip.rb +40 -0
- data/lib/swissmatch/zipcode.rb +303 -0
- data/lib/swissmatch/zipcodes.rb +255 -0
- data/swissmatch-location.gemspec +44 -0
- data/test/data/plz_c_20120000.txt +2510 -0
- data/test/data/plz_p1_20120000.txt +5333 -0
- data/test/data/plz_p1up_20120000.txt +29 -0
- data/test/data/plz_p2_20120000.txt +1830 -0
- data/test/lib/helper.rb +38 -0
- data/test/lib/test/swissmatch_location_fixtures.rb +6 -0
- data/test/runner.rb +20 -0
- data/test/unit/lib/swissmatch/location.rb +10 -0
- data/test/unit/lib/swissmatch/zip_codes.rb +21 -0
- metadata +112 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
require 'swissmatch/loaderror'
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
module SwissMatch
|
10
|
+
|
11
|
+
# Used to indicate an error while loading the swissmatch data.
|
12
|
+
class LoadError < StandardError
|
13
|
+
|
14
|
+
# Data associated with the error
|
15
|
+
attr_reader :data
|
16
|
+
|
17
|
+
# @param [String] message
|
18
|
+
# Same as Exception#initialize, the message of the exception
|
19
|
+
# @param [Object] data
|
20
|
+
# Arbitrary data
|
21
|
+
def initialize(message, data)
|
22
|
+
super(message)
|
23
|
+
@data = data
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Require this file to automatically load swissmatch location data
|
4
|
+
# It will try to load from a try the directory in SWISSMATCH_DATA
|
5
|
+
# If the env variable SWISSMATCH_DATA is not set, it'll try the data dir in the gem.
|
6
|
+
|
7
|
+
require 'swissmatch/location'
|
8
|
+
SwissMatch.load
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file provides a couple of ruby monkey patches.
|
3
|
+
|
4
|
+
module Enumerable
|
5
|
+
unless method_defined?(:last) then
|
6
|
+
|
7
|
+
# @example
|
8
|
+
# ary.last -> obj or nil
|
9
|
+
# ary.last(n) -> new_ary
|
10
|
+
#
|
11
|
+
# @return
|
12
|
+
# The last element(s) of self. If the enumerable is empty, the first form returns
|
13
|
+
# nil, the second an empty Array.
|
14
|
+
# The method is optimized to make use of reverse_each if present.
|
15
|
+
def last(n=nil)
|
16
|
+
reverse_each_method = method(:reverse_each)
|
17
|
+
has_reverse_each = reverse_each_method && reverse_each_method.owner != Enumerable # native reverse_each needed
|
18
|
+
if n then
|
19
|
+
return_value = []
|
20
|
+
if has_reverse_each then
|
21
|
+
reverse_each { |val|
|
22
|
+
return_value.unshift(val)
|
23
|
+
return return_value if return_value.size == n
|
24
|
+
}
|
25
|
+
else
|
26
|
+
each { |val|
|
27
|
+
return_value.push(val)
|
28
|
+
return_value.shift if return_value.size > n
|
29
|
+
}
|
30
|
+
end
|
31
|
+
else
|
32
|
+
if has_reverse_each then
|
33
|
+
reverse_each { |value| return value }
|
34
|
+
else
|
35
|
+
return_value = nil
|
36
|
+
each { |value| return_value = value }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
return_value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems/version' # newer rubygems use this
|
5
|
+
rescue LoadError
|
6
|
+
require 'gem/version' # older rubygems use this
|
7
|
+
end
|
8
|
+
|
9
|
+
module SwissMatch
|
10
|
+
module Location
|
11
|
+
|
12
|
+
# The version of the swissmatch-location gem.
|
13
|
+
Version = Gem::Version.new("0.0.1")
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
require 'date'
|
6
|
+
require 'swissmatch/canton'
|
7
|
+
require 'swissmatch/cantons'
|
8
|
+
require 'swissmatch/communities'
|
9
|
+
require 'swissmatch/community'
|
10
|
+
require 'swissmatch/datafiles'
|
11
|
+
require 'swissmatch/location/ruby'
|
12
|
+
require 'swissmatch/location/version'
|
13
|
+
require 'swissmatch/zipcode'
|
14
|
+
require 'swissmatch/zipcodes'
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
# From SwissMatch::Location
|
19
|
+
# Deal with swiss zip codes, cities, communities and cantons.
|
20
|
+
#
|
21
|
+
# @note
|
22
|
+
# All strings passed to SwissMatch are expected to be utf-8. All strings
|
23
|
+
# returned by SwissMatch are also in utf-8.
|
24
|
+
#
|
25
|
+
# @example Load the data
|
26
|
+
# require 'swissmatch'
|
27
|
+
# SwissMatch.load
|
28
|
+
# # alternatively, just require 'swissmatch/location/autoload'
|
29
|
+
#
|
30
|
+
# @example Get the ONRP for a given zip-code + city
|
31
|
+
# require 'swissmatch/location/autoload'
|
32
|
+
# SwissMatch.zip_code(8000, 'Zürich').ordering_number # => 4384
|
33
|
+
# SwissMatch.zip_code(4384).name # => "Zürich"(de, 0)
|
34
|
+
module SwissMatch
|
35
|
+
|
36
|
+
# This module only contains the version of the swissmatch-location gem
|
37
|
+
module Location
|
38
|
+
end
|
39
|
+
|
40
|
+
@data = nil
|
41
|
+
class <<self
|
42
|
+
# @return [SwissMatch::DataFiles, nil] The data source used
|
43
|
+
attr_reader :data
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param [String] name_or_plate
|
47
|
+
# The name or license_tag of the canton
|
48
|
+
#
|
49
|
+
# @return [SwissMatch::Canton]
|
50
|
+
# The canton with the given name or license_tag
|
51
|
+
def self.canton(name_or_plate)
|
52
|
+
@data.cantons[name_or_plate]
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [SwissMatch::Cantons]
|
56
|
+
# All known cantons
|
57
|
+
def self.cantons
|
58
|
+
@data.cantons
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param [Integer] key
|
62
|
+
# The community number of the community
|
63
|
+
#
|
64
|
+
# @return [SwissMatch::Community]
|
65
|
+
# The community with the community number
|
66
|
+
def self.community(key)
|
67
|
+
@data.communities.by_community_number(key)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param [String] name
|
71
|
+
# The name of the communities
|
72
|
+
#
|
73
|
+
# @return [SwissMatch::Communities]
|
74
|
+
# All communities, or those matching the given name
|
75
|
+
def self.communities(name=nil)
|
76
|
+
name ? @data.communities.by_name(name) : @data.communities
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param [String, Integer] code_or_name
|
80
|
+
# Either the 4 digit zip code as Integer or String, or the city name as a String in
|
81
|
+
# utf-8.
|
82
|
+
#
|
83
|
+
# @return [Array<SwissMatch::ZipCode>]
|
84
|
+
# A list of zip codes with the given code or name.
|
85
|
+
def self.zip_codes(code_or_name=nil)
|
86
|
+
case code_or_name
|
87
|
+
when Integer, /\A\d{4}\z/
|
88
|
+
@data.zip_codes.by_code(code_or_name.to_i)
|
89
|
+
when String
|
90
|
+
@data.zip_codes.by_name(code_or_name)
|
91
|
+
when nil
|
92
|
+
@data.zip_codes
|
93
|
+
else
|
94
|
+
raise ArgumentError, "Invalid argument, must be a ZipCode#code (Integer or String) or ZipCode#name (String)"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns a single zip code. A zip code can be uniquely identified by any of:
|
99
|
+
# * Its ordering_number (ONRP, a 4 digit Integer)
|
100
|
+
# * Its zip code (4 digit Integer) and add-on (2 digit Integer)
|
101
|
+
# * Its zip code (4 digit Integer) and any official name (String)
|
102
|
+
# The data can be passed in different ways, e.g. all numbers can be passed either
|
103
|
+
# as a String or as an Integer. The identification by zip code and add-on can be done
|
104
|
+
# by either using a combined 6 digit number (e.g. 800000 for "8000 Zürich"), or by
|
105
|
+
# passing 2 arguments, passing the zip code and the add-on separately.
|
106
|
+
#
|
107
|
+
# === IMPORTANT
|
108
|
+
# You must be aware, that passing a single 4-digit code to SwissMatch::zip_code uses
|
109
|
+
# the ONRP, and NOT the zip-code. The 4 digit zip code alone does NOT uniquely identify
|
110
|
+
# a zip code.
|
111
|
+
#
|
112
|
+
#
|
113
|
+
# @example Get a zip code by ONRP
|
114
|
+
# SwissMatch.zip_code(4384) # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
|
115
|
+
#
|
116
|
+
# @example Get a zip code by 4-digit code and add-on
|
117
|
+
# SwissMatch.zip_code(8000, 0) # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
|
118
|
+
# SwissMatch.zip_code("8000", "00") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
|
119
|
+
# SwissMatch.zip_code(800000) # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
|
120
|
+
# SwissMatch.zip_code("800000") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
|
121
|
+
#
|
122
|
+
# @example Get a zip code by 4-digit code and name
|
123
|
+
# SwissMatch.zip_code(8000, "Zürich") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
|
124
|
+
# SwissMatch.zip_code(8000, "Zurigo") # => #<SwissMatch::ZipCode:003ff996cf8d3c 8000 Zürich>
|
125
|
+
#
|
126
|
+
#
|
127
|
+
# @param [String, Integer] code
|
128
|
+
# The 4 digit zip code as Integer or String
|
129
|
+
# @param [String, Integer] city_or_add_on
|
130
|
+
# Either the 2 digit zip-code add-on as string or integer, or the city name as a
|
131
|
+
# String in utf-8.
|
132
|
+
#
|
133
|
+
# @return [SwissMatch::ZipCode]
|
134
|
+
# The zip codes with the given code and the given add-on or name.
|
135
|
+
def self.zip_code(code, city_or_add_on=nil)
|
136
|
+
case city_or_add_on
|
137
|
+
when nil
|
138
|
+
@data.zip_codes.by_ordering_number(code.to_i)
|
139
|
+
when Integer, /\A\d\d\z/
|
140
|
+
@data.zip_codes.by_code_and_add_on(code.to_i, city_or_add_on.to_i)
|
141
|
+
when String
|
142
|
+
@data.zip_codes.by_code_and_name(code.to_i, city_or_add_on)
|
143
|
+
else
|
144
|
+
raise ArgumentError, "Invalid second argument, must be nil, ZipCode#add_on or ZipCode#name"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param [String] name
|
149
|
+
# The name for which to return matching zip codes
|
150
|
+
#
|
151
|
+
# @return [Array<SwissMatch::ZipCode>]
|
152
|
+
# Zip codes whose name equals the given name
|
153
|
+
def self.city(name)
|
154
|
+
@data.zip_codes.by_name(name)
|
155
|
+
end
|
156
|
+
|
157
|
+
# @param [String, Integer] code
|
158
|
+
# The 4 digit zip code
|
159
|
+
# @param [nil, Array<Integer>] only_types
|
160
|
+
# An array of zip code types (see ZipCode#type) which the returned zip codes must match.
|
161
|
+
# @param [nil, Symbol] locale
|
162
|
+
# Return the names in the given locale, defaults to nil/:native (nil and :native are
|
163
|
+
# treated the same and will return the native names)
|
164
|
+
#
|
165
|
+
# @return [Array<String>]
|
166
|
+
# A list of unique names matching the parameters (4 digit code, type, locale).
|
167
|
+
def self.cities_for_zip_code(code, only_types=nil, locale=nil)
|
168
|
+
codes = @data.zip_codes.by_code(code.to_i)
|
169
|
+
return [] unless codes
|
170
|
+
codes = codes.select { |code| only_types.include?(code.type) } if only_types
|
171
|
+
names = case locale
|
172
|
+
when :native,nil then codes.map(&:name)
|
173
|
+
when :de then codes.map(&:name_de)
|
174
|
+
when :fr then codes.map(&:name_fr)
|
175
|
+
when :it then codes.map(&:name_it)
|
176
|
+
when :rt then codes.map(&:name_rt)
|
177
|
+
else raise ArgumentError, "Invalid locale #{locale}"
|
178
|
+
end
|
179
|
+
|
180
|
+
names.uniq
|
181
|
+
end
|
182
|
+
|
183
|
+
# Loads the swissmatch data
|
184
|
+
#
|
185
|
+
# @param [DataFiles] data_source
|
186
|
+
# A valid data-source.
|
187
|
+
#
|
188
|
+
# @return [self]
|
189
|
+
# Returns self
|
190
|
+
def self.load(data_source=nil)
|
191
|
+
@data = data_source || DataFiles.new
|
192
|
+
@data.load!
|
193
|
+
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
197
|
+
# @private
|
198
|
+
# Used to transliterate city names
|
199
|
+
Transliteration1 = {
|
200
|
+
"à" => "a",
|
201
|
+
"â" => "a",
|
202
|
+
"ä" => "a",
|
203
|
+
"è" => "e",
|
204
|
+
"é" => "e",
|
205
|
+
"ê" => "e",
|
206
|
+
"ë" => "e",
|
207
|
+
"ì" => "i",
|
208
|
+
"î" => "i",
|
209
|
+
"ï" => "i",
|
210
|
+
"ô" => "o",
|
211
|
+
"ö" => "o",
|
212
|
+
"ù" => "u",
|
213
|
+
"ü" => "u",
|
214
|
+
}
|
215
|
+
|
216
|
+
# @private
|
217
|
+
# Used to transliterate city names
|
218
|
+
Transliteration2 = Transliteration1.merge({
|
219
|
+
"ä" => "ae",
|
220
|
+
"ö" => "oe",
|
221
|
+
"ü" => "ue",
|
222
|
+
})
|
223
|
+
|
224
|
+
# @private
|
225
|
+
# Used to transliterate city names
|
226
|
+
TransMatch1 = /#{Transliteration1.keys.map { |k| Regexp.escape(k) }.join("|")}/
|
227
|
+
|
228
|
+
# @private
|
229
|
+
# Used to transliterate city names
|
230
|
+
TransMatch2 = /#{Transliteration2.keys.map { |k| Regexp.escape(k) }.join("|")}/
|
231
|
+
|
232
|
+
# @private
|
233
|
+
# Used to transliterate city names
|
234
|
+
def self.transliterate1(word)
|
235
|
+
word.gsub(TransMatch1, Transliteration1).delete("^ A-Za-z").downcase
|
236
|
+
end
|
237
|
+
|
238
|
+
# @private
|
239
|
+
# Used to transliterate city names
|
240
|
+
def self.transliterate2(word)
|
241
|
+
word.gsub(TransMatch2, Transliteration2).delete("^ A-Za-z").downcase
|
242
|
+
end
|
243
|
+
|
244
|
+
# @private
|
245
|
+
# Transliterates a string into the unique transliterated (1 & 2) word list
|
246
|
+
def self.transliterated_words(string)
|
247
|
+
"#{transliterate1(string)} #{transliterate2(string)}".split(" ").uniq
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module SwissMatch
|
6
|
+
|
7
|
+
# Adds a couple of properties to the String class.
|
8
|
+
# These properties are relevant to the naming of Cantons, Communities and
|
9
|
+
# ZipCodes. They provide information about the language in which the name is,
|
10
|
+
# and which sequence number that name has.
|
11
|
+
class Name < ::String
|
12
|
+
|
13
|
+
# @return [Integer] The sequence number of this name
|
14
|
+
attr_reader :sequence_number
|
15
|
+
|
16
|
+
# @return [Symbol] The language of this name (:de, :fr, :it or :rt)
|
17
|
+
attr_reader :language
|
18
|
+
|
19
|
+
# @param [String] name
|
20
|
+
# The name (self)
|
21
|
+
# @param [Symbol] language
|
22
|
+
# The language this name is in (:de, :fr, :it or :rt)
|
23
|
+
# @param [Integer] sequence_number
|
24
|
+
# The sequence number of this name
|
25
|
+
def initialize(name, language, sequence_number=0)
|
26
|
+
@language = language
|
27
|
+
@sequence_number = sequence_number
|
28
|
+
super(name.to_s)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Hash]
|
32
|
+
# All properties of the name as a hash.
|
33
|
+
def to_hash
|
34
|
+
{
|
35
|
+
:name => to_s,
|
36
|
+
:language => @language,
|
37
|
+
:sequence_number => @sequence_number,
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# @private
|
42
|
+
# @see Object#inspect
|
43
|
+
def inspect
|
44
|
+
"#{super}(#{@language}, #{@sequence_number})"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# As it seems, some zip files delivered by the post are corrupt as per rubyzips standard.
|
2
|
+
# This file contains a patch to handle the corruption.
|
3
|
+
# As it seems, the zipfiles don't report the "\n" in the zipfile-comment
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'zip/zip'
|
7
|
+
rescue LoadError
|
8
|
+
raise "To update the swissmatch datafiles, the rubyzip gem is required, `gem install rubyzip` or add it to your Gemfile."
|
9
|
+
end
|
10
|
+
|
11
|
+
# @private
|
12
|
+
# Patching the rubyzip gem
|
13
|
+
module Zip
|
14
|
+
|
15
|
+
# @private
|
16
|
+
# Patching the rubyzip gem
|
17
|
+
class ZipCentralDirectory
|
18
|
+
|
19
|
+
# @private
|
20
|
+
# Patching the rubyzip gem
|
21
|
+
def read_e_o_c_d(io) #:nodoc:
|
22
|
+
buf = get_e_o_c_d(io)
|
23
|
+
@numberOfThisDisk = ZipEntry::read_zip_short(buf)
|
24
|
+
@numberOfDiskWithStartOfCDir = ZipEntry::read_zip_short(buf)
|
25
|
+
@totalNumberOfEntriesInCDirOnThisDisk = ZipEntry::read_zip_short(buf)
|
26
|
+
@size = ZipEntry::read_zip_short(buf)
|
27
|
+
@sizeInBytes = ZipEntry::read_zip_long(buf)
|
28
|
+
@cdirOffset = ZipEntry::read_zip_long(buf)
|
29
|
+
commentLength = ZipEntry::read_zip_short(buf)
|
30
|
+
@comment = buf.read(commentLength)
|
31
|
+
|
32
|
+
# ORIGINAL
|
33
|
+
# raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
|
34
|
+
|
35
|
+
# PATCH, doing it in a similar fashion as Archive::Zip in perl does
|
36
|
+
raise ZipError, "Zip consistency problem while reading eocd structure" if @comment.bytesize != commentLength
|
37
|
+
# /PATCH
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,303 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
require 'swissmatch/name'
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
module SwissMatch
|
10
|
+
|
11
|
+
# Represents an area, commonly identified by zip-code and city-name.
|
12
|
+
# A unique zip code is determined by any of these:
|
13
|
+
# * the postal ordering number
|
14
|
+
# * zip code + zip code add on
|
15
|
+
# * zip code + name (city)
|
16
|
+
class ZipCode
|
17
|
+
|
18
|
+
# @return [Integer]
|
19
|
+
# The postal ordering number, also known as ONRP
|
20
|
+
attr_reader :ordering_number # onrp
|
21
|
+
|
22
|
+
# Described under "PLZ light", as field "PLZ-Typ"
|
23
|
+
# * 10 = Domizil- und Fachadressen
|
24
|
+
# * 20 = Nur Domiziladressen
|
25
|
+
# * 30 = Nur Fach-PLZ
|
26
|
+
# * 40 = Firmen-PLZ
|
27
|
+
# * 80 = Postinterne PLZ (Angabe Zustellpoststelle auf Bundzetteln oder auf Sackanschriften)
|
28
|
+
#
|
29
|
+
# @return [Integer]
|
30
|
+
# The type of the zip code in a numeric code, one of the values 10, 20, 30, 40 or 80.
|
31
|
+
attr_reader :type
|
32
|
+
|
33
|
+
# Described under "PLZ light", as field "Postleitzahl"
|
34
|
+
#
|
35
|
+
# @return [Integer]
|
36
|
+
# The 4 digit numeric zip code
|
37
|
+
attr_reader :code
|
38
|
+
|
39
|
+
# @return [Integer]
|
40
|
+
# The 2 digit numeric code addition, to distinguish zip codes with the same 4 digit code.
|
41
|
+
attr_reader :add_on
|
42
|
+
|
43
|
+
# @return [Integer]
|
44
|
+
# The 6 digit numeric zip code and add-on (first 4 digits are the code, last 2
|
45
|
+
# digits the add-on).
|
46
|
+
attr_reader :full_code
|
47
|
+
|
48
|
+
# @return [SwissMatch::Canton]
|
49
|
+
# The canton this zip code belongs to
|
50
|
+
attr_reader :canton
|
51
|
+
|
52
|
+
# @return [SwissMatch::Name]
|
53
|
+
# The official name of this zip code (max. 27 characters)
|
54
|
+
attr_reader :name
|
55
|
+
|
56
|
+
# @return [SwissMatch::Name]
|
57
|
+
# The official name of this zip code (max. 18 characters)
|
58
|
+
attr_reader :name_short
|
59
|
+
|
60
|
+
# @return [Symbol]
|
61
|
+
# The main language in the area of this zip code. One of :de, :fr, :it or :rt.
|
62
|
+
attr_reader :language
|
63
|
+
|
64
|
+
# @return [SwissMatch::Canton]
|
65
|
+
# The second most used language in the area of this zip code. One of :de, :fr, :it or :rt.
|
66
|
+
attr_reader :language_alternative
|
67
|
+
|
68
|
+
# @return [Boolean]
|
69
|
+
# Whether this ZipCode instance is included in the MAT[CH]sort sortfile
|
70
|
+
attr_reader :sortfile_member
|
71
|
+
|
72
|
+
# @return [SwissMatch::ZipCode]
|
73
|
+
# By which postal office delivery of letters is usually taken care of.
|
74
|
+
attr_reader :delivery_by
|
75
|
+
|
76
|
+
# @return [SwissMatch::Community]
|
77
|
+
# The community this zip code belongs to.
|
78
|
+
attr_reader :community
|
79
|
+
|
80
|
+
# @return [Date, nil]
|
81
|
+
# The date from which on this zip code starts to be in use
|
82
|
+
#
|
83
|
+
# @see #in_use?
|
84
|
+
attr_reader :valid_from
|
85
|
+
|
86
|
+
# @return [Date, nil]
|
87
|
+
# The date until which on this zip code is in use
|
88
|
+
#
|
89
|
+
# @see #in_use?
|
90
|
+
attr_reader :valid_until
|
91
|
+
|
92
|
+
def initialize(
|
93
|
+
ordering_number,
|
94
|
+
type,
|
95
|
+
code,
|
96
|
+
add_on,
|
97
|
+
name,
|
98
|
+
names,
|
99
|
+
name_short,
|
100
|
+
names_short,
|
101
|
+
region_names,
|
102
|
+
region_names_short,
|
103
|
+
canton,
|
104
|
+
language,
|
105
|
+
language_alternative,
|
106
|
+
sortfile_member,
|
107
|
+
delivery_by,
|
108
|
+
community,
|
109
|
+
valid_from,
|
110
|
+
valid_until = nil
|
111
|
+
)
|
112
|
+
@ordering_number = ordering_number
|
113
|
+
@type = type
|
114
|
+
@code = code
|
115
|
+
@add_on = add_on
|
116
|
+
@full_code = code*100 + add_on
|
117
|
+
@language = language
|
118
|
+
@language_alternative = language_alternative
|
119
|
+
@name = name.is_a?(Name) ? name : Name.new(name, language)
|
120
|
+
@name_short = name_short.is_a?(Name) ? name_short : Name.new(name_short, language)
|
121
|
+
@names = (names || [@name]).sort_by(&:sequence_number)
|
122
|
+
@names_short = (names_short || [@name_short]).sort_by(&:sequence_number)
|
123
|
+
@region_names = region_names
|
124
|
+
@region_names_short = region_names_short
|
125
|
+
@canton = canton
|
126
|
+
@sortfile_member = sortfile_member
|
127
|
+
@delivery_by = delivery_by == :self ? self : delivery_by
|
128
|
+
@community = community
|
129
|
+
@valid_from = valid_from
|
130
|
+
@valid_until = valid_until
|
131
|
+
end
|
132
|
+
|
133
|
+
# @return [Array<String>]
|
134
|
+
# The name of this zip code in all languages and normalizations (only unique values)
|
135
|
+
def transliterated_names
|
136
|
+
names.flat_map { |name, ary|
|
137
|
+
name = name.to_s # convert from SwissMatch::Name
|
138
|
+
[
|
139
|
+
SwissMatch.transliterate1(name),
|
140
|
+
SwissMatch.transliterate2(name) # TODO: use transliterate gem
|
141
|
+
]
|
142
|
+
}.uniq
|
143
|
+
end
|
144
|
+
|
145
|
+
# Since a zip code can - for any given language - have no name, exactly one name,
|
146
|
+
# or even multiple names, it is sometimes difficult to write good code to
|
147
|
+
# automatically provide well localized addresses. This method helps with that, in that
|
148
|
+
# it guarantees a single name, as well chosen as possible.
|
149
|
+
# It returns the name for the given language, and with the lowest running number, if
|
150
|
+
# no name can be found for the given language, the primary name (@see #name) is
|
151
|
+
# returned.
|
152
|
+
#
|
153
|
+
# @param [Symbol, nil] language
|
154
|
+
# One of nil, :de, :fr, :it or :rt
|
155
|
+
#
|
156
|
+
# @return [SwissMatch::Name]
|
157
|
+
# A single name for the zip code, chosen by a 'best fit' algorithm.
|
158
|
+
def suggested_name(language=nil)
|
159
|
+
(language && @names.find { |name| name.language == language }) || @name
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
# Since a zip code can - for any given language - have no name, exactly one name,
|
164
|
+
# or even multiple names, it is sometimes difficult to write good code to
|
165
|
+
# automatically provide well localized addresses. This method helps with that, in that
|
166
|
+
# it guarantees a single name, as well chosen as possible.
|
167
|
+
# It returns the name for the given language, and with the lowest running number, if
|
168
|
+
# no name can be found for the given language, the primary name (@see #name) is
|
169
|
+
# returned.
|
170
|
+
#
|
171
|
+
# @param [Symbol, nil] language
|
172
|
+
# One of nil, :de, :fr, :it or :rt
|
173
|
+
#
|
174
|
+
# @return [SwissMatch::Name]
|
175
|
+
# A single short name for the zip code, chosen by a 'best fit' algorithm.
|
176
|
+
def suggested_short_name(language=nil)
|
177
|
+
(language && @short_name.find { |name| name.language == language }) || @short_name
|
178
|
+
end
|
179
|
+
|
180
|
+
# @param [Symbol, nil] language
|
181
|
+
# One of nil, :de, :fr, :it or :rt
|
182
|
+
#
|
183
|
+
# @return [Array<SwissMatch::Name>]
|
184
|
+
# All official names (max. 27 chars) of this zip code.
|
185
|
+
def names(language=nil)
|
186
|
+
language ? @names.select { |name| name.language == language } : @names
|
187
|
+
end
|
188
|
+
|
189
|
+
# @param [Symbol, nil] language
|
190
|
+
# One of nil, :de, :fr, :it or :rt
|
191
|
+
#
|
192
|
+
# @return [Array<SwissMatch::Name>]
|
193
|
+
# All official short names (max. 18 chars) of this zip code.
|
194
|
+
def names_short(language=nil)
|
195
|
+
language ? @names_short.select { |name| name.language == language } : @names_short
|
196
|
+
end
|
197
|
+
|
198
|
+
# A region name is a name that can be used along a zip code and city, but must not replace
|
199
|
+
# the city. For more information, read the section about the PLZ_P2 file, "Bezeichnungstyp"
|
200
|
+
# with value "3".
|
201
|
+
#
|
202
|
+
# @param [Symbol, nil] language
|
203
|
+
# One of nil, :de, :fr, :it or :rt
|
204
|
+
#
|
205
|
+
# @return [Array<SwissMatch::Name>]
|
206
|
+
# All official region names (max. 27 chars) of this zip code.
|
207
|
+
def region_names(language=nil)
|
208
|
+
language ? @region_names.select { |name| name.language == language } : @region_names
|
209
|
+
end
|
210
|
+
|
211
|
+
# A region name is a name that can be used along a zip code and city, but must not replace
|
212
|
+
# the city. For more information, read the section about the PLZ_P2 file, "Bezeichnungstyp"
|
213
|
+
# with value "3".
|
214
|
+
#
|
215
|
+
# @param [Symbol, nil] language
|
216
|
+
# One of nil, :de, :fr, :it or :rt
|
217
|
+
#
|
218
|
+
# @return [Array<SwissMatch::Name>]
|
219
|
+
# All official short region names (max. 18 chars) of this zip code.
|
220
|
+
def region_names_short(language=nil)
|
221
|
+
language ? @region_names_short.select { |name| name.language == language } : @region_names_short
|
222
|
+
end
|
223
|
+
|
224
|
+
# Compare two zip codes by their ordering number (ONRP)
|
225
|
+
#
|
226
|
+
# @return [Integer]
|
227
|
+
# Returns -1, 0 or 1.
|
228
|
+
def <=>(other)
|
229
|
+
@ordering_number <=> other.ordering_number
|
230
|
+
end
|
231
|
+
|
232
|
+
# @param [Date] at
|
233
|
+
# The date for which to check the
|
234
|
+
#
|
235
|
+
# @return [Boolean]
|
236
|
+
# Whether the zip code is in active use at the given date.
|
237
|
+
def in_use?(at=Date.today)
|
238
|
+
if @valid_from then
|
239
|
+
if @valid_until then
|
240
|
+
at.between?(@valid_from, @valid_until)
|
241
|
+
else
|
242
|
+
at >= @valid_from
|
243
|
+
end
|
244
|
+
elsif @valid_until
|
245
|
+
at <= @valid_until
|
246
|
+
else
|
247
|
+
true
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# @param [Boolean] retain_references
|
252
|
+
# If set to false, :delivery_by will be set to the ordering number,
|
253
|
+
# :community to the community_number and :canton to the canton's license_tag.
|
254
|
+
#
|
255
|
+
# @return [Hash]
|
256
|
+
# All properties of the zip code as a hash.
|
257
|
+
def to_hash(retain_references=false)
|
258
|
+
delivery_by = retain_references ? @delivery_by : (@delivery_by && @delivery_by.ordering_number)
|
259
|
+
community = retain_references ? @community : (@community && @community.community_number)
|
260
|
+
canton = retain_references ? @canton : (@canton && @canton.license_tag)
|
261
|
+
{
|
262
|
+
:ordering_number => @ordering_number,
|
263
|
+
:type => @type,
|
264
|
+
:code => @code,
|
265
|
+
:add_on => @add_on,
|
266
|
+
:name => @name,
|
267
|
+
:name_short => @name_short,
|
268
|
+
:canton => canton,
|
269
|
+
:language => @language,
|
270
|
+
:language_alternative => @language_alternative,
|
271
|
+
:sortfile_member => @sortfile_member,
|
272
|
+
:delivery_by => delivery_by,
|
273
|
+
:community => community,
|
274
|
+
:valid_from => @valid_from,
|
275
|
+
:valid_until => @valid_until,
|
276
|
+
}
|
277
|
+
end
|
278
|
+
|
279
|
+
# @private
|
280
|
+
# @see Object#hash
|
281
|
+
def hash
|
282
|
+
[self.class, @ordering_number].hash
|
283
|
+
end
|
284
|
+
|
285
|
+
# @private
|
286
|
+
# @see Object#eql?
|
287
|
+
def eql?(other)
|
288
|
+
self.class == other.class && @ordering_number == other.ordering_number
|
289
|
+
end
|
290
|
+
|
291
|
+
# @return [String]
|
292
|
+
# The 4 digit code, followed by the name
|
293
|
+
def to_s
|
294
|
+
"#{@code} #{@name}"
|
295
|
+
end
|
296
|
+
|
297
|
+
# @return [String]
|
298
|
+
# @see Object#inspect
|
299
|
+
def inspect
|
300
|
+
sprintf "\#<%s:%014x %s>", self.class, object_id, self
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|