From b7b8baef9d30d31a757dbbc0fbb8cb2c5cf3585e Mon Sep 17 00:00:00 2001 From: Fabian Guerra Soto Date: Wed, 18 Oct 2017 15:18:54 -0400 Subject: [ios, macos] Add selection support to MGLMultiPoint annotations. (#9984) * [ios, macos] Add selection support to MGLMultiPoint annotations. * [ios, macos] Update changelogs. --- include/mbgl/renderer/renderer.hpp | 2 + platform/ios/CHANGELOG.md | 1 + platform/ios/app/MBXViewController.m | 1 + platform/ios/src/MGLMapView.mm | 107 ++++++++++++++++---------- platform/macos/CHANGELOG.md | 1 + platform/macos/src/MGLMapView.mm | 75 ++++++++++++------ src/mbgl/annotation/annotation_manager.cpp | 1 + src/mbgl/annotation/annotation_manager.hpp | 1 + src/mbgl/annotation/shape_annotation_impl.cpp | 3 +- src/mbgl/renderer/renderer.cpp | 15 ++++ src/mbgl/renderer/renderer_impl.cpp | 19 +++++ src/mbgl/renderer/renderer_impl.hpp | 3 + 12 files changed, 165 insertions(+), 64 deletions(-) diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp index be8abb2c29..23d2451a2e 100644 --- a/include/mbgl/renderer/renderer.hpp +++ b/include/mbgl/renderer/renderer.hpp @@ -40,6 +40,8 @@ public: 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; + AnnotationIDs getAnnotationIDs(const std::vector&) const; // Debug void dumpDebugLogs(); diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index 82de619d7b..3dd46f3e93 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -30,6 +30,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT * Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617)) * Selecting an annotation no longer sets the user tracking mode to `MGLUserTrackingModeNone`. ([#10094](https://github.com/mapbox/mapbox-gl-native/pull/10094)) * Added `-[MGLMapView cameraThatFitsShape:direction:edgePadding:]` to get a camera with zoom level and center coordinate computed to fit a shape. ([#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107)) +* Added support selection of shape and polyline annotations.([#9984](https://github.com/mapbox/mapbox-gl-native/pull/9984)) ### Other changes diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m index 07838bc6bd..5bccb64608 100644 --- a/platform/ios/app/MBXViewController.m +++ b/platform/ios/app/MBXViewController.m @@ -827,6 +827,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) { } 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 ac608dd074..4112df6e76 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -3257,6 +3257,12 @@ public: } std::vector annotationTags = [self annotationTagsInRect:rect]; + std::vector shapeAnnotationTags = [self shapeAnnotationTagsInRect:rect]; + + if (shapeAnnotationTags.size()) { + annotationTags.insert(annotationTags.end(), shapeAnnotationTags.begin(), shapeAnnotationTags.end()); + } + if (annotationTags.size()) { NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()]; @@ -3762,6 +3768,12 @@ public: 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()) { @@ -3769,54 +3781,56 @@ public: CGRect hitRect = CGRectInset({ point, CGSizeZero }, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); - - // 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) + + 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; } - 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) + MGLAnnotationContext annotationContext = _annotationContextsByAnnotationTag.at(annotationTag); + CGRect annotationRect; + + MGLAnnotationView *annotationView = annotationContext.annotationView; + + 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); } + else + { + MGLAnnotationImage *annotationImage = [self imageOfAnnotationWithTag:annotationTag]; + if ( ! annotationImage.enabled) + { + return true; + } - MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; - UIImage *fallbackImage = fallbackAnnotationImage.image; + MGLAnnotationImage *fallbackAnnotationImage = [self dequeueReusableAnnotationImageWithIdentifier:MGLDefaultStyleMarkerSymbolName]; + UIImage *fallbackImage = fallbackAnnotationImage.image; - annotationRect = [self frameOfImage:annotationImage.image ?: fallbackImage centeredAtCoordinate:annotation.coordinate]; - } + 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; @@ -3899,6 +3913,14 @@ public: }); } +- (std::vector)shapeAnnotationTagsInRect:(CGRect)rect +{ + return _rendererFrontend->getRenderer()->queryShapeAnnotations({ + { CGRectGetMinX(rect), CGRectGetMinY(rect) }, + { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, + }); +} + - (id )selectedAnnotation { if (_userLocationAnnotationIsSelected) @@ -3950,8 +3972,6 @@ public: { if ( ! annotation) return; - if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; - if (annotation == self.selectedAnnotation) return; [self deselectAnnotation:self.selectedAnnotation animated:NO]; @@ -4095,6 +4115,13 @@ public: { 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/CHANGELOG.md b/platform/macos/CHANGELOG.md index 31b3540a7c..4b3292c600 100644 --- a/platform/macos/CHANGELOG.md +++ b/platform/macos/CHANGELOG.md @@ -27,6 +27,7 @@ * Increased the default maximum zoom level from 20 to 22. ([#9835](https://github.com/mapbox/mapbox-gl-native/pull/9835)) * Added an `overlays` property to `MGLMapView`. ([#8617](https://github.com/mapbox/mapbox-gl-native/pull/8617)) * Added `-[MGLMapView cameraThatFitsShape:direction:edgePadding:]` to get a camera with zoom level and center coordinate computed to fit a shape. ([#10107](https://github.com/mapbox/mapbox-gl-native/pull/10107)) +* Added support selection of shape and polyline annotations.([#9984](https://github.com/mapbox/mapbox-gl-native/pull/9984)) ### Other changes diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm index 0aa5bdc9db..af8ddbbe11 100644 --- a/platform/macos/src/MGLMapView.mm +++ b/platform/macos/src/MGLMapView.mm @@ -1791,6 +1791,12 @@ public: } std::vector annotationTags = [self annotationTagsInRect:rect]; + std::vector shapeAnnotationTags = [self shapeAnnotationTagsInRect:rect]; + + if (shapeAnnotationTags.size()) { + annotationTags.insert(annotationTags.end(), shapeAnnotationTags.begin(), shapeAnnotationTags.end()); + } + if (annotationTags.size()) { NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()]; @@ -2050,35 +2056,43 @@ public: 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; @@ -2149,6 +2163,14 @@ public: }); } +- (std::vector)shapeAnnotationTagsInRect:(NSRect)rect { + // Cocoa origin is at the lower-left corner. + return _rendererFrontend->getRenderer()->queryShapeAnnotations({ + { NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) }, + { NSMaxX(rect), NSHeight(self.bounds) - NSMinY(rect) }, + }); +} + - (id )selectedAnnotation { if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag) || _selectedAnnotationTag == MGLAnnotationTagNotFound) { @@ -2314,6 +2336,13 @@ public: 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 1f2d01e9eb..a4d53bbd3f 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 a028a8f1ba..22b25cd2ac 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -46,6 +46,7 @@ public: 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 0c1a631ad8..9288159b6a 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 e915f5e146..8953b419f7 100644 --- a/src/mbgl/renderer/renderer.cpp +++ b/src/mbgl/renderer/renderer.cpp @@ -57,6 +57,21 @@ AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const { RenderedQueryOptions options; options.layerIDs = {{ AnnotationManager::PointLayerID }}; auto features = queryRenderedFeatures(box, options); + return getAnnotationIDs(features); +} + +AnnotationIDs Renderer::queryShapeAnnotations(const ScreenBox& box) const { + auto features = impl->queryShapeAnnotations({ + box.min, + {box.max.x, box.min.y}, + box.max, + {box.min.x, box.max.y}, + box.min + }); + return getAnnotationIDs(features); +} + +AnnotationIDs Renderer::getAnnotationIDs(const std::vector& features) const { std::set set; for (auto &feature : features) { assert(feature.id); diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 6a8c18792e..7339756e52 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -629,6 +629,10 @@ std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin } } + return queryRenderedFeatures(geometry, options, layers); +} + +std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector& layers) const { std::unordered_set sourceIDs; for (const RenderLayer* layer : layers) { sourceIDs.emplace(layer->baseImpl->source); @@ -663,6 +667,21 @@ std::vector Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin return result; } +std::vector Renderer::Impl::queryShapeAnnotations(const ScreenLineString& geometry) const { + std::vector shapeAnnotationLayers; + RenderedQueryOptions options; + for (const auto& layerImpl : *layerImpls) { + if (std::mismatch(layerImpl->id.begin(), layerImpl->id.end(), + AnnotationManager::ShapeLayerID.begin(), AnnotationManager::ShapeLayerID.end()).second == AnnotationManager::ShapeLayerID.end()) { + if (const RenderLayer* layer = getRenderLayer(layerImpl->id)) { + shapeAnnotationLayers.emplace_back(layer); + } + } + } + + return queryRenderedFeatures(geometry, options, shapeAnnotationLayers); +} + std::vector Renderer::Impl::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const { const RenderSource* source = getRenderSource(sourceID); if (!source) return {}; diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 30e7f70722..db2a6e7a74 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -49,6 +49,7 @@ public: std::vector queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&) const; std::vector querySourceFeatures(const std::string& sourceID, const SourceQueryOptions&) const; + std::vector queryShapeAnnotations(const ScreenLineString&) const; void onLowMemory(); void dumDebugLogs(); @@ -61,6 +62,8 @@ private: RenderLayer* getRenderLayer(const std::string& id); const RenderLayer* getRenderLayer(const std::string& id) const; + + std::vector queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector&) const; // GlyphManagerObserver implementation. void onGlyphsError(const FontStack&, const GlyphRange&, std::exception_ptr) override; -- cgit v1.2.1