timezone_finder 1.5.6 → 1.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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.