cirandas.net

ref: master

app/services/noosfero/geo_ref.rb


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
module Noosfero::GeoRef

  # May replace this module by http://www.postgresql.org/docs/9.3/static/earthdistance.html

  EARTH_RADIUS = 6378 # aproximate in km

  class << self

    def dist(lat1, lng1, lat2, lng2)
      def deg2rad(d); (d*Math::PI)/180; end
      def c(n); Math.cos(n); end
      def s(n); Math.sin(n); end
      lat1 = deg2rad lat1
      lat2 = deg2rad lat2
      dlng = deg2rad(lng2) - deg2rad(lng1)
      EARTH_RADIUS * Math.atan2(
        Math.sqrt(
          ( c(lat2) * s(dlng) )**2 +
          ( c(lat1) * s(lat2) - s(lat1) * c(lat2) * c(dlng) )**2
        ),
        s(lat1) * s(lat2) + c(lat1) * c(lat2) * c(dlng)
      )
    end

    # Write a SQL expression to return the distance from a profile to a
    # reference point, in kilometers.
    # http://www.plumislandmedia.net/mysql/vicenty-great-circle-distance-formula
    def sql_dist(ref_lat, ref_lng)
      "2*PI()*#{EARTH_RADIUS}*(
        DEGREES(
          ATAN2(
            SQRT(
              POW(COS(RADIANS(#{ref_lat}))*SIN(RADIANS(#{ref_lng}-lng)),2) +
              POW(
                COS(RADIANS(lat)) * SIN(RADIANS(#{ref_lat})) - (
                  SIN(RADIANS(lat)) * COS(RADIANS(#{ref_lat})) * COS(RADIANS(#{ref_lng}-lng))
                ), 2
              )
            ),
            SIN(RADIANS(lat)) * SIN(RADIANS(#{ref_lat})) +
            COS(RADIANS(lat)) * COS(RADIANS(#{ref_lat})) * COS(RADIANS(#{ref_lng}-lng))
          )
        )/360
      )"
    end

    # Asks Google for the georef of a location.
    def location_to_georef(location)
      key = location.downcase
      ll = Rails.cache.read key
      return ll + [:CACHE] if ll.kind_of? Array
      resp = RestClient.get 'https://maps.googleapis.com/maps/api/geocode/json?' +
                            'sensor=false&address=' + url_encode(location)
      if resp.nil? || resp.code.to_i != 200
        if ENV['RAILS_ENV'] == 'test'
          print " Google Maps API fail (code #{resp ? resp.code : :nil}) "
        else
          Rails.logger.warn "Google Maps API request information for " +
                            "\"#{location}\" fail. (code #{resp ? resp.code : :nil})"
        end
        return [ 0, 0, "HTTP_FAIL_#{resp.code}".to_sym ] # do not cache failed response
      else
        json = JSON.parse resp.body
        if json && (r=json['results']) && (r=r[0]) && (r=r['geometry']) &&
           (r=r['location']) && r['lat']
          ll = [ r['lat'], r['lng'], :SUCCESS ]
        else
          status = json['status'] || 'Undefined Error'
          message = "Google Maps API cant find \"#{location}\" (#{status})"
          if ENV['RAILS_ENV'] == 'test'
            print " #{message} "
          else
            Rails.logger.warn message
          end
          ll = [ 0, 0, status.to_sym ]
        end
        Rails.cache.write key, ll
      end
      ll
    end

  end

end