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 Oct 10, 2017
1 parent 5e08ce0 commit a69461d
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 65 deletions.
2 changes: 2 additions & 0 deletions include/mbgl/renderer/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Renderer {
std::vector<Feature> queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const;
std::vector<Feature> querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options = {}) const;
AnnotationIDs queryPointAnnotations(const ScreenBox& box) const;
AnnotationIDs queryShapeAnnotations(const ScreenBox& box, const std::vector<std::string> layerIDs) const;

// Debug
void dumpDebugLogs();
Expand All @@ -50,6 +51,7 @@ class Renderer {
private:
class Impl;
std::unique_ptr<Impl> impl;
AnnotationIDs queryAnnotations(const ScreenBox&, const RenderedQueryOptions& options = {}) const;
};

} // namespace mbgl
1 change: 1 addition & 0 deletions platform/ios/app/MBXViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ - (void)addTestShapes
}

MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]];
polygon.title = feature[@"properties"][@"NAME"];

[self.mapView addAnnotation:polygon];

Expand Down
121 changes: 80 additions & 41 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3246,6 +3246,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 @@ -3751,61 +3756,69 @@ - (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;
}

// 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;

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;
Expand Down Expand Up @@ -3888,6 +3901,27 @@ - (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> annotationsInRect;

if (_annotationTagsByAnnotation.size()) {
__block std::vector<std::string> 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 <MGLAnnotation>)selectedAnnotation
{
if (_userLocationAnnotationIsSelected)
Expand Down Expand Up @@ -3939,8 +3973,6 @@ - (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated
{
if ( ! annotation) return;

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

if (annotation == self.selectedAnnotation) return;

[self deselectAnnotation:self.selectedAnnotation animated:NO];
Expand Down Expand Up @@ -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)
{
Expand Down
87 changes: 64 additions & 23 deletions platform/macos/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,11 @@ - (IBAction)giveFeedback:(id)sender {
}

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 @@ -2036,35 +2041,43 @@ - (MGLAnnotationTag)annotationTagAtPoint:(NSPoint)point persistingResults:(BOOL)
queryRect = NSInsetRect(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.
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 <MGLAnnotation> 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 <MGLAnnotation> 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;
Expand Down Expand Up @@ -2135,6 +2148,27 @@ - (MGLAnnotationTag)annotationTagAtPoint:(NSPoint)point persistingResults:(BOOL)
});
}

- (std::vector<MGLAnnotationTag>)shapeAnnotationTagsInRect:(NSRect)rect {
mbgl::ScreenBox screenBox = {
{ NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) },
{ NSMaxX(rect), NSHeight(self.bounds) - NSMinY(rect) },
};

std::vector<MGLAnnotationTag> annotationsInRect;

if (_annotationTagsByAnnotation.size()) {
__block std::vector<std::string> 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 <MGLAnnotation>)selectedAnnotation {
if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag) ||
_selectedAnnotationTag == MGLAnnotationTagNotFound) {
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/annotation/annotation_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_) {
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/annotation/annotation_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/mbgl/annotation/shape_annotation_impl.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <mbgl/annotation/shape_annotation_impl.hpp>
#include <mbgl/annotation/annotation_tile.hpp>
#include <mbgl/annotation/annotation_manager.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/math/wrap.hpp>
#include <mbgl/math/clamp.hpp>
Expand All @@ -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) {
Expand Down
18 changes: 18 additions & 0 deletions src/mbgl/renderer/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ std::vector<Feature> 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<std::string> layerIDs) const {
RenderedQueryOptions options;
__block std::vector<std::string> 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<AnnotationID> set;
for (auto &feature : features) {
Expand Down

0 comments on commit a69461d

Please sign in to comment.