gmaps4rails 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -217,14 +217,32 @@ class @Gmaps4Rails
217
217
  fillColor: fillColor
218
218
  fillOpacity: fillOpacity
219
219
  clickable: false
220
+ map: @map
220
221
 
221
222
  #save polygon in list
222
223
  polygon.serviceObject = new_poly
223
- new_poly.setMap(@map)
224
224
 
225
225
  #////////////////////////////////////////////////////
226
226
  #/////////////////// POLYLINES //////////////////////
227
227
  #////////////////////////////////////////////////////
228
+
229
+ #replace old markers with new markers on an existing map
230
+ replacePolylines : (new_polylines) ->
231
+ #reset previous polylines and kill them from map
232
+ @destroy_polylines()
233
+ #set new polylines
234
+ @polylines = new_polylines
235
+ #create
236
+ @create_polylines()
237
+ #.... and adjust map boundaries
238
+ @adjustMapToBounds()
239
+
240
+ destroy_polylines : ->
241
+ for polyline in @polylines
242
+ #delete polylines from map
243
+ polyline.serviceObject.setMap(null)
244
+ #empty array
245
+ @polylines = []
228
246
 
229
247
  #polylines is an array of arrays. It loops.
230
248
  create_polylines : ->
@@ -382,9 +400,8 @@ class @Gmaps4Rails
382
400
 
383
401
  #to make the map fit the different LatLng points
384
402
  adjustMapToBounds : ->
385
-
386
403
  #FIRST_STEP: retrieve all bounds
387
- #create the bounds object only if necessary
404
+ #create the bounds object only if necessary
388
405
  if @map_options.auto_adjust or @map_options.bounds isnt null
389
406
  @boundsObject = @createLatLngBounds()
390
407
 
@@ -426,9 +443,9 @@ class @Gmaps4Rails
426
443
  @map_options.center_latitude = map_center.lat()
427
444
  @map_options.center_longitude = map_center.lng()
428
445
  @map.setCenter(map_center)
429
- else
446
+ else
430
447
  @fitBounds()
431
-
448
+
432
449
  #////////////////////////////////////////////////////
433
450
  #///////////////// KML //////////////////
434
451
  #////////////////////////////////////////////////////
@@ -171,7 +171,6 @@ class @Gmaps4RailsGoogle extends Gmaps4Rails
171
171
  @hideMarker marker
172
172
 
173
173
  clearMarker : (marker) ->
174
- console.log marker
175
174
  marker.serviceObject.setMap(null)
176
175
 
177
176
  showMarker : (marker) ->
@@ -253,7 +252,7 @@ class @Gmaps4RailsGoogle extends Gmaps4Rails
253
252
  #////////////////////////////////////////////////////
254
253
 
255
254
  fitBounds : ->
256
- @map.fitBounds(@boundsObject)
255
+ @map.fitBounds(@boundsObject) unless @boundsObject.isEmpty()
257
256
 
258
257
  centerMapOnUser : ->
259
258
  @map.setCenter(@userLocation)
@@ -2,15 +2,13 @@ if defined?(Rails) && Rails::VERSION::MAJOR == 3
2
2
  module Gmaps4rails
3
3
  require 'rails'
4
4
  require 'gmaps4rails/base'
5
- require 'gmaps4rails/acts_as_gmappable'
6
- require 'gmaps4rails/extensions/array'
7
- require 'gmaps4rails/extensions/hash'
8
- require 'gmaps4rails/helper/gmaps4rails_helper'
9
5
 
10
6
  class Engine < Rails::Engine
7
+
11
8
  initializer "gmaps4rails view helpers" do |app|
12
9
  ActionView::Base.send :include, Gmaps4railsHelper
13
10
  end
11
+
14
12
  initializer "add asset directories to pipeline" do |app|
15
13
  if Rails::VERSION::MINOR >= 1
16
14
  app.config.assets.paths << "#{root}/public/stylesheets"
@@ -18,7 +16,14 @@ if defined?(Rails) && Rails::VERSION::MAJOR == 3
18
16
  app.middleware.use ::ActionDispatch::Static, "#{root}/public"
19
17
  end
20
18
  end
19
+
20
+ initializer "include acts_as_gmappable within ActiveRecord" do
21
+ if defined?(::ActiveRecord::Base)
22
+ ActiveRecord::Base.send(:include, Gmaps4rails::ActsAsGmappable::InstanceMethods)
23
+ ActiveRecord::Base.send(:extend, Gmaps4rails::ActsAsGmappable::ClassMethods)
24
+ end
25
+ end
26
+
21
27
  end
22
-
23
28
  end
24
29
  end
@@ -1,8 +1,6 @@
1
1
  module Gmaps4rails
2
2
  module ActsAsGmappable
3
3
 
4
- extend ActiveSupport::Concern
5
-
6
4
  module InstanceMethods
7
5
 
8
6
  # This is a before_filter to trigger the geocoding and save its results
@@ -13,9 +11,9 @@ module Gmaps4rails
13
11
  #try to geocode
14
12
  begin
15
13
  coordinates = Gmaps4rails.geocode(self.send(gmaps4rails_options[:address]), gmaps4rails_options[:language], false, gmaps4rails_options[:protocol])
16
- rescue GeocodeStatus, GeocodeInvalidQuery => e #address was invalid, add error to base.
14
+ rescue GeocodeStatus, GeocodeInvalidQuery => e #address was invalid, add error to address.
17
15
  Rails.logger.warn(e)
18
- errors[gmaps4rails_options[:address]] << gmaps4rails_options[:msg] if gmaps4rails_options[:validation]
16
+ errors[gmaps4rails_options[:address]] << gmaps4rails_options[:msg] if Gmaps4rails.condition_eval(self, gmaps4rails_options[:validation])
19
17
  rescue GeocodeNetStatus => e #connection error, No need to prevent save.
