gmaps4rails 1.3.2 → 1.4.0

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.
@@ -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