ProMotion-mapbox 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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: []