server-side-google-maps 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Adam Hooper <adam@adamhooper.com>
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'rspec'
4
+ gem 'httparty'
5
+ gem 'nayutaya-googlemaps-polyline', '0.0.1', :require => 'googlemaps_polyline'
6
+
7
+ # Specify your gem's dependencies in server-side-google-maps.gemspec
8
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,3 @@
1
+ All software in this repository is public-domain. It was made so by
2
+ Adam Hooper, <adam@adamhooper.com>. Upon request he can provide it to you
3
+ under any reasonable alternate license: GPL, MPL, MIT, anything.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Server-Side Google Maps: map data from Google, for your server
2
+
3
+ Make requests for direction from Google's servers, and receive them in a
4
+ Ruby-usable object.
5
+
6
+ ## Installation and usage
7
+
8
+ To install:
9
+
10
+ sudo gem install server-side-google-maps
11
+
12
+ Then, to use within Ruby:
13
+
14
+ directions = Directions.new('Montreal, QC', 'Ottawa, ON', :mode => :driving)
15
+ # Origin and destination accept [lat,lon] coordinates as well as strings
16
+ # :mode => :driving is the default. Others are :bicycling and :walking
17
+
18
+ directions.status # 'OK'
19
+ directions.origin_address # 'Montreal, QC, Canada'
20
+ directions.origin_point # [ 45.5086700, -73.5536800 ]
21
+ directions.destination_address # 'Ottawa, ON, Canada'
22
+ directions.destination_point # [ 45.4119000, -75.6984600 ]
23
+ directions.points # List of [lat,lon] coordinates of route
24
+ directions.distance # 199901 (metres)
25
+
26
+ route = Route.new(['Montreal, QC', 'Ottawa, ON', 'Toronto, ON'], :mode => :bicycling)
27
+ # All the same methods apply to route as to directions
28
+
29
+ One final `:mode` is `:direct`, which calculates `points` and estimates
30
+ `distance` without querying Google. To ensure Google isn't queried, input
31
+ the origin and destination as latitude/longitude coordinates.
32
+
33
+ ## Limitations
34
+
35
+ As per the Google Maps API license agreement, the data returned from the
36
+ Google Maps API must be used only for display on a Google Map.
37
+
38
+ I'll write that again: Google Maps data is for Google Maps only! You may not
39
+ do any extra calculating and processing, unless the results of those extra
40
+ calculations are displayed on a Google Map.
41
+
42
+ There are also query limits on each Google API, on the order of a few thousand
43
+ queries each day. Design your software accordingly. (Cache heavily and don't
44
+ query excessively.)
45
+
46
+ ## Development
47
+
48
+ Each feature and issue must have a spec. Please write specs if you can.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/autospec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby1.8
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'autospec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('server-side-google-maps', 'autospec')
data/bin/htmldiff ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby1.8
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'htmldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('server-side-google-maps', 'htmldiff')
data/bin/httparty ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby1.8
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'httparty' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('server-side-google-maps', 'httparty')
data/bin/ldiff ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby1.8
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('server-side-google-maps', 'ldiff')
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby1.8
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('server-side-google-maps', 'rspec')
@@ -0,0 +1,11 @@
1
+ Bundler.setup
2
+ Bundler.require
3
+
4
+ require 'server-side-google-maps/version'
5
+ require 'server-side-google-maps/directions'
6
+ require 'server-side-google-maps/geo_math'
7
+ require 'server-side-google-maps/route'
8
+ require 'server-side-google-maps/server'
9
+
10
+ module ServerSideGoogleMaps
11
+ end
@@ -0,0 +1,137 @@
1
+ module ServerSideGoogleMaps
2
+ class Directions
3
+ LATLNG_STRING_REGEXP = /(-?\d+(\.?\d+)?),(-?\d+(\.?\d+)?)/
4
+
5
+ def self.get(params)
6
+ server = Server.new
7
+ server.get('/maps/api/directions', {:sensor => false}.merge(params))
8
+ end
9
+
10
+ # Initializes directions
11
+ #
12
+ # Parameters:
13
+ # origin: string or [lat,lng] of the first point
14
+ # destination: string or [lat,lng] of the last point
15
+ # params:
16
+ # :mode: :driving, :bicycling and :walking will be passed to Google Maps.
17
+ # Another option, :direct, will avoid in-between points and calculate
18
+ # the distance using the Haversine formula. Defaults to :driving.
19
+ # :find_shortcuts: [ {:factor => Float, :mode => :a_mode}, ... ]
20
+ # For each list item (in the order given), determines if
21
+ # using given :mode will cut the distance to less than
22
+ # :factor and if so, chooses it. For example, if :mode is
23
+ # :bicycling and there's a huge detour because of a missing
24
+ # bike lane, pass { :factor => 0.5, :mode => :driving }
25
+ # and if a shortcut cuts the distance in half that route
26
+ # will be chosen instead.
27
+ def initialize(origin, destination, params = {})
28
+ @origin = origin
29
+ @destination = destination
30
+ params = params.dup
31
+ find_shortcuts = params.delete(:find_shortcuts) || []
32
+ raise ArgumentError.new(':find_shortcuts must be an Array') unless Array === find_shortcuts
33
+ @direct = params[:mode] == :direct
34
+ params[:mode] = :driving if params[:mode] == :direct || params[:mode].nil?
35
+
36
+ origin = origin.join(',') if Array === origin
37
+ destination = destination.join(',') if Array === destination
38
+
39
+ unless @direct && origin_point_without_server && destination_point_without_server
40
+ @data = self.class.get(params.merge(:origin => origin, :destination => destination))
41
+ end
42
+
43
+ if !@direct
44
+ find_shortcuts.each do |try_shortcut|
45
+ factor = try_shortcut[:factor]
46
+ mode = try_shortcut[:mode]
47
+
48
+ other = Directions.new(origin, destination, params.merge(:mode => mode))
49
+ if other.distance.to_f / distance < factor
50
+ @points = other.points
51
+ @distance = other.distance
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def origin_input
58
+ @origin
59
+ end
60
+
61
+ def destination_input
62
+ @destination
63
+ end
64
+
65
+ def origin_address
66
+ leg['start_address']
67
+ end
68
+
69
+ def destination_address
70
+ leg['end_address']
71
+ end
72
+
73
+ def origin_point
74
+ @origin_point ||= origin_point_without_server || [ leg['start_location']['lat'], leg['start_location']['lng'] ]
75
+ end
76
+
77
+ def destination_point
78
+ @destination_point ||= destination_point_without_server || [ leg['end_location']['lat'], leg['end_location']['lng'] ]
79
+ end
80
+
81
+ def status
82
+ @data['status']
83
+ end
84
+
85
+ def points
86
+ @points ||= calculate_points
87
+ end
88
+
89
+ def distance
90
+ @distance ||= calculate_distance
91
+ end
92
+
93
+ private
94
+
95
+ def origin_point_without_server
96
+ calculate_point_without_server_or_nil(origin_input)
97
+ end
98
+
99
+ def destination_point_without_server
100
+ calculate_point_without_server_or_nil(destination_input)
101
+ end
102
+
103
+ def calculate_point_without_server_or_nil(input)
104
+ return input if Array === input
105
+ m = LATLNG_STRING_REGEXP.match(input)
106
+ return [ m[1].to_f, m[3].to_f ] if m
107
+ end
108
+
109
+ def route
110
+ @data['routes'].first
111
+ end
112
+
113
+ def leg
114
+ route['legs'].first
115
+ end
116
+
117
+ def points_and_levels
118
+ @points_and_levels ||= calculate_points_and_levels
119
+ end
120
+
121
+ def calculate_points_and_levels
122
+ polyline = route['overview_polyline']
123
+ ::GoogleMapsPolyline.decode_polyline(polyline['points'], polyline['levels'])
124
+ end
125
+
126
+ def calculate_points
127
+ return [ origin_point, destination_point ] if @direct
128
+ points = points_and_levels.map { |lat,lng,level| [lat,lng] }
129
+ points
130
+ end
131
+
132
+ def calculate_distance
133
+ return GeoMath.latlng_distance(*points) if @direct
134
+ leg['distance']['value']
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,24 @@
1
+ module ServerSideGoogleMaps
2
+ module GeoMath
3
+ RADIUS_OF_EARTH = 6367000 # metres
4
+
5
+ # Returns the distance (in m) between two [lat,lng] points with the Haversine formula
6
+ def self.latlng_distance(pt1, pt2)
7
+ lat1 = pt1[0] * Math::PI / 180
8
+ lon1 = pt1[1] * Math::PI / 180
9
+ lat2 = pt2[0] * Math::PI / 180
10
+ lon2 = pt2[1] * Math::PI / 180
11
+ dlon = lon2 - lon1
12
+ dlat = lat2 - lat1
13
+ a = Math.sin(dlat/2)**2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlon/2)**2
14
+ c = 2 * Math.asin(min(1.0, Math.sqrt(a)))
15
+ (RADIUS_OF_EARTH * c).to_i
16
+ end
17
+
18
+ private
19
+
20
+ def self.min(f1, f2)
21
+ f1 < f2 ? f1 : f2
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,53 @@
1
+ module ServerSideGoogleMaps
2
+ class Route
3
+ def initialize(points, params = {})
4
+ raise ArgumentError if points.length < 2
5
+
6
+ @directionses = points[0..-2].zip(points[1..-1]).map do |origin, destination|
7
+ Directions.new(origin, destination, params)
8
+ end
9
+ end
10
+
11
+ def origin_input
12
+ @directionses.first.origin_input
13
+ end
14
+
15
+ def destination_input
16
+ @directionses.last.destination_input
17
+ end
18
+
19
+ def origin_address
20
+ @directionses.first.origin_address
21
+ end
22
+
23
+ def destination_address
24
+ @directionses.last.destination_address
25
+ end
26
+
27
+ def origin_point
28
+ @directionses.first.origin_point
29
+ end
30
+
31
+ def destination_point
32
+ @directionses.last.destination_point
33
+ end
34
+
35
+ def points
36
+ @points ||= calculate_points
37
+ end
38
+
39
+ def distance
40
+ @distance ||= @directionses.map{|d| d.distance}.inject(:+)
41
+ end
42
+
43
+ private
44
+
45
+ def calculate_points
46
+ pointses = @directionses.map { |d| d.points }
47
+
48
+ first = pointses.shift
49
+
50
+ first + pointses.map{ |p| p[1..-1] }.flatten(1)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,13 @@
1
+ require 'httparty'
2
+
3
+ module ServerSideGoogleMaps
4
+ class Server
5
+ include HTTParty
6
+ base_uri('http://maps.googleapis.com')
7
+
8
+ def get(path, params)
9
+ options = { :query => params }
10
+ self.class.get("#{path}/json", options)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module ServerSideGoogleMaps
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "server-side-google-maps/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "server-side-google-maps"
7
+ s.version = ServerSideGoogleMaps::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Adam Hooper"]
10
+ s.email = ["adam@adamhooper.com"]
11
+ s.homepage = "http://github.com/adamh/server-side-google-maps"
12
+ s.summary = %q{Performs calculations with Google Maps}
13
+ s.description = %q{Servers can use Google Maps, too. This library helps fetch and parse data through the Google Maps v3 API. Stick to the terms of usage, though, and don't use the data Google gives you on anything other than a Google map.}
14
+
15
+ s.add_dependency('httparty')
16
+ s.add_dependency('nayutaya-googlemaps-polyline', '0.0.1')
17
+
18
+ s.rubyforge_project = "server-side-google-maps"
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ end