Module: ProMotion::MapScreenModule

Included in:
MapScreen
Defined in:
lib/ProMotion/map/map_screen_module.rb

Defined Under Namespace

Modules: MapClassMethods

Constant Summary collapse

PIN_COLORS =
{
  red: UIColor.redColor,
  green: UIColor.greenColor,
  purple: UIColor.purpleColor
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



457
458
459
# File 'lib/ProMotion/map/map_screen_module.rb', line 457

def self.included(base)
  base.extend(MapClassMethods)
end

Instance Method Details

#add_annotation(annotation) ⇒ Object



126
127
128
129
# File 'lib/ProMotion/map/map_screen_module.rb', line 126

def add_annotation(annotation)
  @promotion_annotation_data << MapScreenAnnotation.new(annotation,self.view)
  self.view.addAnnotation @promotion_annotation_data.last
end

#add_annotations(annotations) ⇒ Object



131
132
133
134
# File 'lib/ProMotion/map/map_screen_module.rb', line 131

def add_annotations(annotations)
  @promotion_annotation_data = Array(annotations).map{|a| MapScreenAnnotation.new(a,self.view)}
  self.view.addAnnotations @promotion_annotation_data
end

#annotation_view(map_view, annotation) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/ProMotion/map/map_screen_module.rb', line 143

def annotation_view(map_view, annotation)
  return if annotation.is_a? RMUserLocation

  params = annotation.params

  identifier = params[:identifier]
  # Set the pin properties
  if params[:image]
    view = RMMarker.alloc.initWithUIImage(params[:image])
  else
    pinColor = (PIN_COLORS[params[:pin_color]] || params[:pin_color])
    view = RMMarker.alloc.initWithMapboxMarkerImage(params[:maki_icon], tintColor: pinColor)
  end
  view.annotation = annotation
  view.canShowCallout = params[:show_callout] if view.respond_to?("canShowCallout=")

  if params[:left_accessory]
    view.leftCalloutAccessoryView = params[:left_accessory]
  end
  if params[:right_accessory]
    view.rightCalloutAccessoryView = params[:right_accessory]
  end

  if params[:action]
    button_type = params[:action_button_type] || UIButtonTypeDetailDisclosure

    action_button = UIButton.buttonWithType(button_type)
    action_button.addTarget(self, action: params[:action], forControlEvents:UIControlEventTouchUpInside)

    view.rightCalloutAccessoryView = action_button
  end
  view
end

#annotationsObject



104
105
106
# File 'lib/ProMotion/map/map_screen_module.rb', line 104

def annotations
  @promotion_annotation_data
end

#centerObject



55
56
57
# File 'lib/ProMotion/map/map_screen_module.rb', line 55

def center
  self.view.centerCoordinate
end

#center=(params = {}) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ProMotion/map/map_screen_module.rb', line 59

def center=(params={})
  PM.logger.error "Missing #:latitude property in call to #center=." unless params[:latitude]
  PM.logger.error "Missing #:longitude property in call to #center=." unless params[:longitude]
  params[:animated] ||= true

  # Set the new region
  self.view.setCenterCoordinate(
    CLLocationCoordinate2D.new(params[:latitude], params[:longitude]),
    animated:params[:animated]
  )
end

#check_annotation_dataObject



41
42
43
# File 'lib/ProMotion/map/map_screen_module.rb', line 41

def check_annotation_data
  PM.logger.error "Missing #annotation_data method in MapScreen #{self.class.to_s}." unless self.respond_to?(:annotation_data)
end

#clear_annotationsObject



136
137
138
139
140
141
# File 'lib/ProMotion/map/map_screen_module.rb', line 136

def clear_annotations
  @promotion_annotation_data.each do |a|
    self.view.removeAnnotation(a)
  end
  @promotion_annotation_data = []
end

#create_region(initialLocation, radius = 10) ⇒ Object Also known as: region



322
323
324
325
326
327
328
# File 'lib/ProMotion/map/map_screen_module.rb', line 322

def create_region(initialLocation,radius=10)
  return nil unless initialLocation.is_a? CLLocationCoordinate2D
  radius = radius * 1.820 # Meters equivalent to 1 Nautical Mile
  southWest = self.point_from_location_bearing_and_distance(initialLocation,225, radius)
  northEast = self.point_from_location_bearing_and_distance(initialLocation,45, radius)
  {:southWest => southWest, :northEast => northEast}
end

#deceleration_modeObject

Cocoa touch Ruby counterparts #################



394
395
396
# File 'lib/ProMotion/map/map_screen_module.rb', line 394

def deceleration_mode
  map.decelerationMode
end

#deceleration_mode=(mode) ⇒ Object



398
399
400
# File 'lib/ProMotion/map/map_screen_module.rb', line 398

def deceleration_mode=(mode)
  map.decelerationMode = mode
end

#deg_to_rad(angle) ⇒ Object



291
292
293
# File 'lib/ProMotion/map/map_screen_module.rb', line 291

def deg_to_rad(angle)
  angle*Math::PI/180
end

#deselect_annotation(animated = false) ⇒ Object



120
121
122
123
124
# File 'lib/ProMotion/map/map_screen_module.rb', line 120

def deselect_annotation(animated=false)
  unless selected_annotation.nil?
    self.view.deselectAnnotation(selected_annotation, animated:animated)
  end
end

#empty_cacheObject



352
353
354
# File 'lib/ProMotion/map/map_screen_module.rb', line 352

def empty_cache
  map.removeAllCachedImages
end

#gesture_drop_pin(gesture_recognizer) ⇒ Object



223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/ProMotion/map/map_screen_module.rb', line 223

def gesture_drop_pin(gesture_recognizer)
  if gesture_recognizer.state == UIGestureRecognizerStateBegan
    NSNotificationCenter.defaultCenter.postNotificationName("ProMotionMapWillAddPin", object:nil)
    touch_point = gesture_recognizer.locationInView(self.view)
    touch_map_coordinate = self.view.convertPoint(touch_point, toCoordinateFromView:self.view)

    add_annotation({
      coordinate: touch_map_coordinate
    }.merge(@tap_to_add_annotation_params))
    NSNotificationCenter.defaultCenter.postNotificationName("ProMotionMapAddedPin", object:@promotion_annotation_data.last)
  end
end

#hide_user_locationObject



79
80
81
# File 'lib/ProMotion/map/map_screen_module.rb', line 79

def hide_user_location
  set_show_user_location false
end

#look_up_address(args = {}, &callback) ⇒ Object



331
332
333
334
335
336
337
338
# File 'lib/ProMotion/map/map_screen_module.rb', line 331

def look_up_address(args={}, &callback)
  args[:address] = args if args.is_a? String # Assume if a string is passed that they want an address

  geocoder = CLGeocoder.new
  return geocoder.geocodeAddressDictionary(args[:address], completionHandler: callback) if args[:address].is_a?(Hash)
  return geocoder.geocodeAddressString(args[:address].to_s, completionHandler: callback) unless args[:region]
  return geocoder.geocodeAddressString(args[:address].to_s, inRegion:args[:region].to_s, completionHandler: callback) if args[:region]
end

#look_up_location(location, &callback) ⇒ Object



340
341
342
343
344
345
346
347
348
349
350
# File 'lib/ProMotion/map/map_screen_module.rb', line 340

def look_up_location(location, &callback)
  location = CLLocation.alloc.initWithLatitude(location.latitude, longitude:location.longitude) if location.is_a?(CLLocationCoordinate2D)

  if location.kind_of?(CLLocation)
    geocoder = CLGeocoder.new
    geocoder.reverseGeocodeLocation(location, completionHandler: callback)
  else
    PM.logger.info("You're trying to reverse geocode something that isn't a CLLocation")
    callback.call nil, nil
  end
end

#mapObject Also known as: mapview



50
51
52
# File 'lib/ProMotion/map/map_screen_module.rb', line 50

def map
  self.view
end

#mapbox_setupObject



20
21
22
23
24
25
26
27
28
# File 'lib/ProMotion/map/map_screen_module.rb', line 20

def mapbox_setup
  if self.class.respond_to?(:get_mapbox_setup) && self.class.get_mapbox_setup
    setup_params = self.class.get_mapbox_setup_params
  else
    PM.logger.error "Missing Mapbox setup data."
  end
  RMConfiguration.sharedInstance.setAccessToken(setup_params[:access_token]) if RMConfiguration.sharedInstance.accessToken.nil?
  @tileSource = RMMapboxSource.alloc.initWithMapID(setup_params[:tile_source])
end

#mapView(map_view, regionDidChangeAnimated: animated) ⇒ Object

Cocoa touch methods #################



357
358
359
360
361
362
# File 'lib/ProMotion/map/map_screen_module.rb', line 357

def mapView(map_view, layerForAnnotation: annotation)
  if annotation.is_a?(RMAnnotation)
    annotation = MapScreenAnnotation.new_with_rmannotation(annotation,self.view)
  end
  annotation_view(map_view, annotation)
end

#point_from_location_bearing_and_distance(initialLocation, bearing, distance) ⇒ Object

Input coordinates and bearing in decimal degrees, distance in kilometers



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/ProMotion/map/map_screen_module.rb', line 300

def point_from_location_bearing_and_distance(initialLocation, bearing, distance)
  distance = distance / 6371.01 # Convert to angular radians dividing by the Earth radius
  bearing = deg_to_rad(bearing)
  input_latitude = deg_to_rad(initialLocation.latitude)
  input_longitude = deg_to_rad(initialLocation.longitude)

  output_latitude = Math.asin( 
                      Math.sin(input_latitude) * Math.cos(distance) + 
                      Math.cos(input_latitude) * Math.sin(distance) * 
                      Math.cos(bearing)
                    )
  
  dlon = input_longitude + Math.atan2(
                            Math.sin(bearing) * Math.sin(distance) * 
                            Math.cos(input_longitude), Math.cos(distance) - 
                            Math.sin(input_longitude) * Math.sin(output_latitude)
                          )
  
  output_longitude = (dlon + 3*Math::PI) % (2*Math::PI) - Math::PI  
  CLLocationCoordinate2DMake(rad_to_deg(output_latitude), rad_to_deg(output_longitude))
end

#rad_to_deg(angle) ⇒ Object



295
296
297
# File 'lib/ProMotion/map/map_screen_module.rb', line 295

def rad_to_deg(angle)
  angle*180/Math::PI
end

#screen_setupObject



10
11
12
13
14
15
16
17
18
# File 'lib/ProMotion/map/map_screen_module.rb', line 10

def screen_setup
  mapbox_setup
  self.view = nil
  self.view = RMMapView.alloc.initWithFrame(self.view.bounds, andTilesource:@tileSource)
  self.view.delegate = self
  check_annotation_data
  @promotion_annotation_data = []
  set_up_tap_to_add
end

#select_annotation(annotation, animated = true) ⇒ Object



108
109
110
# File 'lib/ProMotion/map/map_screen_module.rb', line 108

def select_annotation(annotation, animated=true)
  self.view.selectAnnotation(annotation, animated:animated)
end

#select_annotation_at(annotation_index, animated = true) ⇒ Object



112
113
114
# File 'lib/ProMotion/map/map_screen_module.rb', line 112

def select_annotation_at(annotation_index, animated=true)
  select_annotation(annotations[annotation_index], animated:animated)
end

#selected_annotationObject



116
117
118
# File 'lib/ProMotion/map/map_screen_module.rb', line 116

def selected_annotation
  self.view.selectedAnnotation
end

#set_region(region, animated = true) ⇒ Object



283
284
285
286
287
288
289
# File 'lib/ProMotion/map/map_screen_module.rb', line 283

def set_region(region, animated=true)
  self.view.zoomWithLatitudeLongitudeBoundsSouthWest(
    region[:southWest],
    northEast: region[:northEast], 
    animated: animated
  )
end

#set_show_user_location(show) ⇒ Object



83
84
85
# File 'lib/ProMotion/map/map_screen_module.rb', line 83

def set_show_user_location(show)
  self.view.showsUserLocation = show
end

#set_start_position(params = {}) ⇒ Object



177
178
179
180
181
182
183
184
185
186
# File 'lib/ProMotion/map/map_screen_module.rb', line 177

def set_start_position(params={})
  params = {
    latitude: 37.331789,
    longitude: -122.029620,
    radius: 10
  }.merge(params)
  initialLocation = CLLocationCoordinate2D.new(params[:latitude], params[:longitude])
  region = create_region(initialLocation,params[:radius])
  set_region(region, animated:false)
end

#set_tap_to_add(params = {}) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ProMotion/map/map_screen_module.rb', line 206

def set_tap_to_add(params={})
  params = {
    length: 2.0,
    target: self,
    action: "gesture_drop_pin:",
    annotation: {
      title: "Dropped Pin",
      animates_drop: true
    }
  }.merge(params)
  @tap_to_add_annotation_params = params[:annotation]

  lpgr = UILongPressGestureRecognizer.alloc.initWithTarget(params[:target], action:params[:action])
  lpgr.minimumPressDuration = params[:length]
  self.view.addGestureRecognizer(lpgr)
end

#set_up_start_centerObject



195
196
197
198
199
200
201
202
203
204
# File 'lib/ProMotion/map/map_screen_module.rb', line 195

def set_up_start_center
  if self.class.respond_to?(:get_start_position) && self.class.get_start_position
    start_params = self.class.get_start_position_params
    start_params[:zoom] ||= 5
    self.view.setZoom(start_params[:zoom], atCoordinate: 
      CLLocationCoordinate2D.new(start_params[:latitude], start_params[:longitude]), 
      animated: false
    )
  end
end

#set_up_start_regionObject



188
189
190
191
192
193
# File 'lib/ProMotion/map/map_screen_module.rb', line 188

def set_up_start_region
  if self.class.respond_to?(:get_start_position) && self.class.get_start_position
    start_params = self.class.get_start_position_params
    self.set_start_position start_params unless start_params[:radius].nil?
  end
end

#set_up_tap_to_addObject



236
237
238
239
240
# File 'lib/ProMotion/map/map_screen_module.rb', line 236

def set_up_tap_to_add
  if self.class.respond_to?(:get_tap_to_add) && self.class.get_tap_to_add
    self.set_tap_to_add self.class.get_tap_to_add_params
  end
end

#show_user_locationObject



71
72
73
74
75
76
77
# File 'lib/ProMotion/map/map_screen_module.rb', line 71

def show_user_location
  if location_manager.respondsToSelector('requestWhenInUseAuthorization')
    location_manager.requestWhenInUseAuthorization
  end

  set_show_user_location true
end

#showing_user_location?Boolean

Returns:



87
88
89
# File 'lib/ProMotion/map/map_screen_module.rb', line 87

def showing_user_location?
  self.view.showsUserLocation
end

#tapOnCalloutAccessoryControl(control, forAnnotation: annotation, onMap: map) ⇒ Object



389
390
391
# File 'lib/ProMotion/map/map_screen_module.rb', line 389

def tapOnCalloutAccessoryControl(control, forAnnotation: annotation, onMap: map)
  control.sendActionsForControlEvents(UIControlEventTouchUpInside)
end

#update_annotation_dataObject



45
46
47
48
# File 'lib/ProMotion/map/map_screen_module.rb', line 45

def update_annotation_data
  clear_annotations
  add_annotations annotation_data
end

#user_annotationObject



95
96
97
# File 'lib/ProMotion/map/map_screen_module.rb', line 95

def user_annotation
  self.view.userLocation.nil? ? nil : self.view.userLocation.location
end

#user_locationObject



91
92
93
# File 'lib/ProMotion/map/map_screen_module.rb', line 91

def user_location
  user_annotation.nil? ? nil : user_annotation.coordinate
end

#view_did_appear(animated) ⇒ Object



36
37
38
39
# File 'lib/ProMotion/map/map_screen_module.rb', line 36

def view_did_appear(animated)
  super
  set_up_start_region
end

#view_will_appear(animated) ⇒ Object



30
31
32
33
34
# File 'lib/ProMotion/map/map_screen_module.rb', line 30

def view_will_appear(animated)
  super
  update_annotation_data
  set_up_start_center
end

#zoom_to_fit_annotations(args = {}) ⇒ Object

TODO: Why is this so complex?



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# File 'lib/ProMotion/map/map_screen_module.rb', line 243

def zoom_to_fit_annotations(args={})
  # Preserve backwards compatibility
  args = {animated: args} if args == true || args == false
  args = {animated: true, include_user: false}.merge(args)

  ann = args[:include_user] ? (annotations + [user_annotation]).compact : annotations

  #Don't attempt the rezoom of there are no pins
  return if ann.count == 0

  #Set some crazy boundaries
  topLeft = CLLocationCoordinate2D.new(-90, 180)
  bottomRight = CLLocationCoordinate2D.new(90, -180)

  #Find the bounds of the pins
  ann.each do |a|
    topLeft.longitude = [topLeft.longitude, a.coordinate.longitude].min
    topLeft.latitude = [topLeft.latitude, a.coordinate.latitude].max
    bottomRight.longitude = [bottomRight.longitude, a.coordinate.longitude].max
    bottomRight.latitude = [bottomRight.latitude, a.coordinate.latitude].min
  end

  #Find the bounds of all the pins and set the map_view
  coord = CLLocationCoordinate2D.new(
    topLeft.latitude - (topLeft.latitude - bottomRight.latitude) * 0.5,
    topLeft.longitude + (bottomRight.longitude - topLeft.longitude) * 0.5
  )

  # Add some padding to the edges
  span = MKCoordinateSpanMake(
    ((topLeft.latitude - bottomRight.latitude) * 1.075).abs,
    ((bottomRight.longitude - topLeft.longitude) * 1.075).abs
  )

  region = MKCoordinateRegionMake(coord, span)
  fits = self.view.regionThatFits(region)

  set_region(fits, animated: args[:animated])
end

#zoom_to_user(radius = 0.05, animated = true) ⇒ Object



99
100
101
102
# File 'lib/ProMotion/map/map_screen_module.rb', line 99

def zoom_to_user(radius = 0.05, animated=true)
  show_user_location unless showing_user_location?
  set_region(create_region(user_location,radius), animated)
end