ProMotion-mapbox 0.1.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0a6c7b1a09e8296b378386a2508ff9803ffee0a3
4
+ data.tar.gz: 4f9f1e7093bb2981aeffe516cb783f033cfab280
5
+ SHA512:
6
+ metadata.gz: 16b2612226da961d263dc65d83f1f060238cb18d646c9fcd992961d5516534f13ab7e3c549569c23b469777ebf7bc22587835077f55eea0776bbbe43dcf11af2
7
+ data.tar.gz: ab5d5c5075abb292a91a3fb85650136fd9c2893fc017f9dfb2094120fedfe16e5a1fe95aa1d1ee6663d22fe361dc386980db74c6faf087a96f859ef06385e0c7
data/README.md ADDED
@@ -0,0 +1,376 @@
1
+ # ProMotion-mapbox
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/ProMotion-mapbox.svg)](http://badge.fury.io/rb/ProMotion-mapbox)
4
+
5
+ ProMotion-mapbox provides a PM::MapScreen, forked from the
6
+ popular RubyMotion gem [ProMotion-map](https://github.com/clearsightstudio/ProMotion-map).
7
+
8
+ ## Installation
9
+
10
+ ```ruby
11
+ gem 'ProMotion-mapbox'
12
+ ```
13
+ ```ruby
14
+ rake pod:install
15
+ ```
16
+
17
+
18
+ ## Usage
19
+
20
+ Easily create a map screen, complete with annotations.
21
+
22
+ *Has all the methods of PM::Screen*
23
+
24
+ ```ruby
25
+ class MyMapScreen < PM::MapScreen
26
+ mapbox_setup access_token: "YOU_MAPBOX_ACCESS_TOKEN",
27
+ tile_source: "mylogin.map"
28
+
29
+ title "My Map"
30
+ start_position latitude: 35.090648651123, longitude: -82.965972900391, radius: 4
31
+ tap_to_add
32
+
33
+ def annotation_data
34
+ [{
35
+ longitude: -82.965972900391,
36
+ latitude: 35.090648651123,
37
+ title: "Rainbow Falls",
38
+ subtitle: "Nantahala National Forest",
39
+ action: :show_forest,
40
+ pin_color: :green
41
+ },{
42
+ longitude: -82.966093558105,
43
+ latitude: 35.092520895652,
44
+ title: "Turtleback Falls",
45
+ subtitle: "Nantahala National Forest",
46
+ action: :show_forest,
47
+ pin_color: :purple]
48
+ },{
49
+ longitude: -82.95916,
50
+ latitude: 35.07496,
51
+ title: "Windy Falls",
52
+ action: :show_forest
53
+ },{
54
+ longitude: -82.943031505056,
55
+ latitude: 35.102516828489,
56
+ title: "Upper Bearwallow Falls",
57
+ subtitle: "Gorges State Park",
58
+ action: :show_forest
59
+ },{
60
+ longitude: -82.956244328014,
61
+ latitude: 35.085548421623,
62
+ title: "Stairway Falls",
63
+ subtitle: "Gorges State Park",
64
+ your_param: "CustomWhatever",
65
+ action: :show_forest
66
+ }, {
67
+ coordinate: CLLocationCoordinate2DMake(35.090648651123, -82.965972900391)
68
+ title: "Rainbow Falls",
69
+ subtitle: "Nantahala National Forest",
70
+ image: UIImage.imageNamed("custom-pin"),
71
+ action: :show_forest
72
+ }]
73
+ end
74
+
75
+ def show_forest
76
+ selected = selected_annotations.first
77
+ # Do something with the selected annotation.
78
+ end
79
+ end
80
+ ```
81
+
82
+ Here's a neat way to zoom into a specific marker in an animated fashion and then select the marker:
83
+
84
+ ```ruby
85
+ def zoom_to_marker(marker)
86
+ set_region region(coordinate: marker.coordinate, radius: 5) # Radius are specified in nautical miles.
87
+ select_annotation marker
88
+ end
89
+ ```
90
+
91
+ ---
92
+
93
+ ### Methods
94
+
95
+ #### annotation_data
96
+
97
+ Method that is called to get the map's annotation data and build the map. If you do not want any annotations, simply return an empty array.
98
+
99
+ All possible properties:
100
+
101
+ ```ruby
102
+ {
103
+ # REQUIRED -or- use :coordinate
104
+ longitude: -82.956244328014,
105
+ latitude: 35.085548421623,
106
+
107
+ # REQUIRED -or- use :longitude & :latitude
108
+ coordinate: CLLocationCoordinate2DMake(35.085548421623, -82.956244328014)
109
+
110
+ title: "Stairway Falls", # REQUIRED
111
+ subtitle: "Gorges State Park",
112
+ image: "my_custom_image",
113
+ pin_color: :red, # Defaults to :red. Other options are :green or :purple or any UIColor
114
+ left_accessory: my_button,
115
+ right_accessory: my_other_button,
116
+ action: :my_action, # Overrides :right_accessory
117
+ action_button_type: UIButtonTypeContactAdd # Defaults to UIButtonTypeDetailDisclosure
118
+ }
119
+ ```
120
+
121
+ You may pass whatever properties you want in the annotation hash, but (`:longitude` && `:latitude` || `:coordinate`), and `:title` are required.
122
+
123
+ Use `:image` to specify a custom image. Pass in a string to conserve memory and it will be converted using `UIImage.imageNamed(your_string)`. If you pass in a `UIImage`, we'll use that, but keep in mind that there will be another unnecessary copy of the UIImage in memory.
124
+
125
+ Use `:left_accessory` and `:right_accessory` to specify a custom accessory, like a button.
126
+
127
+ You can access annotation data you've arbitrarily stored in the hash by calling `annotation_instance.params[:your_param]`.
128
+
129
+ The `:action` parameter specifies a method that should be run when the detail button is tapped on the annotation. It automatically adds a `UIButtonTypeDetailDisclosure` button to the `:left_accessory`. In your method you can find out which annotation's accessory was tapped by calling `selected_annotations.first`.
130
+
131
+ #### update_annotation_data
132
+
133
+ Forces a reload of all the annotations
134
+
135
+ #### annotations
136
+
137
+ Returns an array of all the annotations.
138
+
139
+ #### center
140
+
141
+ Returns a `CLLocation2D` instance with the center coordinates of the map.
142
+
143
+ #### center=({latitude: Float, longitude: Float, animated: Boolean})
144
+
145
+ Sets the center of the map. `animated` property defaults to `true`.
146
+
147
+ #### show_user_location
148
+
149
+ Shows the user's location on the map. Must be called in the view initialization sequence on `will_appear` or _after_.
150
+
151
+ #### look_up_location(CLLocation) { |placemark, error| }
152
+
153
+ This method takes a CLLocation object and will return one to many CLPlacemark to represent nearby data.
154
+
155
+ ##### iOS 8 Location Requirements
156
+
157
+ iOS 8 introduced stricter location services requirements. You are now required to add a few key/value pairs to the `Info.plist`. Add these two lines to your `Rakefile` (with your descriptions, obviously):
158
+
159
+ ```ruby
160
+ app.info_plist['NSLocationAlwaysUsageDescription'] = 'Description'
161
+ app.info_plist['NSLocationWhenInUseUsageDescription'] = 'Description'
162
+ ```
163
+
164
+ *Note: you need both keys to use `get_once`, so it's probably best to just include both no matter what.* See [Apple's documentation](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW18) on iOS 8 location services requirements for more information.
165
+
166
+ #### hide_user_location
167
+
168
+ Hides the user's location on the map.
169
+
170
+ #### showing_user_location?
171
+
172
+ Returns a `Boolean` of whether or not the map view is currently showing the user's location.
173
+
174
+ #### user_location
175
+
176
+ Returns a `CLLocation2D` object of the user's location or `nil` if the user location is not being tracked
177
+
178
+ #### zoom_to_user(radius = 0.05, animated=true)
179
+
180
+ Zooms to the user's location. If the user's location is not currently being shown on the map, it will show it first. `radius` is the distance in nautical miles from the center point (user location) to the corners of a virtual bounding box.
181
+
182
+ #### select_annotation(annotation, animated=true)
183
+
184
+ Selects a single annotation.
185
+
186
+ #### select_annotation_at(annotation_index, animated=true)
187
+
188
+ Selects a single annotation using the annotation at the index of your `annotation_data` array.
189
+
190
+ #### selected_annotation
191
+
192
+ Returns the annotation that is selected. If no annotation is selected, returns `nil`.
193
+
194
+ #### deselect_annotation(animated=false)
195
+
196
+ Deselects any selected annotation.
197
+
198
+ #### add_annotation(annotation)
199
+
200
+ Adds a new annotation to the map. Refer to `annotation_data` (above) for hash properties.
201
+
202
+ #### add_annotations(annotations)
203
+
204
+ Adds more than one annotation at a time to the map.
205
+
206
+ #### clear_annotations
207
+
208
+ Removes all annotations from the `MapScreen`.
209
+
210
+ #### zoom_to_fit_annotations({animated:true, include_user:false})
211
+
212
+ Changes the zoom and center point of the `MapScreen` to fit all the annotations. Passing `include_user` as `true` will cause the zoom to not only include the annotations from `annotation_data` but also the user pin in the zoom region calculation.
213
+
214
+ #### set_region(region, animated=true)
215
+
216
+ Sets the region of the `MapScreen`. `region` should be an instance of `MKCoordinateRegion`.
217
+
218
+ #### region(center_location,radius=10)
219
+
220
+ Mapbox API doesn't have the concept of a region. Instead, we can zoom to a virtual bounding box defined by its Sourthwest and Northeast
221
+ corners.
222
+ The ```region``` methods takes a ```center_location``` and a radius. The distance from the center to the corners (and thus the zoom level) will be the ```radius``` times 1820 meters (1 Nautical mile)
223
+
224
+ ```ruby
225
+ my_region = region({
226
+ CLLocationCoordinate2D.new(35.0906,-82.965),
227
+ radius: 11
228
+ })
229
+ ```
230
+
231
+ ---
232
+
233
+ ### Class Methods
234
+
235
+ #### start_position(latitude: Float, longitude: Float, radius: Float)
236
+
237
+ Class method to set the initial starting position of the `MapScreen`.
238
+
239
+ ```ruby
240
+ class MyMapScreen < PM::MapScreen
241
+ start_position latitude: 36.10, longitude: -80.26, radius: 4
242
+ end
243
+ ```
244
+
245
+ `radius` is the zoom level of the map in miles (default: 10).
246
+
247
+ #### tap_to_add(length: Float, target: Object, action: Selector, annotation: Hash)
248
+
249
+ Lets a user long press the map to drop an annotation where they pressed.
250
+
251
+ ##### Default values:
252
+
253
+ You can override any of these values. The `annotation` parameter can take any options specified in the annotation documentation above except `:latitude`, `:longitude`, and `:coordinate`.
254
+
255
+ ```ruby
256
+ length: 2.0,
257
+ target: self,
258
+ action: "gesture_drop_pin:",
259
+ annotation: {
260
+ title: "Dropped Pin",
261
+ animates_drop: true
262
+ }
263
+ ```
264
+
265
+ ##### Notifications
266
+
267
+ This feature posts two different `NSNotificationCenter` notifications:
268
+
269
+ **ProMotionMapWillAddPin:** Fired the moment the long press gesture is recognized, before the pin is added.
270
+
271
+ **ProMotionMapAddedPin:** Fired after the pin has been added to the map.
272
+
273
+ ##### Example:
274
+
275
+ ```ruby
276
+ # Simple Example
277
+ class MyMapScreen < PM::MapScreen
278
+ title "My Map Screen"
279
+ tap_to_add length: 1.5
280
+ def annotations
281
+ []
282
+ end
283
+ end
284
+ ```
285
+
286
+ ```ruby
287
+ # A More Complex Example
288
+ class MyMapScreen < PM::MapScreen
289
+ title "My Map Screen"
290
+ tap_to_add length: 1.5, annotation: {animates_drop: true, title: "A Cool New Pin"}
291
+ def annotations
292
+ []
293
+ end
294
+
295
+ def will_appear
296
+ NSNotificationCenter.defaultCenter.addObserver(self, selector:"pin_adding:") , name:"ProMotionMapWillAddPin", object:nil)
297
+ NSNotificationCenter.defaultCenter.addObserver(self, selector:"pin_added:") , name:"ProMotionMapAddedPin", object:nil)
298
+ end
299
+
300
+ def will_disappear
301
+ NSNotificationCenter.defaultCenter.removeObserver(self)
302
+ end
303
+
304
+ def pin_adding(notification)
305
+ # We only want one pin on the map at a time
306
+ clear_annotations
307
+ end
308
+
309
+ def pin_added(notification)
310
+ # Once the pin is dropped we want to select it
311
+ select_annotation_at(0)
312
+ end
313
+ end
314
+ ```
315
+
316
+ ---
317
+
318
+ ### Delegate callbacks
319
+
320
+ These methods (if implemented in your `MapScreen`) will be called when the corresponding `MKMapViewDelegate` method is invoked:
321
+
322
+ ```ruby
323
+ def will_change_region(animated)
324
+ # Do something when the region will change
325
+ # The animated parameter is optional so you can also define it is simply:
326
+ # def will_change_region
327
+ # end
328
+ end
329
+
330
+ def on_change_region(animated)
331
+ # Do something when the region changed
332
+ # The animated parameter is optional so you can also define it is simply:
333
+ # def on_change_region
334
+ # end
335
+ end
336
+ ```
337
+
338
+ ---
339
+
340
+ ### CocoaTouch Property Convenience Methods
341
+
342
+ `MKMapView` contains multiple property setters and getters that can be accessed in a more ruby-like syntax:
343
+
344
+ ```ruby
345
+ type # Returns a MKMapType
346
+ type = (MKMapType)new_type
347
+
348
+ zoom_enabled?
349
+ zoom_enabled = (bool)enabled
350
+
351
+ scroll_enabled?
352
+ scroll_enabled = (bool)enabled
353
+
354
+ pitch_enabled?
355
+ pitch_enabled = (bool)enabled
356
+
357
+ rotate_enabled?
358
+ rotate_enabled = (bool)enabled
359
+ ```
360
+
361
+ ---
362
+
363
+ ### Accessors
364
+
365
+ #### `map` or `mapview`
366
+
367
+ Reference to the created RMMapView.
368
+
369
+ ## Contributing
370
+
371
+ 1. Fork it
372
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
373
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
374
+ 4. Make some specs pass
375
+ 5. Push to the branch (`git push origin my-new-feature`)
376
+ 6. Create new Pull Request
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ unless defined?(Motion::Project::Config)
3
+ raise "ProMotion-mapbox must be required within a RubyMotion project."
4
+ end
5
+
6
+ require 'motion-cocoapods'
7
+
8
+ Motion::Project::App.setup do |app|
9
+ lib_dir_path = File.dirname(File.expand_path(__FILE__))
10
+ app.files << File.join(lib_dir_path, "ProMotion/map/map_screen_annotation.rb")
11
+ app.files << File.join(lib_dir_path, "ProMotion/map/map_screen_module.rb")
12
+ app.files << File.join(lib_dir_path, "ProMotion/map/map_screen.rb")
13
+
14
+ app.frameworks += %w(CoreLocation)
15
+
16
+ app.pods do
17
+ pod "Mapbox-iOS-SDK"
18
+ end
19
+
20
+ end
@@ -0,0 +1,6 @@
1
+ module ProMotion
2
+ class MapScreen < ViewController
3
+ include ProMotion::ScreenModule
4
+ include ProMotion::MapScreenModule
5
+ end
6
+ end
@@ -0,0 +1,74 @@
1
+ module ProMotion
2
+ class MapScreenAnnotation < RMAnnotation
3
+ attr_reader :params
4
+
5
+ def initialize(params = {},map_view)
6
+ @params = params
7
+ @map_view = map_view
8
+ set_defaults
9
+
10
+ if @params[:coordinate]
11
+ @params[:latitude] = @params[:coordinate].latitude
12
+ @params[:longitude] = @params[:coordinate].longitude
13
+ @coordinate = @params[:coordinate]
14
+ initWithMapView(map_view, coordinate: @coordinate, andTitle: @params[:title])
15
+ elsif @params[:latitude] && @params[:longitude]
16
+ @coordinate = CLLocationCoordinate2D.new(@params[:latitude], @params[:longitude])
17
+ initWithMapView(map_view, coordinate: @coordinate, andTitle: @params[:title])
18
+ else
19
+ PM.logger.error("You are required to specify :latitude and :longitude or :coordinate for annotations.")
20
+ nil
21
+ end
22
+ end
23
+
24
+ def set_defaults
25
+ @params = {
26
+ title: "Title",
27
+ pin_color: :red,
28
+ identifier: "Annotation-#{@params[:pin_color]}-#{@params[:image]}",
29
+ show_callout: true,
30
+ animates_drop: false,
31
+ maki_icon: nil,
32
+ }.merge(@params)
33
+ end
34
+
35
+ def title
36
+ @params[:title]
37
+ end
38
+
39
+ def subtitle
40
+ @params[:subtitle] ||= nil
41
+ end
42
+
43
+ def coordinate
44
+ @coordinate
45
+ end
46
+
47
+ def pin_color
48
+ @params[:pin_color]
49
+ end
50
+
51
+ def cllocation
52
+ CLLocation.alloc.initWithLatitude(@params[:latitude], longitude:@params[:longitude])
53
+ end
54
+
55
+ def setCoordinate(new_coordinate)
56
+ super
57
+ if new_coordinate.is_a? Hash
58
+ @coordinate = CLLocationCoordinate2D.new(new_coordinate[:latitude], new_coordinate[:longitude])
59
+ else
60
+ @coordinate = new_coordinate
61
+ end
62
+ end
63
+
64
+ def method_missing(meth, *args)
65
+ if @params[meth.to_sym]
66
+ @params[meth.to_sym]
67
+ else
68
+ PM.logger.warn "The annotation parameter \"#{meth}\" does not exist on this pin."
69
+ nil
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,448 @@
1
+ module ProMotion
2
+ module MapScreenModule
3
+
4
+ PIN_COLORS = {
5
+ red: UIColor.redColor,
6
+ green: UIColor.greenColor,
7
+ purple: UIColor.purpleColor
8
+ }
9
+
10
+ def screen_setup
11
+ mapbox_setup
12
+ self.view = nil
13
+ self.view = RMMapView.alloc.initWithFrame(self.view.bounds, andTilesource:@tileSource)
14
+ self.view.delegate = self
15
+
16
+ check_annotation_data
17
+ @promotion_annotation_data = []
18
+ set_up_tap_to_add
19
+ end
20
+
21
+ def mapbox_setup
22
+ if self.class.respond_to?(:get_mapbox_setup) && self.class.get_mapbox_setup
23
+ setup_params = self.class.get_mapbox_setup_params
24
+ else
25
+ PM.logger.error "Missing Mapbox setup data."
26
+ end
27
+ RMConfiguration.sharedInstance.setAccessToken(setup_params[:access_token])
28
+ @tileSource = RMMapboxSource.alloc.initWithMapID(setup_params[:tile_source])
29
+ end
30
+
31
+ def view_will_appear(animated)
32
+ super
33
+ update_annotation_data
34
+ end
35
+
36
+ def view_did_appear(animated)
37
+ super
38
+ set_up_start_position
39
+ end
40
+
41
+ def check_annotation_data
42
+ PM.logger.error "Missing #annotation_data method in MapScreen #{self.class.to_s}." unless self.respond_to?(:annotation_data)
43
+ end
44
+
45
+ def update_annotation_data
46
+ clear_annotations
47
+ add_annotations annotation_data
48
+ end
49
+
50
+ def map
51
+ self.view
52
+ end
53
+ alias_method :mapview, :map
54
+
55
+ def center
56
+ self.view.centerCoordinate
57
+ end
58
+
59
+ def center=(params={})
60
+ PM.logger.error "Missing #:latitude property in call to #center=." unless params[:latitude]
61
+ PM.logger.error "Missing #:longitude property in call to #center=." unless params[:longitude]
62
+ params[:animated] ||= true
63
+
64
+ # Set the new region
65
+ self.view.setCenterCoordinate(
66
+ CLLocationCoordinate2D.new(params[:latitude], params[:longitude]),
67
+ animated:params[:animated]
68
+ )
69
+ end
70
+
71
+ def show_user_location
72
+ if location_manager.respondsToSelector('requestWhenInUseAuthorization')
73
+ location_manager.requestWhenInUseAuthorization
74
+ end
75
+
76
+ set_show_user_location true
77
+ end
78
+
79
+ def hide_user_location
80
+ set_show_user_location false
81
+ end
82
+
83
+ def set_show_user_location(show)
84
+ self.view.showsUserLocation = show
85
+ end
86
+
87
+ def showing_user_location?
88
+ self.view.showsUserLocation
89
+ end
90
+
91
+ def user_location
92
+ user_annotation.nil? ? nil : user_annotation.coordinate
93
+ end
94
+
95
+ def user_annotation
96
+ self.view.userLocation.nil? ? nil : self.view.userLocation.location
97
+ end
98
+
99
+ def zoom_to_user(radius = 0.05, animated=true)
100
+ show_user_location unless showing_user_location?
101
+ set_region(create_region(user_location,radius), animated)
102
+ end
103
+
104
+ def annotations
105
+ @promotion_annotation_data
106
+ end
107
+
108
+ def select_annotation(annotation, animated=true)
109
+ self.view.selectAnnotation(annotation, animated:animated)
110
+ end
111
+
112
+ def select_annotation_at(annotation_index, animated=true)
113
+ select_annotation(annotations[annotation_index], animated:animated)
114
+ end
115
+
116
+ def selected_annotation
117
+ self.view.selectedAnnotation
118
+ end
119
+
120
+ def deselect_annotation(animated=false)
121
+ unless selected_annotation.nil?
122
+ self.view.deselectAnnotation(selected_annotation, animated:animated)
123
+ end
124
+ end
125
+
126
+ def add_annotation(annotation)
127
+ @promotion_annotation_data << MapScreenAnnotation.new(annotation,self.view)
128
+ self.view.addAnnotation @promotion_annotation_data.last
129
+ end
130
+
131
+ def add_annotations(annotations)
132
+ @promotion_annotation_data = Array(annotations).map{|a| MapScreenAnnotation.new(a,self.view)}
133
+ self.view.addAnnotations @promotion_annotation_data
134
+ end
135
+
136
+ def clear_annotations
137
+ @promotion_annotation_data.each do |a|
138
+ self.view.removeAnnotation(a)
139
+ end
140
+ @promotion_annotation_data = []
141
+ end
142
+
143
+ def annotation_view(map_view, annotation)
144
+ return if annotation.is_a? RMUserLocation
145
+
146
+ params = annotation.params
147
+
148
+ identifier = params[:identifier]
149
+ # Set the pin properties
150
+ if params[:image]
151
+ view = RMMarker.alloc.initWithUIImage(params[:image])
152
+ else
153
+ pinColor = (PIN_COLORS[params[:pin_color]] || params[:pin_color])
154
+ view = RMMarker.alloc.initWithMapboxMarkerImage(params[:maki_icon], tintColor: pinColor)
155
+ end
156
+ view.annotation = annotation
157
+ view.canShowCallout = params[:show_callout] if view.respond_to?("canShowCallout=")
158
+
159
+ if params[:left_accessory]
160
+ view.leftCalloutAccessoryView = params[:left_accessory]
161
+ end
162
+ if params[:right_accessory]
163
+ view.rightCalloutAccessoryView = params[:right_accessory]
164
+ end
165
+
166
+ if params[:action]
167
+ button_type = params[:action_button_type] || UIButtonTypeDetailDisclosure
168
+
169
+ action_button = UIButton.buttonWithType(button_type)
170
+ action_button.addTarget(self, action: params[:action], forControlEvents:UIControlEventTouchUpInside)
171
+
172
+ view.rightCalloutAccessoryView = action_button
173
+ end
174
+ view
175
+ end
176
+
177
+ def set_start_position(params={})
178
+ params = {
179
+ latitude: 37.331789,
180
+ longitude: -122.029620,
181
+ radius: 10
182
+ }.merge(params)
183
+ initialLocation = CLLocationCoordinate2D.new(params[:latitude], params[:longitude])
184
+ region = create_region(initialLocation,params[:radius])
185
+ set_region(region, animated:false)
186
+ end
187
+
188
+ def set_up_start_position
189
+ if self.class.respond_to?(:get_start_position) && self.class.get_start_position
190
+ self.set_start_position self.class.get_start_position_params
191
+ end
192
+ end
193
+
194
+ def set_tap_to_add(params={})
195
+ params = {
196
+ length: 2.0,
197
+ target: self,
198
+ action: "gesture_drop_pin:",
199
+ annotation: {
200
+ title: "Dropped Pin",
201
+ animates_drop: true
202
+ }
203
+ }.merge(params)
204
+ @tap_to_add_annotation_params = params[:annotation]
205
+
206
+ lpgr = UILongPressGestureRecognizer.alloc.initWithTarget(params[:target], action:params[:action])
207
+ lpgr.minimumPressDuration = params[:length]
208
+ self.view.addGestureRecognizer(lpgr)
209
+ end
210
+
211
+ def gesture_drop_pin(gesture_recognizer)
212
+ if gesture_recognizer.state == UIGestureRecognizerStateBegan
213
+ NSNotificationCenter.defaultCenter.postNotificationName("ProMotionMapWillAddPin", object:nil)
214
+ touch_point = gesture_recognizer.locationInView(self.view)
215
+ touch_map_coordinate = self.view.convertPoint(touch_point, toCoordinateFromView:self.view)
216
+
217
+ add_annotation({
218
+ coordinate: touch_map_coordinate
219
+ }.merge(@tap_to_add_annotation_params))
220
+ NSNotificationCenter.defaultCenter.postNotificationName("ProMotionMapAddedPin", object:@promotion_annotation_data.last)
221
+ end
222
+ end
223
+
224
+ def set_up_tap_to_add
225
+ if self.class.respond_to?(:get_tap_to_add) && self.class.get_tap_to_add
226
+ self.set_tap_to_add self.class.get_tap_to_add_params
227
+ end
228
+ end
229
+
230
+ # TODO: Why is this so complex?
231
+ def zoom_to_fit_annotations(args={})
232
+ # Preserve backwards compatibility
233
+ args = {animated: args} if args == true || args == false
234
+ args = {animated: true, include_user: false}.merge(args)
235
+
236
+ ann = args[:include_user] ? (annotations + [user_annotation]).compact : annotations
237
+
238
+ #Don't attempt the rezoom of there are no pins
239
+ return if ann.count == 0
240
+
241
+ #Set some crazy boundaries
242
+ topLeft = CLLocationCoordinate2D.new(-90, 180)
243
+ bottomRight = CLLocationCoordinate2D.new(90, -180)
244
+
245
+ #Find the bounds of the pins
246
+ ann.each do |a|
247
+ topLeft.longitude = [topLeft.longitude, a.coordinate.longitude].min
248
+ topLeft.latitude = [topLeft.latitude, a.coordinate.latitude].max
249
+ bottomRight.longitude = [bottomRight.longitude, a.coordinate.longitude].max
250
+ bottomRight.latitude = [bottomRight.latitude, a.coordinate.latitude].min
251
+ end
252
+
253
+ #Find the bounds of all the pins and set the map_view
254
+ coord = CLLocationCoordinate2D.new(
255
+ topLeft.latitude - (topLeft.latitude - bottomRight.latitude) * 0.5,
256
+ topLeft.longitude + (bottomRight.longitude - topLeft.longitude) * 0.5
257
+ )
258
+
259
+ # Add some padding to the edges
260
+ span = MKCoordinateSpanMake(
261
+ ((topLeft.latitude - bottomRight.latitude) * 1.075).abs,
262
+ ((bottomRight.longitude - topLeft.longitude) * 1.075).abs
263
+ )
264
+
265
+ region = MKCoordinateRegionMake(coord, span)
266
+ fits = self.view.regionThatFits(region)
267
+
268
+ set_region(fits, animated: args[:animated])
269
+ end
270
+
271
+ def set_region(region, animated=true)
272
+ self.view.zoomWithLatitudeLongitudeBoundsSouthWest(
273
+ region[:southWest],
274
+ northEast: region[:northEast],
275
+ animated: animated
276
+ )
277
+ end
278
+
279
+ def deg_to_rad(angle)
280
+ angle*Math::PI/180
281
+ end
282
+
283
+ def rad_to_deg(angle)
284
+ angle*180/Math::PI
285
+ end
286
+
287
+ # Input coordinates and bearing in decimal degrees, distance in kilometers
288
+ def point_from_location_bearing_and_distance(initialLocation, bearing, distance)
289
+ distance = distance / 6371.01 # Convert to angular radians dividing by the Earth radius
290
+ bearing = deg_to_rad(bearing)
291
+ input_latitude = deg_to_rad(initialLocation.latitude)
292
+ input_longitude = deg_to_rad(initialLocation.longitude)
293
+
294
+ output_latitude = Math.asin(
295
+ Math.sin(input_latitude) * Math.cos(distance) +
296
+ Math.cos(input_latitude) * Math.sin(distance) *
297
+ Math.cos(bearing)
298
+ )
299
+
300
+ dlon = input_longitude + Math.atan2(
301
+ Math.sin(bearing) * Math.sin(distance) *
302
+ Math.cos(input_longitude), Math.cos(distance) -
303
+ Math.sin(input_longitude) * Math.sin(output_latitude)
304
+ )
305
+
306
+ output_longitude = (dlon + 3*Math::PI) % (2*Math::PI) - Math::PI
307
+ CLLocationCoordinate2DMake(rad_to_deg(output_latitude), rad_to_deg(output_longitude))
308
+ end
309
+
310
+ def create_region(initialLocation,radius=10)
311
+ return nil unless initialLocation.is_a? CLLocationCoordinate2D
312
+ radius = radius * 1.820 # Meters equivalent to 1 Nautical Mile
313
+ southWest = self.point_from_location_bearing_and_distance(initialLocation,225, radius)
314
+ northEast = self.point_from_location_bearing_and_distance(initialLocation,45, radius)
315
+ {:southWest => southWest, :northEast => northEast}
316
+ end
317
+ alias_method :region, :create_region
318
+
319
+ def look_up_address(args={}, &callback)
320
+ args[:address] = args if args.is_a? String # Assume if a string is passed that they want an address
321
+
322
+ geocoder = CLGeocoder.new
323
+ return geocoder.geocodeAddressDictionary(args[:address], completionHandler: callback) if args[:address].is_a?(Hash)
324
+ return geocoder.geocodeAddressString(args[:address].to_s, completionHandler: callback) unless args[:region]
325
+ return geocoder.geocodeAddressString(args[:address].to_s, inRegion:args[:region].to_s, completionHandler: callback) if args[:region]
326
+ end
327
+
328
+ def look_up_location(location, &callback)
329
+ location = CLLocation.alloc.initWithLatitude(location.latitude, longitude:location.longitude) if location.is_a?(CLLocationCoordinate2D)
330
+
331
+ if location.kind_of?(CLLocation)
332
+ geocoder = CLGeocoder.new
333
+ geocoder.reverseGeocodeLocation(location, completionHandler: callback)
334
+ else
335
+ PM.logger.info("You're trying to reverse geocode something that isn't a CLLocation")
336
+ callback.call nil, nil
337
+ end
338
+ end
339
+
340
+ ########## Mapbox methods #################
341
+ def mapView(map_view, layerForAnnotation: annotation)
342
+ annotation_view(map_view, annotation)
343
+ end
344
+
345
+ ########## Cocoa touch methods #################
346
+ def mapView(map_view, didUpdateUserLocation:userLocation)
347
+ if self.respond_to?(:on_user_location)
348
+ on_user_location(userLocation)
349
+ else
350
+ PM.logger.info "You're tracking the user's location but have not implemented the #on_user_location(location) method in MapScreen #{self.class.to_s}."
351
+ end
352
+ end
353
+
354
+ def mapView(map_view, regionWillChangeAnimated:animated)
355
+ if self.respond_to?("will_change_region:")
356
+ will_change_region(animated)
357
+ elsif self.respond_to?(:will_change_region)
358
+ will_change_region
359
+ end
360
+ end
361
+
362
+ def mapView(map_view, regionDidChangeAnimated:animated)
363
+ if self.respond_to?("on_change_region:")
364
+ on_change_region(animated)
365
+ elsif self.respond_to?(:on_change_region)
366
+ on_change_region
367
+ end
368
+ end
369
+
370
+ ########## Cocoa touch Ruby counterparts #################
371
+
372
+ def deceleration_mode
373
+ map.decelerationMode
374
+ end
375
+
376
+ def deceleration_mode=(mode)
377
+ map.decelerationMode = mode
378
+ end
379
+
380
+ %w(dragging bouncing clustering).each do |meth|
381
+ define_method("#{meth}_enabled?") do
382
+ map.send("#{meth}Enabled")
383
+ end
384
+
385
+ define_method("#{meth}_enabled=") do |argument|
386
+ map.send("#{meth}Enabled=", argument)
387
+ end
388
+ end
389
+
390
+ module MapClassMethods
391
+ # Start Position
392
+ def start_position(params={})
393
+ @start_position_params = params
394
+ @start_position = true
395
+ end
396
+
397
+ def get_start_position_params
398
+ @start_position_params ||= nil
399
+ end
400
+
401
+ def get_start_position
402
+ @start_position ||= false
403
+ end
404
+
405
+ # Tap to drop pin
406
+ def tap_to_add(params={})
407
+ @tap_to_add_params = params
408
+ @tap_to_add = true
409
+ end
410
+
411
+ def get_tap_to_add_params
412
+ @tap_to_add_params ||= nil
413
+ end
414
+
415
+ def get_tap_to_add
416
+ @tap_to_add ||= false
417
+ end
418
+
419
+ # Mapbox setup
420
+ def mapbox_setup(params={})
421
+ @mapbox_setup_params = params
422
+ @mapbox_setup = true
423
+ end
424
+
425
+ def get_mapbox_setup_params
426
+ @mapbox_setup_params ||= nil
427
+ end
428
+
429
+ def get_mapbox_setup
430
+ @mapbox_setup ||= false
431
+ end
432
+
433
+
434
+ end
435
+ def self.included(base)
436
+ base.extend(MapClassMethods)
437
+ end
438
+
439
+ private
440
+
441
+ def location_manager
442
+ @location_manager ||= CLLocationManager.alloc.init
443
+ @location_manager.delegate ||= self
444
+ @location_manager
445
+ end
446
+
447
+ end
448
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ProMotion-mapbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Diogo Andre
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ProMotion
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: motion-cocoapods
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: motion-stump
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: motion-redgreen
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Adds PM::MapScreen support to ProMotion, using Mapbox as map provider.
84
+ email:
85
+ - diogo@regattapix.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - README.md
91
+ - lib/ProMotion/map/map_screen.rb
92
+ - lib/ProMotion/map/map_screen_annotation.rb
93
+ - lib/ProMotion/map/map_screen_module.rb
94
+ - lib/ProMotion-mapbox.rb
95
+ homepage: https://github.com/diogoandre/ProMotion-mapbox
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - '>='
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.0.6
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Adds PM::MapScreen support to ProMotion, using Mapbox as map provider. Forked
119
+ from Promotion-map
120
+ test_files: []