swissmatch-location 0.0.1.201208 → 0.1.1.201304

Sign up to get free protection for your applications and to get access to all the features.
@@ -145,7 +145,6 @@
145
145
  535 2 3 2 Le Creux VD Le Creux VD
146
146
  541 2 3 2 Vers-chez-Grosjean Vers-chez-Grosjean
147
147
  545 1 3 2 Esserts-de-Rive Les Esserts-de-Rive
148
- 545 2 3 2 Le S�chey Le S�chey
149
148
  546 1 3 2 Chez-le-Ma�tre Chez-le-Ma�tre
150
149
  546 3 3 2 La Golisse La Golisse
151
150
  546 4 3 2 Le Rocheray Le Rocheray
@@ -871,7 +870,6 @@
871
870
  2378 3 3 2 Pinsec Pinsec
872
871
  2378 7 3 2 Mayoux Mayoux
873
872
  2378 8 3 2 Soussillon Soussillon
874
- 2381 1 3 2 Mission Mission
875
873
  2381 5 3 2 Mottec Mottec
876
874
  2383 1 3 2 Moiry VS Moiry VS
877
875
  2386 1 3 2 Diogne Diogne
@@ -1371,7 +1369,6 @@
1371
1369
  4526 1 3 1 Sellenb�ren Sellenb�ren
1372
1370
  4529 2 3 1 Katzenr�ti Katzenr�ti
1373
1371
  4531 3 3 1 Mettmenhasli Mettmenhasli
1374
- 4531 4 3 1 Nassenwil Nassenwil
1375
1372
  4535 1 3 1 Niedersteinmaur Niedersteinmaur
1376
1373
  4535 2 3 1 Obersteinmaur Obersteinmaur
1377
1374
  4543 2 3 1 Riedt b. Neerach Riedt b. Neerach
@@ -104,6 +104,12 @@ module SwissMatch
104
104
  @by_community_number[number]
105
105
  end
106
106
 
107
+ # @return [Array<SwissMatch::Community>]
108
+ # The communities with the given community numbers (also known as BFSNR).
109
+ def by_community_numbers(*numbers)
110
+ @by_community_number.values_at(*numbers)
111
+ end
112
+
107
113
  # @return [SwissMatch::Community]
108
114
  # The community with the given name.
109
115
  def by_name(name)
@@ -76,7 +76,7 @@ module SwissMatch
76
76
  # @private
77
77
  # @see Object#eql?
78
78
  def eql?(other)
79
- self.class == other.class && @community_number == other.community_number
79
+ self.class.eql?(other.class) && @community_number.eql?(other.community_number)
80
80
  end
81
81
 