20
18
  Rails.logger.warn(e)
21
19
  #TODO add customization here?
@@ -27,7 +25,7 @@ module Gmaps4rails
27
25
  end
28
26
  # Call the callback method to let the user do what he wants with the data
29
27
  self.send(gmaps4rails_options[:callback], coordinates.first[:full_data]) unless gmaps4rails_options[:callback].nil?
30
- if gmaps4rails_options[:check_process] == true
28
+ if Gmaps4rails.condition_eval(self, gmaps4rails_options[:check_process])
31
29
  self[gmaps4rails_options[:checker]] = true
32
30
  end
33
31
  end
@@ -35,7 +33,7 @@ module Gmaps4rails
35
33
 
36
34
  def to_gmaps4rails(&block)
37
35
  json = "["
38
- json << Gmaps4rails.create_json(self, &block).to_s.chop.chop #removes the extra comma
36
+ json << Gmaps4rails.create_json(self, &block) #removes the extra comma
39
37
  json << "]"
40
38
  end
41
39
 
@@ -67,20 +65,9 @@ module Gmaps4rails
67
65
  :protocol => args[:protocol] || "http"
68
66
  }
69
67
  end
70
-
71
- include InstanceMethods
72
-
68
+
73
69
  end
74
70
  end
75
71
 
76
72
  end #ActsAsGmappable
77
- end
78
-
79
- ActiveSupport.on_load(:active_record) do
80
- ActiveRecord::Base.send(:include, Gmaps4rails::ActsAsGmappable)
81
- end
82
-
83
- #::ActiveRecord::Base.send :include, Gmaps4rails::ActsAsGmappable
84
- # Mongoid::Document::ClassMethods.class_eval do
85
- # include Gmaps4rails::ActsAsGmappable::Base
86
- # end
73
+ end
@@ -1,335 +1,63 @@
1
- require 'net/http'
2
- require 'uri'
3
- require 'crack'
1
+ require 'gmaps4rails/acts_as_gmappable'
4
2
 
5
- module Gmaps4rails
6
- DEFAULT_MAP_ID = "map"
7
-
8
- class GeocodeStatus < StandardError; end
9
- class GeocodeNetStatus < StandardError; end
10
- class GeocodeInvalidQuery < StandardError; end
11
- class DirectionStatus < StandardError; end
12
- class DirectionNetStatus < StandardError; end
13
- class DirectionInvalidQuery < StandardError; end
3
+ require 'gmaps4rails/js_handler'
4
+ require 'gmaps4rails/json_handler'
5
+ require 'gmaps4rails/geocoding'
6
+ require 'gmaps4rails/google_places'
7
+ require 'gmaps4rails/extensions/array'
8
+ require 'gmaps4rails/extensions/hash'
14
9
 
15
- # Creates the json related to one Object (only tried ActiveRecord objects)
16
- # This json will contian the marker's attributes of the object
17
- mattr_accessor :json_from_block
10
+ require 'gmaps4rails/helper/gmaps4rails_helper'
18
11
 
19
- def Gmaps4rails.create_json(object, &block)
20
- @json_from_block = String.new
21
- unless object.send(object.gmaps4rails_options[:lat_column]).blank? && object.send(object.gmaps4rails_options[:lng_column]).blank?
22
- Gmaps4rails.block_handling(object, &block)
23
- "{#{description(object)}#{get_title(object)}#{get_sidebar(object)}#{marker_picture(object)}#{@json_from_block}\"lng\": \"#{object.send(object.gmaps4rails_options[:lng_column])}\", \"lat\": \"#{object.send(object.gmaps4rails_options[:lat_column])}\"},\n"
24
- end
25
- end
12
+ module Gmaps4rails
26
13
 
27
- # execute block if provided so that it's included in the json string
28
- def Gmaps4rails.block_handling(object, &block)
29
- block_result = yield(object, ::Gmaps4rails) if block_given?
30
- Gmaps4rails.json(block_result) if block_result.is_a? String
31
- end
32
-
33
- def Gmaps4rails.json(string)
34
- @json_from_block << "#{gsub_string(string)}, "
35
- return true
36
- end
37
-
38
- ################################################
39
- # in use to create json from model
40
- def Gmaps4rails.marker_picture(object)
41
- create_js_for_picture object.gmaps4rails_marker_picture if object.respond_to?("gmaps4rails_marker_picture")
42
- end
43
-
44
- # in use in block
45
- def Gmaps4rails.picture(hash)
46
- @json_from_block << (create_js_for_picture hash)
47
- return true
48
- end
49
-
50
- # Returns picture js from a hash
51
- def Gmaps4rails.create_js_for_picture(raw_hash)
52
- hash = raw_hash.with_indifferent_access
53
- result = hash.map do |k,v|
54
- #specific case, anchors are array and should be interpreted this way
55
- if k.include? "_anchor"
56
- "\"#{k}\": [#{v[0]}, #{v[1]}]"
57
- else
58
- "\"#{k}\": \"#{v}\""
59
- end
60
- end.join(", ") + ", "
61
- end
62
- ##################################################
63
- # in use in block
64
- def Gmaps4rails.infowindow(string)
65
- @json_from_block << (create_js_for_infowindow string)
66
- return true
67
- end
14
+ class GeocodeStatus < StandardError; end
15
+ class GeocodeNetStatus < StandardError; end
16
+ class GeocodeInvalidQuery < StandardError; end
68
17
 
