...
|
...
|
@@ -33,6 +33,7 @@ |
|
|
#import "RMProjection.h"
|
|
|
#import "RMMarker.h"
|
|
|
#import "RMPath.h"
|
|
|
#import "RMCircle.h"
|
|
|
#import "RMAnnotation.h"
|
|
|
#import "RMQuadTree.h"
|
|
|
|
...
|
...
|
@@ -45,6 +46,8 @@ |
|
|
#import "RMMapTiledLayerView.h"
|
|
|
#import "RMMapOverlayView.h"
|
|
|
|
|
|
#import "RMUserLocation.h"
|
|
|
|
|
|
#pragma mark --- begin constants ----
|
|
|
|
|
|
#define kiPhoneMilimeteresPerPixel .1543
|
...
|
...
|
@@ -62,6 +65,7 @@ |
|
|
@interface RMMapView (PrivateMethods)
|
|
|
|
|
|
@property (nonatomic, retain) RMMapLayer *overlay;
|
|
|
@property (nonatomic, retain) RMUserLocation *userLocation;
|
|
|
|
|
|
- (void)createMapView;
|
|
|
|
...
|
...
|
@@ -74,6 +78,16 @@ |
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
@interface RMUserLocation (PrivateMethods)
|
|
|
|
|
|
@property (nonatomic, getter=isUpdating) BOOL updating;
|
|
|
@property (nonatomic, retain) CLLocation *location;
|
|
|
@property (nonatomic, retain) CLHeading *heading;
|
|
|
|
|
|
@end
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
|
@implementation RMMapView
|
|
|
{
|
|
|
BOOL _delegateHasBeforeMapMove;
|
...
|
...
|
@@ -95,6 +109,11 @@ |
|
|
BOOL _delegateHasLayerForAnnotation;
|
|
|
BOOL _delegateHasWillHideLayerForAnnotation;
|
|
|
BOOL _delegateHasDidHideLayerForAnnotation;
|
|
|
BOOL _delegateHasWillStartLocatingUser;
|
|
|
BOOL _delegateHasDidStopLocatingUser;
|
|
|
BOOL _delegateHasDidUpdateUserLocation;
|
|
|
BOOL _delegateHasDidFailToLocateUserWithError;
|
|
|
BOOL _delegateHasDidChangeUserTrackingMode;
|
|
|
|
|
|
BOOL _constrainMovement;
|
|
|
RMProjectedRect _constrainingProjectedBounds;
|
...
|
...
|
@@ -113,6 +132,7 @@ |
|
|
@synthesize quadTree;
|
|
|
@synthesize enableClustering, positionClusterMarkersAtTheGravityCenter, clusterMarkerSize, clusterAreaSize;
|
|
|
@synthesize adjustTilesForRetinaDisplay;
|
|
|
@synthesize userLocation, showsUserLocation, userTrackingMode;
|
|
|
|
|
|
#pragma mark -
|
|
|
#pragma mark Initialization
|
...
|
...
|
@@ -128,6 +148,8 @@ |
|
|
|
|
|
self.backgroundColor = [UIColor grayColor];
|
|
|
|
|
|
self.clipsToBounds = YES;
|
|
|
|
|
|
tileSource = nil;
|
|
|
projection = nil;
|
|
|
mercatorToTileProjection = nil;
|
...
|
...
|
@@ -278,6 +300,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];
|
|
|
}
|
|
|
|
...
|
...
|
@@ -338,6 +364,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:)];
|
|
|
}
|
|
|
|
|
|
- (id <RMMapViewDelegate>)delegate
|
...
|
...
|
@@ -721,6 +753,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]);
|
|
|
|
...
|
...
|
@@ -942,6 +977,7 @@ |
|
|
mapScrollView.minimumZoomScale = exp2f([self minZoom]);
|
|
|
mapScrollView.maximumZoomScale = exp2f([self maxZoom]);
|
|
|
mapScrollView.contentOffset = CGPointMake(0.0, 0.0);
|
|
|
mapScrollView.clipsToBounds = NO;
|
|
|
|
|
|
tiledLayerView = [[RMMapTiledLayerView alloc] initWithFrame:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) mapView:self];
|
|
|
tiledLayerView.delegate = self;
|
...
|
...
|
@@ -988,6 +1024,9 @@ |
|
|
|
|
|
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
|
|
|
{
|
|
|
if (self.userTrackingMode != RMUserTrackingModeNone)
|
|
|
self.userTrackingMode = RMUserTrackingModeNone;
|
|
|
|
|
|
if (_delegateHasBeforeMapMove)
|
|
|
[delegate beforeMapMove:self];
|
|
|
}
|
...
|
...
|
@@ -1033,8 +1072,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];
|
|
|
}
|
...
|
...
|
@@ -1129,7 +1174,14 @@ |
|
|
|
|
|
- (void)mapTiledLayerView:(RMMapTiledLayerView *)aTiledLayerView doubleTapAtPoint:(CGPoint)aPoint
|
|
|
{
|
|
|
[self zoomInToNextNativeZoomAt:aPoint animated:YES];
|
|
|
if (self.userTrackingMode != RMUserTrackingModeNone && CGRectContainsPoint(CGRectMake(self.center.x - 75, self.center.y - 75, 150, 150), aPoint))
|
|
|
{
|
|
|
[self zoomInToNextNativeZoomAt:self.center animated:YES];
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
[self zoomInToNextNativeZoomAt:aPoint animated:YES];
|
|
|
}
|
|
|
|
|
|
if (_delegateHasDoubleTapOnMap)
|
|
|
[delegate doubleTapOnMap:self at:aPoint];
|
...
|
...
|
@@ -1665,15 +1717,18 @@ |
|
|
|
|
|
for (RMAnnotation *annotation in previousVisibleAnnotations)
|
|
|
{
|
|
|
if (_delegateHasWillHideLayerForAnnotation)
|
|
|
[delegate mapView:self willHideLayerForAnnotation:annotation];
|
|
|
if ( ! [[NSArray arrayWithObjects:kRMUserLocationAnnotationTypeName, kRMAccuracyCircleAnnotationTypeName, kRMTrackingHaloAnnotationTypeName, nil] containsObject:annotation.annotationType])
|
|
|
{
|
|
|
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];
|
...
|
...
|
@@ -1713,14 +1768,17 @@ |
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (_delegateHasWillHideLayerForAnnotation)
|
|
|
[delegate mapView:self willHideLayerForAnnotation:annotation];
|
|
|
if ( ! [[NSArray arrayWithObjects:kRMUserLocationAnnotationTypeName, kRMAccuracyCircleAnnotationTypeName, kRMTrackingHaloAnnotationTypeName, nil] containsObject:annotation.annotationType])
|
|
|
{
|
|
|
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]);
|
...
|
...
|
@@ -1806,10 +1864,13 @@ |
|
|
{
|
|
|
for (RMAnnotation *annotation in annotationsToRemove)
|
|
|
{
|
|
|
[annotations removeObject:annotation];
|
|
|
[visibleAnnotations removeObject:annotation];
|
|
|
[self.quadTree removeAnnotation:annotation];
|
|
|
annotation.layer = nil;
|
|
|
if ( ! [[NSArray arrayWithObjects:kRMUserLocationAnnotationTypeName, kRMAccuracyCircleAnnotationTypeName, kRMTrackingHaloAnnotationTypeName, nil] containsObject:annotation.annotationType])
|
|
|
{
|
|
|
[annotations removeObject:annotation];
|
|
|
[visibleAnnotations removeObject:annotation];
|
|
|
[self.quadTree removeAnnotation:annotation];
|
|
|
annotation.layer = nil;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
...
|
...
|
@@ -1818,25 +1879,360 @@ |
|
|
|
|
|
- (void)removeAllAnnotations
|
|
|
{
|
|
|
@synchronized (annotations)
|
|
|
[self removeAnnotations:[annotations allObjects]];
|
|
|
}
|
|
|
|
|
|
- (CGPoint)mapPositionForAnnotation:(RMAnnotation *)annotation
|
|
|
{
|
|
|
[self correctScreenPosition:annotation];
|
|
|
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];
|
|
|
|
|
|
for (RMAnnotation *annotation in annotations)
|
|
|
if ([[NSArray arrayWithObjects:kRMUserLocationAnnotationTypeName, kRMAccuracyCircleAnnotationTypeName, kRMTrackingHaloAnnotationTypeName, nil] containsObject:annotation.annotationType])
|
|
|
[self removeAnnotation:annotation];
|
|
|
|
|
|
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; }
|
|
|
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; }
|
|
|
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];
|
|
|
[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];
|
|
|
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];
|
|
|
|
|
|
((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"]];
|
|
|
|
|
|
[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)
|
|
|
{
|
|
|
[UIView animateWithDuration:1.0
|
|
|
delay:0.0
|
|
|
options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationCurveEaseInOut
|
|
|
animations:^(void) { mapScrollView.transform = CGAffineTransformMakeRotation((M_PI / -180) * newHeading.trueHeading); }
|
|
|
completion:nil];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
|
|
|
{
|
|
|
if ([error code] != kCLErrorLocationUnknown)
|
|
|
{
|
|
|
self.userTrackingMode = RMUserTrackingModeNone;
|
|
|
|
|
|
if (_delegateHasDidFailToLocateUserWithError)
|
|
|
[delegate mapView:self didFailToLocateUserWithError:error];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@end |
...
|
...
|
|