82
82
  # @return [String]
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ module SwissMatch
6
+
7
+ # Represents a swiss district.
8
+ class District
9
+
10
+ # @return [String]
11
+ # The district number.
12
+ attr_reader :district_number
13
+
14
+ # @return [String]
15
+ # The name of the district.
16
+ attr_reader :name
17
+
18
+ # @return [SwissMatch::Communities]
19
+ # The political communities belonging to this district
20
+ attr_reader :communities
21
+
22
+ attr_reader :canton
23
+
24
+ # @param [String] district_number
25
+ # The two letter abbreviation of the districts name as used on license plates.
26
+ # @param [String] name
27
+ # The official name of the district.
28
+ # @param [SwissMatch::Canton] canton
29
+ # The canton this district belongs to
30
+ # @param [SwissMatch::Communities] communities
31
+ # The communities belonging to this district
32
+ def initialize(district_number, name, canton, communities)
33
+ @district_number = district_number
34
+ @name = name
35
+ @canton = canton
36
+ @communities = communities
37
+ end
38
+
39
+ # @param [Boolean] retain_references
40
+ # If set to false, :agglomeration will be set to the community_number and
41
+ # :canton to the canton's license_tag.
42
+ #
43
+ # @return [Hash]
44
+ # All properties of the district as a hash.
45
+ def to_hash(retain_references=false)
46
+ if retain_references
47
+ canton = @canton
48
+ communities = @communities
49
+ else
50
+ canton = @canton && @canton.license_tag
51
+ communities = @communities.map(&:community_number)
52
+ end
53
+
54
+ {
55
+ :name => @name,
56
+ :district_number => @district_number,
57
+ :canton => canton,
58
+ :communities => communities,
59
+ }
60
+ end
61
+
62
+ alias to_s name
63
+
64
+ # @private
65
+ # @see Object#hash
66
+ def hash
67
+ [self.class, @number].hash
68
+ end
69
+
70
+ # @private
71
+ # @see Object#eql?
72
+ def eql?(other)
73
+ self.class.eql?(other.class) && @number.eql?(other.number)
74
+ end
75
+
76
+ # @return [String]
77
+ # @see Object#inspect
78
+ def inspect
79
+ sprintf "\#<%s:%014x %d %p>", self.class, object_id, @district_number, to_s
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,119 @@
1
+ # encoding: utf-8
2
+
3
+
4
+
5
+ require 'swissmatch/district'
6
+
7
+
8
+
9
+ module SwissMatch
10
+
11
+ # Represents a collection of swiss districts and provides a query interface.
12
+ class Districts
13
+ include Enumerable
14
+
15
+ # @param [Array<SwissMatch::District>] districts
16
+ # The SwissMatch::District objects this SwissMatch::Districts should contain
17
+ def initialize(districts)
18
+ @districts = districts
19
+ @by_district_number = {}
20
+ @by_name = {}
21
+
22
+ districts.each do |district|
23
+ @by_district_number[district.district_number] = district
24
+ @by_name[district.name] = district
25
+ end
26
+ end
27
+
28
+ # Calls the block once for every SwissMatch::District in this SwissMatch::Districts
29
+ # instance, passing that district as a parameter.
30
+ # The order is the same as the instance was constructed.
31
+ #
32
+ # @yield [district]
33
+ # @yieldparam [SwissMatch::District] district
34
+ #
35
+ # @return [self] Returns self
36
+ def each(&block)
37
+ @districts.each(&block)
38
+ self
39
+ end
40
+
41
+ # Calls the block once for every SwissMatch::District in this SwissMatch::Districts
42
+ # instance, passing that district as a parameter.
43
+ # The order is the reverse of what the instance was constructed.
44
+ #
45
+ # @yield [district]
46
+ # @yieldparam [SwissMatch::District] district
47
+ #
48
+ # @return [self] Returns self
49
+ def reverse_each(&block)
50
+ @districts.reverse_each(&block)
51
+ self
52
+ end
53
+
54
+ # @return [SwissMatch::Districts]
55
+ # A SwissMatch::Districts collection with all SwissMatch::District objects for which the block
56
+ # returned true (or a trueish value)
57
+ def select(*args, &block)
58
+ Districts.new(@districts.select(*args, &block))
59
+ end
60
+
61
+ # @return [SwissMatch::Districts]
62
+ # A SwissMatch::Districts collection with all SwissMatch::District objects for which the block
63
+ # returned false (or a falseish value)
64
+ def reject(*args, &block)
65
+ Districts.new(@districts.reject(*args, &block))
66
+ end
67
+
68
+ # @see Enumerable#sort
69
+ #
70
+ # @return [SwissMatch::Districts]
71
+ # A SwissMatch::Districts collection sorted by the block
72
+ def sort(*args, &block)
73
+ Districts.new(@districts.sort(*args, &block))
74
+ end
75
+
76
+ # @see Enumerable#sort_by
77
+ #
78
+ # @return [SwissMatch::Districts]
79
+ # A SwissMatch::Districts collection sorted by the block
80
+ def sort_by(*args, &block)
81
+ Districts.new(@districts.sort_by(*args, &block))
82
+ end
83
+
84
+ # @return [SwissMatch::District]
85
+ # The district with the given district_number or name
86
+ def [](district_number_or_name)
87
+ @by_district_number[district_number_or_name] || @by_name[district_number_or_name]
88
+ end
89
+
90
+ # @return [SwissMatch::District]
91
+ # The district with the given license tag.
92
+ def by_district_number(tag)
93
+ @by_district_number[tag]
94
+ end
95
+
96
+ # @return [SwissMatch::District]
97
+ # The district with the given name (any language)
98
+ def by_name(name)
99
+ @by_name[name]
100
+ end
101
+
102
+ # @return [Integer] The number of SwissMatch::District objects in this collection.
103
+ def size
104
+ @districts.size
105
+ end
106
+
107
+ # @return [Array<SwissMatch::District>]
108
+ # An Array with all SwissMatch::District objects in this SwissMatch::Districts.
109
+ def to_a
110
+ @districts.dup
111
+ end
112
+
113
+ # @private
114
+ # @see Object#inspect
115
+ def inspect
116
+ sprintf "\#<%s:%x size: %d>", self.class, object_id>>1, size
117
+ end
118
+ end
119
+ end
@@ -57,6 +57,21 @@ module SwissMatch
57
57
  @data.cantons
58
58
  end
59
59
 
