PageRenderTime 26ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/v3/cartographer/gmap.rb

https://github.com/antond/cartographer
Ruby | 312 lines | 205 code | 49 blank | 58 comment | 69 complexity | 4903fe70e70fdeef6bcb8026c4214784 MD5 | raw file
  1. # The core of the library. To display a map, you must first create it. For details, see <tt>#new</tt>. Then, you can tweak various parameters of map display, detailed below:
  2. #
  3. # +draggable+:: Set to +false+ to disable the user's ability to drag the map around.
  4. # +type+:: The type of map to display. Can be set to +roadmap+ (default), +satellite+, +terrain+ or +hybrid+.
  5. # +controls+:: Specify which map controls you would like displayed. See below for more details.
  6. # +center+:: An array specifying the center point of the map, as [ +latitude+, +longitude+ ]
  7. # +zoom+:: Specify the zoom level of the map. 0 is world view, 17 is closest.
  8. # +debug+:: Set to +true+ to enable debugging output in the Cartographer html (see <tt>#to_html</tt> and <tt>#to_js</tt> for more)
  9. # +marker_mgr+ Set to +true+ to use a MarkerManager utility library to manage markers. Set markers min_zoom, max_zoom attributes to control the display of markers.
  10. # +current_marker+ Set to variable name of one of the markers to display the marker unfo window on load.
  11. # ==== Collections
  12. #
  13. # +icons+:: A collection of <tt>Cartographer::Gicon</tt> objects to be added to the map.
  14. # +markers+:: A collection of <tt>Cartographer::Gmarker</tt> objects to be added to the map.
  15. # +polylines+:: A collection of <tt>Cartographer::GpolyLine</tt> objects to be added to the map.
  16. # +ad+:: A <tt>Cartographer::Gad</tt> object to be added to the map.
  17. #
  18. # ==== Control types
  19. #
  20. # +small+:: A small navigation/zoom control in the upper-left. Conflicts with +large+ and +zoom+.
  21. # +large+:: A large navigation/zoom control in the upper-left. Conflicts with +small+ and +zoom+.
  22. # +zoom+:: A zoom control in the the upper-left. Conflicts with +small+ and +large+.
  23. # +type+:: Buttons to change the map type, shown in the upper-right
  24. # +overview+:: A small, collapsible overview map, shown in the lower-right
  25. # +scale+:: Scale of current map/zoom level, shown in the lower-left
  26. class Cartographer::Gmap
  27. attr_accessor :dom_id, :draggable, :polylines,:type, :controls,
  28. :markers, :ad, :center, :zoom, :scroll, :icons, :debug, :marker_mgr, :current_marker, :marker_clusterer, :shared_info_window, :marker_clusterer_icons,
  29. :type
  30. @@window_onload = ""
  31. # Create a new <tt>Cartographer::Gmap</tt> object.
  32. # Cartographer::Gmap.new('map')
  33. #
  34. # +dom_id+:: DOM ID of HTML element that will contain the map.
  35. # +opts+:: Initializing options for the map, includes :draggable,
  36. # :type, :controls, :center, :zoom, and :debug
  37. #
  38. # You can also pass a block to set options, like:
  39. #
  40. # Cartographer::Gmap.new('map') do |m|
  41. # m.zoom = 2
  42. # m.debug = false
  43. # end
  44. def initialize(dom_id, opts = {}, &block)
  45. @dom_id = dom_id
  46. @draggable = opts[:draggable]
  47. @type = opts[:type] || :roadmap
  48. @controls = opts[:controls] || [ :zoom ]
  49. @center = opts[:center] || [0,0]
  50. @zoom = opts[:zoom] || 1
  51. @scroll = opts[:scroll] || true
  52. @type = opts[:type] || "ROADMAP"
  53. @debug = opts[:debug]
  54. @markers = []
  55. @polylines = []
  56. @icons = []
  57. @ad = []
  58. @move_delay = 2000
  59. @marker_mgr = opts[:marker_mgr] || false
  60. @current_marker = opts[:current_marker] || nil
  61. @marker_clusterer = false #by default marker_clustering is disabled
  62. @shared_info_window = opts[:shared_info_window] || Cartographer::InfoWindow.new(:name => "default_shared_info_window",:content => '')
  63. @marker_clusterer_icons = opts[:marker_clusterer_icons] || []
  64. yield self if block_given?
  65. end
  66. # Generates the HTML used to display the map. Set
  67. # :include_onload to false if you don't want the map
  68. # loaded in the window.onload function (for example, if
  69. # you're generating it after an Ajax call).
  70. #
  71. # @map.to_html(:include_onload => true)
  72. def to_html(opts = {:include_onload => true})
  73. @markers.each { |m| m.map = self } # markers need to know their parent
  74. @center ||= auto_center
  75. html = []
  76. # setup the JS header
  77. html << "<!-- initialize the google map and your markers -->" if @debug
  78. html << "<script type=\"text/javascript\">\n/* <![CDATA[ */\n"
  79. html << to_js(opts[:include_onload])
  80. html << "/* ]]> */</script> "
  81. html << "<!-- end of cartographer code -->" if @debug
  82. return @debug ? html.join("\n") : html.join.gsub(/\s+/, ' ')
  83. end
  84. # Generates the JavaScript used to display the map.
  85. def to_js(include_onload = true)
  86. html = []
  87. html << "// define the map-holding variable so we can use it from outside the onload event" if @debug
  88. html << "var #{@dom_id};\n"
  89. html << "// define the marker variables for your map so they can be accessed from outside the onload event" if @debug
  90. @markers.collect do |m|
  91. html << "var #{m.name};" unless m.info_window_url
  92. html << m.header_js
  93. end
  94. if @shared_info_window
  95. html << "// Emit js for default info window" if @debug
  96. html << @shared_info_window.to_js
  97. end
  98. html << cartographer_ajax_fetch_url #This will inject a simple ajax function as replacement for old GDownloadUrl of google api
  99. html << "// define the map-initializing function for the onload event" if @debug
  100. html << "function initialize_gmap_#{@dom_id}() {
  101. #{@dom_id} = new google.maps.Map(document.getElementById(\"#{@dom_id}\"),{center: new google.maps.LatLng(0, 0), zoom: 0, scrollWheel: #{@scroll}, mapTypeId: google.maps.MapTypeId.#{@type}});"
  102. html << " #{@dom_id}.draggable = false;" if @draggable == false
  103. if( @zoom == :bound )
  104. sw_ne = self.bounding_points
  105. html << "#{@dom_id}.setCenter(new google.maps.LatLng(0,0),0);\n"
  106. html << "var #{@dom_id}_bounds = new google.maps.LatLngBounds(new google.maps.LatLng(#{sw_ne[0][0]}, #{sw_ne[0][1]}), new google.maps.LatLng(#{sw_ne[1][0]}, #{sw_ne[1][1]}));\n"
  107. html << "#{@dom_id}.setCenter(#{@dom_id}_bounds.getCenter());\n"
  108. html << "#{@dom_id}.fitBounds(#{@dom_id}_bounds);\n"
  109. else
  110. html << "#{@dom_id}.setCenter(new google.maps.LatLng(#{@center[0]}, #{@center[1]}));#{@dom_id}.setZoom(#{@zoom});\n"
  111. end
  112. html << " // set the default map type" if @debug
  113. html << " #{@dom_id}.setMapTypeId(google.maps.MapTypeId.#{@type.to_s.upcase});\n"
  114. # setup markers
  115. html << "\n // create markers from the @markers array" if @debug
  116. html << "\n setupMarkers();"
  117. html << "\n setupAds();"
  118. # trigger marker info window is current_marker is defined
  119. (html << "google.maps.event.trigger(#{@current_marker}, \"click\");\n") unless @current_marker.nil?
  120. html << " // create polylines from the @polylines array" if @debug
  121. @polylines.each { |pl| html << pl.to_js }
  122. # ending the gmap_#{name} function
  123. html << "}\n"
  124. # Setup AdSense ad function
  125. html << "function setupAds(){"
  126. html << "\n #{@ad.to_js}" if @ad != []
  127. html << "}\n"
  128. # Setup markers function
  129. html << "function setupMarkers(){"
  130. # Render the Icons
  131. html << " // create icons from the @icons array" if @debug
  132. @icons.each { |i| html << i.to_js }
  133. html << "mgr = new MarkerManager(#{@dom_id});" if @marker_mgr
  134. hmarkers = Hash.new
  135. hmarkers_no_zoom =[]
  136. @markers.each do |m|
  137. if (m.min_zoom.nil?) || (m.min_zoom == '')
  138. hmarkers_no_zoom << m
  139. else
  140. hmarkers[m.min_zoom] = [] unless hmarkers[m.min_zoom]
  141. hmarkers[m.min_zoom] << m
  142. end
  143. end
  144. add_marker_js = ""
  145. hmarkers.each do |zoom, markers|
  146. html << "var batch#{zoom} = [];"
  147. markers.each do |m|
  148. html << m.to_js(@marker_mgr, @marker_clusterer)
  149. html << "batch#{zoom}.push(#{m.name});"
  150. end
  151. add_marker_js << "mgr.addMarkers(batch#{zoom}, #{zoom});"
  152. end
  153. if (hmarkers_no_zoom.size > 0)
  154. html << "var batch = [];"
  155. hmarkers_no_zoom.each do |m|
  156. html << m.to_js(@marker_mgr, @marker_clusterer)
  157. html << "batch.push(#{m.name});" if @marker_mgr
  158. end
  159. add_marker_js << "mgr.addMarkers(batch, 0);" if @marker_mgr
  160. end
  161. add_marker_js << "mgr.refresh();\n" if @marker_mgr
  162. html << "google.maps.event.addListener(mgr, 'loaded', function(){ #{add_marker_js}});" if @marker_mgr
  163. if @marker_clusterer
  164. available_cluster_icons = []
  165. @marker_clusterer_icons.each {|cluster_icon|
  166. html << cluster_icon.to_js
  167. available_cluster_icons << cluster_icon.marker_type
  168. }
  169. marker_types = @markers.collect{|marker| marker.marker_type }
  170. marker_types = marker_types.uniq
  171. marker_types.each {|marker_type| html << "var clusterBatch_#{marker_type} = [];"}
  172. @markers.each {|m| html << "clusterBatch_#{m.marker_type}.push(#{m.name});"}
  173. marker_types.sort.each_with_index {|marker_type, index|
  174. clustr_opts =[]
  175. clustr_opts << "gridSize: 20"
  176. clustr_opts << "maxZoom:10 "
  177. clustr_opts << "styles: cluster_style_#{marker_type}" if available_cluster_icons.include?(marker_type)
  178. html << "var markerCluster_#{marker_type} = new MarkerClusterer(#{@dom_id}, clusterBatch_#{marker_type}, {#{clustr_opts.join(",")}});"
  179. }
  180. end
  181. html << "
  182. google.maps.event.addListener(map, \"zoomend\", function(oldzoom,zoom) {
  183. google.maps.log.write('Current Zoom:' + zoom);
  184. });" if @debug
  185. html << "}" #End of setup marker method
  186. html << " // Dynamically attach to the window.onload event while still allowing for your existing onload events." if @debug
  187. # todo: allow for onload to happen before, or after, the existing onload events, like :before or :after
  188. if include_onload
  189. # all these functions need to be added to window.onload due to an IE bug
  190. @@window_onload << "gmap_#{@dom_id}();\n"
  191. html << "
  192. if (typeof window.onload !== 'function') {
  193. window.onload = initialize_gmap_#{@dom_id}; }
  194. else {
  195. old_before_cartographer_#{@dom_id} = window.onload;
  196. window.onload = function() {
  197. old_before_cartographer_#{@dom_id}();
  198. initialize_gmap_#{@dom_id}();
  199. };
  200. }"
  201. else #include_onload == false
  202. html << "initialize_gmap_#{@dom_id}();"
  203. end
  204. return @debug ? html.join("\n") : html.join.gsub(/\s+/, ' ')
  205. end
  206. # Doesn't get called anywhere and the js method it calls doesn't seem to exist. Is this used?
  207. def follow_route_link(link_text = 'Follow route', options = {})#:nodoc:
  208. anchor = '#' + (options[:anchor].to_s || '')
  209. move_delay = (options[:delay] || @move_delay)
  210. "<a href='#{anchor}' onclick='follow_gmap_route_#{@dom_id}_function(#{move_delay}); return false;'>#{link_text}</a>"
  211. end
  212. # Shortcut for <tt>#to_html(true)</tt>
  213. def to_s
  214. self.to_html
  215. end
  216. # Returns the coordinates of the center of the bounding box that contains all of the map's markers.
  217. def auto_center
  218. return [45,0] unless @markers
  219. return @markers.first.position if @markers.length == 1
  220. maxlat, minlat, maxlon, minlon = Float::MIN, Float::MAX, Float::MIN, Float::MAX
  221. @markers.each do |marker|
  222. if marker.lat > maxlat then maxlat = marker.lat end
  223. if marker.lat < minlat then minlat = marker.lat end
  224. if marker.lon > maxlon then maxlon = marker.lon end
  225. if marker.lon < minlon then minlon = marker.lon end
  226. end
  227. return [((maxlat+minlat)/2), ((maxlon+minlon)/2)]
  228. end
  229. def bounding_points
  230. maxlat, minlat, maxlon, minlon = nil, nil, nil, nil
  231. @markers.each do |marker|
  232. if ! maxlat || marker.lat > maxlat then maxlat = marker.lat end
  233. if ! minlat || marker.lat < minlat then minlat = marker.lat end
  234. if ! maxlon || marker.lon > maxlon then maxlon = marker.lon end
  235. if ! minlon || marker.lon < minlon then minlon = marker.lon end
  236. end
  237. @polylines.each do |line|
  238. line.points.each do |point|
  239. if ! maxlat || point[0] > maxlat then maxlat = point[0] end
  240. if ! minlat || point[0] < minlat then minlat = point[0] end
  241. if ! maxlon || point[1] > maxlon then maxlon = point[1] end
  242. if ! minlon || point[1] < minlon then minlon = point[1] end
  243. end
  244. end
  245. return [[minlat, minlon], [maxlat, maxlon]]
  246. end
  247. def cartographer_ajax_fetch_url
  248. "function cartographer_ajax_fetch_url(url){
  249. if (window.XMLHttpRequest)
  250. {/* code for IE7+, Firefox, Chrome, Opera, Safari */
  251. xmlhttp=new XMLHttpRequest();
  252. }
  253. else
  254. {/* code for IE6, IE5 */
  255. xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");
  256. }
  257. xmlhttp.open(\"GET\",url,false);
  258. xmlhttp.send();
  259. return xmlhttp.responseText;
  260. }
  261. "
  262. end
  263. end