Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[ios, macos] Add selection support to MGLMultiPoint annotations.
Browse files Browse the repository at this point in the history
  • Loading branch information
fabian-guerra committed Sep 14, 2017
1 parent efe9928 commit 7152903
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 50 deletions.
2 changes: 2 additions & 0 deletions include/mbgl/map/map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ class Map : private util::noncopyable {
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);
Expand All @@ -213,6 +214,7 @@ class Map : private util::noncopyable {
private:
class Impl;
const std::unique_ptr<Impl> impl;
AnnotationIDs queryAnnotations(const ScreenBox&, const RenderedQueryOptions& options = {});
};

} // namespace mbgl
156 changes: 106 additions & 50 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingState) {
/// Mapping from an annotation object to an annotation tag.
typedef std::map<id<MGLAnnotation>, MGLAnnotationTag> MGLAnnotationObjectTagMap;

typedef std::map<id<MGLAnnotation>, std::string> MGLShapeAnnotationObjectLayerIDMap;

static NSString *const MGLShapeAnnotationLayerIdentifierPrefix = @"com.mapbox.annotations.shape.";

mbgl::util::UnitBezier MGLUnitBezierForMediaTimingFunction(CAMediaTimingFunction *function)
{
if ( ! function)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -471,6 +476,7 @@ - (void)commonInit
_annotationViewReuseQueueByIdentifier = [NSMutableDictionary dictionary];
_selectedAnnotationTag = MGLAnnotationTagNotFound;
_annotationsNearbyLastTap = {};
_shapeAnnotationLayerIDsByAnnotation = {};

// setup logo bug
//
Expand Down Expand Up @@ -3133,6 +3139,11 @@ - (void)removeStyleClass:(NSString *)styleClass
}

std::vector<MGLAnnotationTag> annotationTags = [self annotationTagsInRect:rect];

if (!annotationTags.size()) {
annotationTags = [self shapeAnnotationTagsInRect:rect];
}

if (annotationTags.size())
{
NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()];
Expand Down Expand Up @@ -3230,6 +3241,9 @@ - (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)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];
}
Expand Down Expand Up @@ -3528,6 +3542,7 @@ - (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations

_annotationContextsByAnnotationTag.erase(annotationTag);
_annotationTagsByAnnotation.erase(annotation);
_shapeAnnotationLayerIDsByAnnotation.erase(annotation);

if ([annotation isKindOfClass:[NSObject class]] && ![annotation isKindOfClass:[MGLMultiPoint class]])
{
Expand Down Expand Up @@ -3622,61 +3637,71 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)
queryRect = CGRectInset(queryRect, -MGLAnnotationImagePaddingForHitTest,
-MGLAnnotationImagePaddingForHitTest);
std::vector<MGLAnnotationTag> 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.
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 <MGLAnnotation> 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 <MGLAnnotation> 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;
Expand Down Expand Up @@ -3759,6 +3784,29 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)
});
}

- (std::vector<MGLAnnotationTag>)shapeAnnotationTagsInRect:(CGRect)rect
{
mbgl::ScreenBox screenBox = {
{ CGRectGetMinX(rect), CGRectGetMinY(rect) },
{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) },
};

std::vector<MGLAnnotationTag> nearbyAnnotations;

mbgl::optional<std::vector<std::string>> optionalLayerIDs;
if (_shapeAnnotationLayerIDsByAnnotation.size()) {
__block std::vector<std::string> 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 <MGLAnnotation>)selectedAnnotation
{
if (_userLocationAnnotationIsSelected)
Expand Down Expand Up @@ -3810,7 +3858,7 @@ - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated
{
if ( ! annotation) return;

if ([annotation isKindOfClass:[MGLMultiPoint class]]) return;
//if ([annotation isKindOfClass:[MGLMultiPoint class]]) return;

if (annotation == self.selectedAnnotation) return;

Expand Down Expand Up @@ -3858,8 +3906,9 @@ - (void)selectAnnotation:(id <MGLAnnotation>)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])
{
Expand Down Expand Up @@ -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)
{
Expand Down
9 changes: 9 additions & 0 deletions src/mbgl/map/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,15 @@ std::vector<Feature> 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<AnnotationID> set;
for (auto &feature : features) {
Expand Down

0 comments on commit 7152903

Please sign in to comment.