69
- # in use to create json from model
70
- def Gmaps4rails.description(object)
71
- create_js_for_infowindow object.gmaps4rails_infowindow if object.respond_to?("gmaps4rails_infowindow")
72
- end
73
-
74
- def Gmaps4rails.create_js_for_infowindow(string)
75
- "\"description\": \"#{gsub_string(string)}\", "
76
- end
77
-
78
- ##################################################
79
- # in use in block
80
- def Gmaps4rails.title(string)
81
- create_js_for_title string
82
- end
83
-
84
- # in use to create json from model
85
- def Gmaps4rails.get_title(object)
86
- create_js_for_title object.gmaps4rails_title if object.respond_to?("gmaps4rails_title")
87
- end
18
+ class DirectionStatus < StandardError; end
19
+ class DirectionNetStatus < StandardError; end
20
+ class DirectionInvalidQuery < StandardError; end
88
21
 
89
- def Gmaps4rails.create_js_for_title(string)
90
- "\"title\": \"#{gsub_string(string)}\", "
91
- end
92
- ##################################################
22
+ class PlacesStatus < StandardError; end
23
+ class PlacesNetStatus < StandardError; end
24
+ class PlacesInvalidQuery < StandardError; end
93
25
 
94
- # in use in block
95
- def Gmaps4rails.sidebar(string)
96
- create_js_for_sidebar string
97
- end
26
+ mattr_accessor :http_proxy
98
27
 
99
- # in use to create json from model
100
- def Gmaps4rails.get_sidebar(object)
101
- create_js_for_sidebar object.gmaps4rails_sidebar if object.respond_to?("gmaps4rails_sidebar")
102
- end
103
-
104
- def Gmaps4rails.create_js_for_sidebar(string)
105
- "\"sidebar\": \"#{gsub_string(string)}\", "
106
- end
107
- ##################################################
108
-
109
-
110
- def Gmaps4rails.gsub_string(string)
111
- string #you could do override with something like: string.gsub(/\n/, '').gsub(/"/, '\"')
112
- end
113
-
114
- # This method geocodes an address using the GoogleMaps webservice
115
- # options are:
116
- # * address: string, mandatory
117
- # * lang: to set the language one wants the result to be translated (default is english)
118
- # * raw: to get the raw response from google, default is false
119
- def Gmaps4rails.geocode(address, lang="en", raw = false, protocol = "http")
120
- if address.nil? || address.empty?
121
- raise Gmaps4rails::GeocodeInvalidQuery, "You must provide an address"
122
- else #coordinates are valid
123
- geocoder = "#{protocol}://maps.googleapis.com/maps/api/geocode/json?language=#{lang}&address="
124
- output = "&sensor=false"
125
- #send request to the google api to get the lat/lng
126
- request = geocoder + address + output
127
- url = URI.escape(request)
128
- Gmaps4rails.handle_geocoding_response(request, Net::HTTP.get_response(URI.parse(url)), raw)
129
- end # end address valid
28
+ def Gmaps4rails.condition_eval(object, condition)
29
+ case condition
30
+ when Symbol then object.send condition
31
+ when Proc then condition.call(object)
32
+ when TrueClass then condition
33
+ when FalseClass then condition
34
+ end
130
35
  end
131
36
 
132
- # This method retrieves destination results provided by GoogleMaps webservice
133
- # options are:
134
- # * start_end: Hash { "from" => string, "to" => string}, mandatory
135
- # * options: details given in the github's wiki
136
- # * output: could be "pretty", "raw" or "clean"; filters the output from google
137
- #output could be raw, pretty or clean
138
- def Gmaps4rails.destination(start_end, options={}, output="pretty")
139
- if start_end["from"].nil? || start_end["to"].empty?
140
- raise Gmaps4rails::DirectionInvalidQuery, "Origin and destination must be provided in a hash as first argument"
141
- else #great, we have stuff to work with
142
- geocoder = "http://maps.googleapis.com/maps/api/directions/json?origin=#{start_end["from"]}&destination=#{start_end["to"]}"
143
- #if value is an Array, it means it contains the waypoints, otherwise it's chained normally
144
- dest_options = options.empty? ? "" : "&" + options.map {|k,v| v.is_a?(Array) ? k + "=" + v * ("|") : k + "=" + v }*("&")
145
- #send request to the google api to get the directions
146
- request = geocoder + dest_options + "&sensor=false"
147
- url = URI.escape(request)
148
- Gmaps4rails.handle_destination_response(request, Net::HTTP.get_response(URI.parse(url)), output)
149
- end # end origin + destination exist
150
- end #end destination
37
+ private
151
38
 
152
39
  # To create valid js, this method escapes everything but Numeric, true or false
153
40
  def Gmaps4rails.filter(data)
