diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index bbeeeac6cc2..2881abc9fe1 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -197,6 +197,7 @@ class Map : private util::noncopyable { std::vector querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}); AnnotationIDs queryPointAnnotations(const ScreenBox&); + AnnotationIDs queryShapeAnnotations(const ScreenBox&, const RenderedQueryOptions& options = {}); // Memory void setSourceTileCacheSize(size_t); @@ -213,6 +214,7 @@ class Map : private util::noncopyable { private: class Impl; const std::unique_ptr impl; + AnnotationIDs queryAnnotations(const ScreenBox&, const RenderedQueryOptions& options = {}); }; } // namespace mbgl diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 7d47da694c7..ac4eb03ec24 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -152,6 +152,10 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) { /// Mapping from an annotation object to an annotation tag. typedef std::map, MGLAnnotationTag> MGLAnnotationObjectTagMap; +typedef std::map, std::string> MGLShapeAnnotationObjectLayerIDMap; + +static NSString *const MGLShapeAnnotationLayerIdentifierPrefix = @"com.mapbox.annotations.shape."; + mbgl::util::UnitBezier MGLUnitBezierForMediaTimingFunction(CAMediaTimingFunction *function) { if ( ! function) @@ -285,6 +289,7 @@ @implementation MGLMapView MGLAnnotationTagContextMap _annotationContextsByAnnotationTag; MGLAnnotationObjectTagMap _annotationTagsByAnnotation; + MGLShapeAnnotationObjectLayerIDMap _shapeAnnotationLayerIDsByAnnotation; /// Tag of the selected annotation. If the user location annotation is selected, this ivar is set to `MGLAnnotationTagNotFound`. MGLAnnotationTag _selectedAnnotationTag; @@ -471,6 +476,7 @@ - (void)commonInit _annotationViewReuseQueueByIdentifier = [NSMutableDictionary dictionary]; _selectedAnnotationTag = MGLAnnotationTagNotFound; _annotationsNearbyLastTap = {}; + _shapeAnnotationLayerIDsByAnnotation = {}; // setup logo bug // @@ -3133,6 +3139,11 @@ - (void)removeStyleClass:(NSString *)styleClass } std::vector annotationTags = [self annotationTagsInRect:rect]; + + if (!annotationTags.size()) { + annotationTags = [self shapeAnnotationTagsInRect:rect]; + } + if (annotationTags.size()) { NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()]; @@ -3230,6 +3241,9 @@ - (void)addAnnotations:(NS_ARRAY_OF(id ) *)annotations context.annotation = annotation; _annotationContextsByAnnotationTag[annotationTag] = context; _annotationTagsByAnnotation[annotation] = annotationTag; + + NSString *layerID = [NSString stringWithFormat:@"%@%u", MGLShapeAnnotationLayerIdentifierPrefix, annotationTag]; + _shapeAnnotationLayerIDsByAnnotation[annotation] = layerID.UTF8String; [(NSObject *)annotation addObserver:self forKeyPath:@"coordinates" options:0 context:(void *)(NSUInteger)annotationTag]; } @@ -3528,6 +3542,7 @@ - (void)removeAnnotations:(NS_ARRAY_OF(id ) *)annotations _annotationContextsByAnnotationTag.erase(annotationTag); _annotationTagsByAnnotation.erase(annotation); + _shapeAnnotationLayerIDsByAnnotation.erase(annotation); if ([annotation isKindOfClass:[NSObject class]] && ![annotation isKindOfClass:[MGLMultiPoint class]]) { @@ -3622,6 +3637,12 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); std::vector nearbyAnnotations = [self annotationTagsInRect:queryRect]; + BOOL queryingShapeAnnotations = NO; + + if (!nearbyAnnotations.size()) { + nearbyAnnotations = [self shapeAnnotationTagsInRect:queryRect]; + queryingShapeAnnotations = YES; + } if (nearbyAnnotations.size()) { @@ -3629,54 +3650,58 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) CGRect hitRect = CGRectInset({ point, CGSizeZero }, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); + + if (!queryingShapeAnnotations) { + // Filter out any annotation whose image or view is unselectable or for which + // hit testing fails. + auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), + [&](const MGLAnnotationTag annotationTag) + { + id annotation = [self annotationWithTag:annotationTag]; + NSAssert(annotation, @"Unknown annotation found nearby tap"); + if ( ! annotation) + { + return true; + } + + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + CGRect annotationRect; + + MGLAnnotationView *annotationView = annotationContext.annotationView; + + if (annotationView) + { + if ( ! annotationView.enabled) + { + return true; + } + + CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self]; + CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2); + annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets); + } + else + { + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + if ( ! annotationImage.enabled) + { + return true; + } + + MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; + UIImage *fallbackImage = fallbackAnnotationImage.image; + + annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; + } + + // Filter out the annotation if the fattened finger didn’t land + // within the image’s alignment rect. + return !!!CGRectIntersectsRect(annotationRect, hitRect); + }); + + nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); + } - // Filter out any annotation whose image or view is unselectable or for which - // hit testing fails. - auto end = std::remove_if(nearbyAnnotations.begin(), nearbyAnnotations.end(), - [&](const MGLAnnotationTag annotationTag) - { - id annotation = [self annotationWithTag:annotationTag]; - NSAssert(annotation, @"Unknown annotation found nearby tap"); - if ( ! annotation) - { - return true; - } - - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - CGRect annotationRect; - - MGLAnnotationView *annotationView = annotationContext.annotationView; - if (annotationView) - { - if ( ! annotationView.enabled) - { - return true; - } - - CGPoint calloutAnchorPoint = [self convertCoordinate:annotation.coordinate toPointToView:self]; - CGRect frame = CGRectInset({ calloutAnchorPoint, CGSizeZero }, -CGRectGetWidth(annotationView.frame) / 2, -CGRectGetHeight(annotationView.frame) / 2); - annotationRect = UIEdgeInsetsInsetRect(frame, annotationView.alignmentRectInsets); - } - else - { - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; - if ( ! annotationImage.enabled) - { - return true; - } - - MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; - UIImage *fallbackImage = fallbackAnnotationImage.image; - - annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; - } - - // Filter out the annotation if the fattened finger didn’t land - // within the image’s alignment rect. - return !!!CGRectIntersectsRect(annotationRect, hitRect); - }); - - nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); } MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound; @@ -3759,6 +3784,29 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) }); } +- (std::vector)shapeAnnotationTagsInRect:(CGRect)rect +{ + mbgl::ScreenBox screenBox = { + { CGRectGetMinX(rect), CGRectGetMinY(rect) }, + { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, + }; + + std::vector nearbyAnnotations; + + mbgl::optional> optionalLayerIDs; + if (_shapeAnnotationLayerIDsByAnnotation.size()) { + __block std::vector layerIDs; + layerIDs.reserve(_shapeAnnotationLayerIDsByAnnotation.size()); + for (const auto &annotation : _shapeAnnotationLayerIDsByAnnotation) { + layerIDs.push_back(annotation.second); + } + optionalLayerIDs = layerIDs; + return nearbyAnnotations = _mbglMap->queryShapeAnnotations(screenBox, { optionalLayerIDs }); + } + + return nearbyAnnotations; +} + - (id )selectedAnnotation { if (_userLocationAnnotationIsSelected) @@ -3810,7 +3858,7 @@ - (void)selectAnnotation:(id )annotation animated:(BOOL)animated { if ( ! annotation) return; - if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; + //if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; if (annotation == self.selectedAnnotation) return; @@ -3858,8 +3906,9 @@ - (void)selectAnnotation:(id )annotation animated:(BOOL)animated self.selectedAnnotation = annotation; - if ([annotation respondsToSelector:@selector(title)] && - annotation.title && + if ((([annotation respondsToSelector:@selector(title)] && + annotation.title) || + ([annotation isKindOfClass:[MGLMultiPoint class]])) && [self.delegate respondsToSelector:@selector(mapView:annotationCanShowCallout:)] && [self.delegate mapView:self annotationCanShowCallout:annotation]) { @@ -3960,6 +4009,13 @@ - (CGRect)positioningRectForCalloutForAnnotationWithTag:(MGLAnnotationTag)annota { return CGRectZero; } + + if ([annotation isKindOfClass:[MGLMultiPoint class]]) { + CLLocationCoordinate2D origin = annotation.coordinate; + CGPoint originPoint = [self convertCoordinate:origin toPointToView:self]; + return CGRectMake(originPoint.x, originPoint.y, MGLAnnotationImagePaddingForHitTest, MGLAnnotationImagePaddingForHitTest); + + } UIImage *image = [self imageOfAnnotationWithTag:annotationTag].image; if ( ! image) { diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 35457f3a5b8..57eb42f01ad 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -854,6 +854,15 @@ std::vector Map::querySourceFeatures(const std::string& sourceID, const AnnotationIDs Map::queryPointAnnotations(const ScreenBox& box) { RenderedQueryOptions options; options.layerIDs = {{ AnnotationManager::PointLayerID }}; + return queryAnnotations(box, options); + +} + +AnnotationIDs Map::queryShapeAnnotations(const ScreenBox& box, const RenderedQueryOptions& options) { + return queryAnnotations(box, options); +} + +AnnotationIDs Map::queryAnnotations(const ScreenBox& box, const RenderedQueryOptions& options) { auto features = queryRenderedFeatures(box, options); std::set set; for (auto &feature : features) {