Module: GeoFaker

Defined in:
lib/geo_faker.rb,
lib/geo_faker/point.rb,
lib/geo_faker/version.rb,
lib/geo_faker/geo_transform.rb

Defined Under Namespace

Classes: GeoTransform, Point

Constant Summary collapse

BASE_URL =
'https://nominatim.openstreetmap.org/search'
VERSION =
"0.1.0"
@@geo_data =
{}

Class Method Summary collapse

Class Method Details

.around(query, radius_in_km:) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/geo_faker.rb', line 36

def self.around(query, radius_in_km:)
  data = geo_data(query)
  lat = data['lat'].to_f
  lon = data['lon'].to_f

  angle = 2 * Math::PI * rand()
  distance = nil
  loop do
    distance = radius_in_km * gaussian_rand()
    break if distance.abs < 3 * radius_in_km
  end

  delta_lat = GeoTransform.km_to_degree_lat(distance * Math.cos(angle))
  delta_lon = GeoTransform.km_to_degree_lon(distance * Math.sin(angle), lat)

  Point.new(
    lat: lat + delta_lat,
    lon: lon + delta_lon,
  )
end

.gaussian_randObject



57
58
59
60
61
62
63
# File 'lib/geo_faker.rb', line 57

def self.gaussian_rand
  theta = 2 * Math::PI * rand
  rho = Math.sqrt(-2 * Math.log(1 - rand))
  x = rho * Math.cos(theta)
  #y = rho * Math.sin(theta)
  x
end

.geo_data(query, with_polygon: false) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/geo_faker.rb', line 13

def self.geo_data(query, with_polygon: false)
  @@geo_data[query] ||= load_geo_data(query, with_polygon: with_polygon)
  if with_polygon && !@@geo_data[query].key?('geojson')
    @@geo_data[query] = load_geo_data(query, with_polygon: with_polygon)
  end
  @@geo_data[query]
end

.load_geo_data(query, with_polygon: false) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/geo_faker.rb', line 21

def self.load_geo_data(query, with_polygon: false)
  response = RestClient.get(BASE_URL, params: {
    q: query,
    format: 'json',
    limit: 1,
    polygon_geojson: with_polygon ? '1' : '0',
  })

  raise "API error: #{response.code}" unless response.code == 200

  data = JSON.parse(response.body)
  raise "No matching result." if data.empty?
  data.first
end

.point_in_poly(poly, point) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/geo_faker.rb', line 103

def self.point_in_poly(poly, point)
  last_point = poly[-1]
  oddNodes = false
  y = point.lon
  x = point.lat

  poly.each do |p|
    yi = p[0]
    xi = p[1]
    yj = last_point[0]
    xj = last_point[1]
    if yi < y && yj >= y ||
        yj < y && yi >= y
      oddNodes = !oddNodes if xi + (y - yi) / (yj - yi) * (xj - xi) < x
    end
    last_point = p
  end

  oddNodes
end

.within(query) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/geo_faker.rb', line 80

def self.within(query)
  data = geo_data(query, with_polygon: true)

  bounds = data['boundingbox'].map(&:to_f)
  south = bounds[0]
  north = bounds[1]
  west = bounds[2]
  east = bounds[3]

  geojson = data['geojson']
  raise 'geojson is not Polygon' unless geojson['type'] == 'Polygon'
  outer_poly = geojson['coordinates'][0]

  loop do
    point = Point.new(
      lat: rand(south..north),
      lon: rand(west..east),
    )

    return point if point_in_poly(outer_poly, point)
  end
end

.within_bounds(query) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/geo_faker.rb', line 65

def self.within_bounds(query)
  data = geo_data(query)
  bounds = data['boundingbox'].map(&:to_f)

  south = bounds[0]
  north = bounds[1]
  west = bounds[2]
  east = bounds[3]

  Point.new(
    lat: rand(south..north),
    lon: rand(west..east),
  )
end