154
- return data if data.is_a?(Numeric) || data.is_a?(TrueClass) || data.is_a?(FalseClass)
155
- "'#{data}'"
156
- end
157
-
158
- private
159
-
160
- def Gmaps4rails.handle_geocoding_response(request, response, raw)
161
- #parse result if result received properly
162
- if response.is_a?(Net::HTTPSuccess)
163
- #parse the json
164
- parse = Crack::JSON.parse(response.body)
165
- #check if google went well
166
- if parse["status"] == "OK"
167
- return parse if raw == true
168
- array = []
169
- parse["results"].each do |result|
170
- array << {
171
- :lat => result["geometry"]["location"]["lat"],
172
- :lng => result["geometry"]["location"]["lng"],
173
- :matched_address => result["formatted_address"],
174
- :bounds => result["geometry"]["bounds"],
175
- :full_data => result
176
- }
177
- end
178
- return array
179
- else #status != OK
180
- raise Gmaps4rails::GeocodeStatus, "The address you passed seems invalid, status was: #{parse["status"]}.
181
- Request was: #{request}"
182
- end #end parse status
183
-
184
- else #if not http success
185
- raise Gmaps4rails::GeocodeNetStatus, "The request sent to google was invalid (not http success): #{request}.
186
- Response was: #{response}"
187
- end #end resp test
188
- end
189
-
190
- def Gmaps4rails.handle_destination_response(request, response, output)
191
- if response.is_a?(Net::HTTPSuccess)
192
- #parse the json
193
- parse = Crack::JSON.parse(response.body)
194
- #check if google went well
195
- if parse["status"] == "OK"
196
- legs = []
197
- #Each element in the legs array specifies a single leg of the journey from the origin to the destination in the calculated route
198
- parse["routes"].first["legs"].each do |leg|
199
- #delete coded polyline elements from legs and store it in polylines to make output cleaner
200
- polylines = leg["steps"].map {|step| step.delete("polyline")} if output == "pretty" || output == "clean"
201
- legs << {
202
- "duration" => { "text" => leg["duration"]["text"], "value" => leg["duration"]["value"].to_f },
203
- "distance" => { "text" => leg["distance"]["text"], "value" => leg["distance"]["value"].to_f },
204
- "steps" => leg["steps"]
205
- }
206
- if output == "pretty"
207
- #polylines contain levels data, which are not that useful.
208
- polylines.map{|poly| poly.delete("levels")}
209
- #create valid json from all polylines, this could be directly passed to javascript for display
210
- json = polylines.map { |poly| {"coded_array" => poly["points"]} }.to_json
211
- #merge results in legs
212
- legs.last.merge!({ "polylines" => json })
213
- end
214
- end
215
- return legs
216
- else #status != OK
217
- raise Gmaps4rails::DirectionStatus, "The query you passed seems invalid, status was: #{parse["status"]}.
218
- Request was: #{request}"
219
- end #end parse status
220
- else #if not http success
221
- raise Gmaps4rails::DirectionNetStatus, "The request sent to google was invalid (not http success): #{request}.
222
- Response was: #{response}"
223
- end #end resp test
224
- end
225
-
226
- ###### JS CREATION #######
227
-
228
- def Gmaps4rails.js_function_name(hash)
229
- "load_" + Gmaps4rails.get_map_id(hash[:map_options])
230
- end
231
-
232
- def Gmaps4rails.get_map_id(hash)
233
- hash.nil? || hash[:id].nil? ? DEFAULT_MAP_ID : hash[:id]
41
+ data.to_json
234
42
  end
235
43
 
236
- def Gmaps4rails.get_constructor(hash)
237
- hash.nil? || hash[:provider].nil? ? "Gmaps4RailsGoogle()" : "Gmaps4Rails#{hash[:provider].capitalize}()"
44
+ # get the response from the url encoded address string
45
+ def Gmaps4rails.get_response(url)
46
+ url = URI.parse(url)
47
+ http = Gmaps4rails.http_agent
48
+ http.get_response(url)
238
49
  end
239
50
 
240
- def Gmaps4rails.create_map_js(map_hash, map_id)
241
- output = Array.new
242
- skipped_keys = [:class, :container_class]
243
- map_hash.each do |option_k, option_v|
244
- unless skipped_keys.include? option_k.to_sym
245
- case option_k.to_sym
246
- when :bounds, :raw #particular case, render the content unescaped
247
- output << "#{map_id}.map_options.#{option_k} = #{option_v};"
248
- else
249
- output << "#{map_id}.map_options.#{option_k} = #{Gmaps4rails.filter option_v};"
250
- end
251
- end
51
+ # looks for proxy settings and returns a Net::HTTP or Net::HTTP::Proxy class
52
+ def Gmaps4rails.http_agent
53
+ proxy = ENV['HTTP_PROXY'] || ENV['http_proxy'] || self.http_proxy
54
+ if proxy
55
+ proxy = URI.parse(proxy)
56
+ http_agent = Net::HTTP::Proxy(proxy.host,proxy.port)
57
+ else
58
+ http_agent = Net::HTTP
252
59
  end
253
- output
60
+ http_agent
254
61
  end
255
62
 
