summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2019-04-28 17:23:23 -0700
committerMinh Nguyễn <mxn@1ec5.org>2019-05-07 22:04:48 -0700
commit910639168aaad8f06749c5be184ae50dbe9d566b (patch)
tree9bd1642efe2c9ed65b51a8a317602975b7c5c73f
parent84a11ba4e5a6c64edd63b81d606e2ae57862c798 (diff)
downloadqtlocation-mapboxgl-upstream/1ec5-circle-2167.tar.gz
[ios, macos] Added circle geometryupstream/1ec5-circle-2167
Added an MGLCircle class that generates a many-sided polygon under the hood.
-rw-r--r--platform/darwin/scripts/style-spec-overrides-v8.json2
-rw-r--r--platform/darwin/src/MGLCircle.h95
-rw-r--r--platform/darwin/src/MGLCircle.mm177
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.h4
-rw-r--r--platform/darwin/src/MGLCircle_Private.h26
-rw-r--r--platform/darwin/src/MGLGeometry.mm17
-rw-r--r--platform/darwin/src/MGLGeometry_Private.h14
-rw-r--r--platform/darwin/src/MGLMultiPoint_Private.h3
-rw-r--r--platform/darwin/src/MGLPolygon.h5
-rw-r--r--platform/darwin/src/MGLPolygon.mm2
-rw-r--r--platform/darwin/test/MGLGeometryTests.mm68
-rw-r--r--platform/ios/CHANGELOG.md2
-rw-r--r--platform/ios/app/MBXViewController.m8
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj18
-rw-r--r--platform/ios/jazzy.yml1
-rw-r--r--platform/ios/sdk-files.json3
-rw-r--r--platform/ios/src/MGLMapView.h2
-rw-r--r--platform/ios/src/MGLMapView.mm56
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h30
-rw-r--r--platform/ios/src/Mapbox.h1
-rw-r--r--platform/ios/test/MGLMapViewDelegateIntegrationTests.swift2
-rw-r--r--platform/macos/CHANGELOG.md2
-rw-r--r--platform/macos/app/Base.lproj/MainMenu.xib4
-rw-r--r--platform/macos/app/MapDocument.m78
-rw-r--r--platform/macos/jazzy.yml1
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj12
-rw-r--r--platform/macos/sdk-files.json3
-rw-r--r--platform/macos/src/MGLMapView.mm63
-rw-r--r--platform/macos/src/MGLMapViewDelegate.h16
-rw-r--r--platform/macos/src/Mapbox.h1
-rw-r--r--platform/macos/test/MGLMapViewDelegateIntegrationTests.swift2
31 files changed, 642 insertions, 76 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
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 1940cd306f..633d27890b 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -4,6 +4,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
## 4.12.0
+* Added the `MGLCircle` class for adding physical circles to the map view as overlays or to a shape source as polygons. ([#14534](https://github.com/mapbox/mapbox-gl-native/pull/14534))
+* Deprecated the `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]` method in favor of `-[MGLMapViewDelegate mapView:fillColorForShape:]`, which is also called for `MGLCircle` annotations. ([#14534](https://github.com/mapbox/mapbox-gl-native/pull/14534))
* Fixed an issue where `-[MGLMapView setVisibleCoordinates:count:edgePadding:direction:duration:animationTimingFunction:completionHandler:]` interpreted a negative `direction` as due north instead of maintaining the current direction. ([#14575](https://github.com/mapbox/mapbox-gl-native/pull/14575))
## 4.11.0
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 3335606f98..f98ee1244d 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -2128,9 +2128,13 @@ CLLocationCoordinate2D randomWorldCoordinate() {
return [color colorWithAlphaComponent:0.9];
}
-- (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotation:(__unused MGLPolygon *)annotation
+- (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForShape:(MGLShape *)shape
{
- UIColor *color = annotation.pointCount > 3 ? [UIColor greenColor] : [UIColor redColor];
+ UIColor *color = [UIColor redColor];
+ if ([shape isKindOfClass:[MGLPolygon class]] && [(MGLPolygon *)shape pointCount] > 3)
+ {
+ color = [UIColor greenColor];
+ }
return [color colorWithAlphaComponent:0.5];
}
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index c1b0b875a2..d6e760ef06 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -516,6 +516,12 @@
DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2C81CCAAAD200E826B2 /* NSValue+MGLAdditions.m */; };
DA35D0881E1A6309007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0871E1A6309007DED41 /* one-liner.json */; };
+ DA44897E2270FFC5005B8357 /* MGLCircle.h in Headers */ = {isa = PBXBuildFile; fileRef = DA44897C2270FFC5005B8357 /* MGLCircle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA44897F2270FFC5005B8357 /* MGLCircle.h in Headers */ = {isa = PBXBuildFile; fileRef = DA44897C2270FFC5005B8357 /* MGLCircle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA4489802270FFC5005B8357 /* MGLCircle.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA44897D2270FFC5005B8357 /* MGLCircle.mm */; };
+ DA4489812270FFC5005B8357 /* MGLCircle.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA44897D2270FFC5005B8357 /* MGLCircle.mm */; };
+ DA448989227311C5005B8357 /* MGLCircle_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA448988227311C5005B8357 /* MGLCircle_Private.h */; };
+ DA44898A227311C5005B8357 /* MGLCircle_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA448988227311C5005B8357 /* MGLCircle_Private.h */; };
DA5DB12A1FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */; };
DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA6408DC1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA6408D91DA4E7D300908C90 /* MGLVectorStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -1173,6 +1179,9 @@
DA35A2D11CCAB25200E826B2 /* jazzy.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = jazzy.yml; sourceTree = "<group>"; };
DA35D0871E1A6309007DED41 /* one-liner.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "one-liner.json"; path = "../../darwin/test/one-liner.json"; sourceTree = "<group>"; };
DA3C6FF21E2859E700F962BE /* test-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "test-Bridging-Header.h"; path = "../../darwin/test/test-Bridging-Header.h"; sourceTree = "<group>"; };
+ DA44897C2270FFC5005B8357 /* MGLCircle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLCircle.h; sourceTree = "<group>"; };
+ DA44897D2270FFC5005B8357 /* MGLCircle.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCircle.mm; sourceTree = "<group>"; };
+ DA448988227311C5005B8357 /* MGLCircle_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCircle_Private.h; sourceTree = "<group>"; };
DA4A26961CB6E795000B7809 /* Mapbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Mapbox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DA57D4AA1EBA8ED300793288 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DA57D4AB1EBA909900793288 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = lt; path = lt.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
@@ -2151,6 +2160,9 @@
children = (
CA65C4F721E9BB080068B0D4 /* MGLCluster.h */,
DA8847E01CBAFA5100AB86E3 /* MGLAnnotation.h */,
+ DA448988227311C5005B8357 /* MGLCircle_Private.h */,
+ DA44897C2270FFC5005B8357 /* MGLCircle.h */,
+ DA44897D2270FFC5005B8357 /* MGLCircle.mm */,
DAD1656A1CF41981001FF4B9 /* MGLFeature_Private.h */,
DAD165691CF41981001FF4B9 /* MGLFeature.h */,
DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */,
@@ -2314,6 +2326,7 @@
DA8848231CBAFA6200AB86E3 /* MGLOfflineStorage_Private.h in Headers */,
404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */,
CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */,
+ DA44897E2270FFC5005B8357 /* MGLCircle.h in Headers */,
DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */,
35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */,
3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
@@ -2373,6 +2386,7 @@
74CB5ED1219B286400102936 /* MGLSymbolStyleLayer_Private.h in Headers */,
9221BAAD2069843A0054BDF4 /* MGLTilePyramidOfflineRegion_Private.h in Headers */,
96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */,
+ DA448989227311C5005B8357 /* MGLCircle_Private.h in Headers */,
408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */,
DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */,
558DE7A01E5615E400C7916D /* MGLFoundation_Private.h in Headers */,
@@ -2480,6 +2494,7 @@
DA35A2CA1CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
350098BC1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */,
FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
+ DA44897F2270FFC5005B8357 /* MGLCircle.h in Headers */,
96E516DE200054F700A02306 /* MGLGeometry_Private.h in Headers */,
353933FC1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
3566C76D1D4A8DFA008152BC /* MGLRasterTileSource.h in Headers */,
@@ -2573,6 +2588,7 @@
353933F31D3FB753003F57D7 /* MGLCircleStyleLayer.h in Headers */,
558DE7A11E5615E400C7916D /* MGLFoundation_Private.h in Headers */,
96E516F820005A3000A02306 /* MGLCompactCalloutView.h in Headers */,
+ DA44898A227311C5005B8357 /* MGLCircle_Private.h in Headers */,
96E516E22000551900A02306 /* MGLPointCollection_Private.h in Headers */,
3538AA1E1D542239008EC33D /* MGLForegroundStyleLayer.h in Headers */,
30E578181DAA85520050F07E /* UIImage+MGLAdditions.h in Headers */,
@@ -3148,6 +3164,7 @@
96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
ACA65F592140697200537748 /* MMEDispatchManager.m in Sources */,
40834BF31FE05E1800C1BD0D /* MMETimerManager.m in Sources */,
+ DA4489802270FFC5005B8357 /* MGLCircle.mm in Sources */,
35136D421D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
3538AA1F1D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
AC1B0918221CA14D00DB56C8 /* CLLocationManager+MMEMobileEvents.m in Sources */,
@@ -3272,6 +3289,7 @@
ACA65F5A2140697200537748 /* MMEDispatchManager.m in Sources */,
3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC911D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
+ DA4489812270FFC5005B8357 /* MGLCircle.mm in Sources */,
40834C051FE05E1800C1BD0D /* MMEDate.m in Sources */,
AC1B0919221CA14D00DB56C8 /* CLLocationManager+MMEMobileEvents.m in Sources */,
40834BFA1FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */,
diff --git a/platform/ios/jazzy.yml b/platform/ios/jazzy.yml
index d459e93613..29ecc6b66e 100644
--- a/platform/ios/jazzy.yml
+++ b/platform/ios/jazzy.yml
@@ -42,6 +42,7 @@ custom_categories:
- MGLMultiPoint
- MGLPointAnnotation
- MGLPointCollection
+ - MGLCircle
- MGLPolygon
- MGLPolyline
- MGLMultiPolygon
diff --git a/platform/ios/sdk-files.json b/platform/ios/sdk-files.json
index 3031a87fd6..1e9e46d1fe 100644
--- a/platform/ios/sdk-files.json
+++ b/platform/ios/sdk-files.json
@@ -33,6 +33,7 @@
"platform/ios/src/NSOrthography+MGLAdditions.m",
"platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMEDispatchManager.m",
"platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/MMETimerManager.m",
+ "platform/darwin/src/MGLCircle.mm",
"platform/darwin/src/MGLRasterStyleLayer.mm",
"platform/darwin/src/MGLForegroundStyleLayer.mm",
"platform/ios/vendor/mapbox-events-ios/MapboxMobileEvents/CLLocationManager+MMEMobileEvents.m",
@@ -132,6 +133,7 @@
"MGLPolyline.h": "platform/darwin/src/MGLPolyline.h",
"MGLStyleLayer.h": "platform/darwin/src/MGLStyleLayer.h",
"MGLCameraChangeReason.h": "platform/ios/src/MGLCameraChangeReason.h",
+ "MGLCircle.h": "platform/darwin/src/MGLCircle.h",
"MGLCalloutView.h": "platform/ios/src/MGLCalloutView.h",
"NSExpression+MGLAdditions.h": "platform/darwin/src/NSExpression+MGLAdditions.h",
"MGLShape.h": "platform/darwin/src/MGLShape.h",
@@ -243,6 +245,7 @@
"MGLSymbolStyleLayer_Private.h": "platform/darwin/src/MGLSymbolStyleLayer_Private.h",
"MGLTilePyramidOfflineRegion_Private.h": "platform/darwin/src/MGLTilePyramidOfflineRegion_Private.h",
"MGLUserLocationHeadingIndicator.h": "platform/ios/src/MGLUserLocationHeadingIndicator.h",
+ "MGLCircle_Private.h": "platform/darwin/src/MGLCircle_Private.h",
"NSDictionary+MGLAdditions.h": "platform/darwin/src/NSDictionary+MGLAdditions.h",
"MGLFoundation_Private.h": "platform/darwin/src/MGLFoundation_Private.h",
"MGLUserLocationHeadingBeamLayer.h": "platform/ios/src/MGLUserLocationHeadingBeamLayer.h",
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 63bd28fc0c..ed5462a755 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -11,8 +11,6 @@ NS_ASSUME_NONNULL_BEGIN
@class MGLAnnotationView;
@class MGLAnnotationImage;
@class MGLUserLocation;
-@class MGLPolyline;
-@class MGLPolygon;
@class MGLShape;
@class MGLStyle;
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index fdd91dacd7..f43bb93115 100644
--- a/platform/ios/src/MGLMapView.mm
+++ b/platform/ios/src/MGLMapView.mm
@@ -38,6 +38,7 @@
#import "MGLFeature_Private.h"
#import "MGLGeometry_Private.h"
#import "MGLMultiPoint_Private.h"
+#import "MGLCircle_Private.h"
#import "MGLOfflineStorage_Private.h"
#import "MGLVectorTileSource_Private.h"
#import "MGLFoundation_Private.h"
@@ -317,6 +318,7 @@ public:
BOOL _delegateHasAlphasForShapeAnnotations;
BOOL _delegateHasStrokeColorsForShapeAnnotations;
+ BOOL _delegateHasFillColorsForPolygonAnnotations;
BOOL _delegateHasFillColorsForShapeAnnotations;
BOOL _delegateHasLineWidthsForShapeAnnotations;
@@ -826,7 +828,8 @@ public:
_delegateHasAlphasForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:alphaForShapeAnnotation:)];
_delegateHasStrokeColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:strokeColorForShapeAnnotation:)];
- _delegateHasFillColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:fillColorForPolygonAnnotation:)];
+ _delegateHasFillColorsForPolygonAnnotations = [_delegate respondsToSelector:@selector(mapView:fillColorForPolygonAnnotation:)];
+ _delegateHasFillColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:fillColorForShape:)];
_delegateHasLineWidthsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:lineWidthForPolylineAnnotation:)];
}
@@ -2557,7 +2560,7 @@ public:
[MGLMapboxEvents ensureMetricsOptoutExists];
}
}
- else if ([keyPath isEqualToString:@"coordinate"] && [object conformsToProtocol:@protocol(MGLAnnotation)] && ![object isKindOfClass:[MGLMultiPoint class]])
+ else if ([keyPath isEqualToString:@"coordinate"] && [object conformsToProtocol:@protocol(MGLAnnotation)] && ![object isKindOfClass:[MGLMultiPoint class]] && ![object isKindOfClass:[MGLCircle class]])
{
id <MGLAnnotation> annotation = object;
MGLAnnotationTag annotationTag = (MGLAnnotationTag)(NSUInteger)context;
@@ -2588,7 +2591,8 @@ public:
}
}
}
- else if ([keyPath isEqualToString:@"coordinates"] && [object isKindOfClass:[MGLMultiPoint class]])
+ else if (([keyPath isEqualToString:@"coordinates"] && [object isKindOfClass:[MGLMultiPoint class]]) ||
+ (([keyPath isEqualToString:@"coordinate"] || [keyPath isEqualToString:@"radius"]) && [object isKindOfClass:[MGLCircle class]]))
{
MGLMultiPoint *annotation = object;
MGLAnnotationTag annotationTag = (MGLAnnotationTag)(NSUInteger)context;
@@ -4222,6 +4226,21 @@ public:
[(NSObject *)annotation addObserver:self forKeyPath:@"coordinates" options:0 context:(void *)(NSUInteger)annotationTag];
}
+ else if ([annotation isKindOfClass:[MGLCircle class]])
+ {
+ // The circle knows how to style itself (with the map view’s help).
+ MGLCircle *circle = (MGLCircle *)annotation;
+
+ _isChangingAnnotationLayers = YES;
+ MGLAnnotationTag annotationTag = _mbglMap->addAnnotation([circle annotationObjectWithDelegate:self]);
+ MGLAnnotationContext context;
+ context.annotation = annotation;
+ _annotationContextsByAnnotationTag[annotationTag] = context;
+ _annotationTagsByAnnotation[annotation] = annotationTag;
+
+ [(NSObject *)annotation addObserver:self forKeyPath:@"coordinate" options:0 context:(void *)(NSUInteger)annotationTag];
+ [(NSObject *)annotation addObserver:self forKeyPath:@"radius" options:0 context:(void *)(NSUInteger)annotationTag];
+ }
else if ( ! [annotation isKindOfClass:[MGLMultiPolyline class]]
&& ![annotation isKindOfClass:[MGLMultiPolygon class]]
&& ![annotation isKindOfClass:[MGLShapeCollection class]]
@@ -4439,11 +4458,26 @@ public:
return color.mgl_color;
}
-- (mbgl::Color)fillColorForPolygonAnnotation:(MGLPolygon *)annotation
+- (mbgl::Color)fillColorForShape:(MGLShape *)shape
{
- UIColor *color = (_delegateHasFillColorsForShapeAnnotations
- ? [self.delegate mapView:self fillColorForPolygonAnnotation:annotation]
- : self.tintColor);
+ UIColor *color = self.tintColor;
+ if (_delegateHasFillColorsForShapeAnnotations)
+ {
+ color = [self.delegate mapView:self fillColorForShape:shape];
+ }
+ else if (_delegateHasFillColorsForPolygonAnnotations && [shape isKindOfClass:[MGLPolygon class]])
+ {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSLog(@"-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:] is deprecated; "
+ @"use -[MGLMapViewDelegate mapView:fillColorForShape:] instead."
+ @"This warning will only appear once.");
+ });
+ color = [self.delegate mapView:self fillColorForPolygonAnnotation:(MGLPolygon *)shape];
+#pragma clang diagnostic pop
+ }
return color.mgl_color;
}
@@ -4535,6 +4569,10 @@ public:
{
[(NSObject *)annotation removeObserver:self forKeyPath:@"coordinates" context:(void *)(NSUInteger)annotationTag];
}
+ if ([annotation isKindOfClass:[MGLCircle class]])
+ {
+ [(NSObject *)annotation removeObserver:self forKeyPath:@"radius" context:(void *)(NSUInteger)annotationTag];
+ }
_isChangingAnnotationLayers = YES;
self.mbglMap.removeAnnotation(annotationTag);
@@ -5286,6 +5324,10 @@ public:
{
bounds.extend(MGLLatLngBoundsFromCoordinateBounds(((id <MGLOverlay>)annotation).overlayBounds));
}
+ else if ([annotation isKindOfClass:[MGLCircle class]])
+ {
+ bounds.extend(MGLLatLngBoundsFromCoordinateBounds(((MGLCircle *)annotation).coordinateBounds));
+ }
else
{
bounds.extend(MGLLatLngFromLocationCoordinate2D(annotation.coordinate));
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 055d4c9517..c6128ae953 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -1,11 +1,19 @@
#import <UIKit/UIKit.h>
-#import "Mapbox.h"
#import "MGLCameraChangeReason.h"
NS_ASSUME_NONNULL_BEGIN
+@protocol MGLAnnotation;
+@protocol MGLCalloutView;
+@class MGLAnnotationImage;
+@class MGLAnnotationView;
+@class MGLCircle;
+@class MGLMapCamera;
@class MGLMapView;
+@class MGLPolygon;
+@class MGLPolyline;
+@class MGLShape;
/**
The `MGLMapViewDelegate` protocol defines a set of optional methods that you
@@ -422,25 +430,25 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation;
+- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation __attribute__((deprecated("", "-mapView:fillColorForShape:")));
+
/**
- Returns the color to use when rendering the fill of a polygon annotation.
+ Returns the color to use when rendering the fill of a shape annotation.
+
+ This method is only called for `MGLPolygon` and `MGLCircle` annotations. It is
+ not possible to fill a polyline or point annotation.
- The default fill color is the map view’s tint color. If a pattern color is
+ The default fill color is the selected menu item color. If a pattern color is
specified, the result is undefined.
Opacity may be set by specifying an alpha component. The default alpha value is
`1.0` and results in a completely opaque shape.
@param mapView The map view rendering the polygon annotation.
- @param annotation The annotation being rendered.
- @return The polygon’s interior fill color.
-
- #### Related examples
- See the <a href="https://docs.mapbox.com/ios/maps/examples/polygon/">Add
- a polygon annotation</a> example to learn how to modify the color of a an
- `MGLPolygon` at runtime.
+ @param shape The annotation being rendered.
+ @return The shape’s fill color.
*/
-- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation;
+- (UIColor *)mapView:(MGLMapView *)mapView fillColorForShape:(MGLShape *)shape;
/**
Returns the line width in points to use when rendering the outline of a
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index 635bda490f..b4f5a707e9 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -34,6 +34,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLPointCollection.h"
#import "MGLPolygon.h"
#import "MGLPolyline.h"
+#import "MGLCircle.h"
#import "MGLShape.h"
#import "MGLShapeCollection.h"
#import "MGLStyle.h"
diff --git a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
index 1330281faa..e117298d5a 100644
--- a/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
+++ b/platform/ios/test/MGLMapViewDelegateIntegrationTests.swift
@@ -82,6 +82,8 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -> UIColor { return .black }
func mapView(_ mapView: MGLMapView, fillColorForPolygonAnnotation annotation: MGLPolygon) -> UIColor { return .black }
+
+ func mapView(_ mapView: MGLMapView, fillColorFor shape: MGLShape) -> UIColor { return .black }
func mapView(_ mapView: MGLMapView, leftCalloutAccessoryViewFor annotation: MGLAnnotation) -> UIView? { return nil }
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index 98bdda0abc..f0ba162cf6 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -20,6 +20,8 @@
### Annotations
+* Added the `MGLCircle` class for adding physical circles to the map view as overlays or to a shape source as polygons. ([#14534](https://github.com/mapbox/mapbox-gl-native/pull/14534))
+* Deprecated the `-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:]` method in favor of `-[MGLMapViewDelegate mapView:fillColorForShape:]`, which is also called for `MGLCircle` annotations. ([#14534](https://github.com/mapbox/mapbox-gl-native/pull/14534))
* Fixed a bug with `MGLMapView.visibleAnnotations` that resulted in incorrect results and performance degradation. ([#13745](https://github.com/mapbox/mapbox-gl-native/pull/13745))
* Fixed a bug where selecting partially on-screen annotations (without a callout) would move the map. ([#13727](https://github.com/mapbox/mapbox-gl-native/pull/13727))
diff --git a/platform/macos/app/Base.lproj/MainMenu.xib b/platform/macos/app/Base.lproj/MainMenu.xib
index 6f8f24ce99..a359570d8a 100644
--- a/platform/macos/app/Base.lproj/MainMenu.xib
+++ b/platform/macos/app/Base.lproj/MainMenu.xib
@@ -542,9 +542,9 @@
<action selector="dropManyPins:" target="-1" id="Rtv-8N-3Z8"/>
</connections>
</menuItem>
- <menuItem title="Add Polygon and Polyline" keyEquivalent="l" id="DVr-vT-lpe">
+ <menuItem title="Simulate Nightfall" keyEquivalent="l" id="DVr-vT-lpe">
<connections>
- <action selector="drawPolygonAndPolyLineAnnotations:" target="-1" id="EhT-CB-gee"/>
+ <action selector="addNightAndLighthouse:" target="-1" id="EhT-CB-gee"/>
</connections>
</menuItem>
<menuItem title="Add Animated Annotation" id="Etf-JN-Aoc">
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 213aa33107..7716dfeb05 100644
--- a/platform/macos/app/MapDocument.m
+++ b/platform/macos/app/MapDocument.m
@@ -99,7 +99,7 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha
BOOL _showsToolTipsOnDroppedPins;
BOOL _randomizesCursorsOnDroppedPins;
BOOL _isTouringWorld;
- BOOL _isShowingPolygonAndPolylineAnnotations;
+ BOOL _isShowingNightAndLighthouse;
BOOL _isShowingAnimatedAnnotation;
MGLMapSnapshotter *_snapshotter;
@@ -627,7 +627,7 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha
- (IBAction)removeAllAnnotations:(id)sender {
[self.mapView removeAnnotations:self.mapView.annotations];
- _isShowingPolygonAndPolylineAnnotations = NO;
+ _isShowingNightAndLighthouse = NO;
_isShowingAnimatedAnnotation = NO;
}
@@ -673,33 +673,55 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha
self.mapView.camera = self.mapView.camera;
}
-- (IBAction)drawPolygonAndPolyLineAnnotations:(id)sender {
+- (IBAction)addNightAndLighthouse:(id)sender {
- if (_isShowingPolygonAndPolylineAnnotations) {
- [self removeAllAnnotations:sender];
+ if (_isShowingNightAndLighthouse) {
return;
}
- _isShowingPolygonAndPolylineAnnotations = YES;
+ _isShowingNightAndLighthouse = YES;
- // Pacific Northwest triangle
- CLLocationCoordinate2D triangleCoordinates[3] = {
- CLLocationCoordinate2DMake(44, -122),
- CLLocationCoordinate2DMake(46, -122),
- CLLocationCoordinate2DMake(46, -121)
+ // Daylight map, produced by multiple spherical caps simulating night
+ // https://en.wikipedia.org/wiki/Twilight
+ CLLocationDistance nightRadius = M_PI_2 * 6378137.0;
+ CLLocationDistance nightCapRadii[] = {
+ nightRadius, // civil twilight
+ nightRadius * (1.0 - 18.0 / 180 * 2), // nautical twilight
+ nightRadius * (1.0 - 12.0 / 180 * 2), // astronomical twilight
+ nightRadius * (1.0 - 6.0 / 180 * 2), // night
};
- MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3];
- [self.mapView addAnnotation:triangle];
-
- // West coast line
- CLLocationCoordinate2D lineCoordinates[4] = {
- CLLocationCoordinate2DMake(47.6025, -122.3327),
- CLLocationCoordinate2DMake(45.5189, -122.6726),
- CLLocationCoordinate2DMake(37.7790, -122.4177),
- CLLocationCoordinate2DMake(34.0532, -118.2349)
- };
- MGLPolyline *line = [MGLPolyline polylineWithCoordinates:lineCoordinates count:4];
- [self.mapView addAnnotation:line];
+ NSMutableArray *nightCaps = [NSMutableArray array];
+ for (size_t i = 0; i < sizeof(nightCapRadii) / sizeof(nightCapRadii[0]); i++) {
+ MGLCircle *cap = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(23.5, 0)
+ radius:nightCapRadii[i]];
+ cap.title = @"Night";
+ [nightCaps addObject:cap];
+ }
+ [self.mapView addAnnotations:nightCaps];
+
+ // Concentric circles around Boston Light
+ // https://en.wikipedia.org/wiki/Boston_Light
+ CLLocationCoordinate2D epicenter = CLLocationCoordinate2DMake(42.32792025, -70.8901050288306);
+ NSMutableArray *concentricCircles = [NSMutableArray array];
+ for (NSUInteger magnitude = 0; magnitude < 8; magnitude++) {
+ CLLocationDistance radius = exp(magnitude);
+ MGLCircle *circle = [MGLCircle circleWithCenterCoordinate:epicenter radius:radius];
+ [concentricCircles addObject:circle];
+ }
+ [self.mapView addAnnotations:concentricCircles];
+ [self.mapView showAnnotations:@[concentricCircles[5]] animated:NO];
+
+ __block NSUInteger stepCount = 0;
+ [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
+ // Pulse each circle for a minute.
+ [concentricCircles enumerateObjectsUsingBlock:^(MGLCircle * _Nonnull circle, NSUInteger magnitude, BOOL * _Nonnull stop) {
+ circle.radius = exp(magnitude) + exp(magnitude - 1) * sin(M_PI / 10 * stepCount);
+ }];
+
+ if (++stepCount >= 60) {
+ [timer invalidate];
+ }
+ }];
}
- (IBAction)drawAnimatedAnnotation:(id)sender {
@@ -1249,8 +1271,8 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha
if (menuItem.action == @selector(dropManyPins:)) {
return YES;
}
- if (menuItem.action == @selector(drawPolygonAndPolyLineAnnotations:)) {
- return !_isShowingPolygonAndPolylineAnnotations;
+ if (menuItem.action == @selector(addNightAndLighthouse:)) {
+ return !_isShowingNightAndLighthouse;
}
if (menuItem.action == @selector(drawAnimatedAnnotation:)) {
return !_isShowingAnimatedAnnotation;
@@ -1442,7 +1464,11 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha
}
- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation {
- return 0.8;
+ return [annotation isKindOfClass:[MGLCircle class]] ? 0.1 : 0.8;
+}
+
+- (NSColor *)mapView:(MGLMapView *)mapView fillColorForShape:(MGLShape *)shape {
+ return shape.title ? [NSColor blackColor] : [NSColor whiteColor];
}
#pragma mark - MGLComputedShapeSourceDataSource
diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml
index 381e6f8b33..3cea18e80d 100644
--- a/platform/macos/jazzy.yml
+++ b/platform/macos/jazzy.yml
@@ -41,6 +41,7 @@ custom_categories:
- MGLMultiPoint
- MGLPointAnnotation
- MGLPointCollection
+ - MGLCircle
- MGLPolygon
- MGLPolyline
- MGLMultiPolygon
diff --git a/platform/macos/macos.xcodeproj/project.pbxproj b/platform/macos/macos.xcodeproj/project.pbxproj
index 91ed2f4cfa..e0c584b0ad 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -145,6 +145,9 @@
DA35A2CF1CCAAED300E826B2 /* NSValue+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA35A2D01CCAAED300E826B2 /* NSValue+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */; };
DA35D08A1E1A631B007DED41 /* one-liner.json in Resources */ = {isa = PBXBuildFile; fileRef = DA35D0891E1A631B007DED41 /* one-liner.json */; };
+ DA44898422730B4A005B8357 /* MGLCircle.h in Headers */ = {isa = PBXBuildFile; fileRef = DA44898222730B49005B8357 /* MGLCircle.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA44898522730B4A005B8357 /* MGLCircle.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA44898322730B4A005B8357 /* MGLCircle.mm */; };
+ DA44898722731139005B8357 /* MGLCircle_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA44898622731139005B8357 /* MGLCircle_Private.h */; };
DA551B821DB496AC0009AFAF /* MGLTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DA551B7F1DB496AC0009AFAF /* MGLTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA551B831DB496AC0009AFAF /* MGLTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA551B801DB496AC0009AFAF /* MGLTileSource_Private.h */; };
DA551B841DB496AC0009AFAF /* MGLTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA551B811DB496AC0009AFAF /* MGLTileSource.mm */; };
@@ -488,6 +491,9 @@
DA35A2CD1CCAAED300E826B2 /* NSValue+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLAdditions.h"; sourceTree = "<group>"; };
DA35A2CE1CCAAED300E826B2 /* NSValue+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSValue+MGLAdditions.m"; sourceTree = "<group>"; };
DA35D0891E1A631B007DED41 /* one-liner.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "one-liner.json"; path = "../../darwin/test/one-liner.json"; sourceTree = "<group>"; };
+ DA44898222730B49005B8357 /* MGLCircle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCircle.h; sourceTree = "<group>"; };
+ DA44898322730B4A005B8357 /* MGLCircle.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLCircle.mm; sourceTree = "<group>"; };
+ DA44898622731139005B8357 /* MGLCircle_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLCircle_Private.h; sourceTree = "<group>"; };
DA551B7F1DB496AC0009AFAF /* MGLTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource.h; sourceTree = "<group>"; };
DA551B801DB496AC0009AFAF /* MGLTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource_Private.h; sourceTree = "<group>"; };
DA551B811DB496AC0009AFAF /* MGLTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLTileSource.mm; sourceTree = "<group>"; };
@@ -1047,6 +1053,9 @@
isa = PBXGroup;
children = (
DAE6C34B1CC31E0400DB3429 /* MGLAnnotation.h */,
+ DA44898622731139005B8357 /* MGLCircle_Private.h */,
+ DA44898222730B49005B8357 /* MGLCircle.h */,
+ DA44898322730B4A005B8357 /* MGLCircle.mm */,
CA4045C4216720D700B356E1 /* MGLCluster.h */,
DACC22171CF3D4F700D220D9 /* MGLFeature_Private.h */,
DACC22121CF3D3E200D220D9 /* MGLFeature.h */,
@@ -1361,6 +1370,7 @@
DAE6C3621CC31E0400DB3429 /* MGLOverlay.h in Headers */,
DAE6C3651CC31E0400DB3429 /* MGLPolyline.h in Headers */,
DAE6C39A1CC31E2A00DB3429 /* NSProcessInfo+MGLAdditions.h in Headers */,
+ DA44898722731139005B8357 /* MGLCircle_Private.h in Headers */,
92F2C3EB1F0E3A1900268EC0 /* MGLRendererFrontend.h in Headers */,
DA8F258B1D51CA540010E6B5 /* MGLLineStyleLayer.h in Headers */,
35C6DF841E214C0400ACA483 /* MGLDistanceFormatter.h in Headers */,
@@ -1370,6 +1380,7 @@
359819591E02F611008FC139 /* NSCoder+MGLAdditions.h in Headers */,
DAE6C38E1CC31E2A00DB3429 /* MGLOfflineStorage_Private.h in Headers */,
747ABE61219B2C0000523B67 /* MGLLineStyleLayer_Private.h in Headers */,
+ DA44898422730B4A005B8357 /* MGLCircle.h in Headers */,
747ABE5F219B2BED00523B67 /* MGLHillshadeStyleLayer_Private.h in Headers */,
DA87A9A01DC9DC6200810D09 /* MGLValueEvaluator.h in Headers */,
8946239D200E744800DA8EF2 /* MGLHeatmapStyleLayer.h in Headers */,
@@ -1652,6 +1663,7 @@
35602BFB1D3EA99F0050646F /* MGLFillStyleLayer.mm in Sources */,
DAE6C3931CC31E2A00DB3429 /* MGLShape.mm in Sources */,
352742861D4C244700A1ECE6 /* MGLRasterTileSource.mm in Sources */,
+ DA44898522730B4A005B8357 /* MGLCircle.mm in Sources */,
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */,
DAE6C39D1CC31E2A00DB3429 /* NSString+MGLAdditions.m in Sources */,
3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.mm in Sources */,
diff --git a/platform/macos/sdk-files.json b/platform/macos/sdk-files.json
index 4448de1f5b..bb68180f65 100644
--- a/platform/macos/sdk-files.json
+++ b/platform/macos/sdk-files.json
@@ -23,6 +23,7 @@
"platform/darwin/src/MGLFillStyleLayer.mm",
"platform/darwin/src/MGLShape.mm",
"platform/darwin/src/MGLRasterTileSource.mm",
+ "platform/darwin/src/MGLCircle.mm",
"platform/darwin/src/MGLFoundation.mm",
"platform/darwin/src/NSString+MGLAdditions.m",
"platform/darwin/src/NSCoder+MGLAdditions.mm",
@@ -118,6 +119,7 @@
"MGLPolyline.h": "platform/darwin/src/MGLPolyline.h",
"MGLLineStyleLayer.h": "platform/darwin/src/MGLLineStyleLayer.h",
"MGLDistanceFormatter.h": "platform/darwin/src/MGLDistanceFormatter.h",
+ "MGLCircle.h": "platform/darwin/src/MGLCircle.h",
"MGLHeatmapStyleLayer.h": "platform/darwin/src/MGLHeatmapStyleLayer.h",
"MGLOfflineRegion.h": "platform/darwin/src/MGLOfflineRegion.h",
"MGLTilePyramidOfflineRegion.h": "platform/darwin/src/MGLTilePyramidOfflineRegion.h",
@@ -178,6 +180,7 @@
"NSCompoundPredicate+MGLAdditions.h": "platform/darwin/src/NSCompoundPredicate+MGLAdditions.h",
"MGLSymbolStyleLayer_Private.h": "platform/darwin/src/MGLSymbolStyleLayer_Private.h",
"NSProcessInfo+MGLAdditions.h": "platform/macos/src/NSProcessInfo+MGLAdditions.h",
+ "MGLCircle_Private.h": "platform/darwin/src/MGLCircle_Private.h",
"MGLRendererFrontend.h": "platform/darwin/src/MGLRendererFrontend.h",
"NSValue+MGLStyleAttributeAdditions.h": "platform/darwin/src/NSValue+MGLStyleAttributeAdditions.h",
"NSImage+MGLAdditions.h": "platform/macos/src/NSImage+MGLAdditions.h",
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index e9259cf907..9c1cc890f6 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -21,6 +21,7 @@
#import "MGLMapCamera.h"
#import "MGLPolygon.h"
#import "MGLPolyline.h"
+#import "MGLCircle_Private.h"
#import "MGLAnnotationImage.h"
#import "MGLMapViewDelegate.h"
#import "MGLImageSource.h"
@@ -194,6 +195,7 @@ public:
BOOL _delegateHasAlphasForShapeAnnotations;
BOOL _delegateHasStrokeColorsForShapeAnnotations;
+ BOOL _delegateHasFillColorsForPolygonAnnotations;
BOOL _delegateHasFillColorsForShapeAnnotations;
BOOL _delegateHasLineWidthsForShapeAnnotations;
@@ -566,7 +568,8 @@ public:
[self adjustContentInsets];
} else if ([keyPath isEqualToString:@"coordinate"] &&
[object conformsToProtocol:@protocol(MGLAnnotation)] &&
- ![object isKindOfClass:[MGLMultiPoint class]]) {
+ ![object isKindOfClass:[MGLMultiPoint class]] &&
+ ![object isKindOfClass:[MGLCircle class]]) {
id <MGLAnnotation> annotation = object;
MGLAnnotationTag annotationTag = (MGLAnnotationTag)(NSUInteger)context;
// We can get here because a subclass registered itself as an observer
@@ -581,8 +584,11 @@ public:
_mbglMap->updateAnnotation(annotationTag, mbgl::SymbolAnnotation { point, annotationImage.styleIconIdentifier.UTF8String ?: "" });
[self updateAnnotationCallouts];
}
- } else if ([keyPath isEqualToString:@"coordinates"] &&
- [object isKindOfClass:[MGLMultiPoint class]]) {
+ } else if (([keyPath isEqualToString:@"coordinates"] &&
+ [object isKindOfClass:[MGLMultiPoint class]]) ||
+ (([keyPath isEqualToString:@"coordinate"] ||
+ [keyPath isEqualToString:@"radius"]) &&
+ [object isKindOfClass:[MGLCircle class]])) {
MGLMultiPoint *annotation = object;
MGLAnnotationTag annotationTag = (MGLAnnotationTag)(NSUInteger)context;
// We can get here because a subclass registered itself as an observer
@@ -609,7 +615,8 @@ public:
// hot loop, namely the annotation style methods.
_delegateHasAlphasForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:alphaForShapeAnnotation:)];
_delegateHasStrokeColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:strokeColorForShapeAnnotation:)];
- _delegateHasFillColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:fillColorForPolygonAnnotation:)];
+ _delegateHasFillColorsForPolygonAnnotations = [_delegate respondsToSelector:@selector(mapView:fillColorForPolygonAnnotation:)];
+ _delegateHasFillColorsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:fillColorForShape:)];
_delegateHasLineWidthsForShapeAnnotations = [_delegate respondsToSelector:@selector(mapView:lineWidthForPolylineAnnotation:)];
#pragma clang diagnostic push
@@ -1979,6 +1986,19 @@ public:
_annotationTagsByAnnotation[annotation] = annotationTag;
[(NSObject *)annotation addObserver:self forKeyPath:@"coordinates" options:0 context:(void *)(NSUInteger)annotationTag];
+ } else if ([annotation isKindOfClass:[MGLCircle class]]) {
+ // The circle knows how to style itself (with the map view’s help).
+ MGLCircle *circle = (MGLCircle *)annotation;
+
+ _isChangingAnnotationLayers = YES;
+ MGLAnnotationTag annotationTag = _mbglMap->addAnnotation([circle annotationObjectWithDelegate:self]);
+ MGLAnnotationContext context;
+ context.annotation = annotation;
+ _annotationContextsByAnnotationTag[annotationTag] = context;
+ _annotationTagsByAnnotation[annotation] = annotationTag;
+
+ [(NSObject *)annotation addObserver:self forKeyPath:@"coordinate" options:0 context:(void *)(NSUInteger)annotationTag];
+ [(NSObject *)annotation addObserver:self forKeyPath:@"radius" options:0 context:(void *)(NSUInteger)annotationTag];
} else if (![annotation isKindOfClass:[MGLMultiPolyline class]]
&& ![annotation isKindOfClass:[MGLMultiPolygon class]]
&& ![annotation isKindOfClass:[MGLShapeCollection class]]
@@ -2111,6 +2131,9 @@ public:
} else if ([annotation isKindOfClass:[MGLMultiPoint class]]) {
[(NSObject *)annotation removeObserver:self forKeyPath:@"coordinates" context:(void *)(NSUInteger)annotationTag];
}
+ if ([annotation isKindOfClass:[MGLCircle class]]) {
+ [(NSObject *)annotation removeObserver:self forKeyPath:@"radius" context:(void *)(NSUInteger)annotationTag];
+ }
_isChangingAnnotationLayers = YES;
_mbglMap->removeAnnotation(annotationTag);
@@ -2477,14 +2500,12 @@ public:
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
- for (id <MGLAnnotation> annotation in annotations)
- {
- if ([annotation conformsToProtocol:@protocol(MGLOverlay)])
- {
+ for (id <MGLAnnotation> annotation in annotations) {
+ if ([annotation conformsToProtocol:@protocol(MGLOverlay)]) {
bounds.extend(MGLLatLngBoundsFromCoordinateBounds(((id <MGLOverlay>)annotation).overlayBounds));
- }
- else
- {
+ } else if ([annotation isKindOfClass:[MGLCircle class]]) {
+ bounds.extend(MGLLatLngBoundsFromCoordinateBounds(((MGLCircle *)annotation).coordinateBounds));
+ } else {
bounds.extend(MGLLatLngFromLocationCoordinate2D(annotation.coordinate));
}
}
@@ -2634,10 +2655,22 @@ public:
return color.mgl_color;
}
-- (mbgl::Color)fillColorForPolygonAnnotation:(MGLPolygon *)annotation {
- NSColor *color = (_delegateHasFillColorsForShapeAnnotations
- ? [self.delegate mapView:self fillColorForPolygonAnnotation:annotation]
- : [NSColor selectedMenuItemColor]);
+- (mbgl::Color)fillColorForShape:(MGLShape *)shape {
+ NSColor *color = [NSColor selectedMenuItemColor];
+ if (_delegateHasFillColorsForShapeAnnotations) {
+ color = [self.delegate mapView:self fillColorForShape:shape];
+ } else if (_delegateHasFillColorsForPolygonAnnotations && [shape isKindOfClass:[MGLPolygon class]]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSLog(@"-[MGLMapViewDelegate mapView:fillColorForPolygonAnnotation:] is deprecated; "
+ @"use -[MGLMapViewDelegate mapView:fillColorForShape:] instead."
+ @"This warning will only appear once.");
+ });
+ color = [self.delegate mapView:self fillColorForPolygonAnnotation:(MGLPolygon *)shape];
+#pragma clang diagnostic pop
+ }
return color.mgl_color;
}
diff --git a/platform/macos/src/MGLMapViewDelegate.h b/platform/macos/src/MGLMapViewDelegate.h
index c7d6786666..4717df68ea 100644
--- a/platform/macos/src/MGLMapViewDelegate.h
+++ b/platform/macos/src/MGLMapViewDelegate.h
@@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN
@class MGLAnnotationImage;
@class MGLPolygon;
@class MGLPolyline;
+@class MGLCircle;
@class MGLShape;
/**
@@ -227,20 +228,25 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (NSColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation;
+- (NSColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation __attribute__((deprecated("", "-mapView:fillColorForShape:")));
+
/**
- Returns the color to use when rendering the fill of a polygon annotation.
+ Returns the color to use when rendering the fill of a shape annotation.
+ This method is only called for `MGLPolygon` and `MGLCircle` annotations. It is
+ not possible to fill a polyline or point annotation.
+
The default fill color is the selected menu item color. If a pattern color is
specified, the result is undefined.
Opacity may be set by specifying an alpha component. The default alpha value is
`1.0` and results in a completely opaque shape.
- @param mapView The map view rendering the polygon annotation.
- @param annotation The annotation being rendered.
- @return The polygon’s interior fill color.
+ @param mapView The map view rendering the shape annotation.
+ @param shape The annotation being rendered.
+ @return The shape’s fill color.
*/
-- (NSColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation;
+- (NSColor *)mapView:(MGLMapView *)mapView fillColorForShape:(MGLShape *)shape;
/**
Returns the line width in points to use when rendering the outline of a
diff --git a/platform/macos/src/Mapbox.h b/platform/macos/src/Mapbox.h
index 6728992d6b..595ce08d92 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -32,6 +32,7 @@ FOUNDATION_EXPORT MGL_EXPORT const unsigned char MapboxVersionString[];
#import "MGLPointCollection.h"
#import "MGLPolygon.h"
#import "MGLPolyline.h"
+#import "MGLCircle.h"
#import "MGLShape.h"
#import "MGLShapeCollection.h"
#import "MGLStyle.h"
diff --git a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift
index 83c7160fde..345b15df49 100644
--- a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift
+++ b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift
@@ -54,6 +54,8 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -> NSColor { return .black }
func mapView(_ mapView: MGLMapView, fillColorForPolygonAnnotation annotation: MGLPolygon) -> NSColor { return .black }
+
+ func mapView(_ mapView: MGLMapView, fillColorFor shape: MGLShape) -> NSColor { return .black }
func mapView(_ mapView: MGLMapView, calloutViewControllerFor annotation: MGLAnnotation) -> NSViewController? { return nil }