60
+ # @param [String] district_number_or_name
61
+ # The district_number or name of the district
62
+ #
63
+ # @return [SwissMatch::District]
64
+ # The district with the given district_number or name
65
+ def self.district(district_number_or_name)
66
+ @data.districts[district_number_or_name]
67
+ end
68
+
69
+ # @return [SwissMatch::Districts]
70
+ # All known districts
71
+ def self.districts
72
+ @data.districts
73
+ end
74
+
60
75
  # @param [Integer] key
61
76
  # The community number of the community
62
77
  #
@@ -204,6 +219,16 @@ module SwissMatch
204
219
  SwissMatch::Location.cantons(*args, &block)
205
220
  end
206
221
 
222
+ # @see SwissMatch::Location::district
223
+ def self.district(*args, &block)
224
+ SwissMatch::Location.district(*args, &block)
225
+ end
226
+
227
+ # @see SwissMatch::Location::districts
228
+ def self.districts(*args, &block)
229
+ SwissMatch::Location.districts(*args, &block)
230
+ end
231
+
207
232
  # @see SwissMatch::Location::community
208
233
  def self.community(*args, &block)
209
234
  SwissMatch::Location.community(*args, &block)
@@ -3,6 +3,15 @@
3
3
 
4
4
 
5
5
  require 'swissmatch/loaderror'
6
+ require 'swissmatch/name'
7
+ require 'swissmatch/canton'
8
+ require 'swissmatch/cantons'
9
+ require 'swissmatch/district'
10
+ require 'swissmatch/districts'
11
+ require 'swissmatch/community'
12
+ require 'swissmatch/communities'
13
+ require 'swissmatch/zipcode'
14
+ require 'swissmatch/zipcodes'
6
15
 
7
16
 
8
17
 
@@ -21,16 +30,18 @@ module SwissMatch
21
30
  # Generates a regular expression, that matches +size+ tab separated fields,
22
31
  # delimited by \r\n.
23
32
  # @private
24
- def self.generate_expression(size)
25
- /^#{Array.new(size) { '([^\t]*)' }.join('\t')}\r\n/
33
+ def self.generate_expression(size, separator, terminator)
34
+ /^#{Array.new(size) { "([^#{separator}]*)" }.join(eval("'#{separator}'"))}#{terminator}/
26
35
  end
27
36
 
28
37
  # Regular expressions used to parse the different files.
29
38
  # @private
30
39
  Expressions = {
31
- :community => generate_expression(4),
32
- :zip_2 => generate_expression(6),
33
- :zip_1 => generate_expression(13),
40
+ :community => generate_expression(4, '\t', '\r\n'),
41
+ :zip_2 => generate_expression(6, '\t', '\r\n'),
42
+ :zip_1 => generate_expression(13, '\t', '\r\n'),
43
+ :districts => generate_expression(3, ',', '\n'),
44
+ :communities => generate_expression(10, ',', '\n'),
34
45
  }
35
46
 
36
47
  # @private
@@ -89,6 +100,9 @@ module SwissMatch
89
100
  # @return [SwissMatch::Cantons] The loaded swiss cantons
90
101
  attr_reader :cantons
91
102
 
103
+ # @return [SwissMatch::Districts] The loaded swiss districts
104
+ attr_reader :districts
105
+
92
106
  # @return [SwissMatch::Communities] The loaded swiss communities
93
107
  attr_reader :communities
94
108
 
@@ -107,8 +121,8 @@ module SwissMatch
107
121
  elsif ENV['SWISSMATCH_DATA'] then
108
122
  @data_directory = ENV['SWISSMATCH_DATA']
109
123
  else
110
- data_directory = File.expand_path('../../../../data/swissmatch', __FILE__)
111
- data_directory = Gem.datadir 'swissmatch' if defined?(Gem) && !File.directory?(data_directory)
124
+ data_directory = File.expand_path('../../../../data/swissmatch-location', __FILE__)
125
+ data_directory = Gem.datadir 'swissmatch-location' if defined?(Gem) && !File.directory?(data_directory)
112
126
  @data_directory = data_directory
113
127
  end
114
128
  end
@@ -176,7 +190,7 @@ module SwissMatch
176
190
  # @return [self]
177
191
  # Returns self.
178
192
  def load!
179
- @cantons, @communities, @zip_codes = *load
193
+ @cantons, @districts, @communities, @zip_codes = *load
180
194
  self
181
195
  end
182
196
 
@@ -187,10 +201,11 @@ module SwissMatch
187
201
  reset_errors!
188
202
 
189
203
  cantons = load_cantons
204
+ districts = load_districts(cantons)
190
205
  communities = load_communities(cantons)
