diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp index be8abb2c29d..924cd9e8258 100644 --- a/include/mbgl/renderer/renderer.hpp +++ b/include/mbgl/renderer/renderer.hpp @@ -40,6 +40,7 @@ class Renderer { std::vector queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const; std::vector querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}) const; AnnotationIDs queryPointAnnotations(const ScreenBox& box) const; + AnnotationIDs queryShapeAnnotations(const ScreenBox& box, const std::vector layerIDs) const; // Debug void dumpDebugLogs(); @@ -50,6 +51,7 @@ class Renderer { private: class Impl; std::unique_ptr impl; + AnnotationIDs queryAnnotations(const ScreenBox&, const RenderedQueryOptions& options = {}) const; }; } // namespace mbgl diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 07838bc6bde..5bccb64608b 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -827,6 +827,7 @@ - (void)addTestShapes } MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]]; + polygon.title = feature[@"properties"][@"NAME"]; [self.mapView addAnnotation:polygon]; diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 3288a93ab48..9740b0f7883 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3246,6 +3246,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()]; @@ -3751,6 +3756,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()) { @@ -3758,54 +3769,56 @@ - (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; + } - // 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; - MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); - CGRect annotationRect; + MGLAnnotationView *annotationView = annotationContext.annotationView; - MGLAnnotationView *annotationView = annotationContext.annotationView; - if (annotationView) - { - if ( ! annotationView.enabled) + if (annotationView) { - return true; + 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); } - - 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) + else { - return true; + 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]; } - 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); - }); + // 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)); + } - nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); } MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound; @@ -3888,6 +3901,27 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL) }); } +- (std::vector)shapeAnnotationTagsInRect:(CGRect)rect +{ + mbgl::ScreenBox screenBox = { + { CGRectGetMinX(rect), CGRectGetMinY(rect) }, + { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, + }; + + std::vector annotationsInRect; + + if (_annotationTagsByAnnotation.size()) { + __block std::vector layerIDs; + layerIDs.reserve(_annotationTagsByAnnotation.size()); + for (const auto &annotation : _annotationTagsByAnnotation) { + layerIDs.push_back(mbgl::util::toString(annotation.second)); + } + return annotationsInRect = _rendererFrontend->getRenderer()->queryShapeAnnotations(screenBox, layerIDs); + } + + return annotationsInRect; +} + - (id )selectedAnnotation { if (_userLocationAnnotationIsSelected) @@ -3939,8 +3973,6 @@ - (void)selectAnnotation:(id )annotation animated:(BOOL)animated { if ( ! annotation) return; - if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; - if (annotation == self.selectedAnnotation) return; [self deselectAnnotation:self.selectedAnnotation animated:NO]; @@ -4084,6 +4116,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/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index eb4099f3300..9d18c7c9830 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -1777,6 +1777,11 @@ - (IBAction)giveFeedback:(id)sender { } std::vector annotationTags = [self annotationTagsInRect:rect]; + + if (!annotationTags.size()) { + annotationTags = [self shapeAnnotationTagsInRect:rect]; + } + if (annotationTags.size()) { NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()]; @@ -2036,35 +2041,43 @@ - (MGLAnnotationTag)annotationTagAtPoint:(NSPoint)point persistingResults:(BOOL) queryRect = NSInsetRect(queryRect, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); std::vector nearbyAnnotations = [self annotationTagsInRect:queryRect]; + BOOL queryingShapeAnnotations = NO; + + if (!nearbyAnnotations.size()) { + nearbyAnnotations = [self shapeAnnotationTagsInRect:queryRect]; + queryingShapeAnnotations = YES; + } if (nearbyAnnotations.size()) { // Assume that the user is fat-fingering an annotation. NSRect hitRect = NSInsetRect({ point, NSZeroSize }, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); - - // Filter out any annotation whose image 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 click"); - if (!annotation) { - return true; - } - - MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; - if (!annotationImage.selectable) { - return true; - } - - // Filter out the annotation if the fattened finger didn’t land on a - // translucent or opaque pixel in the image. - NSRect annotationRect = [self frameOfImage:annotationImage.image - centeredAtCoordinate:annotation.coordinate]; - return !!![annotationImage.image hitTestRect:hitRect withImageDestinationRect:annotationRect - context:nil hints:nil flipped:NO]; - }); - nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); + + if (!queryingShapeAnnotations) { + // Filter out any annotation whose image 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 click"); + if (!annotation) { + return true; + } + + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + if (!annotationImage.selectable) { + return true; + } + + // Filter out the annotation if the fattened finger didn’t land on a + // translucent or opaque pixel in the image. + NSRect annotationRect = [self frameOfImage:annotationImage.image + centeredAtCoordinate:annotation.coordinate]; + return !!![annotationImage.image hitTestRect:hitRect withImageDestinationRect:annotationRect + context:nil hints:nil flipped:NO]; + }); + nearbyAnnotations.resize(std::distance(nearbyAnnotations.begin(), end)); + } } MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound; @@ -2135,6 +2148,27 @@ - (MGLAnnotationTag)annotationTagAtPoint:(NSPoint)point persistingResults:(BOOL) }); } +- (std::vector)shapeAnnotationTagsInRect:(NSRect)rect { + mbgl::ScreenBox screenBox = { + { NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) }, + { NSMaxX(rect), NSHeight(self.bounds) - NSMinY(rect) }, + }; + + std::vector annotationsInRect; + + if (_annotationTagsByAnnotation.size()) { + __block std::vector layerIDs; + layerIDs.reserve(_annotationTagsByAnnotation.size()); + for (const auto &annotation : _annotationTagsByAnnotation) { + layerIDs.push_back(mbgl::util::toString(annotation.second)); + } + return annotationsInRect = _rendererFrontend->getRenderer()->queryShapeAnnotations(screenBox, layerIDs); + } + + return annotationsInRect; + +} + - (id )selectedAnnotation { if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag) || _selectedAnnotationTag == MGLAnnotationTagNotFound) { @@ -2300,6 +2334,13 @@ - (NSRect)positioningRectForCalloutForAnnotationWithTag:(MGLAnnotationTag)annota if (!annotation) { return NSZeroRect; } + if ([annotation isKindOfClass:[MGLMultiPoint class]]) { + CLLocationCoordinate2D origin = annotation.coordinate; + CGPoint originPoint = [self convertCoordinate:origin toPointToView:self]; + return CGRectMake(originPoint.x, originPoint.y, MGLAnnotationImagePaddingForHitTest, MGLAnnotationImagePaddingForHitTest); + + } + NSImage *image = [self imageOfAnnotationWithTag:annotationTag].image; if (!image) { image = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName].image; diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index 1f2d01e9eb6..a4d53bbd3f5 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -18,6 +18,7 @@ using namespace style; const std::string AnnotationManager::SourceID = "com.mapbox.annotations"; const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape."; AnnotationManager::AnnotationManager(Style& style_) : style(style_) { diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp index a028a8f1ba6..22b25cd2acb 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -46,6 +46,7 @@ class AnnotationManager : private util::noncopyable { static const std::string SourceID; static const std::string PointLayerID; + static const std::string ShapeLayerID; private: void add(const AnnotationID&, const SymbolAnnotation&, const uint8_t); diff --git a/src/mbgl/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp index 0c1a631ad8b..9288159b6a6 100644 --- a/src/mbgl/annotation/shape_annotation_impl.cpp +++ b/src/mbgl/annotation/shape_annotation_impl.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -15,7 +16,7 @@ namespace geojsonvt = mapbox::geojsonvt; ShapeAnnotationImpl::ShapeAnnotationImpl(const AnnotationID id_, const uint8_t maxZoom_) : id(id_), maxZoom(maxZoom_), - layerID("com.mapbox.annotations.shape." + util::toString(id)) { + layerID(AnnotationManager::ShapeLayerID + util::toString(id)) { } void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, AnnotationTileData& data) { diff --git a/src/mbgl/renderer/renderer.cpp b/src/mbgl/renderer/renderer.cpp index e915f5e1468..14e414b48c8 100644 --- a/src/mbgl/renderer/renderer.cpp +++ b/src/mbgl/renderer/renderer.cpp @@ -56,6 +56,24 @@ std::vector Renderer::queryRenderedFeatures(const ScreenBox& box, const AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const { RenderedQueryOptions options; options.layerIDs = {{ AnnotationManager::PointLayerID }}; + return queryAnnotations(box, options); +} + +AnnotationIDs Renderer::queryShapeAnnotations(const ScreenBox& box, const std::vector layerIDs) const { + RenderedQueryOptions options; + __block std::vector shapeLayerIDs; + if (layerIDs.size()) { + shapeLayerIDs.reserve(layerIDs.size()); + for (const auto &layerID : layerIDs) { + shapeLayerIDs.push_back(AnnotationManager::ShapeLayerID + layerID); + } + options.layerIDs = {{ shapeLayerIDs }}; + } + + return queryAnnotations(box, options); +} + +AnnotationIDs Renderer::queryAnnotations(const ScreenBox& box, const RenderedQueryOptions& options) const { auto features = queryRenderedFeatures(box, options); std::set set; for (auto &feature : features) {