timezone_finder 1.5.6 → 1.5.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e74c3bfc9784467f66b1c270bc2473168e8b8838
4
- data.tar.gz: c5bce138f2d33d467b46a3cdf8bfd214e3d01971
3
+ metadata.gz: df6c6cb43816fb26a809e41666886aa1482abdb6
4
+ data.tar.gz: db835dab01964df180ee3d9edbe56439ae521ddf
5
5
  SHA512:
6
- metadata.gz: 205cb31ffe251b8c2104fbe97f4535462d94a1a71625f7e090ecdb05937fd9c79861e8c912858d344ff6cf6b8aa8e00ab720c5d48f62ac2fa82010b0b3e1d4b9
7
- data.tar.gz: c183ace8223052a82150e8627e91779647be2ca83ee83b33ca456c0033c56eecb2e40e315d70734794232cca6f6db355db4e015b4fdf7ebbcd4e283e5c39dad3
6
+ metadata.gz: 3dd3d72c1dd9dd6b43949748874c9aea5e2591ddb87f3f3413a8b5cf4b29147c8ec753aecac0ae27559de63f1b5e46436e95a3d4f21a05b985e1fef46a8e9636
7
+ data.tar.gz: 497db6944dba486a6db9b4033b8c6d9540b46f082d67737a905ecc2589125396c17498599c319cbcb9d5b960dc24078e74f2c81216ad7f9d69f21313cae21371
data/ChangeLog CHANGED
@@ -1,3 +1,9 @@
1
+ == 2017-03-14 version 1.5.7
2
+
3
+ * clarified usage of the API in the Readme
4
+ * all functions are now keyword-args only (to prevent lng lat mix-up errors)
5
+ * sorting the polygons to check in the order of how often their zones appear gives a speed bonus
6
+
1
7
  == 2016-06-19 version 1.5.6
2
8
 
3
9
  * using little endian encoding now
