...
|
...
|
@@ -33,6 +33,7 @@ |
|
|
#import "RMProjection.h"
|
|
|
#import "RMMarker.h"
|
|
|
#import "RMPath.h"
|
|
|
#import "RMCircle.h"
|
|
|
#import "RMShape.h"
|
|
|
#import "RMAnnotation.h"
|
|
|
#import "RMQuadTree.h"
|
...
|
...
|
@@ -46,6 +47,8 @@ |
|
|
#import "RMMapTiledLayerView.h"
|
|
|
#import "RMMapOverlayView.h"
|
|
|
|
|
|
#import "RMUserLocation.h"
|
|
|
|
|
|
#pragma mark --- begin constants ----
|
|
|
|
|
|
#define kZoomRectPixelBuffer 150.0
|
...
|
...
|
@@ -61,6 +64,8 @@ |
|
|
|
|
|
@interface RMMapView (PrivateMethods)
|
|
|
|
|
|
@property (nonatomic, retain) RMUserLocation *userLocation;
|
|
|
|
|
|
- (void)createMapView;
|
|
|
|
|
|
- (void)correctPositionOfAllAnnotations;
|
...
|
...
|
@@ -72,6 +77,24 @@ |
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
@interface RMUserLocation (PrivateMethods)
|
|
|
|
|
|
@property (nonatomic, getter=isUpdating) BOOL updating;
|
|
|
@property (nonatomic, retain) CLLocation *location;
|
|
|
@property (nonatomic, retain) CLHeading *heading;
|
|
|
|
|
|
@end
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
@interface RMAnnotation (PrivateMethods)
|
|
|
|
|
|
@property (nonatomic, assign) BOOL isUserLocationAnnotation;
|
|
|
|
|
|
@end
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
@implementation RMMapView
|
|
|
{
|
|
|
id <RMMapViewDelegate> _delegate;
|
...
|
...
|
@@ -96,6 +119,11 @@ |
|
|
BOOL _delegateHasLayerForAnnotation;
|
|
|
BOOL _delegateHasWillHideLayerForAnnotation;
|
|
|
BOOL _delegateHasDidHideLayerForAnnotation;
|
|
|
BOOL _delegateHasWillStartLocatingUser;
|
|
|
BOOL _delegateHasDidStopLocatingUser;
|
|
|
BOOL _delegateHasDidUpdateUserLocation;
|
|
|
BOOL _delegateHasDidFailToLocateUserWithError;
|
|
|
BOOL _delegateHasDidChangeUserTrackingMode;
|
|
|
|
|
|
UIView *_backgroundView;
|
|
|
RMMapScrollView *_mapScrollView;
|
...
|
...
|
@@ -122,6 +150,14 @@ |
|
|
|
|
|
CGPoint _lastDraggingTranslation;
|
|
|
RMAnnotation *_draggedAnnotation;
|
|
|
|
|
|
CLLocationManager *locationManager;
|
|
|
RMUserLocation *userLocation;
|
|
|
BOOL showsUserLocation;
|
|
|
RMUserTrackingMode userTrackingMode;
|
|
|
|
|
|
UIImageView *userLocationTrackingView;
|
|
|
UIImageView *userHeadingTrackingView;
|
|
|
}
|
|
|
|
|
|
@synthesize decelerationMode = _decelerationMode;
|
...
|
...
|
@@ -136,6 +172,7 @@ |
|
|
@synthesize positionClusterMarkersAtTheGravityCenter = _positionClusterMarkersAtTheGravityCenter;
|
|
|
@synthesize clusterMarkerSize = _clusterMarkerSize, clusterAreaSize = _clusterAreaSize;
|
|
|
@synthesize adjustTilesForRetinaDisplay = _adjustTilesForRetinaDisplay;
|
|
|
@synthesize userLocation, showsUserLocation, userTrackingMode;
|
|
|
@synthesize missingTilesDepth = _missingTilesDepth;
|
|
|
@synthesize debugTiles = _debugTiles;
|
|
|
|
...
|
...
|
@@ -157,6 +194,8 @@ |
|
|
|
|
|
self.backgroundColor = [UIColor grayColor];
|
|
|
|
|
|
self.clipsToBounds = YES;
|
|
|
|
|
|
_tileSourcesContainer = [RMTileSourcesContainer new];
|
|
|
_tiledLayersSuperview = nil;
|
|
|
|
...
|
...
|
@@ -311,6 +350,10 @@ |
|
|
[_projection release]; _projection = nil;
|
|
|
[_mercatorToTileProjection release]; _mercatorToTileProjection = nil;
|
|
|
[self setTileCache:nil];
|
|
|
[locationManager release]; locationManager = nil;
|
|
|
[userLocation release]; userLocation = nil;
|
|
|
[userLocationTrackingView release]; userLocationTrackingView = nil;
|
|
|
[userHeadingTrackingView release]; userHeadingTrackingView = nil;
|
|
|
[super dealloc];
|
|
|
}
|
|
|
|
...
|
...
|
@@ -375,6 +418,12 @@ |
|
|
_delegateHasLayerForAnnotation = [_delegate respondsToSelector:@selector(mapView:layerForAnnotation:)];
|
|
|
_delegateHasWillHideLayerForAnnotation = [_delegate respondsToSelector:@selector(mapView:willHideLayerForAnnotation:)];
|
|
|
_delegateHasDidHideLayerForAnnotation = [_delegate respondsToSelector:@selector(mapView:didHideLayerForAnnotation:)];
|
|
|
|
|
|
_delegateHasWillStartLocatingUser = [_delegate respondsToSelector:@selector(mapViewWillStartLocatingUser:)];
|
|
|
_delegateHasDidStopLocatingUser = [_delegate respondsToSelector:@selector(mapViewDidStopLocatingUser:)];
|
|
|
_delegateHasDidUpdateUserLocation = [_delegate respondsToSelector:@selector(mapView:didUpdateUserLocation:)];
|
|
|
_delegateHasDidFailToLocateUserWithError = [_delegate respondsToSelector:@selector(mapView:didFailToLocateUserWithError:)];
|
|
|
_delegateHasDidChangeUserTrackingMode = [_delegate respondsToSelector:@selector(mapView:didChangeUserTrackingMode:animated:)];
|
|
|
}
|
|
|
|
|
|
#pragma mark -
|
...
|
...
|
@@ -768,6 +817,9 @@ |
|
|
|
|
|
- (void)zoomInToNextNativeZoomAt:(CGPoint)pivot animated:(BOOL)animated
|
|
|
{
|
|
|
if (self.userTrackingMode != RMUserTrackingModeNone && ! CGPointEqualToPoint(pivot, self.center))
|
|
|
self.userTrackingMode = RMUserTrackingModeNone;
|
|
|
|
|
|
// Calculate rounded zoom
|
|
|
float newZoom = fmin(ceilf([self zoom]) + 0.99, [self maxZoom]);
|
|
|
|
...
|
...
|
@@ -930,6 +982,7 @@ |
|
|
_mapScrollView.minimumZoomScale = exp2f([self minZoom]);
|
|
|
_mapScrollView.maximumZoomScale = exp2f([self maxZoom]);
|
|
|
_mapScrollView.contentOffset = CGPointMake(0.0, 0.0);
|
|
|
_mapScrollView.clipsToBounds = NO;
|
|
|
|
|
|
_tiledLayersSuperview = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height)];
|
|
|
_tiledLayersSuperview.userInteractionEnabled = NO;
|
...
|
...
|
@@ -1022,6 +1075,9 @@ |
|
|
|
|
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
|
{
|
|
|
if (self.userTrackingMode != RMUserTrackingModeNone)
|
|
|
self.userTrackingMode = RMUserTrackingModeNone;
|
|
|
|
|
|
if (_delegateHasBeforeMapMove)
|
|
|
[_delegate beforeMapMove:self];
|
|
|
}
|
...
|
...
|
@@ -1067,8 +1123,14 @@ |
|
|
|
|
|
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
|
|
|
{
|
|
|
if (self.userTrackingMode != RMUserTrackingModeNone && scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateChanged)
|
|
|
self.userTrackingMode = RMUserTrackingModeNone;
|
|
|
|
|
|
[self correctPositionOfAllAnnotations];
|
|
|
|
|
|
if (_zoom < 3 && self.userTrackingMode == RMUserTrackingModeFollowWithHeading)
|
|
|
self.userTrackingMode = RMUserTrackingModeFollow;
|
|
|
|
|
|
if (_delegateHasAfterMapZoom)
|
|
|
[_delegate afterMapZoom:self];
|
|
|
}
|
...
|
...
|
@@ -2200,15 +2262,18 @@ |
|
|
|
|
|
for (RMAnnotation *annotation in previousVisibleAnnotations)
|
|
|
{
|
|
|
if (_delegateHasWillHideLayerForAnnotation)
|
|
|
[_delegate mapView:self willHideLayerForAnnotation:annotation];
|
|
|
if ( ! annotation.isUserLocationAnnotation)
|
|
|
{
|
|
|
if (_delegateHasWillHideLayerForAnnotation)
|
|
|
[_delegate mapView:self willHideLayerForAnnotation:annotation];
|
|
|
|
|
|
annotation.layer = nil;
|
|
|
annotation.layer = nil;
|
|
|
|
|
|
if (_delegateHasDidHideLayerForAnnotation)
|
|
|
[_delegate mapView:self didHideLayerForAnnotation:annotation];
|
|
|
if (_delegateHasDidHideLayerForAnnotation)
|
|
|
[_delegate mapView:self didHideLayerForAnnotation:annotation];
|
|
|
|
|
|
[_visibleAnnotations removeObject:annotation];
|
|
|
[_visibleAnnotations removeObject:annotation];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
[previousVisibleAnnotations release];
|
...
|
...
|
@@ -2248,14 +2313,17 @@ |
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (_delegateHasWillHideLayerForAnnotation)
|
|
|
[_delegate mapView:self willHideLayerForAnnotation:annotation];
|
|
|
if ( ! annotation.isUserLocationAnnotation)
|
|
|
{
|
|
|
if (_delegateHasWillHideLayerForAnnotation)
|
|
|
[_delegate mapView:self willHideLayerForAnnotation:annotation];
|
|
|
|
|
|
annotation.layer = nil;
|
|
|
[_visibleAnnotations removeObject:annotation];
|
|
|
annotation.layer = nil;
|
|
|
[_visibleAnnotations removeObject:annotation];
|
|
|
|
|
|
if (_delegateHasDidHideLayerForAnnotation)
|
|
|
[_delegate mapView:self didHideLayerForAnnotation:annotation];
|
|
|
if (_delegateHasDidHideLayerForAnnotation)
|
|
|
[_delegate mapView:self didHideLayerForAnnotation:annotation];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// RMLog(@"%d annotations on screen, %d total", [overlayView sublayersCount], [annotations count]);
|
...
|
...
|
@@ -2346,10 +2414,13 @@ |
|
|
{
|
|
|
for (RMAnnotation *annotation in annotationsToRemove)
|
|
|
{
|
|
|
[_annotations removeObject:annotation];
|
|
|
[_visibleAnnotations removeObject:annotation];
|
|
|
[self.quadTree removeAnnotation:annotation];
|
|
|
annotation.layer = nil;
|
|
|
if ( ! annotation.isUserLocationAnnotation)
|
|
|
{
|
|
|
[_annotations removeObject:annotation];
|
|
|
[_visibleAnnotations removeObject:annotation];
|
|
|
[self.quadTree removeAnnotation:annotation];
|
|
|
annotation.layer = nil;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
...
|
...
|
@@ -2358,25 +2429,401 @@ |
|
|
|
|
|
- (void)removeAllAnnotations
|
|
|
{
|
|
|
@synchronized (_annotations)
|
|
|
[self removeAnnotations:[_annotations allObjects]];
|
|
|
}
|
|
|
|
|
|
- (CGPoint)mapPositionForAnnotation:(RMAnnotation *)annotation
|
|
|
{
|
|
|
[self correctScreenPosition:annotation animated:NO];
|
|
|
return annotation.position;
|
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
|
#pragma mark User Location
|
|
|
|
|
|
- (void)setShowsUserLocation:(BOOL)newShowsUserLocation
|
|
|
{
|
|
|
if (newShowsUserLocation == showsUserLocation)
|
|
|
return;
|
|
|
|
|
|
showsUserLocation = newShowsUserLocation;
|
|
|
|
|
|
if (newShowsUserLocation)
|
|
|
{
|
|
|
if (_delegateHasWillStartLocatingUser)
|
|
|
[_delegate mapViewWillStartLocatingUser:self];
|
|
|
|
|
|
self.userLocation = [RMUserLocation annotationWithMapView:self coordinate:CLLocationCoordinate2DMake(0, 0) andTitle:nil];
|
|
|
|
|
|
locationManager = [[CLLocationManager alloc] init];
|
|
|
locationManager.headingFilter = 5;
|
|
|
locationManager.delegate = self;
|
|
|
[locationManager startUpdatingLocation];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
[locationManager stopUpdatingLocation];
|
|
|
[locationManager stopUpdatingHeading];
|
|
|
locationManager.delegate = nil;
|
|
|
[locationManager release];
|
|
|
locationManager = nil;
|
|
|
|
|
|
if (_delegateHasDidStopLocatingUser)
|
|
|
[_delegate mapViewDidStopLocatingUser:self];
|
|
|
|
|
|
[self setUserTrackingMode:RMUserTrackingModeNone animated:YES];
|
|
|
|
|
|
NSMutableArray *annotationsToRemove = [NSMutableArray array];
|
|
|
|
|
|
for (RMAnnotation *annotation in _annotations)
|
|
|
if (annotation.isUserLocationAnnotation)
|
|
|
[annotationsToRemove addObject:annotation];
|
|
|
|
|
|
for (RMAnnotation *annotationToRemove in annotationsToRemove)
|
|
|
[self removeAnnotation:annotationToRemove];
|
|
|
|
|
|
self.userLocation = nil;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (void)setUserLocation:(RMUserLocation *)newUserLocation
|
|
|
{
|
|
|
if ( ! [newUserLocation isEqual:userLocation])
|
|
|
{
|
|
|
[userLocation release];
|
|
|
userLocation = [newUserLocation retain];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (BOOL)isUserLocationVisible
|
|
|
{
|
|
|
if (userLocation)
|
|
|
{
|
|
|
CGPoint locationPoint = [self mapPositionForAnnotation:userLocation];
|
|
|
|
|
|
CGRect locationRect = CGRectMake(locationPoint.x - userLocation.location.horizontalAccuracy,
|
|
|
locationPoint.y - userLocation.location.horizontalAccuracy,
|
|
|
userLocation.location.horizontalAccuracy * 2,
|
|
|
userLocation.location.horizontalAccuracy * 2);
|
|
|
|
|
|
return CGRectIntersectsRect([self bounds], locationRect);
|
|
|
}
|
|
|
|
|
|
return NO;
|
|
|
}
|
|
|
|
|
|
- (void)setUserTrackingMode:(RMUserTrackingMode)mode
|
|
|
{
|
|
|
[self setUserTrackingMode:mode animated:YES];
|
|
|
}
|
|
|
|
|
|
- (void)setUserTrackingMode:(RMUserTrackingMode)mode animated:(BOOL)animated
|
|
|
{
|
|
|
if (mode == userTrackingMode)
|
|
|
return;
|
|
|
|
|
|
userTrackingMode = mode;
|
|
|
|
|
|
switch (userTrackingMode)
|
|
|
{
|
|
|
case RMUserTrackingModeNone:
|
|
|
default:
|
|
|
{
|
|
|
[locationManager stopUpdatingHeading];
|
|
|
|
|
|
[UIView animateWithDuration:(animated ? 0.5 : 0.0)
|
|
|
delay:0.0
|
|
|
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
|
|
|
animations:^(void)
|
|
|
{
|
|
|
_mapScrollView.transform = CGAffineTransformIdentity;
|
|
|
_overlayView.transform = CGAffineTransformIdentity;
|
|
|
|
|
|
for (RMAnnotation *annotation in _annotations)
|
|
|
if ( ! annotation.isUserLocationAnnotation)
|
|
|
annotation.layer.transform = CATransform3DIdentity;
|
|
|
}
|
|
|
completion:nil];
|
|
|
|
|
|
if (userLocationTrackingView || userHeadingTrackingView)
|
|
|
{
|
|
|
[userLocationTrackingView removeFromSuperview];
|
|
|
userLocationTrackingView = nil;
|
|
|
[userHeadingTrackingView removeFromSuperview];
|
|
|
userHeadingTrackingView = nil;
|
|
|
}
|
|
|
|
|
|
userLocation.layer.hidden = NO;
|
|
|
|
|
|
break;
|
|
|
}
|
|
|
case RMUserTrackingModeFollow:
|
|
|
{
|
|
|
self.showsUserLocation = YES;
|
|
|
|
|
|
[locationManager stopUpdatingHeading];
|
|
|
|
|
|
if (self.userLocation)
|
|
|
[self locationManager:locationManager didUpdateToLocation:self.userLocation.location fromLocation:self.userLocation.location];
|
|
|
|
|
|
if (userLocationTrackingView || userHeadingTrackingView)
|
|
|
{
|
|
|
[userLocationTrackingView removeFromSuperview];
|
|
|
userLocationTrackingView = nil;
|
|
|
[userHeadingTrackingView removeFromSuperview];
|
|
|
userHeadingTrackingView = nil;
|
|
|
}
|
|
|
|
|
|
[UIView animateWithDuration:(animated ? 0.5 : 0.0)
|
|
|
delay:0.0
|
|
|
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
|
|
|
animations:^(void)
|
|
|
{
|
|
|
_mapScrollView.transform = CGAffineTransformIdentity;
|
|
|
_overlayView.transform = CGAffineTransformIdentity;
|
|
|
|
|
|
for (RMAnnotation *annotation in _annotations)
|
|
|
if ( ! annotation.isUserLocationAnnotation)
|
|
|
annotation.layer.transform = CATransform3DIdentity;
|
|
|
}
|
|
|
completion:nil];
|
|
|
|
|
|
userLocation.layer.hidden = NO;
|
|
|
|
|
|
break;
|
|
|
}
|
|
|
case RMUserTrackingModeFollowWithHeading:
|
|
|
{
|
|
|
// Remove the layer from the screen
|
|
|
annotation.layer = nil;
|
|
|
self.showsUserLocation = YES;
|
|
|
|
|
|
userLocation.layer.hidden = YES;
|
|
|
|
|
|
userHeadingTrackingView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"HeadingAngleSmall.png"]];
|
|
|
|
|
|
userHeadingTrackingView.center = CGPointMake(round([self bounds].size.width / 2),
|
|
|
round([self bounds].size.height / 2) - (userHeadingTrackingView.bounds.size.height / 2) - 4);
|
|
|
|
|
|
userHeadingTrackingView.alpha = 0.0;
|
|
|
|
|
|
[self addSubview:userHeadingTrackingView];
|
|
|
|
|
|
[UIView animateWithDuration:0.5 animations:^(void) { userHeadingTrackingView.alpha = 1.0; }];
|
|
|
|
|
|
userLocationTrackingView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"TrackingDot.png"]];
|
|
|
|
|
|
userLocationTrackingView.center = CGPointMake(round([self bounds].size.width / 2),
|
|
|
round([self bounds].size.height / 2));
|
|
|
|
|
|
[self addSubview:userLocationTrackingView];
|
|
|
|
|
|
if (self.zoom < 3)
|
|
|
[self zoomByFactor:exp2f(3 - [self zoom]) near:self.center animated:YES];
|
|
|
|
|
|
if (self.userLocation)
|
|
|
[self locationManager:locationManager didUpdateToLocation:self.userLocation.location fromLocation:self.userLocation.location];
|
|
|
|
|
|
[locationManager startUpdatingHeading];
|
|
|
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
[_annotations removeAllObjects];
|
|
|
[_visibleAnnotations removeAllObjects];
|
|
|
[self.quadTree removeAllObjects];
|
|
|
[self correctPositionOfAllAnnotations];
|
|
|
if (_delegateHasDidChangeUserTrackingMode)
|
|
|
[_delegate mapView:self didChangeUserTrackingMode:userTrackingMode animated:animated];
|
|
|
}
|
|
|
|
|
|
- (CGPoint)mapPositionForAnnotation:(RMAnnotation *)annotation
|
|
|
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
|
|
|
{
|
|
|
[self correctScreenPosition:annotation animated:NO];
|
|
|
return annotation.position;
|
|
|
if ( ! showsUserLocation || _mapScrollView.isDragging)
|
|
|
return;
|
|
|
|
|
|
if ([newLocation distanceFromLocation:oldLocation])
|
|
|
{
|
|
|
userLocation.location = newLocation;
|
|
|
|
|
|
if (_delegateHasDidUpdateUserLocation)
|
|
|
[_delegate mapView:self didUpdateUserLocation:userLocation];
|
|
|
}
|
|
|
|
|
|
if (self.userTrackingMode != RMUserTrackingModeNone)
|
|
|
{
|
|
|
// zoom centered on user location unless we're already centered there (or very close)
|
|
|
//
|
|
|
CGPoint mapCenterPoint = [self convertPoint:self.center fromView:self.superview];
|
|
|
CGPoint userLocationPoint = [self mapPositionForAnnotation:userLocation];
|
|
|
|
|
|
if (fabsf(userLocationPoint.x - mapCenterPoint.x) > 2 || fabsf(userLocationPoint.y - mapCenterPoint.y > 2))
|
|
|
{
|
|
|
float delta = newLocation.horizontalAccuracy / 110000; // approx. meter per degree latitude
|
|
|
|
|
|
CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(newLocation.coordinate.latitude - delta,
|
|
|
newLocation.coordinate.longitude - delta);
|
|
|
|
|
|
CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(newLocation.coordinate.latitude + delta,
|
|
|
newLocation.coordinate.longitude + delta);
|
|
|
|
|
|
if (northEast.latitude != [self latitudeLongitudeBoundingBox].northEast.latitude ||
|
|
|
northEast.longitude != [self latitudeLongitudeBoundingBox].northEast.longitude ||
|
|
|
southWest.latitude != [self latitudeLongitudeBoundingBox].southWest.latitude ||
|
|
|
southWest.longitude != [self latitudeLongitudeBoundingBox].southWest.longitude)
|
|
|
[self zoomWithLatitudeLongitudeBoundsSouthWest:southWest northEast:northEast animated:YES];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
RMAnnotation *accuracyCircleAnnotation = nil;
|
|
|
|
|
|
for (RMAnnotation *annotation in _annotations)
|
|
|
if ([annotation.annotationType isEqualToString:kRMAccuracyCircleAnnotationTypeName])
|
|
|
accuracyCircleAnnotation = annotation;
|
|
|
|
|
|
if ( ! accuracyCircleAnnotation)
|
|
|
{
|
|
|
accuracyCircleAnnotation = [RMAnnotation annotationWithMapView:self coordinate:newLocation.coordinate andTitle:nil];
|
|
|
|
|
|
accuracyCircleAnnotation.annotationType = kRMAccuracyCircleAnnotationTypeName;
|
|
|
|
|
|
accuracyCircleAnnotation.clusteringEnabled = NO;
|
|
|
|
|
|
accuracyCircleAnnotation.layer = [[RMCircle alloc] initWithView:self radiusInMeters:newLocation.horizontalAccuracy];
|
|
|
|
|
|
accuracyCircleAnnotation.isUserLocationAnnotation = YES;
|
|
|
|
|
|
((RMCircle *)accuracyCircleAnnotation.layer).lineColor = [UIColor colorWithRed:0.378 green:0.552 blue:0.827 alpha:0.7];
|
|
|
((RMCircle *)accuracyCircleAnnotation.layer).fillColor = [UIColor colorWithRed:0.378 green:0.552 blue:0.827 alpha:0.15];
|
|
|
|
|
|
((RMCircle *)accuracyCircleAnnotation.layer).lineWidthInPixels = 2.0;
|
|
|
|
|
|
[self addAnnotation:accuracyCircleAnnotation];
|
|
|
}
|
|
|
|
|
|
if ([newLocation distanceFromLocation:oldLocation])
|
|
|
accuracyCircleAnnotation.coordinate = newLocation.coordinate;
|
|
|
|
|
|
if (newLocation.horizontalAccuracy != oldLocation.horizontalAccuracy)
|
|
|
((RMCircle *)accuracyCircleAnnotation.layer).radiusInMeters = newLocation.horizontalAccuracy;
|
|
|
|
|
|
RMAnnotation *trackingHaloAnnotation = nil;
|
|
|
|
|
|
for (RMAnnotation *annotation in _annotations)
|
|
|
if ([annotation.annotationType isEqualToString:kRMTrackingHaloAnnotationTypeName])
|
|
|
trackingHaloAnnotation = annotation;
|
|
|
|
|
|
if ( ! trackingHaloAnnotation)
|
|
|
{
|
|
|
trackingHaloAnnotation = [RMAnnotation annotationWithMapView:self coordinate:newLocation.coordinate andTitle:nil];
|
|
|
|
|
|
trackingHaloAnnotation.annotationType = kRMTrackingHaloAnnotationTypeName;
|
|
|
|
|
|
trackingHaloAnnotation.clusteringEnabled = NO;
|
|
|
|
|
|
// create image marker
|
|
|
//
|
|
|
trackingHaloAnnotation.layer = [[RMMarker alloc] initWithUIImage:[UIImage imageNamed:@"TrackingDotHalo.png"]];
|
|
|
|
|
|
trackingHaloAnnotation.isUserLocationAnnotation = YES;
|
|
|
|
|
|
[CATransaction begin];
|
|
|
[CATransaction setAnimationDuration:2.5];
|
|
|
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
|
|
|
|
|
|
// scale out radially
|
|
|
//
|
|
|
CABasicAnimation *boundsAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
|
|
|
|
|
|
boundsAnimation.repeatCount = MAXFLOAT;
|
|
|
|
|
|
boundsAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
|
|
|
boundsAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(2.0, 2.0, 1.0)];
|
|
|
|
|
|
boundsAnimation.removedOnCompletion = NO;
|
|
|
|
|
|
boundsAnimation.fillMode = kCAFillModeForwards;
|
|
|
|
|
|
[trackingHaloAnnotation.layer addAnimation:boundsAnimation forKey:@"animateScale"];
|
|
|
|
|
|
// go transparent as scaled out
|
|
|
//
|
|
|
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
|
|
|
|
|
opacityAnimation.repeatCount = MAXFLOAT;
|
|
|
|
|
|
opacityAnimation.fromValue = [NSNumber numberWithFloat:1.0];
|
|
|
opacityAnimation.toValue = [NSNumber numberWithFloat:-1.0];
|
|
|
|
|
|
opacityAnimation.removedOnCompletion = NO;
|
|
|
|
|
|
opacityAnimation.fillMode = kCAFillModeForwards;
|
|
|
|
|
|
[trackingHaloAnnotation.layer addAnimation:opacityAnimation forKey:@"animateOpacity"];
|
|
|
|
|
|
[CATransaction commit];
|
|
|
|
|
|
[self addAnnotation:trackingHaloAnnotation];
|
|
|
}
|
|
|
|
|
|
if ([newLocation distanceFromLocation:oldLocation])
|
|
|
trackingHaloAnnotation.coordinate = newLocation.coordinate;
|
|
|
|
|
|
userLocation.layer.hidden = ((trackingHaloAnnotation.coordinate.latitude == 0 && trackingHaloAnnotation.coordinate.longitude == 0) || self.userTrackingMode == RMUserTrackingModeFollowWithHeading);
|
|
|
|
|
|
accuracyCircleAnnotation.layer.hidden = newLocation.horizontalAccuracy <= 10;
|
|
|
|
|
|
trackingHaloAnnotation.layer.hidden = ((trackingHaloAnnotation.coordinate.latitude == 0 && trackingHaloAnnotation.coordinate.longitude == 0) || newLocation.horizontalAccuracy > 10);
|
|
|
|
|
|
if ( ! [_annotations containsObject:userLocation])
|
|
|
[self addAnnotation:userLocation];
|
|
|
}
|
|
|
|
|
|
- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
|
|
|
{
|
|
|
return YES;
|
|
|
}
|
|
|
|
|
|
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
|
|
|
{
|
|
|
if ( ! showsUserLocation || _mapScrollView.isDragging)
|
|
|
return;
|
|
|
|
|
|
userLocation.heading = newHeading;
|
|
|
|
|
|
if (_delegateHasDidUpdateUserLocation)
|
|
|
[_delegate mapView:self didUpdateUserLocation:userLocation];
|
|
|
|
|
|
if (newHeading.trueHeading != 0 && self.userTrackingMode == RMUserTrackingModeFollowWithHeading)
|
|
|
{
|
|
|
[CATransaction begin];
|
|
|
[CATransaction setAnimationDuration:1.0];
|
|
|
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
|
|
|
|
|
|
[UIView animateWithDuration:1.0
|
|
|
delay:0.0
|
|
|
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
|
|
|
animations:^(void)
|
|
|
{
|
|
|
CGFloat angle = (M_PI / -180) * newHeading.trueHeading;
|
|
|
|
|
|
_mapScrollView.transform = CGAffineTransformMakeRotation(angle);
|
|
|
_overlayView.transform = CGAffineTransformMakeRotation(angle);
|
|
|
|
|
|
for (RMAnnotation *annotation in _annotations)
|
|
|
if ( ! annotation.isUserLocationAnnotation)
|
|
|
annotation.layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMakeRotation(-angle));
|
|
|
}
|
|
|
completion:nil];
|
|
|
|
|
|
[CATransaction commit];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
|
|
|
{
|
|
|
if ([error code] != kCLErrorLocationUnknown)
|
|
|
{
|
|
|
self.userTrackingMode = RMUserTrackingModeNone;
|
|
|
|
|
|
if (_delegateHasDidFailToLocateUserWithError)
|
|
|
[_delegate mapView:self didFailToLocateUserWithError:error];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@end |
...
|
...
|
|