191
206
  zip_codes = load_zipcodes(cantons, communities)
192
207
 
193
- [cantons, communities, zip_codes]
208
+ [cantons, districts, communities, zip_codes]
194
209
  end
195
210
 
196
211
  # @return [SwissMatch::Cantons]
@@ -203,6 +218,20 @@ module SwissMatch
203
218
  )
204
219
  end
205
220
 
221
+ def load_districts(cantons)
222
+ # File format: GDEKT,GDEBZNR,GDEBZNA
223
+ path = Dir.enum_for(:glob, "#{@data_directory}/districts_*.csv").last
224
+ data = File.read(path, encoding: Encoding::UTF_8.to_s).scan(Expressions[:districts])
225
+ districts = data[1..-1].map { |canton_tag, district_number, district_name|
226
+ district_number = Integer(district_number, 10)
227
+ canton = cantons.by_license_tag(canton_tag)
228
+
229
+ District.new(district_number, district_name, canton, SwissMatch::Communities.new([]))
230
+ }
231
+
232
+ Districts.new(districts)
233
+ end
234
+
206
235
  # @return [SwissMatch::Communities]
207
236
  # An instance of SwissMatch::Communities containing all communities defined by the
208
237
  # files known to this DataFiles instance.
@@ -243,13 +272,37 @@ module SwissMatch
243
272
  raise "Must load cantons first" unless cantons
244
273
  raise "Must load communities first" unless communities
245
274
 
246
- temporary = {}
247
- self_delivered = []
248
- others = []
249
- zip1_file = Dir.enum_for(:glob, "#{@data_directory}/plz_p1_*.txt").last
250
- zip2_file = Dir.enum_for(:glob, "#{@data_directory}/plz_p2_*.txt").last
275
+ temporary = Hash.new { |h,k| h[k] = [] }
276
+ community_mapping = {}
277
+ self_delivered = []
278
+ others = []
279
+ zip1_file = Dir.enum_for(:glob, "#{@data_directory}/plz_p1_*.txt").last
280
+ zip2_file = Dir.enum_for(:glob, "#{@data_directory}/plz_p2_*.txt").last
281
+ communities_file = Dir.enum_for(:glob, "#{@data_directory}/communities_*.csv").last
282
+
283
+ # KTKZ,OHW,ORTNAME,GHW,GDENR,GDENAMK,PHW,PLZ4,PLZZ,PLZNAMK
284
+ communities_data = File.read(
285
+ communities_file,
286
+ encoding: Encoding::UTF_8.to_s
287
+ ).scan(Expressions[:communities])[1..-1].transpose.values_at(4,7,8)
288
+ communities_data[0].map!(&:to_i)
289
+ communities_data[1].map!(&:to_i)
290
+ communities_data[2].map!(&:to_i)
291
+ communities_data.transpose.each do |data|
292
+ temporary[data.last(2)] << data.at(0)
293
+ end
294
+
295
+ temporary.each do |key,coms|
296
+ # compact, because some communities already no longer exist, so by_community_numbers can
297
+ # contain nils which must be removed
298
+ community_mapping[key] = Communities.new(communities.by_community_numbers(*coms.uniq.sort).compact)
299
+ end
300
+
301
+ temporary = {}
251
302
  load_table(zip1_file, :zip_1).each do |row|
252
303
  onrp = row.at(0).to_i
304
+ code = row.at(2).to_i
305
+ addon = row.at(3).to_i
253
306
  delivery_by = row.at(10).to_i
254
307
  delivery_by = case delivery_by when 0 then nil; when onrp then :self; else delivery_by; end
255
308
  language = LanguageCodes[row.at(7).to_i]
@@ -259,8 +312,8 @@ module SwissMatch
259
312
  data = [
260
313
  onrp, # ordering_number
261
314
  row.at(1).to_i, # type
262
- row.at(2).to_i, # code
263
- row.at(3).to_i, # add_on
315
+ code,
316
+ addon,
264
317
  name, # name (official)
265
318
  [name], # names (official + alternative)
266
319
  name_short, # name_short (official)
@@ -273,6 +326,7 @@ module SwissMatch
273
326
  row.at(9) == "1", # sortfile_member
274
327
  delivery_by, # delivery_by
275
328
  communities.by_community_number(row.at(11).to_i), # community_number
329
+ community_mapping[[code, addon]],
276
330
  Date.civil(*row.at(12).match(/^(\d{4})(\d\d)(\d\d)$/).captures.map(&:to_i)) # valid_from
277
331
  ]
278
332
  temporary[onrp] = data