data/README.md CHANGED
@@ -2,13 +2,16 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/gunyarakun/timezone_finder.svg?branch=master)](https://travis-ci.org/gunyarakun/timezone_finder)
4
4
  [![Gem Version](https://badge.fury.io/rb/timezone_finder.svg)](https://badge.fury.io/rb/timezone_finder)
5
+ ![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)
5
6
 
6
- This is a fast and lightweight ruby project for looking up the corresponding
7
+ **NOTE:** version 1.5.7 is incompatible with version 1.5.6 because the argument were changed into named arguments following the original Python version's change.
8
+
9
+ This is a fast and lightweight pure ruby project with no runtime dependency for looking up the corresponding
7
10
  timezone for a given lat/lng on earth entirely offline.
8
11
 
9
12
  This project is derived from
10
13
  [timezonefinder](https://pypi.python.org/pypi/timezonefinder)
11
- ([github](https://github.com/MrMinimal64/timezonefinder>)).
14
+ ([github](https://github.com/MrMinimal64/timezonefinder)).
12
15
 
13
16
  The underlying timezone data is based on work done by [Eric Muller](http://efele.net/maps/tz/world/).
14
17
 
@@ -36,16 +39,17 @@ tf = TimezoneFinder.create
36
39
 
37
40
  #### timezone\_at():
38
41
 
39
- This is the default function to check which timezone a point lies in.
42
+ This is the default function to check which timezone a point lies within.
40
43
  If no timezone has been found, `nil` is being returned.
41
- **NOTE:** This approach is optimized for speed and the common case to only query points actually within a timezone.
42
- This might not be what you are looking for however: When there is only one possible timezone in proximity, this timezone would be returned
43
- (without checking if the point is included first).
44
+
45
+ **PLEASE NOTE:** This approach is optimized for speed and the common case to only query points within a timezone.
46
+ The last possible timezone in proximity is always returned (without checking if the point is really included).
47
+ So results might be misleading for points outside of any timezone.
44
48
 
45
49
  ```ruby
46
- # point = (longitude, latitude)
47
- point = (13.358, 52.5061)
48
- puts tf.timezone_at(*point)
50
+ longitude = 13.358
51
+ latitude = 52.5061
52
+ puts tf.timezone_at(lng: longitude, lat: latitude)
49
53
  # = Europe/Berlin
50
54
  ```
51
55
 
@@ -55,18 +59,19 @@ This function is for making sure a point is really inside a timezone. It is slow
55
59
  are checked until one polygon is matched.
56
60
 
57
61
  ```ruby
58
- puts tf.certain_timezone_at(*point)
62
+ puts tf.certain_timezone_at(lng: longitude, lat: latitude)
59
63
  # = Europe/Berlin
60
64
  ```
61
65
 
62
66
  #### Proximity algorithm
63
67
 
64
68
  Only use this when the point is not inside a polygon, because the approach otherwise makes no sense.
65
- This returns the closest timezone of all polygons within +-1 degree lng and +-1 degree lat (or None).
69
+ This returns the closest timezone of all polygons within +-1 degree lng and +-1 degree lat (or nil).
66
70
 
67
71
  ```ruby
68
- point = (12.773955, 55.578595)
69
- puts tf.closest_timezone_at(*point)
72
+ longitude = 12.773955
73
+ latitude = 55.578595
74
+ puts tf.closest_timezone_at(lng: longitude, lat: latitude)
70
75
  # = Europe/Copenhagens
71
76
  ```
72
77
 
@@ -75,7 +80,7 @@ puts tf.closest_timezone_at(*point)
75
80
  To increase search radius even more, use the `delta_degree`-option:
76
81
 
77
82
  ```ruby
78
- puts tf.closest_timezone_at(*point, 3)
83
+ puts tf.closest_timezone_at(lng: longitude, lat: latitude, delta_degree: 3)
79
84
  # = Europe/Copenhagens
80
85
  ```
81
86
 
@@ -85,10 +90,10 @@ I recommend only slowly increasing the search radius, since computation time inc
85
90
 
86
91
  Also keep in mind that x degrees lat are not the same distance apart than x degree lng (earth is a sphere)!
87
92
  So to really make sure you got the closest timezone increase the search radius until you get a result,
88
- then increase the radius once more and take this result. (this should only make a difference in really rare cases)
93
+ then increase the radius once more and take this result. (should only make a difference in really rare cases)
89
94
 
90
- With `exact_computation=true` the distance to every polygon edge is computed (way more complicated)
91
- , instead of just evaluating the distances to all the vertices. This only makes a real difference when polygons are very close.
95
+ With `exact_computation=true` the distance to every polygon edge is computed (way more complicated), instead of just evaluating the distances to all the vertices.
96
+ This only makes a real difference when polygons are very close.
92
97
 
93
98
  With `return_distances=true` the output looks like this:
94
99
 
@@ -97,11 +102,22 @@ With `return_distances=true` the output looks like this:
97
102
  Note that some polygons might not be tested (for example when a zone is found to be the closest already).
98
103
  To prevent this use `force_evaluation=true`.
99
104
 
105
+ ```ruby
106
+ longitude = 42.1052479
107
+ latitude = -16.622686
108
+ puts tf.closest_timezone_at(lng: longitude, lat: latitude, delta_degree: 2,
109
+ exact_computation: true, return_distances: true, force_evaluation: true)
110
+ # = ["uninhabited",
111
+ # [238.1846260648566, 267.91867468894895, 207.43831938964382, 209.6790144988556, 228.4213564154256, 80.66907784731693, 217.1092486625455, 293.54672523493076, 304.527493783916],
112
+ # ["Africa/Maputo", "Africa/Maputo", "Africa/Maputo", "Africa/Maputo", "Africa/Maputo", "uninhabited", "Indian/Antananarivo", "Indian/Antananarivo", "Indian/Antananarivo"]
113
+ # ]
114
+ ```
115
+
100
116
  ## Developer
101
117
 
102
118
  ### Using the conversion tool:
103
119
 
104
- Make sure you installed the GDAL framework (thats for converting .shp shapefiles into .json)
120
+ Make sure you installed the GDAL framework (that's for converting .shp shapefiles into .json)
105
121
  Change to the directory of the timezone\_finder package (location of ``file_converter.rb``) in your terminal and then:
106
122
 
107
123
  ```sh
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # rubocop:disable Metrics/ClassLength,Metrics/MethodLength,Metrics/LineLength
3
3
  # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/ParameterLists
4
- # rubocop:disable Style/PredicateName,Style/Next
4
+ # rubocop:disable Style/PredicateName
5
5
  # rubocop:disable Lint/Void
6
6
  require 'set'
7
7
  require_relative 'helpers'
@@ -112,7 +112,7 @@ module TimezoneFinder
112
112
  # tz_name = /(TZID)/.match(row)
113
113
  # puts tz_name
114
114
  if tz_name_match
115
- tz_name = tz_name_match['name'].gsub('\\', '')
115
+ tz_name = tz_name_match['name'].delete('\\')
116
116
  @all_tz_names << tz_name
117
117
  @nr_of_lines += 1
118
118
  # puts tz_name
@@ -135,14 +135,14 @@ module TimezoneFinder
135
135
  end
136
136
 
137
137
  if actual_depth != 0
138
- fail ArgumentError, "uneven number of brackets detected. Something is wrong in line #{file_line}"
138
+ raise ArgumentError, "uneven number of brackets detected. Something is wrong in line #{file_line}"
139
139
  end
140
140
 
141
141
  coordinates = row.scan(/[-]?\d+\.?\d+/)
142
142
 
143
143
  sum = encountered_nr_of_coordinates.inject(0) { |a, e| a + e }
144
144
  if coordinates.length != sum * 2
145
- fail ArgumentError, "There number of coordinates is counten wrong: #{coordinates.length} #{sum * 2}"
145
+ raise ArgumentError, "There number of coordinates is counten wrong: #{coordinates.length} #{sum * 2}"
146
146
  end
147
147
  # TODO: detect and store all the holes in the bin
148
148
  # puts coordinates
@@ -386,7 +386,7 @@ module TimezoneFinder
386
386
 
387
387
  nr_of_intersects = intersects.length
388
388
  if nr_of_intersects.odd?
389
- fail 'an uneven number of intersections has been accounted'
389
+ raise 'an uneven number of intersections has been accounted'
390
390
  end
391
391
 
392
392
  (0...nr_of_intersects).step(2).each do |i|
@@ -458,7 +458,7 @@ module TimezoneFinder
458
458
 
459
459
  nr_of_intersects = intersects.length
460
460
  if nr_of_intersects.odd?
461
- fail 'an uneven number of intersections has been accounted'
461
+ raise 'an uneven number of intersections has been accounted'
462
462
  end
463
463
 
464
464
  possible_latitudes = []
@@ -591,10 +591,10 @@ EOT
591
591
  EOT
592
592
 
593
593
  if shortcuts_for_line.length > column_nrs.length * row_nrs.length
594
- fail 'there are more shortcuts than before now. there is something wrong with the algorithm!'
594
+ raise 'there are more shortcuts than before now. there is something wrong with the algorithm!'
595
595
  end
596
596
  if shortcuts_for_line.length < 3
597
- fail 'algorithm not valid! less than 3 zones detected (should be at least 4)'
597
+ raise 'algorithm not valid! less than 3 zones detected (should be at least 4)'
598
598
  end
599
599
 
600
600
  else
@@ -668,7 +668,7 @@ EOT
668
668
  amount_of_shortcuts = nr_of_entries_in_shortcut.length
669
669
  if amount_of_shortcuts != 64_800 * NR_SHORTCUTS_PER_LNG * NR_SHORTCUTS_PER_LAT
670
670
  puts(amount_of_shortcuts)
671
- fail ArgumentError, 'this number of shortcut zones is wrong'
671
+ raise ArgumentError, 'this number of shortcut zones is wrong'
672
672
  end
673
673
 
674
674
  puts("number of filled shortcut zones are: #{amount_filled_shortcuts} (=#{(amount_filled_shortcuts.fdiv(amount_of_shortcuts) * 100).round(2)}% of all shortcuts)")
@@ -716,7 +716,7 @@ EOT
716
716
 
717
717
  if shortcut_start_address != polygon_address
718
718
  # both should be the same!
719
- fail 'shortcut_start_address and polygon_address should now be the same!'
719
+ raise 'shortcut_start_address and polygon_address should now be the same!'
720
720
  end
721
721
 
722
722
  # write boundary_data
@@ -739,7 +739,7 @@ EOT
739
739
  # [SHORTCUT AREA]
740
740
  # write all nr of entries
741
741
  nr_of_entries_in_shortcut.each do |nr|
742
- fail "There are too many polygons in this shortcuts: #{nr}" if nr > 300
742
+ raise "There are too many polygons in this shortcuts: #{nr}" if nr > 300
743
743
  output_file.write([nr].pack('S<'))
744
744
  end
745
745
 
@@ -759,7 +759,7 @@ EOT
759
759
  # write Line_Nrs for every shortcut
760
760
  shortcut_entries.each do |entries|
761
761
  entries.each do |entry|
762
- fail entry if entry > @nr_of_lines
762
+ raise entry if entry > @nr_of_lines
763
763
  output_file.write([entry].pack('S<'))
764
764
  end
765
765
  end
@@ -769,13 +769,13 @@ EOT
769
769
  # 'S<' for every hole store the related line
770
770
  i = 0
771
771
  @related_line.each do |line|
772
- fail ArgumentError, line if line > @nr_of_lines
772
+ raise ArgumentError, line if line > @nr_of_lines
773
773
  output_file.write([line].pack('S<'))
774
774
  i += 1
775
775
  end
776
776
 
777
777
  if i > @amount_of_holes
778
- fail ArgumentError, 'There are more related lines than holes.'
778
+ raise ArgumentError, 'There are more related lines than holes.'
779
779
  end
780
780
 
781
781
  # 'S<' Y times [H unsigned short: nr of values (coordinate PAIRS! x,y in int32 int32) in this hole]
@@ -805,13 +805,13 @@ EOT
805
805
  last_address = output_file.tell
806
806
  hole_space = last_address - hole_start_address
807
807
  if shortcut_space != last_address - shortcut_start_address - hole_space
808
- fail ArgumentError, 'shortcut space is computed wrong'
808
+ raise ArgumentError, 'shortcut space is computed wrong'
809
809
  end
810
810
  polygon_space = nr_of_floats * 4
811
811
 
812
812
  puts("the polygon data makes up #{(polygon_space.fdiv(last_address) * 100).round(2)}% of the file")
813
- puts("the shortcuts make up #{(shortcut_space.fdiv(last_address) * 100).round(2) }% of the file")
814
- puts("the holes make up #{(hole_space.fdiv(last_address) * 100).round(2) }% of the file")
813
+ puts("the shortcuts make up #{(shortcut_space.fdiv(last_address) * 100).round(2)}% of the file")
814
+ puts("the holes make up #{(hole_space.fdiv(last_address) * 100).round(2)}% of the file")
815
815
 
816
816
  puts('Success!')
817
817
  end
@@ -1,5 +1,5 @@
1
1
  module TimezoneFinder
2
- VERSION = '1.5.6' unless defined? TimezoneFinder::Version
2
+ VERSION = '1.5.7'.freeze unless defined? TimezoneFinder::Version
3
3
  # https://github.com/MrMinimal64/timezonefinder
4
- BASED_SHA1_OF_PYTHON = 'c948967a2f9c8bd03535b181fa6faa13168955ca'
4
+ BASED_SHA1_OF_PYTHON = '82bc84ab145c69494c1efe15c0afc9cdd46ab626'.freeze
5
5
  end
@@ -1,7 +1,5 @@
1
1
  # rubocop:disable Metrics/ClassLength,Metrics/MethodLength,Metrics/LineLength
2
2
  # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/ParameterLists
3
- # rubocop:disable Style/PredicateName,Style/Next
4
- # rubocop:disable Lint/Void
5
3
  module TimezoneFinder
6
4
  class Helpers
7
5
  # tests if a point pX(x,y) is Left|On|Right of an infinite line from p1 to p2
@@ -69,7 +67,7 @@ module TimezoneFinder
69
67
  # compute the x-intersection of the point with the line p1-p2
70
68
  # delta_y cannot be 0 here because of the condition 'y lies within ]y1;y2]'
71
69
  # NOTE: bracket placement is important here (we are dealing with 64-bit ints!). first divide then multiply!
72
- delta_x = ((y - y1) * ((x2 - x1).fdiv(y2 - y1))) + x1 - x
70
+ delta_x = ((y - y1) * (x2 - x1).fdiv(y2 - y1)) + x1 - x
73
71
 
74
72
  if delta_x > 0
75
73
  if x1gtx2
@@ -210,7 +208,7 @@ module TimezoneFinder
210
208
  # this is actually a rotation with -rad (use symmetry of sin/cos)
211
209
  sin_rad = Math.sin(rad)
212
210
  cos_rad = Math.cos(rad)
213
- [point[0] * cos_rad - point[2] * sin_rad, point[1], point[2] * cos_rad - point[0] * sin_rad]
211
+ [point[0] * cos_rad + point[2] * sin_rad, point[1], point[2] * cos_rad - point[0] * sin_rad]
214
212
  end
215
213
 
216
214
  def self.coords2cartesian(lng_rad, lat_rad)
@@ -225,7 +223,7 @@ module TimezoneFinder
225
223
  # this is only an approximation since the earth is not a real sphere
226
224
  def self.distance_to_point_on_equator(lng_rad, lat_rad, lng_rad_p1)
227
225
  # 2* for the distance in rad and * 12742 (mean diameter of earth) for the distance in km
228
- 12742 * Math.asin(Math.sqrt((Math.sin(lat_rad / 2.0)) ** 2 + Math.cos(lat_rad) * Math.sin((lng_rad - lng_rad_p1) / 2.0) ** 2))
226
+ 12_742 * Math.asin(Math.sqrt(Math.sin(lat_rad / 2.0)**2 + Math.cos(lat_rad) * Math.sin((lng_rad - lng_rad_p1) / 2.0)**2))
229
227
  end
230
228
 
231
229
  # :param lng_p1: the longitude of point 1 in radians
@@ -236,7 +234,7 @@ module TimezoneFinder
236
234
  # this is only an approximation since the earth is not a real sphere
237
235
  def self.haversine(lng_p1, lat_p1, lng_p2, lat_p2)
238
236
  # 2* for the distance in rad and * 12742(mean diameter of earth) for the distance in km
239
- 12742 * Math.asin(Math.sqrt(Math.sin((lat_p1 - lat_p2) / 2.0) ** 2 + Math.cos(lat_p2) * Math.cos(lat_p1) * Math.sin((lng_p1 - lng_p2) / 2.0) ** 2))
237
+ 12_742 * Math.asin(Math.sqrt(Math.sin((lat_p1 - lat_p2) / 2.0)**2 + Math.cos(lat_p2) * Math.cos(lat_p1) * Math.sin((lng_p1 - lng_p2) / 2.0)**2))
240
238
  end
241
239
 
242
240
  # :param lng_rad: lng of px in radians
@@ -320,7 +318,7 @@ module TimezoneFinder
320
318
 
321
319
  min_distance = [min_distance,
322
320
  compute_min_distance(lng_rad, lat_rad, trans_points[0][index_p0], trans_points[1][index_p0],
323
- pm1_lng, pm1_lat, p1_lng, p1_lat)].min
321
+ pm1_lng, pm1_lat, p1_lng, p1_lat)].min
324
322
 
325
323
  index_p0 += 2
326
324
  index_p1 += 2
@@ -332,11 +330,11 @@ module TimezoneFinder
332
330
  end
333
331
 
334
332
  def self.distance_to_polygon(lng_rad, lat_rad, nr_points, points)
335
- min_distance = 40100000
333
+ min_distance = 40_100_000
336
334
 
337
335
  (0...nr_points).each do |i|
338
336
  min_distance = [min_distance, haversine(lng_rad, lat_rad, radians(int2coord(points[0][i])),
339
- radians(int2coord(points[1][i])))].min
337
+ radians(int2coord(points[1][i])))].min
340
338
  end
341
339
 
342
340
  min_distance
@@ -360,7 +358,7 @@ module TimezoneFinder
360
358
  end
361
359
 
362
360
  unless unpack_format
363
- fail "#{unsigned ? 'unsigned' : 'signed'} #{byte_width}-byte width is not supported in fromfile"
361
+ raise "#{unsigned ? 'unsigned' : 'signed'} #{byte_width}-byte width is not supported in fromfile"
364
362
  end
365
363
 
366
364
  file.read(count * byte_width).unpack(unpack_format)
@@ -1,7 +1,7 @@
1
1
  # rubocop:disable Metrics/ClassLength,Metrics/MethodLength,Metrics/LineLength
2
2
  # rubocop:disable Metrics/AbcSize,Metrics/PerceivedComplexity,Metrics/CyclomaticComplexity,Metrics/ParameterLists
3
- # rubocop:disable Style/PredicateName,Style/Next,Style/AndOr
4
- # rubocop:disable Lint/Void,Lint/HandleExceptions
3
+ # rubocop:disable Style/Next,Style/AndOr
4
+ # rubocop:disable Lint/HandleExceptions
5
5
  require_relative 'helpers'
6
6
  require_relative 'timezone_names'
7
7
 
@@ -62,12 +62,12 @@ module TimezoneFinder
62
62
  # write the entry for the last hole(s) in the registry
63
63
  @hole_registry.update(last_encountered_line_nr => [amount_of_holes, first_hole_id])
64
64
 
65
- ObjectSpace.define_finalizer(self, self.class.__del__)
65
+ ObjectSpace.define_finalizer(self, self.class.__del__(@binary_file))
66
66
  end
67
67
 
68
- def self.__del__
68
+ def self.__del__(file)
69
69
  proc do
70
- @binary_file.close
70
+ file.close
71
71
  end
72
72
  end
73
73
 
@@ -152,6 +152,97 @@ module TimezoneFinder
152
152
  rescue KeyError
153
153
  end
154
154
 
155
+ # sorts the polygons_id list from least to most occurrences of the zone ids (->speed up)
156
+ # approx. 0.24% of all realistic points benefit from sorting (0.4% for random points)
157
+ # = percentage of sorting usage for 100k points
158
+ # in most of those cases there are only two types of zones (= entries in counted_zones) and one of them
159
+ # has only one entry. That means after checking one polygon timezone_at() already stops.
160
+ # Sorting only really makes sense for closest_timezone_at().
161
+ # :param polygon_id_list:
162
+ # :param nr_of_polygons: length of polygon_id_list
163
+ # :param dont_sort: if this is set to True, the sorting algorithms is skipped
164
+ # :return: sorted list of polygon_ids, sorted list of zone_ids, boolean: do all entries belong to the same zone
165
+ def compile_id_list(polygon_id_list, nr_of_polygons, dont_sort: false)
166
+ all_equal = lambda do |input_data|
167
+ x = nil
168
+ for x in input_data
169
+ # first_val = x
170
+ break
171
+ end
172
+ input_data.each do |y|
173
+ return false if x != y
174
+ end
175
+ true
176
+ end
177
+
178
+ # print(polygon_id_list)
179
+ # print(zone_id_list)
180
+ zone_id_list = [0] * nr_of_polygons
181
+ if dont_sort
182
+ pointer_local = 0
183
+ first_id = id_of(polygon_id_list[0])
184
+ equal = true
185
+ polygon_id_list.each do |polygon_id|
186
+ zone_id = id_of(polygon_id)
187
+ equal = false if zone_id != first_id
188
+ zone_id_list[pointer_local] = zone_id
189
+ pointer_local += 1
190
+ end
191
+
192
+ return polygon_id_list, zone_id_list, equal
193
+ end
194
+
195
+ counted_zones = {}
196
+ pointer_local = 0
197
+ polygon_id_list.each do |polygon_id|
198
+ zone_id = id_of(polygon_id)
199
+ zone_id_list[pointer_local] = zone_id
200
+ pointer_local += 1
201
+ counted_zones[zone_id] = counted_zones.fetch(zone_id, 0) + 1
202
+ end
203
+ # print(counted_zones)
204
+
205
+ return polygon_id_list, zone_id_list, true if counted_zones.length == 1
206
+
207
+ if all_equal.call(counted_zones.values)
208
+ return polygon_id_list, zone_id_list, false
209
+ end
210
+
211
+ counted_zones_sorted = counted_zones.sort_by { |_key, value| value }
212
+ # print(counted_zones_sorted)
213
+
214
+ sorted_polygon_id_list = [0] * nr_of_polygons
215
+ sorted_zone_id_list = [0] * nr_of_polygons
216
+
217
+ pointer_output = 0
218
+ pointer_output2 = 0
219
+ counted_zones_sorted.each do |zone_id, amount|
220
+ # write all polygons from this zone in the new list
221
+ pointer_local = 0
222
+ detected_polygons = 0
223
+ while detected_polygons < amount
224
+ if zone_id_list[pointer_local] == zone_id
225
+ # the polygon at the pointer has the wanted zone_id
226
+ detected_polygons += 1
227
+ sorted_polygon_id_list[pointer_output] = polygon_id_list[pointer_local]
228
+ pointer_output += 1
229
+ end
230
+
231
+ pointer_local += 1
232
+ end
233
+
234
+ (0...amount).each do |_pointer_local|
235
+ sorted_zone_id_list[pointer_output2] = zone_id
236
+ pointer_output2 += 1
237
+ end
238
+ end
239
+
240
+ # print(sorted_polygon_id_list)
241
+ # print(sorted_zone_id_list)
242
+
243
+ [sorted_polygon_id_list, sorted_zone_id_list, false]
244
+ end
245
+
155
246
  # This function searches for the closest polygon in the surrounding shortcuts.
156
247
  # Make sure that the point does not lie within a polygon (for that case the algorithm is simply wrong!)
157
248
  # Note that the algorithm won't find the closest polygon when it's on the 'other end of earth'
@@ -172,7 +263,7 @@ module TimezoneFinder
172
263
  # ( 'tz_name_of_the_closest_polygon',[ distances to all polygons in km], [tz_names of all polygons])
173
264
  # :param force_evaluation:
174
265
  # :return: the timezone name of the closest found polygon, the list of distances or None
175
- def closest_timezone_at(lng, lat, delta_degree = 1, exact_computation = false, return_distances = false, force_evaluation = false)
266
+ def closest_timezone_at(lng: nil, lat: nil, delta_degree: 1, exact_computation: false, return_distances: false, force_evaluation: false)
176
267
  exact_routine = lambda do |polygon_nr|
177
268
  coords = coords_of(polygon_nr)
178
269
  nr_points = coords[0].length
@@ -187,17 +278,17 @@ module TimezoneFinder
187
278
  end
188
279
 
189
280
  if lng > 180.0 or lng < -180.0 or lat > 90.0 or lat < -90.0
190
- fail "The coordinates are out ouf bounds: (#{lng}, #{lat})"
281
+ raise "The coordinates are out ouf bounds: (#{lng}, #{lat})"
191
282
  end
192
283
 
193
- if exact_computation
194
- routine = exact_routine
195
- else
196
- routine = normal_routine
197
- end
284
+ routine = if exact_computation
285
+ exact_routine
286
+ else
287
+ normal_routine
288
+ end
198
289
 
199
290
  # the maximum possible distance is half the perimeter of earth pi * 12743km = 40,054.xxx km
200
- min_distance = 40100
291
+ min_distance = 40_100
201
292
  # transform point X into cartesian coordinates
202
293
  current_closest_id = nil
203
294
  central_x_shortcut = (lng + 180).floor.to_i
@@ -206,7 +297,7 @@ module TimezoneFinder
206
297
  lng = Helpers.radians(lng)
207
298
  lat = Helpers.radians(lat)
208
299
 
209
- polygon_nrs = []
300
+ possible_polygons = []
210
301
 
211
302
  # there are 2 shortcuts per 1 degree lat, so to cover 1 degree two shortcuts (rows) have to be checked
212
303
  # the highest shortcut is 0
@@ -223,32 +314,30 @@ module TimezoneFinder
223
314
  (left..right).each do |x|
224
315
  (top..bottom).each do |y|
225
316
  polygons_of_shortcut(x, y).each do |p|
226
- polygon_nrs << p if polygon_nrs.index(p).nil?
317
+ possible_polygons << p if possible_polygons.index(p).nil?
227
318
  end
228
319
  end
229
320
  end
230
321
 
231
- polygons_in_list = polygon_nrs.length
322
+ polygons_in_list = possible_polygons.length
232
323
 
233
324
  return nil if polygons_in_list == 0
234
325
 
235
326
  # initialize the list of ids
236
- ids = polygon_nrs.map { |x| id_of(x) }
327
+ # TODO sorting doesn't give a bonus here?!
328
+ possible_polygons, ids, zones_are_equal = compile_id_list(possible_polygons, polygons_in_list,
329
+ dont_sort: true)
237
330
 
238
331
  # if all the polygons in this shortcut belong to the same zone return it
239
- first_entry = ids[0]
240
- if ids.count(first_entry) == polygons_in_list
241
- unless return_distances || force_evaluation
242
- return TIMEZONE_NAMES[first_entry]
243
- # TODO: sort from least to most occurrences
244
- end
332
+ if zones_are_equal
333
+ return TIMEZONE_NAMES[ids[0]] unless return_distances || force_evaluation
245
334
  end
246
335
 
247
336
  distances = [nil] * polygons_in_list
248
337
  pointer = 0
249
338
  if force_evaluation
250
- polygon_nrs.each do |polygon_nr|
251
- distance = routine.call(polygon_nr)
339
+ possible_polygons.each do |possible_polygon|
340
+ distance = routine.call(possible_polygon)
252
341
  distances[pointer] = distance
253
342
  if distance < min_distance
254
343
  min_distance = distance
@@ -269,11 +358,11 @@ module TimezoneFinder
269
358
 
270
359
  else
271
360
  # this polygon has to be checked
272
- distance = routine.call(polygon_nrs[pointer])
361
+ distance = routine.call(possible_polygons[pointer])
273
362
  distances[pointer] = distance
274
363
 
275
364
  already_checked[pointer] = true
276
- if distance < min_distance
365
+ if distance < min_distance # rubocop:disable Metrics/BlockNesting
277
366
  min_distance = distance
278
367
  current_closest_id = ids[pointer]
279
368
  # whole list has to be searched again!
@@ -300,9 +389,9 @@ module TimezoneFinder
300
389
  # :param lng: longitude of the point in degree (-180 to 180)
301
390
  # :param lat: latitude in degree (90 to -90)
302
391
  # :return: the timezone name of the matching polygon or None
303
- def timezone_at(lng = 0.0, lat = 0.0)
392
+ def timezone_at(lng: 0.0, lat: 0.0)
304
393
  if lng > 180.0 or lng < -180.0 or lat > 90.0 or lat < -90.0
305
- fail "The coordinates are out ouf bounds: (#{lng}, #{lat})"
394
+ raise "The coordinates are out ouf bounds: (#{lng}, #{lat})"
306
395
  end
307
396
 
308
397
  possible_polygons = shortcuts_of(lng, lat)
@@ -318,24 +407,23 @@ module TimezoneFinder
318
407
  return TIMEZONE_NAMES[id_of(possible_polygons[0])] if nr_possible_polygons == 1
319
408
 
320
409
  # initialize the list of ids
321
- # TODO: sort from least to most occurrences
322
- ids = possible_polygons.map { |p| id_of(p) }
410
+ # and sort possible_polygons from least to most occurrences of zone_id
411
+ possible_polygons, ids, only_one_zone = compile_id_list(possible_polygons, nr_possible_polygons)
412
+
413
+ return TIMEZONE_NAMES[ids[0]] if only_one_zone
323
414
 
324
415
  # otherwise check if the point is included for all the possible polygons
325
416
  (0...nr_possible_polygons).each do |i|
326
417
  polygon_nr = possible_polygons[i]
327
418
 
328
- same_element = Helpers.all_the_same(i, nr_possible_polygons, ids)
329
- return TIMEZONE_NAMES[same_element] if same_element != -1
330
-
331
419
  # get the boundaries of the polygon = (lng_max, lng_min, lat_max, lat_min)
332
420
  @binary_file.seek((@bound_start_address + 16 * polygon_nr))
333
421
  boundaries = Helpers.fromfile(@binary_file, false, 4, 4)
334
- # only run the algorithm if it the point is withing the boundaries
422
+ # only run the expensive algorithm if the point is withing the boundaries
335
423
  unless x > boundaries[0] or x < boundaries[1] or y > boundaries[2] or y < boundaries[3]
336
424
 
337
425
  outside_all_holes = true
338
- # when the point is within a hole of the polygon this timezone doesn't need to be checked
426
+ # when the point is within a hole of the polygon, this timezone doesn't need to be checked
339
427
  _holes_of_line(polygon_nr) do |hole_coordinates|
340
428
  if Helpers.inside_polygon(x, y, hole_coordinates)
341
429
  outside_all_holes = false
@@ -349,6 +437,9 @@ module TimezoneFinder
349
437
  end
350
438
  end
351
439
  end
440
+ # when after the current polygon only polygons from the same zone appear, return this zone
441
+ same_element = Helpers.all_the_same(i + 1, nr_possible_polygons, ids)
442
+ return TIMEZONE_NAMES[same_element] if same_element != -1
352
443
  end
353
444
  nil
354
445
  end
@@ -358,9 +449,9 @@ module TimezoneFinder
358
449
  # :param lng: longitude of the point in degree
359
450
  # :param lat: latitude in degree
360
451
  # :return: the timezone name of the polygon the point is included in or None
361
- def certain_timezone_at(lng = 0.0, lat = 0.0)
452
+ def certain_timezone_at(lng: 0.0, lat: 0.0)
362
453
  if lng > 180.0 or lng < -180.0 or lat > 90.0 or lat < -90.0
363
- fail "The coordinates are out ouf bounds: (#{lng}, #{lat})"
454
+ raise "The coordinates are out ouf bounds: (#{lng}, #{lat})"
364
455
  end
365
456
 
366
457
  possible_polygons = shortcuts_of(lng, lat)
@@ -10,11 +10,11 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ['tasuku-s-github@titech.ac']
11
11
 
12
12
  spec.summary = 'Look up timezone from lat / long offline.'
13
- spec.description = %(
13
+ spec.description = %(
14
14
  A pure Ruby library to look up timezone from latitude / longitude offline.
15
15
  Ported version of 'timezonefinder' on PyPI.
16
16
  ).strip.gsub(/\s+/, ' ')
17
- spec.homepage = 'https://github.com/gunyarakun/timezone_finder'
17
+ spec.homepage = 'https://github.com/gunyarakun/timezone_finder'
18
18
  spec.license = 'MIT'
19
19
 
20
20
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timezone_finder
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.6
4
+ version: 1.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tasuku SUENAGA a.k.a. gunyarakun
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-19 00:00:00.000000000 Z
11
+ date: 2017-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -126,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
126
  version: '0'
127
127
  requirements: []
128
128
  rubyforge_project:
129
- rubygems_version: 2.4.5
129
+ rubygems_version: 2.6.10
130
130
  signing_key:
131
131
  specification_version: 4
132
132
  summary: Look up timezone from lat / long offline.