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 13, 2017
1 parent 5e08ce0 commit fe5bc8d
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 64 deletions.
2 changes: 2 additions & 0 deletions include/mbgl/renderer/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ class Renderer {
std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions& options = {}) const;
std::vector<Feature> queryRenderedFeatures(const ScreenCoordinate& point, const RenderedQueryOptions& options = {}) const;
std::vector<Feature> queryRenderedFeatures(const ScreenBox& box, const RenderedQueryOptions& options = {}) const;
std::vector<Feature> queryRenderedSourceFeatures(const ScreenBox& box) 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;

// Debug
void dumpDebugLogs();
Expand Down
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
107 changes: 67 additions & 40 deletions platform/ios/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3246,6 +3246,12 @@ - (void)removeStyleClass:(NSString *)styleClass
}

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

if (shapeAnnotationTags.size()) {
annotationTags.insert(annotationTags.end(), shapeAnnotationTags.begin(), shapeAnnotationTags.end());
}

if (annotationTags.size())
{
NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()];
Expand Down Expand Up @@ -3751,61 +3757,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);

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

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

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;
Expand Down Expand Up @@ -3888,6 +3902,14 @@ - (MGLAnnotationTag)annotationTagAtPoint:(CGPoint)point persistingResults:(BOOL)
});
}

- (std::vector<MGLAnnotationTag>)shapeAnnotationTagsInRect:(CGRect)rect
{
return _rendererFrontend->getRenderer()->queryShapeAnnotations({
{ CGRectGetMinX(rect), CGRectGetMinY(rect) },
{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) },
});
}

- (id <MGLAnnotation>)selectedAnnotation
{
if (_userLocationAnnotationIsSelected)
Expand Down Expand Up @@ -3939,8 +3961,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 +4104,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
75 changes: 52 additions & 23 deletions platform/macos/src/MGLMapView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,12 @@ - (IBAction)giveFeedback:(id)sender {
}

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

if (shapeAnnotationTags.size()) {
annotationTags.insert(annotationTags.end(), shapeAnnotationTags.begin(), shapeAnnotationTags.end());
}

if (annotationTags.size())
{
NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:annotationTags.size()];
Expand Down Expand Up @@ -2036,35 +2042,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 +2149,14 @@ - (MGLAnnotationTag)annotationTagAtPoint:(NSPoint)point persistingResults:(BOOL)
});
}

- (std::vector<MGLAnnotationTag>)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 <MGLAnnotation>)selectedAnnotation {
if ( ! _annotationContextsByAnnotationTag.count(_selectedAnnotationTag) ||
_selectedAnnotationTag == MGLAnnotationTagNotFound) {
Expand Down Expand Up @@ -2300,6 +2322,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
26 changes: 26 additions & 0 deletions src/mbgl/renderer/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ std::vector<Feature> Renderer::queryRenderedFeatures(const ScreenBox& box, const
options
);
}

std::vector<Feature> Renderer::queryRenderedSourceFeatures(const ScreenBox& box) const {
return impl->queryShapeAnnotations(
{
box.min,
{box.max.x, box.min.y},
box.max,
{box.min.x, box.max.y},
box.min
});
}

AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const {
RenderedQueryOptions options;
Expand All @@ -70,6 +81,21 @@ AnnotationIDs Renderer::queryPointAnnotations(const ScreenBox& box) const {
return ids;
}

AnnotationIDs Renderer::queryShapeAnnotations(const ScreenBox& box) const {
auto features = queryRenderedSourceFeatures(box);
std::set<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;
}

std::vector<Feature> Renderer::querySourceFeatures(const std::string& sourceID, const SourceQueryOptions& options) const {
return impl->querySourceFeatures(sourceID, options);
}
Expand Down
Loading

0 comments on commit fe5bc8d

Please sign in to comment.