swissmatch-location 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|