diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index 38460612f4d..95e8e2f99c6 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -50,6 +50,8 @@ '../platform/ios/MGLPolygon.m', '../include/mbgl/ios/MGLShape.h', '../platform/ios/MGLShape.m', + '../include/mbgl/ios/MGLCircle.h', + '../platform/ios/MGLCircle.mm', '../include/mbgl/ios/MGLAnnotationImage.h', '../platform/ios/MGLAnnotationImage.m', '../platform/ios/NSBundle+MGLAdditions.h', diff --git a/include/mbgl/annotation/circle_annotation.hpp b/include/mbgl/annotation/circle_annotation.hpp new file mode 100644 index 00000000000..41ff4e564e3 --- /dev/null +++ b/include/mbgl/annotation/circle_annotation.hpp @@ -0,0 +1,22 @@ +#ifndef MBGL_ANNOTATION_CIRCLE_ANNOTATION +#define MBGL_ANNOTATION_CIRCLE_ANNOTATION + +#include + +#include + +namespace mbgl { + + class CircleAnnotation { + public: + inline CircleAnnotation(const LatLng& position_, const double radius_) + : position(position_), radius(radius_) { + } + + const LatLng position; + const double radius; + }; + +} + +#endif diff --git a/include/mbgl/annotation/shape_annotation.hpp b/include/mbgl/annotation/shape_annotation.hpp index 90f2a96dd0e..81dd21005b5 100644 --- a/include/mbgl/annotation/shape_annotation.hpp +++ b/include/mbgl/annotation/shape_annotation.hpp @@ -16,9 +16,10 @@ using AnnotationSegments = std::vector; class ShapeAnnotation { public: using Properties = mapbox::util::variant< - FillPaintProperties, // creates a fill annotation - LinePaintProperties, // creates a line annotation - std::string>; // creates an annotation whose type and properties are sourced from a style layer + FillPaintProperties, // creates a fill annotation + LinePaintProperties, // creates a line annotation + CirclePaintProperties, // creates a circle annotation + std::string>; // creates an annotation whose type and properties are sourced from a style layer ShapeAnnotation(const AnnotationSegments& segments_, const Properties& properties_) : segments(segments_), properties(properties_) { diff --git a/include/mbgl/ios/MGLCircle.h b/include/mbgl/ios/MGLCircle.h new file mode 100644 index 00000000000..de712144296 --- /dev/null +++ b/include/mbgl/ios/MGLCircle.h @@ -0,0 +1,22 @@ +#import +#import + +#import "MGLShape.h" +#import "MGLOverlay.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MGLCircle : MGLShape + +@property (nonatomic, assign) CLLocationCoordinate2D coordinate; +@property (nonatomic, readonly) CLLocationDistance radius; +@property (nonatomic, readonly) MGLCoordinateBounds overlayBounds; + ++ (instancetype)circleWithCenterCoordinate:(CLLocationCoordinate2D)coordinate + radius:(CLLocationDistance)radius; + ++ (instancetype)circleWithOverlayBounds:(MGLCoordinateBounds)overlayBounds; + +@end + +NS_ASSUME_NONNULL_END diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index dd8a120e149..0fee5d54f0e 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -509,6 +509,12 @@ IB_DESIGNABLE * @return A color to use for the polygon interior. */ - (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation; +/** Returns the fill color to use when rendering a circle annotation. Defaults to red. +* @param mapView The map view rendering the circle annotation. +* @param annotation The annotation being rendered. +* @return A color to use for the circle interior. */ +- (UIColor *)mapView:(MGLMapView *)mapView fillColorForCircleAnnotation:(MGLCircle *)annotation; + /** Returns the line width to use when rendering a polyline annotation. Defaults to `3.0`. * @param mapView The map view rendering the polygon annotation. * @param annotation The annotation being rendered. diff --git a/include/mbgl/ios/Mapbox.h b/include/mbgl/ios/Mapbox.h index f05f0c84294..35c43a50902 100644 --- a/include/mbgl/ios/Mapbox.h +++ b/include/mbgl/ios/Mapbox.h @@ -1,6 +1,7 @@ #import "MGLAccountManager.h" #import "MGLAnnotation.h" #import "MGLAnnotationImage.h" +#import "MGLCircle.h" #import "MGLMapCamera.h" #import "MGLGeometry.h" #import "MGLMapView.h" diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index 6a04b7fa248..e8568805b68 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -200,7 +200,7 @@ - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSIn MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3]; - [self.mapView addAnnotation:triangle]; + [self.mapView addOverlay:triangle]; // Orcas Island hike // @@ -222,7 +222,7 @@ - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSIn MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates count:[hikeCoordinatePairs count]]; - [self.mapView addAnnotation:polyline]; + [self.mapView addOverlay:polyline]; free(polylineCoordinates); @@ -249,10 +249,16 @@ - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSIn MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]]; - [self.mapView addAnnotation:polygon]; + [self.mapView addOverlay:polygon]; free(polygonCoordinates); } + + // Rabat circle + // + MGLCircle *circle = [MGLCircle circleWithCenterCoordinate:CLLocationCoordinate2DMake(34.020882, -6.84165) radius:100000]; + + [self.mapView addOverlay:circle]; } else if (buttonIndex == actionSheet.firstOtherButtonIndex + 8) { @@ -437,6 +443,11 @@ - (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotatio return (annotation.pointCount > 3 ? [UIColor greenColor] : [UIColor redColor]); } +//- (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForCircleAnnotation:(__unused MGLCircle *)annotation +//{ +// return [UIColor greenColor]; +//} + - (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(__unused BOOL)animated { UIImage *newButtonImage; diff --git a/platform/ios/MGLCircle.mm b/platform/ios/MGLCircle.mm new file mode 100644 index 00000000000..a34c4e9076a --- /dev/null +++ b/platform/ios/MGLCircle.mm @@ -0,0 +1,101 @@ +#import "MGLCircle.h" + +#import +#import + +@implementation MGLCircle +{ + CLLocationDistance _radius; + mbgl::LatLngBounds _bounds; +} + +@synthesize coordinate=_coordinate; + +- (instancetype)initWithCenterCoordinate:(CLLocationCoordinate2D)coordinate + radius:(CLLocationDistance)radius + overlayBounds:(MGLCoordinateBounds)overlayBounds +{ + self = [super init]; + + if (self) + { + _coordinate = coordinate; + _radius = radius; + + if (MGLCoordinateBoundsIsEmpty(overlayBounds)) + { + using Projection = mbgl::Projection; + using ProjectedMeters = mbgl::ProjectedMeters; + + ProjectedMeters projectedCenter = Projection::projectedMetersForLatLng(mbgl::LatLng(coordinate.latitude, coordinate.longitude)); + + ProjectedMeters projectedWest = ProjectedMeters(projectedCenter.northing, projectedCenter.easting - radius); + ProjectedMeters projectedEast = ProjectedMeters(projectedCenter.northing, projectedCenter.easting + radius); + ProjectedMeters projectedNorth = ProjectedMeters(projectedCenter.northing + radius, projectedCenter.easting); + ProjectedMeters projectedSouth = ProjectedMeters(projectedCenter.northing - radius, projectedCenter.easting); + + _bounds.extend(Projection::latLngForProjectedMeters(projectedWest)); + _bounds.extend(Projection::latLngForProjectedMeters(projectedEast)); + _bounds.extend(Projection::latLngForProjectedMeters(projectedNorth)); + _bounds.extend(Projection::latLngForProjectedMeters(projectedSouth)); + } + else + { + _bounds.extend(mbgl::LatLng(overlayBounds.sw.latitude, overlayBounds.sw.longitude)); + _bounds.extend(mbgl::LatLng(overlayBounds.ne.latitude, overlayBounds.ne.longitude)); + } + } + + return self; +} + ++ (instancetype)circleWithCenterCoordinate:(CLLocationCoordinate2D)coordinate + radius:(CLLocationDistance)radius +{ + CLLocationCoordinate2D nullIsland = CLLocationCoordinate2DMake(0, 0); + + return [[self alloc] initWithCenterCoordinate:coordinate + radius:radius + overlayBounds:MGLCoordinateBoundsMake(nullIsland, nullIsland)]; +} + ++ (instancetype)circleWithOverlayBounds:(MGLCoordinateBounds)overlayBounds +{ + CLLocation *sw = [[CLLocation alloc] initWithLatitude:overlayBounds.sw.latitude + longitude:overlayBounds.sw.longitude]; + CLLocation *ne = [[CLLocation alloc] initWithLatitude:overlayBounds.ne.latitude + longitude:overlayBounds.ne.longitude]; + + CLLocationCoordinate2D centerCoordinate = CLLocationCoordinate2DMake( + (sw.coordinate.latitude + ne.coordinate.latitude) / 2, + (sw.coordinate.longitude + ne.coordinate.longitude) / 2); + + CLLocation *se = [[CLLocation alloc] initWithLatitude:sw.coordinate.latitude + longitude:ne.coordinate.longitude]; + + CLLocationDistance radius = [sw distanceFromLocation:se] / 2; + + return [[self alloc] initWithCenterCoordinate:centerCoordinate + radius:radius + overlayBounds:overlayBounds]; +} + +- (MGLCoordinateBounds)overlayBounds +{ + return { + CLLocationCoordinate2DMake(_bounds.sw.latitude, _bounds.sw.longitude), + CLLocationCoordinate2DMake(_bounds.ne.latitude, _bounds.ne.longitude) + }; +} + +- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds +{ + mbgl::LatLngBounds area( + mbgl::LatLng(overlayBounds.sw.latitude, overlayBounds.sw.longitude), + mbgl::LatLng(overlayBounds.ne.latitude, overlayBounds.ne.longitude) + ); + + return _bounds.intersects(area); +} + +@end diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 45c95de090d..b2bd1d7c100 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -2107,6 +2107,7 @@ - (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations BOOL delegateImplementsAlphaForShape = [self.delegate respondsToSelector:@selector(mapView:alphaForShapeAnnotation:)]; BOOL delegateImplementsStrokeColorForShape = [self.delegate respondsToSelector:@selector(mapView:strokeColorForShapeAnnotation:)]; BOOL delegateImplementsFillColorForPolygon = [self.delegate respondsToSelector:@selector(mapView:fillColorForPolygonAnnotation:)]; + BOOL delegateImplementsFillColorForCircle = [self.delegate respondsToSelector:@selector(mapView:fillColorForCircleAnnotation:)]; BOOL delegateImplementsLineWidthForPolyline = [self.delegate respondsToSelector:@selector(mapView:lineWidthForPolylineAnnotation:)]; for (id annotation in annotations) @@ -2187,6 +2188,36 @@ - (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations shapes.emplace_back(mbgl::AnnotationSegments {{ segment }}, shapeProperties); } + else if ([annotation isKindOfClass:[MGLCircle class]]) + { + CGFloat alpha = (delegateImplementsAlphaForShape ? + [self.delegate mapView:self alphaForShapeAnnotation:annotation] : + 1.0); + + mbgl::ShapeAnnotation::Properties shapeProperties; + + UIColor *circleColor = (delegateImplementsFillColorForCircle ? + [self.delegate mapView:self fillColorForCircleAnnotation:(MGLCircle *)annotation] : + [UIColor redColor]); + + assert(circleColor); + + CGFloat r,g,b,a; + [circleColor getRed:&r green:&g blue:&b alpha:&a]; + mbgl::Color circleNativeColor({{ (float)r, (float)g, (float)b, (float)a }}); + + mbgl::CirclePaintProperties circleProperties; + circleProperties.opacity = alpha; + circleProperties.color = circleNativeColor; + shapeProperties.set(circleProperties); + + mbgl::AnnotationSegment segment = { mbgl::LatLng( + annotation.coordinate.latitude, + annotation.coordinate.longitude) + }; + + shapes.emplace_back(mbgl::AnnotationSegments {{ segment }}, shapeProperties); + } else { MGLAnnotationImage *annotationImage = delegateImplementsImageForPoint ? [self.delegate mapView:self imageForAnnotation:annotation] : nil; diff --git a/platform/ios/MGLMultiPoint.mm b/platform/ios/MGLMultiPoint.mm index 624c1658ad5..a2329236b3b 100644 --- a/platform/ios/MGLMultiPoint.mm +++ b/platform/ios/MGLMultiPoint.mm @@ -84,7 +84,7 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range - (MGLCoordinateBounds)overlayBounds { return { - CLLocationCoordinate2DMake(_bounds.sw.latitude, _bounds.sw.longitude), + CLLocationCoordinate2DMake(_bounds.sw.latitude, _bounds.sw.longitude), CLLocationCoordinate2DMake(_bounds.ne.latitude, _bounds.ne.longitude) }; } diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp index 42acbb99838..e6013642480 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,7 @@ namespace mbgl { class PointAnnotation; class ShapeAnnotation; +class CircleAnnotation; class AnnotationTile; class Style; @@ -24,6 +26,7 @@ class AnnotationManager : private util::noncopyable { AnnotationIDs addPointAnnotations(const std::vector&, const uint8_t maxZoom); AnnotationIDs addShapeAnnotations(const std::vector&, const uint8_t maxZoom); + AnnotationIDs addCircleAnnotations(const std::vector&, const uint8_t maxZoom); void removeAnnotations(const AnnotationIDs&); AnnotationIDs getPointAnnotationsInBounds(const LatLngBounds&) const; @@ -40,6 +43,7 @@ class AnnotationManager : private util::noncopyable { PointAnnotationImpl::Tree pointTree; PointAnnotationImpl::Map pointAnnotations; ShapeAnnotationImpl::Map shapeAnnotations; + CircleAnnotationImpl::Map circleAnnotations; std::vector obsoleteShapeAnnotationLayers; }; diff --git a/src/mbgl/annotation/circle_annotation_impl.cpp b/src/mbgl/annotation/circle_annotation_impl.cpp new file mode 100644 index 00000000000..abfc8bb1de0 --- /dev/null +++ b/src/mbgl/annotation/circle_annotation_impl.cpp @@ -0,0 +1,34 @@ +#include +//#include +#include + +namespace mbgl { + +CircleAnnotationImpl::CircleAnnotationImpl(const AnnotationID id_, const CircleAnnotation& circle_) +: id(id_), + layerID("com.mapbox.annotations.circle." + util::toString(id)), + circle(circle_) { +} + +void CircleAnnotationImpl::updateLayer(const TileID& tileID, AnnotationTileLayer& layer) const { + std::unordered_map featureProperties; + featureProperties.emplace("sprite", point.icon.empty() ? std::string("default_marker") : point.icon); + + const uint16_t extent = 4096; + const vec2 pp = point.position.project(); + const uint32_t z2 = 1 << tileID.z; + const uint32_t x = pp.x * z2; + const uint32_t y = pp.y * z2; + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + layer.features.emplace_back( + std::make_shared(FeatureType::Point, + GeometryCollection {{ {{ coordinate }} }}, + featureProperties)); +} + +LatLngBounds CircleAnnotationImpl::bounds() const { + return LatLngBounds(circle.position, circle.position); // +} + +} diff --git a/src/mbgl/annotation/circle_annotation_impl.hpp b/src/mbgl/annotation/circle_annotation_impl.hpp new file mode 100644 index 00000000000..9694e7fca8b --- /dev/null +++ b/src/mbgl/annotation/circle_annotation_impl.hpp @@ -0,0 +1,29 @@ +#ifndef MBGL_CIRCLE_ANNOTATION_IMPL +#define MBGL_CIRCLE_ANNOTATION_IMPL + +#include +#include +#include + +#include + +namespace mbgl { + +class AnnotationTileLayer; + +class CircleAnnotationImpl { +public: + CircleAnnotationImpl(const AnnotationID, const CircleAnnotation&); + + LatLngBounds bounds() const; + void updateStyle(Style&); + void updateLayer(const TileID&, AnnotationTile&) const; + + const AnnotationID id; + const std::string layerID; + const CircleAnnotation circle; +}; + +} + +#endif diff --git a/src/mbgl/map/geometry_tile.hpp b/src/mbgl/map/geometry_tile.hpp index 37d7bf1427b..78fec7d1a65 100644 --- a/src/mbgl/map/geometry_tile.hpp +++ b/src/mbgl/map/geometry_tile.hpp @@ -19,7 +19,8 @@ enum class FeatureType : uint8_t { Unknown = 0, Point = 1, LineString = 2, - Polygon = 3 + Polygon = 3, + Circle = 4 }; typedef std::vector> GeometryCollection;