diff options
Diffstat (limited to 'platform/darwin')
-rw-r--r-- | platform/darwin/scripts/style-spec-overrides-v8.json | 2 | ||||
-rw-r--r-- | platform/darwin/src/MGLCircle.h | 95 | ||||
-rw-r--r-- | platform/darwin/src/MGLCircle.mm | 177 | ||||
-rw-r--r-- | platform/darwin/src/MGLCircleStyleLayer.h | 4 | ||||
-rw-r--r-- | platform/darwin/src/MGLCircle_Private.h | 26 | ||||
-rw-r--r-- | platform/darwin/src/MGLGeometry.mm | 17 | ||||
-rw-r--r-- | platform/darwin/src/MGLGeometry_Private.h | 14 | ||||
-rw-r--r-- | platform/darwin/src/MGLMultiPoint_Private.h | 3 | ||||
-rw-r--r-- | platform/darwin/src/MGLPolygon.h | 5 | ||||
-rw-r--r-- | platform/darwin/src/MGLPolygon.mm | 2 | ||||
-rw-r--r-- | platform/darwin/test/MGLGeometryTests.mm | 68 |
11 files changed, 407 insertions, 6 deletions
diff --git a/platform/darwin/scripts/style-spec-overrides-v8.json b/platform/darwin/scripts/style-spec-overrides-v8.json index 0ba2b77dc5..ae8fbac785 100644 --- a/platform/darwin/scripts/style-spec-overrides-v8.json +++ b/platform/darwin/scripts/style-spec-overrides-v8.json @@ -26,7 +26,7 @@ "examples": "See the <a href=\"https://docs.mapbox.com/ios/maps/examples/runtime-multiple-annotations/\">Dynamically style interactive points</a> and <a href=\"https://docs.mapbox.com/ios/maps/examples/clustering-with-images/\">Use images to cluster point data</a> examples learn how to style data on your map using this layer." }, "circle": { - "doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use many-sided regular polygons and configure their appearance using an `MGLFillStyleLayer` object.", + "doc": "An `MGLCircleStyleLayer` is a style layer that renders one or more filled circles on the map.\n\nUse a circle style layer to configure the visual appearance of point or point collection features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.\n\nA circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, use `MGLCircle` or many-sided regular `MGLPolygon` objects and configure their appearance using an `MGLFillStyleLayer` object.", "examples": "See the <a href=\"https://docs.mapbox.com/ios/maps/examples/dds-circle-layer/\">Data-driven circles</a>, <a href=\"https://docs.mapbox.com/ios/maps/examples/shape-collection/\">Add multiple shapes from a single shape source</a>, and <a href=\"https://docs.mapbox.com/ios/maps/examples/clustering/\">Cluster point data</a> examples to learn how to add circles to your map using this style layer." }, "heatmap": { diff --git a/platform/darwin/src/MGLCircle.h b/platform/darwin/src/MGLCircle.h new file mode 100644 index 0000000000..7d0b3b11d6 --- /dev/null +++ b/platform/darwin/src/MGLCircle.h @@ -0,0 +1,95 @@ +#import <Foundation/Foundation.h> +#import <CoreLocation/CoreLocation.h> + +#import "MGLShape.h" +#import "MGLGeometry.h" + +#import "MGLTypes.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + An `MGLCircle` object represents a closed, circular shape also known as a + <a href="https://en.wikipedia.org/wiki/Spherical_cap">spherical cap</a>. A + circle is defined by a center coordinate, specified as a + `CLLocationCoordinate2D` instance, and a physical radius measured in meters. + The circle is approximated as a polygon with a large number of vertices and + edges. Due to the map’s Spherical Mercator projection, a large enough circle + appears as an elliptical or even sinusoidal shape. You could use a circle to + visualize for instance an impact zone, the `CLLocation.horizontalAccuracy` of a + GPS location update, the regulated airspace around an airport, or the + addressable consumer market around a city. + + You can add a circle overlay directly to a map view using the + `-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure + a circle overlay’s appearance using + `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` and + `-[MGLMapViewDelegate mapView:fillColorForShape:]`. + + Alternatively, you can add a circle to the map by adding it to an + `MGLShapeSource` object. Because GeoJSON cannot represent a curve per se, the + circle is automatically converted to a polygon. See the `MGLPolygon` class for + more information about polygons. + + Do not confuse this class with `MGLCircleStyleLayer`, which renders a circle + defined by a center coordinate and a fixed _screen_ radius measured in points + regardless of the map’s zoom level. + + The polygon that approximates an `MGLCircle` has a large number of vertices. + If you do not need the circle to appear smooth at every possible zoom level, + use a many-sided regular `MGLPolygon` instead for better performance. + */ +MGL_EXPORT +@interface MGLCircle : MGLShape + +/** + The coordinate around which the circle is centered. + + Each coordinate along the circle’s edge is equidistant from this coordinate. + The center coordinate’s latitude helps determine the minimum spacing between + each vertex along the edge of the polygon that approximates the circle. + */ +@property (nonatomic) CLLocationCoordinate2D coordinate; + +/** + The radius of the circular area, measured in meters across the Earth’s surface. + */ +@property (nonatomic) CLLocationDistance radius; + +/** + Creates and returns an `MGLCircle` object centered around the given coordinate + and extending in all directions by the given physical distance. + + @param centerCoordinate The coordinate around which the circle is centered. + @param radius The radius of the circular area, measured in meters across the + Earth’s surface. + @return A new circle object. + */ ++ (instancetype)circleWithCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate radius:(CLLocationDistance)radius; + +/** + Creates and returns an `MGLCircle` object that fills the given coordinate + bounds. + + @param coordinateBounds The coordinate bounds to fill. The circle is centered + around the center of the coordinate bounds. The circle’s edge touches at + least two of the sides of the coordinate bounds. If the coordinate bounds + does not represent a square area, the circle extends beyond two of its + sides. + @return A new circle object. + */ ++ (instancetype)circleWithCoordinateBounds:(MGLCoordinateBounds)coordinateBounds; + +/** + The smallest coordinate rectangle that completely encompasses the circle. + + If the circle spans the antimeridian, its bounds may extend west of −180 + degrees longitude or east of 180 degrees longitude. For example, a circle + covering the Pacific Ocean from Tokyo to San Francisco might have a bounds + extending from (35.68476, −220.24257) to (37.78428, −122.41310). + */ +@property (nonatomic, readonly) MGLCoordinateBounds coordinateBounds; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLCircle.mm b/platform/darwin/src/MGLCircle.mm new file mode 100644 index 0000000000..f4f507ac44 --- /dev/null +++ b/platform/darwin/src/MGLCircle.mm @@ -0,0 +1,177 @@ +#import "MGLCircle.h" + +#import "MGLGeometry_Private.h" +#import "MGLMultiPoint_Private.h" +#import "NSCoder+MGLAdditions.h" + +#import <mbgl/util/projection.hpp> + +#import <vector> + +@implementation MGLCircle + +@synthesize coordinate = _coordinate; + ++ (instancetype)circleWithCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate radius:(CLLocationDistance)radius { + return [[self alloc] initWithCenterCoordinate:centerCoordinate radius:radius]; +} + ++ (instancetype)circleWithCoordinateBounds:(MGLCoordinateBounds)coordinateBounds { + MGLCoordinateSpan span = MGLCoordinateBoundsGetCoordinateSpan(coordinateBounds); + BOOL latitudinal = span.latitudeDelta > span.longitudeDelta; + // TODO: Latitudinal distances aren’t uniform, so get the mean northing. + CLLocationCoordinate2D center = CLLocationCoordinate2DMake(coordinateBounds.ne.latitude - span.latitudeDelta / 2.0, + coordinateBounds.ne.longitude - span.longitudeDelta / 2.0); + CLLocationCoordinate2D southOrWest = CLLocationCoordinate2DMake(latitudinal ? coordinateBounds.sw.latitude : 0, + latitudinal ? 0 : coordinateBounds.sw.longitude); + CLLocationCoordinate2D northOrEast = CLLocationCoordinate2DMake(latitudinal ? coordinateBounds.ne.latitude : 0, + latitudinal ? 0 : coordinateBounds.ne.longitude); + CLLocationDistance majorAxis = MGLDistanceBetweenLocationCoordinates(southOrWest, northOrEast); + return [[self alloc] initWithCenterCoordinate:center radius:majorAxis / 2.0]; +} + +- (instancetype)initWithCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate radius:(CLLocationDistance)radius { + if (self = [super init]) { + _coordinate = centerCoordinate; + _radius = radius; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + if (self = [super initWithCoder:decoder]) { + _coordinate = [decoder decodeMGLCoordinateForKey:@"coordinate"]; + _radius = [decoder decodeDoubleForKey:@"radius"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + [coder encodeMGLCoordinate:_coordinate forKey:@"coordinate"]; + [coder encodeDouble:_radius forKey:@"radius"]; +} + +- (BOOL)isEqual:(id)other { + if (other == self) { + return YES; + } + if (![other isKindOfClass:[MGLCircle class]]) { + return NO; + } + + MGLCircle *otherCircle = other; + return ([super isEqual:other] + && self.coordinate.latitude == otherCircle.coordinate.latitude + && self.coordinate.longitude == otherCircle.coordinate.longitude + && self.radius == otherCircle.radius); +} + +- (NSUInteger)hash { + return super.hash + @(self.coordinate.latitude).hash + @(self.coordinate.longitude).hash; +} + +- (NSUInteger)numberOfVertices { + // Due to the z16 zoom level and Douglas–Peucker tolerance specified by + // mbgl::ShapeAnnotationImpl::updateTileData() and GeoJSONVT, the smallest + // circle that can be displayed at z22 at the poles has a radius of about + // 5 centimeters and is simplified to four sides each about 0.31 meters + // (50 points) long. The smallest displayable circle at the Equator has a + // radius of about 5 decimeters and is simplified to four sides each about + // 3.1 meters (75 points) long. + constexpr NSUInteger maximumZoomLevel = 16; + CLLocationDistance maximumEdgeLength = mbgl::Projection::getMetersPerPixelAtLatitude(self.coordinate.latitude, maximumZoomLevel); + CLLocationDistance circumference = 2 * M_PI * self.radius; + NSUInteger maximumSides = ceil(fabs(circumference) / maximumEdgeLength); + + // The smallest perceptible angle is about 1 arcminute. + // https://en.wikipedia.org/wiki/Naked_eye#Small_objects_and_maps + constexpr CLLocationDirection maximumInternalAngle = 180.0 - 1.0 / 60; + constexpr CLLocationDirection maximumCentralAngle = 180.0 - maximumInternalAngle; + constexpr CGFloat maximumVertices = 360.0 / maximumCentralAngle; + + // Make the circle’s resolution high enough that the user can’t perceive any + // angles, but not so high that detail would be lost through simplification. + return ceil(MIN(maximumSides, maximumVertices)); +} + +- (mbgl::LinearRing<double>)linearRingWithNumberOfVertices:(NSUInteger)numberOfVertices { + CLLocationCoordinate2D center = self.coordinate; + CLLocationDistance radius = fabs(self.radius); + + mbgl::LinearRing<double> ring; + ring.reserve(numberOfVertices); + for (NSUInteger i = 0; i < numberOfVertices; i++) { + // Start at due north and go counterclockwise, or phase shift by 90° if + // centered in the southern hemisphere, so it’s easy to fix up for ±90° + // latitude in the conditional below. + CLLocationDirection direction = 360.0 / numberOfVertices * i + (center.latitude >= 0 ? 0 : 180); + CLLocationCoordinate2D vertex = MGLCoordinateAtDistanceFacingDirection(center, radius, direction); + // If the circle extends to ±90° latitude and has wrapped around, extend + // the polygon to include all of ±90° latitude and beyond. + if (i == 0 && radius > 1 + && fabs(vertex.latitude) < fabs(MGLCoordinateAtDistanceFacingDirection(center, radius - 1, direction).latitude)) { + short hemisphere = center.latitude >= 0 ? 1 : -1; + ring.push_back({ center.longitude - 180.0, vertex.latitude }); + ring.push_back({ center.longitude - 180.0, 90.0 * hemisphere }); + ring.push_back({ center.longitude + 180.0, 90.0 * hemisphere }); + } + ring.push_back(MGLPointFromLocationCoordinate2D(vertex)); + } + return ring; +} + +- (mbgl::Polygon<double>)polygon { + mbgl::Polygon<double> polygon; + polygon.push_back([self linearRingWithNumberOfVertices:self.numberOfVertices]); + return polygon; +} + +- (mbgl::Geometry<double>)geometryObject { + return [self polygon]; +} + +- (NSDictionary *)geoJSONDictionary { + return @{ + @"type": @"Polygon", + @"coordinates": self.geoJSONGeometry, + }; +} + +- (NSArray<id> *)geoJSONGeometry { + NSMutableArray *coordinates = [NSMutableArray array]; + + mbgl::LinearRing<double> ring = [self polygon][0]; + NSMutableArray *geoJSONRing = [NSMutableArray array]; + for (auto &point : ring) { + [geoJSONRing addObject:@[@(point.x), @(point.y)]]; + } + [coordinates addObject:geoJSONRing]; + + return [coordinates copy]; +} + +- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate { + + mbgl::FillAnnotation annotation { [self polygon] }; + annotation.opacity = { static_cast<float>([delegate alphaForShapeAnnotation:self]) }; + annotation.outlineColor = { [delegate strokeColorForShapeAnnotation:self] }; + annotation.color = { [delegate fillColorForShape:self] }; + + return annotation; +} + +- (MGLCoordinateBounds)coordinateBounds { + mbgl::LinearRing<double> ring = [self linearRingWithNumberOfVertices:4]; + CLLocationCoordinate2D southWest = CLLocationCoordinate2DMake(ring[2].y, ring[3].x); + CLLocationCoordinate2D northEast = CLLocationCoordinate2DMake(ring[0].y, ring[1].x); + return MGLCoordinateBoundsMake(southWest, northEast); +} + +- (NSString *)description { + return [NSString stringWithFormat:@"<%@: %p; coordinate = %@; radius = %f m>", + NSStringFromClass([self class]), (void *)self, + MGLStringFromCLLocationCoordinate2D(self.coordinate), self.radius]; +} + +@end diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h index e2b043a729..4ffe0d5289 100644 --- a/platform/darwin/src/MGLCircleStyleLayer.h +++ b/platform/darwin/src/MGLCircleStyleLayer.h @@ -69,8 +69,8 @@ typedef NS_ENUM(NSUInteger, MGLCircleTranslationAnchor) { A circle style layer renders circles whose radii are measured in screen units. To display circles on the map whose radii correspond to real-world distances, - use many-sided regular polygons and configure their appearance using an - `MGLFillStyleLayer` object. + use `MGLCircle` or many-sided regular `MGLPolygon` objects and configure their + appearance using an `MGLFillStyleLayer` object. You can access an existing circle style layer using the `-[MGLStyle layerWithIdentifier:]` method if you know its identifier; diff --git a/platform/darwin/src/MGLCircle_Private.h b/platform/darwin/src/MGLCircle_Private.h new file mode 100644 index 0000000000..ef8fb372bf --- /dev/null +++ b/platform/darwin/src/MGLCircle_Private.h @@ -0,0 +1,26 @@ +#import "MGLCircle.h" + +#import <mbgl/annotation/annotation.hpp> + +NS_ASSUME_NONNULL_BEGIN + +@protocol MGLMultiPointDelegate; + +@interface MGLCircle (Private) + +/** + The optimal number of vertices in the circle’s polygonal approximation. + */ +@property (nonatomic, readonly) NSUInteger numberOfVertices; + +/** + Returns a linear ring with the given number of vertices. + */ +- (mbgl::LinearRing<double>)linearRingWithNumberOfVertices:(NSUInteger)numberOfVertices; + +/** Constructs a circle annotation object, asking the delegate for style values. */ +- (mbgl::Annotation)annotationObjectWithDelegate:(id <MGLMultiPointDelegate>)delegate; + +@end + +NS_ASSUME_NONNULL_END diff --git a/platform/darwin/src/MGLGeometry.mm b/platform/darwin/src/MGLGeometry.mm index c6fb5a5fc2..bd25b00475 100644 --- a/platform/darwin/src/MGLGeometry.mm +++ b/platform/darwin/src/MGLGeometry.mm @@ -3,6 +3,7 @@ #import "MGLFoundation.h" #import <mbgl/util/projection.hpp> +#import <mbgl/util/constants.hpp> #if !TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR #import <Cocoa/Cocoa.h> @@ -67,6 +68,12 @@ MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from return 2 * atan2(sqrt(a), sqrt(1 - a)); } +CLLocationDistance MGLDistanceBetweenLocationCoordinates(CLLocationCoordinate2D from, CLLocationCoordinate2D to) { + MGLRadianDistance radianDistance = MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinateFromLocationCoordinate(from), + MGLRadianCoordinateFromLocationCoordinate(to)); + return radianDistance * mbgl::util::EARTH_RADIUS_M; +} + MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to) { double a = sin(to.longitude - from.longitude) * cos(to.latitude); double b = cos(from.latitude) * sin(to.latitude) @@ -84,6 +91,16 @@ MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoor return MGLRadianCoordinate2DMake(otherLatitude, otherLongitude); } +CLLocationCoordinate2D MGLCoordinateAtDistanceFacingDirection(CLLocationCoordinate2D coordinate, + CLLocationDistance distance, + CLLocationDirection direction) { + MGLRadianCoordinate2D radianCenter = MGLRadianCoordinateFromLocationCoordinate(coordinate); + MGLRadianCoordinate2D radianVertex = MGLRadianCoordinateAtDistanceFacingDirection(radianCenter, + distance / mbgl::util::EARTH_RADIUS_M, + MGLRadiansFromDegrees(direction)); + return MGLLocationCoordinateFromRadianCoordinate(radianVertex); +} + CLLocationDirection MGLDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate) { // Ported from https://github.com/mapbox/turf-swift/blob/857e2e8060678ef4a7a9169d4971b0788fdffc37/Turf/Turf.swift#L23-L31 MGLRadianCoordinate2D firstRadianCoordinate = MGLRadianCoordinateFromLocationCoordinate(firstCoordinate); diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h index b91d4e0f81..1b0bcc3861 100644 --- a/platform/darwin/src/MGLGeometry_Private.h +++ b/platform/darwin/src/MGLGeometry_Private.h @@ -112,12 +112,22 @@ NS_INLINE MGLRadianCoordinate2D MGLRadianCoordinateFromLocationCoordinate(CLLoca MGLRadiansFromDegrees(locationCoordinate.longitude)); } +NS_INLINE CLLocationCoordinate2D MGLLocationCoordinateFromRadianCoordinate(MGLRadianCoordinate2D radianCoordinate) { + return CLLocationCoordinate2DMake(MGLDegreesFromRadians(radianCoordinate.latitude), + MGLDegreesFromRadians(radianCoordinate.longitude)); +} + /** Returns the distance in radians given two coordinates. */ MGLRadianDistance MGLDistanceBetweenRadianCoordinates(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to); /** + Returns the distance given two coordinates. + */ +CLLocationDistance MGLDistanceBetweenLocationCoordinates(CLLocationCoordinate2D from, CLLocationCoordinate2D to); + +/** Returns direction in radians given two coordinates. */ MGLRadianDirection MGLRadianCoordinatesDirection(MGLRadianCoordinate2D from, MGLRadianCoordinate2D to); @@ -129,6 +139,10 @@ MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoor MGLRadianDistance distance, MGLRadianDirection direction); +CLLocationCoordinate2D MGLCoordinateAtDistanceFacingDirection(CLLocationCoordinate2D coordinate, + CLLocationDistance distance, + CLLocationDirection direction); + /** Returns the direction from one coordinate to another. */ diff --git a/platform/darwin/src/MGLMultiPoint_Private.h b/platform/darwin/src/MGLMultiPoint_Private.h index a9b4b72ca5..37ab6dc4af 100644 --- a/platform/darwin/src/MGLMultiPoint_Private.h +++ b/platform/darwin/src/MGLMultiPoint_Private.h @@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @class MGLPolygon; @class MGLPolyline; +@class MGLCircle; @protocol MGLMultiPointDelegate; @@ -36,7 +37,7 @@ NS_ASSUME_NONNULL_BEGIN - (mbgl::Color)strokeColorForShapeAnnotation:(MGLShape *)annotation; /** Returns the fill color object for the given annotation. */ -- (mbgl::Color)fillColorForPolygonAnnotation:(MGLPolygon *)annotation; +- (mbgl::Color)fillColorForShape:(MGLShape *)annotation; /** Returns the stroke width object for the given annotation. */ - (CGFloat)lineWidthForPolylineAnnotation:(MGLPolyline *)annotation; diff --git a/platform/darwin/src/MGLPolygon.h b/platform/darwin/src/MGLPolygon.h index 900e43334e..f75c4481be 100644 --- a/platform/darwin/src/MGLPolygon.h +++ b/platform/darwin/src/MGLPolygon.h @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN `-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure a polygon overlay’s appearance using `-[MGLMapViewDelegate mapView:strokeColorForShapeAnnotation:]` and - `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]`. + `-[MGLMapViewDelegate mapView:fillColorForShape:]`. The vertices are automatically connected in the order in which you provide them. You should close the polygon by specifying the same @@ -45,6 +45,9 @@ NS_ASSUME_NONNULL_BEGIN To make the polygon straddle the antimeridian, specify some longitudes less than −180 degrees or greater than 180 degrees. + To approximate a circle that corresponds to a physical radius, use an + `MGLCircle` object. + #### Related examples See the <a href="https://docs.mapbox.com/ios/maps/examples/polygon/"> Add a polygon annotation</a> example to learn how to initialize an diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm index 52bff01b20..164ba759b3 100644 --- a/platform/darwin/src/MGLPolygon.mm +++ b/platform/darwin/src/MGLPolygon.mm @@ -95,7 +95,7 @@ mbgl::FillAnnotation annotation { [self polygon] }; annotation.opacity = { static_cast<float>([delegate alphaForShapeAnnotation:self]) }; annotation.outlineColor = { [delegate strokeColorForShapeAnnotation:self] }; - annotation.color = { [delegate fillColorForPolygonAnnotation:self] }; + annotation.color = { [delegate fillColorForShape:self] }; return annotation; } diff --git a/platform/darwin/test/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm index e3b1836e8d..b7e0398400 100644 --- a/platform/darwin/test/MGLGeometryTests.mm +++ b/platform/darwin/test/MGLGeometryTests.mm @@ -2,6 +2,7 @@ #import <XCTest/XCTest.h> #import "../../darwin/src/MGLGeometry_Private.h" +#import "../../darwin/src/MGLCircle_Private.h" @interface MGLGeometryTests : XCTestCase @end @@ -208,4 +209,71 @@ } } +- (void)testCircles { + { + MGLCircle *circle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(0, 0) radius:0]; + XCTAssertEqual(circle.coordinate.latitude, 0); + XCTAssertEqual(circle.coordinate.longitude, 0); + XCTAssertEqual(circle.radius, 0); + XCTAssertEqual(circle.numberOfVertices, 0ul); + XCTAssertEqual([circle linearRingWithNumberOfVertices:0].size(), 0ul); + } + { + MGLCircle *positiveCircle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(0, 0) radius:10]; + XCTAssertEqual(positiveCircle.radius, 10); + MGLCircle *negativeCircle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(0, 0) radius:-10]; + XCTAssertEqual(negativeCircle.radius, -10); + XCTAssertEqual(positiveCircle.numberOfVertices, negativeCircle.numberOfVertices); + } + { + MGLCircle *bigCircle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(0, 0) radius:1000]; + XCTAssertEqual(bigCircle.radius, 1000); + XCTAssertEqual(bigCircle.numberOfVertices, 5261ul); + MGLCircle *biggerCircle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(0, 0) radius:10000]; + XCTAssertEqual(biggerCircle.radius, 10000); + XCTAssertEqual(biggerCircle.numberOfVertices, 21600ul); + } + { + MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(0, 0)); + MGLCircle *circle = [MGLCircle circleWithCoordinateBounds:bounds]; + XCTAssertEqual(circle.coordinate.latitude, 0); + XCTAssertEqual(circle.coordinate.longitude, 0); + XCTAssertEqual(circle.radius, 0); + } + { + MGLCoordinateBounds bounds = MGLCoordinateBoundsMake(CLLocationCoordinate2DMake(0, 0), CLLocationCoordinate2DMake(1, 0)); + MGLCircle *circle = [MGLCircle circleWithCoordinateBounds:bounds]; + XCTAssertEqual(circle.coordinate.latitude, 0.5); + XCTAssertEqual(circle.coordinate.longitude, 0); + XCTAssertGreaterThan(circle.radius, 0); + MGLCoordinateSpan span = MGLCoordinateBoundsGetCoordinateSpan(circle.coordinateBounds); + XCTAssertGreaterThan(span.latitudeDelta, 0); + XCTAssertGreaterThan(span.longitudeDelta, 0); + } + const CLLocationDistance earthRadius = 6378137.0; + { + MGLCircle *circle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(0, 0) radius:M_PI_2 * earthRadius]; + XCTAssertEqual(circle.coordinate.latitude, 0); + XCTAssertEqual(circle.coordinate.longitude, 0); + XCTAssertEqual(circle.radius, M_PI_2 * earthRadius); + XCTAssertEqual([circle linearRingWithNumberOfVertices:128].size(), 128ul); + } + { + MGLCircle *circle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(23.5, 0) radius:M_PI_2 * earthRadius]; + XCTAssertEqual(circle.coordinate.latitude, 23.5); + XCTAssertEqual(circle.coordinate.longitude, 0); + XCTAssertEqual(circle.radius, M_PI_2 * earthRadius); + XCTAssertEqual([circle linearRingWithNumberOfVertices:128].size(), 128ul + 3, + @"Polar cap should have extra vertices to cover North Pole."); + } + { + MGLCircle *circle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(-23.5, 20) radius:M_PI_2 * earthRadius]; + XCTAssertEqual(circle.coordinate.latitude, -23.5); + XCTAssertEqual(circle.coordinate.longitude, 20); + XCTAssertEqual(circle.radius, M_PI_2 * earthRadius); + XCTAssertEqual([circle linearRingWithNumberOfVertices:128].size(), 128ul + 3, + @"Polar cap should have extra vertices to cover South Pole."); + } +} + @end |