diff options
Diffstat (limited to 'platform/ios/src/MGLUserLocationHeadingBeamLayer.m')
-rw-r--r-- | platform/ios/src/MGLUserLocationHeadingBeamLayer.m | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/platform/ios/src/MGLUserLocationHeadingBeamLayer.m b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m new file mode 100644 index 0000000000..efe7e4db93 --- /dev/null +++ b/platform/ios/src/MGLUserLocationHeadingBeamLayer.m @@ -0,0 +1,104 @@ +#import "MGLUserLocationHeadingBeamLayer.h" + +#import "MGLFaux3DUserLocationAnnotationView.h" +#import "MGLGeometry.h" + +@implementation MGLUserLocationHeadingBeamLayer +{ + CAShapeLayer *_maskLayer; +} + +- (instancetype)initWithUserLocationAnnotationView:(MGLUserLocationAnnotationView *)userLocationView +{ + CGFloat size = MGLUserLocationAnnotationHaloSize; + + self = [super init]; + self.bounds = CGRectMake(0, 0, size, size); + self.position = CGPointMake(CGRectGetMidX(userLocationView.bounds), CGRectGetMidY(userLocationView.bounds)); + self.contents = (__bridge id)[self gradientImageWithTintColor:userLocationView.tintColor.CGColor]; + self.contentsGravity = kCAGravityBottom; + self.contentsScale = UIScreen.mainScreen.scale; + self.opacity = 0.4; + self.shouldRasterize = YES; + self.rasterizationScale = UIScreen.mainScreen.scale; + self.drawsAsynchronously = YES; + + _maskLayer = [CAShapeLayer layer]; + _maskLayer.frame = self.bounds; + _maskLayer.path = [self clippingMaskForAccuracy:0]; + self.mask = _maskLayer; + + return self; +} + +- (void)updateHeadingAccuracy:(CLLocationDirection)accuracy +{ + // recalculate the clipping mask based on updated accuracy + _maskLayer.path = [self clippingMaskForAccuracy:accuracy]; +} + +- (void)updateTintColor:(CGColorRef)color +{ + // redraw the raw tinted gradient + self.contents = (__bridge id)[self gradientImageWithTintColor:color]; +} + +- (CGImageRef)gradientImageWithTintColor:(CGColorRef)tintColor +{ + UIImage *image; + + CGFloat haloRadius = MGLUserLocationAnnotationHaloSize / 2.0; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(MGLUserLocationAnnotationHaloSize, haloRadius), NO, 0); + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGContextRef context = UIGraphicsGetCurrentContext(); + + // gradient from the tint color to no-alpha tint color + CGFloat gradientLocations[] = {0.0, 1.0}; + CGGradientRef gradient = CGGradientCreateWithColors( + colorSpace, + (__bridge CFArrayRef)@[(__bridge id)tintColor, + (id)CFBridgingRelease(CGColorCreateCopyWithAlpha(tintColor, 0))], + gradientLocations); + + // draw the gradient from the center point to the edge (full halo radius) + CGPoint centerPoint = CGPointMake(haloRadius, haloRadius); + CGContextDrawRadialGradient(context, gradient, + centerPoint, 0.0, + centerPoint, haloRadius, + kNilOptions); + + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + CGGradientRelease(gradient); + CGColorSpaceRelease(colorSpace); + + return image.CGImage; +} + +- (CGPathRef)clippingMaskForAccuracy:(CGFloat)accuracy +{ + // size the mask using accuracy, but keep within a good display range + CGFloat clippingDegrees = 90 - accuracy; + clippingDegrees = fmin(clippingDegrees, 70); // most accurate + clippingDegrees = fmax(clippingDegrees, 10); // least accurate + + CGRect ovalRect = CGRectMake(0, 0, MGLUserLocationAnnotationHaloSize, MGLUserLocationAnnotationHaloSize); + UIBezierPath *ovalPath = UIBezierPath.bezierPath; + + // clip the oval to ± incoming accuracy degrees (converted to radians), from the top + [ovalPath addArcWithCenter:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)) + radius:CGRectGetWidth(ovalRect) / 2.0 + startAngle:MGLRadiansFromDegrees(-180 + clippingDegrees) + endAngle:MGLRadiansFromDegrees(-clippingDegrees) + clockwise:YES]; + + [ovalPath addLineToPoint:CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect))]; + [ovalPath closePath]; + + return ovalPath.CGPath; +} + +@end |