256
- def Gmaps4rails.create_general_js(hash, map_id, category)
257
- hash = hash.with_indifferent_access
258
- output = Array.new
259
- output << "#{map_id}.#{category} = #{hash[:data]};"
260
- hash[:options] ||= Array.new
261
- hash[:options].each do |option_k, option_v|
262
- if option_k.to_sym == :raw
263
- output << "#{map_id}.#{category}_conf.#{option_k} = #{option_v};"
264
- else
265
- output << "#{map_id}.#{category}_conf.#{option_k} = #{Gmaps4rails.filter option_v};"
266
- end
267
- end
268
- output << "#{map_id}.create_#{category}();"
269
- output
270
- end
271
-
272
- def Gmaps4rails.create_direction_js(hash, map_id)
273
- hash = hash.with_indifferent_access
274
- output = Array.new
275
- output << "#{map_id}.direction_conf.origin = '#{hash["data"]["from"]}';"
276
- output << "#{map_id}.direction_conf.destination = '#{hash["data"]["to"]}';"
277
- hash[:options] ||= Array.new
278
- hash[:options].each do |option_k, option_v|
279
- if option_k.to_sym == :waypoints
280
- waypoints = Array.new
281
- option_v.each do |waypoint|
282
- waypoints << { "location" => waypoint, "stopover" => true }.to_json
283
- end
284
- output << "#{map_id}.direction_conf.waypoints = [#{waypoints * (",")}];"
285
- else #option_k != "waypoint"
286
- output << "#{map_id}.direction_conf.#{option_k} = #{Gmaps4rails.filter option_v};"
287
- end
288
- end #end .each
289
- output << "#{map_id}.create_direction();"
290
- output
291
- end
292
-
293
- def Gmaps4rails.create_js_from_hash(hash)
294
- #the variable 'options' must have the following structure
295
- #{
296
- # :map_options => hash,
297
- # :markers => { :data => json, :options => hash },
298
- # :polylines => { :data => json, :options => hash },
299
- # :polygons => { :data => json, :options => hash },
300
- # :circles => { :data => json, :options => hash },
301
- # :direction => { :data => hash, :options => hash },
302
- # :kml => { :data => json, :options => hash }
303
- #}
304
- result = Array.new
305
- map_id = "Gmaps." + Gmaps4rails.get_map_id(hash[:map_options])
306
-
307
- #means we are creating a new map
308
- result << "#{map_id} = new #{Gmaps4rails.get_constructor hash[:map_options] }" + ";"
309
- result << "Gmaps.#{Gmaps4rails.js_function_name hash } = function() {"
310
- result << Gmaps4rails.create_map_js(hash[:map_options], map_id) unless hash[:map_options].nil?
311
- result << "#{map_id}.initialize();"
312
-
313
- hash.each do |category, content| #loop through options hash
314
- skipped_categories = [:map_options, :last_map, :scripts]
315
- unless skipped_categories.include? category.to_sym
316
- if category.to_sym == :direction
317
- result << Gmaps4rails.create_direction_js(content, map_id)
318
- else
319
- result << Gmaps4rails.create_general_js(content, map_id, category)
320
- end
321
- end
322
- end
323
- result << "#{map_id}.adjustMapToBounds();"
324
- result << "#{map_id}.callback();"
325
-
326
- result << "};"
327
- if hash[:last_map].nil? || hash[:last_map] == true
328
- result << "window.onload = function() { Gmaps.loadMaps(); };"
329
- end
330
-
331
- result * ('
332
- ')
333
- end
334
-
335
- end
63
+ end
@@ -2,11 +2,12 @@ class Array
2
2
  #Scopes on models generate Arrays
3
3
  #this method enables short call to the json creation for all elements in the array
4
4
  def to_gmaps4rails(&block)
5
- json = "["
5
+ output = "["
6
+ json_array = []
6
7
  each do |object|
7
- json << Gmaps4rails.create_json(object, &block).to_s
8
+ json_array << Gmaps4rails.create_json(object, &block).to_s
8
9
  end
9
- json.chop!.chop! unless json == "["
10
- json << "]"
10
+ output << json_array * (",")
11
+ output << "]"
11
12
  end
12
13
  end
@@ -0,0 +1,115 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'json'
4
+
5
+ module Gmaps4rails
6
+
7
+ # This method geocodes an address using the GoogleMaps webservice
8
+ # options are:
9
+ # * address: string, mandatory
10
+ # * lang: to set the language one wants the result to be translated (default is english)
11
+ # * raw: to get the raw response from google, default is false
12
+ def Gmaps4rails.geocode(address, lang="en", raw = false, protocol = "http")
13
+ if address.nil? || address.empty?
14
+ raise Gmaps4rails::GeocodeInvalidQuery, "You must provide an address"
15
+ else #coordinates are valid
16
+ geocoder = "#{protocol}://maps.googleapis.com/maps/api/geocode/json?language=#{lang}&address="
17
+ output = "&sensor=false"
18
+ #send request to the google api to get the lat/lng
19
+ request = geocoder + address + output
20
+ url = URI.escape(request)
21
+ Gmaps4rails.handle_geocoding_response(request, Gmaps4rails.get_response(url), raw)
22
+ end # end address valid
23
+ end
24
+
25
+ # This method retrieves destination results provided by GoogleMaps webservice
26
+ # options are:
27
+ # * start_end: Hash { "from" => string, "to" => string}, mandatory
28
+ # * options: details given in the github's wiki
29
+ # * output: could be "pretty", "raw" or "clean"; filters the output from google
30
+ #output could be raw, pretty or clean
31
+ def Gmaps4rails.destination(start_end, options={}, output="pretty")
32
+ if start_end["from"].nil? || start_end["to"].empty?
33
+ raise Gmaps4rails::DirectionInvalidQuery, "Origin and destination must be provided in a hash as first argument"
34
+ else #great, we have stuff to work with
35
+ geocoder = "http://maps.googleapis.com/maps/api/directions/json?origin=#{start_end["from"]}&destination=#{start_end["to"]}"
36
+ #if value is an Array, it means it contains the waypoints, otherwise it's chained normally
37
+ dest_options = options.empty? ? "" : "&" + options.map {|k,v| v.is_a?(Array) ? k + "=" + v * ("|") : k + "=" + v }*("&")
38
+ #send request to the google api to get the directions
39
+ request = geocoder + dest_options + "&sensor=false"
40
+ url = URI.escape(request)
41
+ Gmaps4rails.handle_destination_response(request, Gmaps4rails.get_response(url), output)
42
+ end # end origin + destination exist
43
+ end #end destination
44
+
45
+ private
46
+
47
+ def Gmaps4rails.handle_geocoding_response(request, response, raw)
48
+ #parse result if result received properly
49
+ if response.is_a?(Net::HTTPSuccess)
50
+ #parse the json
51
+ #parse = Crack::JSON.parse(response.body)
52
+ parse = JSON.parse(response.body)
53
+ #check if google went well
54
+ if parse["status"] == "OK"
55
+ return parse if raw == true
56
+ array = []
57
+ parse["results"].each do |result|
58
+ array << {
59
+ :lat => result["geometry"]["location"]["lat"],
60
+ :lng => result["geometry"]["location"]["lng"],
61
+ :matched_address => result["formatted_address"],
62
+ :bounds => result["geometry"]["bounds"],
63
+ :full_data => result
64
+ }
65
+ end
66
+ return array
67
+ else #status != OK
68
+ raise Gmaps4rails::GeocodeStatus, "The address you passed seems invalid, status was: #{parse["status"]}.
69
+ Request was: #{request}"
70
+ end #end parse status
71
+
72
+ else #if not http success
73
+ raise Gmaps4rails::GeocodeNetStatus, "The request sent to google was invalid (not http success): #{request}.
74
+ Response was: #{response}"
75
+ end #end resp test
76
+ end
77
+
78
+ def Gmaps4rails.handle_destination_response(request, response, output)
79
+ if response.is_a?(Net::HTTPSuccess)
80
+ #parse the json
81
+ #parse = Crack::JSON.parse(response.body)
82
+ parse = JSON.parse(response.body)
83
+ #check if google went well
84
+ if parse["status"] == "OK"
85
+ legs = []
86
+ #Each element in the legs array specifies a single leg of the journey from the origin to the destination in the calculated route
87
+ parse["routes"].first["legs"].each do |leg|
88
+ #delete coded polyline elements from legs and store it in polylines to make output cleaner
89
+ polylines = leg["steps"].map {|step| step.delete("polyline")} if output == "pretty" || output == "clean"
90
+ legs << {
91
+ "duration" => { "text" => leg["duration"]["text"], "value" => leg["duration"]["value"].to_f },
92
+ "distance" => { "text" => leg["distance"]["text"], "value" => leg["distance"]["value"].to_f },
93
+ "steps" => leg["steps"]
94
+ }
95
+ if output == "pretty"
96
+ #polylines contain levels data, which are not that useful.
97
+ polylines.map{|poly| poly.delete("levels")}
98
+ #create valid json from all polylines, this could be directly passed to javascript for display
99
+ json = polylines.map { |poly| {"coded_array" => poly["points"]} }.to_json
100
+ #merge results in legs
101
+ legs.last.merge!({ "polylines" => json })
102
+ end
103
+ end
104
+ return legs
105
+ else #status != OK
106
+ raise Gmaps4rails::DirectionStatus, "The query you passed seems invalid, status was: #{parse["status"]}.
107
+ Request was: #{request}"
108
+ end #end parse status
109
+ else #if not http success
110
+ raise Gmaps4rails::DirectionNetStatus, "The request sent to google was invalid (not http success): #{request}.
111
+ Response was: #{response}"
112
+ end #end resp test
113
+ end
114
+
115
+ end
@@ -0,0 +1,76 @@
1
+ module Gmaps4rails
2
+
3
+ # does two things... 1) gecode the given address string and 2) triggers a a places query around that geo location
4
+ # optionally a keyword can be given for a filter over all places fields (e.g. "Bungy" to give all Bungy related places)
5
+ # IMPORTANT: Places API calls require an API key (param "key")
6
+
7
+ def Gmaps4rails.places_for_address(address, key, keyword = nil, radius = 7500, lang="en", raw = false)
8
+ if address.nil?
9
+ raise Gmaps4rails::GeocodeInvalidQuery, "you must provide an address for a places_for_address query"
10
+ elsif key.nil?
11
+ raise "Google Places API requires an API key"
12
+ else
13
+ res = Gmaps4rails.geocode(address) # will throw exception if nothing could be geocoded
14
+ Gmaps4rails.places(res.first[:lat], res.first[:lng], key, keyword, radius, lang, raw)
15
+ end
16
+ end
17
+
18
+ # does a places query around give geo location (lat/lng)
19
+ # optionally a keyword can be given for a filter over all places fields (e.g. "Bungy" to give all Bungy related places)
20
+ # IMPORTANT: Places API calls require an API key (param "key")
21
+ def Gmaps4rails.places(lat, lng, key, keyword = nil, radius = 7500, lang="en", raw = false, protocol = "https")
22
+ if lat.nil? || lng.nil?
23
+ raise Gmaps4rails::PlacesInvalidQuery, "You must provide at least a lat/lon for a Google places query"
24
+ elsif key.nil?
25
+ raise "Google Places API requires an API key"
26
+ else #lat/lng are valid
27
+ geocoder = "#{protocol}://maps.googleapis.com/maps/api/place/search/json?language=#{lang}&location=#{[lat.to_s, lng.to_s].join(",")}"
28
+ output = "&sensor=false&radius=#{radius}&key=#{key}"
29
+
30
+ # add filter keyword
31
+ geocoder += "&keyword=#{keyword}" unless keyword.nil?
32
+
33
+ #send request to the google api to get the lat/lng
34
+ request = geocoder + output
35
+ url = URI.escape(request)
36
+ uri = URI.parse(url)
37
+ http = Net::HTTP.new(uri.host, uri.port)
38
+ http.use_ssl = true # Places API wants https
39
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE # to avoid any cert issues
40
+
41
+ Gmaps4rails.handle_places_response(request, http.request(Net::HTTP::Get.new(uri.request_uri)), raw)
42
+ end # end lat/lng valid
43
+ end
44
+
45
+ # handles the places query response
46
+ def Gmaps4rails.handle_places_response(request, response, raw)
47
+ #parse result if result received properly
48
+ if response.is_a?(Net::HTTPSuccess)
49
+ #parse the json
50
+ parse = JSON.parse(response.body)
51
+ #check if google went well
52
+ if parse["status"] == "OK"
53
+ return parse if raw == true
54
+ array = []
55
+ parse["results"].each do |result|
56
+ array << {
57
+ :lat => result["geometry"]["location"]["lat"],
58
+ :lng => result["geometry"]["location"]["lng"],
59
+ :name => result["name"],
60
+ :reference => result["reference"],
61
+ :vicinity => result["vicinity"],
62
+ :full_data => result
63
+ }
64
+ end
65
+ return array
66
+ else #status != OK
67
+ raise Gmaps4rails::PlacesStatus, "The address you passed seems invalid, status was: #{parse["status"]}.
68
+ Request was: #{request}"
69
+ end #end parse status
70
+
71
+ else #if not http success
72
+ raise Gmaps4rails::PlacesNetStatus, "The request sent to google was invalid (not http success): #{request}.
73
+ Response was: #{response}"
74
+ end #end resp test
75
+ end
76
+ end
@@ -0,0 +1,113 @@
1
+ module Gmaps4rails
2
+
3
+ DEFAULT_MAP_ID = "map"
4
+
5
+ def Gmaps4rails.js_function_name(hash)
6
+ "load_" + Gmaps4rails.get_map_id(hash[:map_options])
7
+ end
8
+
9
+ def Gmaps4rails.get_map_id(hash)
10
+ hash.nil? || hash[:id].nil? ? DEFAULT_MAP_ID : hash[:id]
11
+ end
12
+
13
+ def Gmaps4rails.get_constructor(hash)
14
+ hash.nil? || hash[:provider].nil? ? "Gmaps4RailsGoogle()" : "Gmaps4Rails#{hash[:provider].capitalize}()"
15
+ end
16
+
17
+ def Gmaps4rails.create_map_js(map_hash, map_id)
18
+ output = Array.new
19
+ skipped_keys = [:class, :container_class]
20
+ map_hash.each do |option_k, option_v|
21
+ unless skipped_keys.include? option_k.to_sym
22
+ case option_k.to_sym
23
+ when :bounds, :raw #particular case, render the content unescaped
24
+ output << "#{map_id}.map_options.#{option_k} = #{option_v};"
25
+ else
26
+ output << "#{map_id}.map_options.#{option_k} = #{Gmaps4rails.filter option_v};"
27
+ end
28
+ end
29
+ end
30
+ output
31
+ end
32
+
33
+ def Gmaps4rails.create_general_js(hash, map_id, category)
34
+ hash = hash.with_indifferent_access
35
+ output = Array.new
36
+ output << "#{map_id}.#{category} = #{hash[:data]};"
37
+ hash[:options] ||= Array.new
38
+ hash[:options].each do |option_k, option_v|
39
+ if option_k.to_sym == :raw
40
+ output << "#{map_id}.#{category}_conf.#{option_k} = #{option_v};"
41
+ else
42
+ output << "#{map_id}.#{category}_conf.#{option_k} = #{Gmaps4rails.filter option_v};"
43
+ end
44
+ end
45
+ output << "#{map_id}.create_#{category}();"
46
+ output
47
+ end
48
+
49
+ def Gmaps4rails.create_direction_js(hash, map_id)
50
+ hash = hash.with_indifferent_access
51
+ output = Array.new
52
+ output << "#{map_id}.direction_conf.origin = '#{hash["data"]["from"]}';"
53
+ output << "#{map_id}.direction_conf.destination = '#{hash["data"]["to"]}';"
54
+ hash[:options] ||= Array.new
55
+ hash[:options].each do |option_k, option_v|
56
+ if option_k.to_sym == :waypoints
57
+ waypoints = Array.new
58
+ option_v.each do |waypoint|
59
+ waypoints << { "location" => waypoint, "stopover" => true }.to_json
60
+ end
61
+ output << "#{map_id}.direction_conf.waypoints = [#{waypoints * (",")}];"
62
+ else #option_k != "waypoint"
63
+ output << "#{map_id}.direction_conf.#{option_k} = #{Gmaps4rails.filter option_v};"
64
+ end
65
+ end #end .each
66
+ output << "#{map_id}.create_direction();"
67
+ output
68
+ end
69
+
70
+ #the variable 'hash' must have the following structure
71
+ #{
72
+ # :map_options => hash,
73
+ # :markers => { :data => json, :options => hash },
74
+ # :polylines => { :data => json, :options => hash },
75
+ # :polygons => { :data => json, :options => hash },
76
+ # :circles => { :data => json, :options => hash },
77
+ # :direction => { :data => hash, :options => hash },
78
+ # :kml => { :data => json, :options => hash }
79
+ #}
80
+ def Gmaps4rails.create_js_from_hash(hash)
81
+ result = Array.new
82
+ map_id = "Gmaps." + Gmaps4rails.get_map_id(hash[:map_options])
83
+
84
+ #means we are creating a new map
85
+ result << "#{map_id} = new #{Gmaps4rails.get_constructor hash[:map_options] }" + ";"
86
+ result << "Gmaps.#{Gmaps4rails.js_function_name hash } = function() {"
87
+ result << Gmaps4rails.create_map_js(hash[:map_options], map_id) unless hash[:map_options].nil?
88
+ result << "#{map_id}.initialize();"
89
+
90
+ hash.each do |category, content| #loop through options hash
91
+ skipped_categories = [:map_options, :last_map, :scripts]
92
+ unless skipped_categories.include? category.to_sym
93
+ if category.to_sym == :direction
94
+ result << Gmaps4rails.create_direction_js(content, map_id)
95
+ else
96
+ result << Gmaps4rails.create_general_js(content, map_id, category)
97
+ end
98
+ end
99
+ end
100
+ result << "#{map_id}.adjustMapToBounds();"
101
+ result << "#{map_id}.callback();"
102
+
103
+ result << "};"
104
+ if hash[:last_map].nil? || hash[:last_map] == true
105
+ result << "window.onload = function() { Gmaps.loadMaps(); };"
106
+ end
107
+
108
+ result * ('
109
+ ')
110
+ end
111
+
112
+ end
113
+
@@ -0,0 +1,111 @@
1
+ module Gmaps4rails
2
+
3
+ mattr_accessor :json_hash, :custom_json, :object
4
+
5
+ def Gmaps4rails.create_json(object, &block)
6
+ @object, @json_hash, @custom_json = object, Hash.new, nil
7
+ if compliant?
8
+ handle_block(&block) if block_given?
9
+ handle_model_methods
10
+ return_json
11
+ end
12
+ end
13
+
14
+ def Gmaps4rails.infowindow(string)
15
+ @json_hash[:description] = string
16
+ end
17
+
18
+ def Gmaps4rails.title(string)
19
+ @json_hash[:title] = string
20
+ end
21
+
22
+ def Gmaps4rails.sidebar(string)
23
+ @json_hash[:sidebar] = string
24
+ end
25
+
26
+ def Gmaps4rails.json(json)
27
+ return @json_hash.merge! json if json.is_a? Hash
28
+ json
29
+ end
30
+
31
+ def Gmaps4rails.picture(hash)
32
+ @json_hash.merge! hash
33
+ end
34
+
35
+ private
36
+
37
+ def Gmaps4rails.model_attributes
38
+ {
39
+ :description => :gmaps4rails_infowindow,
40
+ :title => :gmaps4rails_title,
41
+ :sidebar => :gmaps4rails_sidebar,
42
+ :marker_picture => :gmaps4rails_marker_picture,
43
+ :lat => @object.gmaps4rails_options[:lat_column],
44
+ :lng => @object.gmaps4rails_options[:lng_column]
45
+ }
46
+ end
47
+
48
+ def Gmaps4rails.handle_model_methods
49
+ model_attributes.each do |json_name, method_name|
50
+ if @object.respond_to? method_name
51
+ if json_name == :marker_picture
52
+ @json_hash.merge! @object.send(method_name)
53
+ else
54
+ @json_hash[json_name] = @object.send(method_name)
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ # returns the proper json
61
+ # three cases here:
62
+ # - no custom json provided
63
+ # - custom json provided as a hash (marker.json { :id => user.id }) => merge hashes then create json
64
+ # - custom json provided as string (marker.json {"\"id\": #{user.id}" } => create json from hash then insert string inside
65
+ def Gmaps4rails.return_json
66
+ return @json_hash.to_json if @custom_json.nil?
67
+ if @custom_json.is_a? Hash
68
+ @json_hash.merge! @custom_json
69
+ return @json_hash.to_json
70
+ elsif @custom_json.is_a? String
71
+ output = @json_hash.to_json
72
+ return output.insert(1, @custom_json + ",")
73
+ end
74
+ end
75
+
76
+ def Gmaps4rails.compliant?
77
+ !(@object.send(@object.gmaps4rails_options[:lat_column]).blank? && @object.send(@object.gmaps4rails_options[:lng_column]).blank?)
78
+ end
79
+
80
+ # the to_gmaps4rails method accepts a block to customize:
81
+ # - infowindow
82
+ # - picture
83
+ # - title
84
+ # - sidebar
85
+ # - json
86
+ #
87
+ # This works this way:
88
+ # @json = User.all.to_gmaps4rails do |user, marker|
89
+ # marker.infowindow render_to_string(:partial => "/users/my_template", :locals => { :object => user}).gsub(/\n/, '').gsub(/"/, '\"')
90
+ # marker.picture({
91
+ # :picture => "http://www.blankdots.com/img/github-32x32.png",
92
+ # :width => "32",
93
+ # :height => "32"
94
+ # })
95
+ # marker.title "i'm the title"
96
+ # marker.sidebar "i'm the sidebar"
97
+ # marker.json { :id => user.id }
98
+ # end
99
+ #
100
+ # For backward compability, a mere string could be passed:
101
+ # @json = User.all.to_gmaps4rails do |user, marker|
102
+ # "\"id\": #{user.id}"
103
+ # end
104
+ #
105
+ def Gmaps4rails.handle_block(&block)
106
+ block_result = yield(@object, ::Gmaps4rails)
107
+ return Gmaps4rails.json(block_result) unless block_result.is_a? String
108
+ @custom_json = block_result
109
+ end
110
+
111
+ end
@@ -193,7 +193,6 @@
193
193
  return _results;
194
194
  };
195
195
  Gmaps4RailsGoogle.prototype.clearMarker = function(marker) {
196
- console.log(marker);
197
196
  return marker.serviceObject.setMap(null);
198
197
  };
199
198
  Gmaps4RailsGoogle.prototype.showMarker = function(marker) {
@@ -277,7 +276,9 @@
277
276
  return kml;
278
277
  };
279
278
  Gmaps4RailsGoogle.prototype.fitBounds = function() {
280
- return this.map.fitBounds(this.boundsObject);
279
+ if (!this.boundsObject.isEmpty()) {
280
+ return this.map.fitBounds(this.boundsObject);
281
+ }
281
282
  };
282
283
  Gmaps4RailsGoogle.prototype.centerMapOnUser = function() {
283
284
  return this.map.setCenter(this.userLocation);
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gmaps4rails
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 7
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 3
9
- - 2
10
- version: 1.3.2
8
+ - 4
9
+ - 0
10
+ version: 1.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Benjamin Roth
@@ -16,10 +16,10 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-10-31 00:00:00 Z
19
+ date: 2012-01-02 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: crack
22
+ name: json
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
@@ -62,7 +62,11 @@ files:
62
62
  - lib/gmaps4rails/base.rb
63
63
  - lib/gmaps4rails/extensions/array.rb
64
64
  - lib/gmaps4rails/extensions/hash.rb
65
+ - lib/gmaps4rails/geocoding.rb
66
+ - lib/gmaps4rails/google_places.rb
65
67
  - lib/gmaps4rails/helper/gmaps4rails_helper.rb
68
+ - lib/gmaps4rails/js_handler.rb
69
+ - lib/gmaps4rails/json_handler.rb
66
70
  - public/images/marker.png
67
71
  - public/javascripts/gmaps4rails/gmaps4rails.base.js
68
72
  - public/javascripts/gmaps4rails/gmaps4rails.bing.js