diff options
author | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-08-03 15:57:14 -0400 |
---|---|---|
committer | Fabian Guerra <fabian.guerra@mapbox.com> | 2017-08-04 20:31:09 -0400 |
commit | 84d1be0ef51dc5006d3fa508b9d606c5d4cd1475 (patch) | |
tree | 332e6bdef7bad5b5e095d06e6bf907fe8feff104 | |
parent | ebab0c1a1bb6e8e76cefff72b262e33f9ac0555c (diff) | |
download | qtlocation-mapboxgl-84d1be0ef51dc5006d3fa508b9d606c5d4cd1475.tar.gz |
[ios] Add shape tap delegatesupstream/fabian-shape-polyline-2082
-rw-r--r-- | include/mbgl/map/map.hpp | 1 | ||||
-rw-r--r-- | platform/ios/src/MGLMapView.mm | 125 | ||||
-rw-r--r-- | src/mbgl/map/map.cpp | 16 |
3 files changed, 141 insertions, 1 deletions
diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index bbeeeac6cc..54bfbc020a 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -197,6 +197,7 @@ public: std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}); AnnotationIDs queryPointAnnotations(const ScreenBox&); + AnnotationIDs queryShapeAnnotations(const ScreenBox&, const RenderedQueryOptions& options = {}); // Memory void setSourceTileCacheSize(size_t); diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 9cc4c42e5a..7e2b50b042 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -152,6 +152,11 @@ typedef std::unordered_map<MGLAnnotationTag, MGLAnnotationContext> MGLAnnotation /// Mapping from an annotation object to an annotation tag. typedef std::map<id<MGLAnnotation>, MGLAnnotationTag> MGLAnnotationObjectTagMap; +/// Mapping from a shape annotation object to shape layer id. +typedef std::map<id<MGLAnnotation>, std::string> MGLShapeAnnotationObjectLayerIDMap; + +const NSString *MGLLayerIDShapeAnnotation = @"com.mapbox.annotations.shape."; + mbgl::util::UnitBezier MGLUnitBezierForMediaTimingFunction(CAMediaTimingFunction *function) { if ( ! function) @@ -285,6 +290,7 @@ public: MGLAnnotationTagContextMap _annotationContextsByAnnotationTag; MGLAnnotationObjectTagMap _annotationTagsByAnnotation; + MGLShapeAnnotationObjectLayerIDMap _shapeAnnotationLayerIDs; /// Tag of the selected annotation. If the user location annotation is selected, this ivar is set to `MGLAnnotationTagNotFound`. MGLAnnotationTag _selectedAnnotationTag; @@ -294,6 +300,7 @@ public: CGSize _unionedAnnotationRepresentationSize; CGSize _largestAnnotationViewSize; std::vector<MGLAnnotationTag> _annotationsNearbyLastTap; + std::vector<MGLAnnotationTag> _shapesNearbyLastTap; CGPoint _initialImplicitCalloutViewOffset; NSDate *_userLocationAnimationCompletionDate; /// True if a willChange notification has been issued for shape annotation layers and a didChange notification is pending. @@ -468,9 +475,11 @@ public: _annotationImagesByIdentifier = [NSMutableDictionary dictionary]; _annotationContextsByAnnotationTag = {}; _annotationTagsByAnnotation = {}; + _shapeAnnotationLayerIDs = {}; _annotationViewReuseQueueByIdentifier = [NSMutableDictionary dictionary]; _selectedAnnotationTag = MGLAnnotationTagNotFound; _annotationsNearbyLastTap = {}; + _shapesNearbyLastTap = {}; // setup logo bug // @@ -1554,6 +1563,15 @@ public: NSAssert(annotation, @"Cannot select nonexistent annotation with tag %u", hitAnnotationTag); return annotation; } + } else { + // Handle the case is a shape + hitAnnotationTag = [self shapesTagAtPoint:tapPoint persistingResults:persist]; + if (hitAnnotationTag != _selectedAnnotationTag) + { + id <MGLAnnotation> annotation = [self annotationWithTag:hitAnnotationTag]; + NSAssert(annotation, @"Cannot select nonexistent annotation with tag %u", hitAnnotationTag); + return annotation; + } } return nil; @@ -3125,6 +3143,7 @@ public: } std::vector<MGLAnnotationTag> annotationTags = [self annotationTagsInRect:rect]; + if (annotationTags.size()) { NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()]; @@ -3222,6 +3241,8 @@ public: context.annotation = annotation; _annotationContextsByAnnotationTag[annotationTag] = context; _annotationTagsByAnnotation[annotation] = annotationTag; + NSString *layerID = [NSString stringWithFormat:@"%@%u", MGLLayerIDShapeAnnotation, annotationTag]; + _shapeAnnotationLayerIDs[annotation] = layerID.UTF8String; [(NSObject *)annotation addObserver:self forKeyPath:@"coordinates" options:0 context:(void *)(NSUInteger)annotationTag]; } @@ -3520,6 +3541,7 @@ public: _annotationContextsByAnnotationTag.erase(annotationTag); _annotationTagsByAnnotation.erase(annotation); + _shapeAnnotationLayerIDs.erase(annotation); if ([annotation isKindOfClass:[NSObject class]] && ![annotation isKindOfClass:[MGLMultiPoint class]]) { @@ -3614,6 +3636,8 @@ public: queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest, -MGLAnnotationImagePaddingForHitTest); std::vector<MGLAnnotationTag> nearbyAnnotations = [self annotationTagsInRect:queryRect]; + + if (nearbyAnnotations.size()) { @@ -3742,6 +3766,86 @@ public: return hitAnnotationTag; } +- (MGLAnnotationTag)shapesTagAtPoint:(CGPoint)point persistingResults:(BOOL)persist +{ + CGRect queryRect = CGRectInset({ point, CGSizeZero }, + -_unionedAnnotationRepresentationSize.width, + -_unionedAnnotationRepresentationSize.height); + queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest, + -MGLAnnotationImagePaddingForHitTest); + std::vector<MGLAnnotationTag> nearbyShapes = [self shapesTagsInRect:queryRect]; + + MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound; + if (nearbyShapes.size()) + { + // The annotation tags need to be stable in order to compare them with + // the remembered tags. + std::sort(nearbyShapes.begin(), nearbyShapes.end()); + + if (nearbyShapes == _shapesNearbyLastTap) + { + // The first selection in the cycle should be the one nearest to the + // tap. + CLLocationCoordinate2D currentCoordinate = [self convertPoint:point toCoordinateFromView:self]; + std::sort(nearbyShapes.begin(), nearbyShapes.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) { + CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate]; + CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate]; + CLLocationDegrees deltaA = hypot(coordinateA.latitude - currentCoordinate.latitude, + coordinateA.longitude - currentCoordinate.longitude); + CLLocationDegrees deltaB = hypot(coordinateB.latitude - currentCoordinate.latitude, + coordinateB.longitude - currentCoordinate.longitude); + return deltaA < deltaB; + }); + + // The last time we persisted a set of annotations, we had the same + // set of annotations as we do now. Cycle through them. + if (_selectedAnnotationTag == MGLAnnotationTagNotFound + || _selectedAnnotationTag == _shapesNearbyLastTap.back()) + { + // Either no annotation is selected or the last annotation in + // the set was selected. Wrap around to the first annotation in + // the set. + hitAnnotationTag = _shapesNearbyLastTap.front(); + } + else + { + auto result = std::find(_shapesNearbyLastTap.begin(), + _shapesNearbyLastTap.end(), + _selectedAnnotationTag); + if (result == _shapesNearbyLastTap.end()) + { + // An annotation from this set hasn’t been selected before. + // Select the first (nearest) one. + hitAnnotationTag = _shapesNearbyLastTap.front(); + } + else + { + // Step to the next annotation in the set. + auto distance = std::distance(_shapesNearbyLastTap.begin(), result); + hitAnnotationTag = _shapesNearbyLastTap[distance + 1]; + } + } + } + else + { + // Remember the nearby annotations for the next time this method is + // called. + if (persist) + { + _shapesNearbyLastTap = nearbyShapes; + } + + // Choose the first nearby annotation. + if (nearbyShapes.size()) + { + hitAnnotationTag = nearbyShapes.front(); + } + } + } + + return hitAnnotationTag; +} + /// Returns the tags of the annotations coincident with the given rectangle. - (std::vector<MGLAnnotationTag>)annotationTagsInRect:(CGRect)rect { @@ -3751,6 +3855,25 @@ public: }); } +/// Returns the tags of the shapes annotations coincident with the given rectangle. +- (std::vector<MGLAnnotationTag>)shapesTagsInRect:(CGRect)rect +{ + std::vector<std::string> layerIDs; + for (const auto &annotation : _shapeAnnotationLayerIDs) { + layerIDs.push_back(annotation.second); + } + + mbgl::RenderedQueryOptions options; + options.layerIDs = {{ layerIDs }}; + mbgl::ScreenBox box = { + { CGRectGetMinX(rect), CGRectGetMinY(rect) }, + { CGRectGetMaxX(rect), CGRectGetMaxY(rect) }, + }; + + return _mbglMap->queryShapeAnnotations(box, options); + +} + - (id <MGLAnnotation>)selectedAnnotation { if (_userLocationAnnotationIsSelected) @@ -3802,7 +3925,7 @@ public: { if ( ! annotation) return; - if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; +// if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; if (annotation == self.selectedAnnotation) return; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 35457f3a5b..338877800c 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -867,6 +867,22 @@ AnnotationIDs Map::queryPointAnnotations(const ScreenBox& box) { std::move(set.begin(), set.end(), std::back_inserter(ids)); return ids; } + +AnnotationIDs Map::queryShapeAnnotations(const ScreenBox& box, const RenderedQueryOptions& options) { + auto features = queryRenderedFeatures(box, options); + + std::set<mbgl::AnnotationID> set; + for (auto &feature : features) { + assert(feature.id); + assert(feature.id->is<uint64_t>()); + assert(feature.id->get<uint64_t>() <= std::numeric_limits<AnnotationID>::max()); + set.insert(static_cast<AnnotationID>(feature.id->get<uint64_t>())); + } + AnnotationIDs ids; + ids.reserve(set.size()); + std::move(set.begin(), set.end(), std::back_inserter(ids)); + return ids; +} #pragma mark - Style API |