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

Support selection of shape and polyline annotations #2082

Closed
friedbunny opened this issue Aug 14, 2015 · 30 comments
Closed

Support selection of shape and polyline annotations #2082

friedbunny opened this issue Aug 14, 2015 · 30 comments
Assignees
Labels
feature iOS Mapbox Maps SDK for iOS MapKit parity For feature parity with MapKit on iOS or macOS

Comments

@friedbunny
Copy link
Contributor

We explicitly don't support selecting non-point annotations in iOS (820d2da), so polylines and polygons aren't particularly interactive yet.

Related: #1504

/cc @incanus @1ec5

@friedbunny friedbunny added feature iOS Mapbox Maps SDK for iOS labels Aug 14, 2015
@friedbunny
Copy link
Contributor Author

I'm unsure where Android stands on annotation selection — @ljbade @bleege?

@incanus
Copy link
Contributor

incanus commented Aug 14, 2015

General Android annotation selection has been on my plate over in #1969 but is incomplete.

As for shapes, I don't think MapKit even supports this, or if it does, I know for a fact it doesn't support callouts on them. We hit this with the raster SDK and it was due to difficult callout placement algorithms. But we could try here?

@incanus incanus removed the iOS Mapbox Maps SDK for iOS label Aug 14, 2015
@1ec5 1ec5 added the GL JS parity For feature parity with Mapbox GL JS label Aug 14, 2015
@1ec5
Copy link
Contributor

1ec5 commented Aug 14, 2015

This would be nice for Leaflet parity.

@lucaswoj
Copy link
Contributor

The way this is done in JS is with

  • runtime styling API
  • featuresAt API

EDIT: AFIK

@ljbade
Copy link
Contributor

ljbade commented Aug 15, 2015

You don't necessarily need to support callouts.

Perhaps just a way to select/hit test polygons.

@maciekish
Copy link
Contributor

We definitely need callout support as well as selection.

@Zakay
Copy link

Zakay commented Sep 14, 2015

+1 for callout support as well.

@incanus
Copy link
Contributor

incanus commented Sep 14, 2015

Would it be acceptable to require the developer to indicate where on a polyline/polygon the callout should attach? That would take the guesswork off of the framework. We could support some simple options like top/bottom/left/right/centroid and leave it at that.

The problem is when you get into polylines like this — where do you attach the callout?

file sep 14 8 36 35 am

Prior art: mapbox/DEPRECATED-mapbox-ios-sdk#285

@maciekish
Copy link
Contributor

Definiteyly, that's a lot better than not being able to do it at all!

@mb12
Copy link

mb12 commented Sep 14, 2015

@incanus The callout for line can be attached to a 'point on the line' that passed the hit-test for tap.

@friedbunny
Copy link
Contributor Author

The callout for line can be attached to a 'point on the line' that passed the hit-test for tap.

Aye, tapped-point would be the most intuitive solution for polyline callouts. Polygons, seems like centroid would be best?

@maciekish
Copy link
Contributor

@friedbunny Please leave an option to set any point or tapped point on the polygon as well. Centroid won't always work in our use case.

@incanus
Copy link
Contributor

incanus commented Sep 14, 2015

The callout for line can be attached to a 'point on the line' that passed the hit-test for tap.

Just a note that this may require we allow callout arrows in directions other than just down.

Centroid won't always work in our use case.

img_4189

@incanus
Copy link
Contributor

incanus commented Sep 23, 2015

Callout arrows aside, this is trickier without full support for featuresAt per #352.

@1ec5
Copy link
Contributor

1ec5 commented Jun 29, 2016

#5502 (which I think we should fold into #5165) adds support for selecting polygon and polyline annotations.

As to the default callout anchor point strategy, another consideration is that the shape may extend beyond the viewport, meaning that an obvious anchor point like the centroid could fall outside the viewport. We’d have to crop the shape to the viewport before computing an anchor point.

As an alternative to adjusting the callout anchor point of a shape, once #5502/#5165 lands, you could also display your own callout in response to a shape annotation getting selected. As part of #4392, we could perhaps expose an API for displaying a popup independently of a particular annotation; you’d use that API to create a popup that you could position anywhere you want on the shape.

@1ec5
Copy link
Contributor

1ec5 commented Jul 22, 2016

The anchor placement issue illustrated in #2082 (comment) would be addressed by a port of @mourner's Polylabel library to C++. The additional consideration in #2082 (comment) still applies, but mbgl probably already has a solution for clipping polygons that could be reused for this problem.

@1ec5 1ec5 added MapKit parity For feature parity with MapKit on iOS or macOS and removed GL JS parity For feature parity with Mapbox GL JS labels Aug 15, 2016
@1ec5 1ec5 added this to the ios-future milestone Aug 25, 2016
@1ec5
Copy link
Contributor

1ec5 commented Sep 24, 2016

While we wait for a port of Polylabel, plan B would be to anchor the callout as close as possible to the tap location. This would require porting some minimal Turf functionally from JavaScript. Then once Polylabel is ready, we could switch to that implementation without any API changes.

@1ec5
Copy link
Contributor

1ec5 commented Jan 25, 2017

The anchor placement issue illustrated in #2082 (comment) would be addressed by a port of @mourner's Polylabel library to C++.

Polylabel was integrated into mbgl in #7070.

@simplyira
Copy link

simplyira commented Feb 13, 2017

I am implementing the workaround, since I need support for tapping polylines representing route sections. For simplicity the whole route is one section at the moment. I cannot find any ways to associate the MGLPolylineFeature returned from MGLMapView.visibleFeatures(at:styleLayerIdentifiers: to the original MGLPolylineFeature I add to the source let polyline = MGLPolylineFeature(coordinates: &routeCoordinates, count: UInt(routeCoordinates.count)) self.routeSource?.shape = polyline
The returned polyline feature has no identifiers, attributes and I do not know how to get the coordinates out of the UnsafeMutablePointer: features.first?.coordinates It appears to be a completely new object (which is understandable), which does not preserve or double any of the information the original object had (which poses a problem.)
The only workaround I see at the moment is to put each section on a different layer and use the layerIdentifiers to distinguish them, but that is very ugly :( there could be hundreds of sections - users are not given restrictions, so this would have to be coordinated dynamically.
Help would be very much appreciated!

@1ec5
Copy link
Contributor

1ec5 commented Feb 13, 2017

The returned polyline feature has no identifiers, attributes and I do not know how to get the coordinates out of the UnsafeMutablePointer: features.first?.coordinates

Comparing the returned feature’s coordinates is currently the only way to match feature querying results with a feature you’re interested in. See this document for guidance on working with UnsafeMutablePointer in Swift.

@iFarmSupport
Copy link

Howdy! I've seen several references to being able to get an event at click/touch MGLPolyLine where you might release it at 3.4.1. It's currently 3.4.2. Can you give a current estimate of when you think that feature will be realized? I appreciate all of the effort and any direction you can lend on this would be helpful. Thanks!

@simplyira
Copy link

@iFarmSupport I obviously can't speak on behalf of the developers, but in case you need a temporary workaround, mine is just that.
I did get the coordinates in the polyline returned from MGLMapView.visibleFeatures(at:styleLayerIdentifiers:. Unfortunately they lose precision compared to original line added:
Original polyline:

latitude: 55.864750000000001, longitude: -4.2521399999999998
latitude: 55.864820000000002, longitude: -4.2527099999999995
latitude: 55.864960000000004, longitude: -4.2528199999999998
latitude: 55.865080000000006, longitude: -4.2538900000000002
latitude: 55.864700000000006, longitude: -4.2540300000000002
latitude: 55.864320000000006, longitude: -4.2541500000000001
latitude: 55.864190000000008, longitude: -4.25305
latitude: 55.863740000000007, longitude: -4.2528800000000002
latitude: 55.863430000000008, longitude: -4.2529900000000005
latitude: 55.863070000000008, longitude: -4.2531400000000001
latitude: 55.86272000000001, longitude: -4.2532700000000006
latitude: 55.862390000000012, longitude: -4.253400000000001
latitude: 55.862300000000012, longitude: -4.2526400000000013
latitude: 55.862310000000015, longitude: -4.252600000000001
latitude: 55.862300000000012, longitude: -4.2524900000000008
latitude: 55.86234000000001, longitude: -4.2524700000000006
latitude: 55.862320000000011, longitude: -4.2523400000000002
latitude: 55.862320000000011, longitude: -4.2523200000000001
latitude: 55.862310000000008, longitude: -4.2523
latitude: 55.862320000000011, longitude: -4.2522900000000003

Returned polyline:

latitude: 55.864750112256786, longitude: -4.252140149474144
latitude: 55.864820100139724, longitude: -4.2527101188898087
latitude: 55.864960075527279, longitude: -4.2528200894594193
latitude: 55.865080107783143, longitude: -4.2538902908563614
latitude: 55.864700067080378, longitude: -4.2540297657251358
latitude: 55.864320022659314, longitude: -4.2541497945785522
latitude: 55.864189828368893, longitude: -4.2530500888824463
latitude: 55.863740165541458, longitude: -4.2528797686100006
latitude: 55.863430102116212, longitude: -4.2529897391796112
latitude: 55.863069988939515, longitude: -4.2531399428844452
latitude: 55.862390016920983, longitude: -4.2534001171588898
latitude: 55.862300080605081, longitude: -4.2526397109031677
latitude: 55.862309864482171, longitude: -4.2526001483201981
latitude: 55.862300080605081, longitude: -4.2524901777505875 
latitude: 55.862339968703935, longitude: -4.2524700611829758 
latitude: 55.862320024659624, longitude: -4.2523399740457535 
latitude: 55.862320024659624, longitude: -4.2523198574781418
latitude: 55.862309864482171, longitude: -4.2522997409105301
latitude: 55.862320024659624, longitude: -4.2522896826267242

Depending on your use case, it is definitely possible to match the returned line with original. For my use case, I found a simpler solution though. I keep references to all the polylines the user can tap on (it is never too many) and then check for intersection of bounds.

// poly is the area around the point where the user tapped converted to map coordinates
 let poly = MGLPolygon(coordinates: &polygonCoords, count: UInt(polygonCoords.count))
        
        var found: RouteSection? = nil
        //TODO: Could be improved by looking for streets closer to the centre of the tap?
        for route in routeSections {
            if(poly.intersects(route.line.overlayBounds)) {
                found = route
                break
            }
        }
        
        if let selectedRouteSection = found {
            
            if let selectedIndex = routeSections.index(of: selectedRouteSection) {
                //if the user did select something, display it
                self.selectedFeaturesSource?.shape = selectedRouteSection.line
                self.navigationView.detailsOnSelectedSection(selectedRouteSectionIndex: selectedIndex)
            }
        } else {
            // the tap was not on any of the routes, so we deselect everything
            self.selectedFeaturesSource?.shape = nil
        }

The side effect of this is that bounds refers to whole shape, rather than line. (True marks where tap is detected)
image
The above is just an example. My use case breaks the route into small sections, so user experience is acceptable. I am waiting for the proper functionality as well. I needed more than a pop-up to appear, since I have more data relating to each polyline, so for now this works :)

@kkaefer kkaefer added Android Mapbox Maps SDK for Android iOS Mapbox Maps SDK for iOS labels May 30, 2017
@tobrun
Copy link
Member

tobrun commented Jul 7, 2017

Removing android label, this is going to be integrated as part of #3720, which is aimed at next release.

@tobrun tobrun removed the Android Mapbox Maps SDK for Android label Jul 7, 2017
@boundsj boundsj modified the milestones: ios-future, ios-v3.6.1 Jul 10, 2017
@fabian-guerra fabian-guerra self-assigned this Jul 24, 2017
@friedbunny friedbunny modified the milestones: ios-v3.6.1, ios-v3.6.2 Aug 1, 2017
@CodeKunal
Copy link

Hi
I have my own CustomPolygonFeature subclass

class CustomPolygonFeature: MGLPolygonFeature {
var fillColor: UIColor?
var strokeColor:UIColor?
var name:String?
}

I am adding these polygons on top of mapView using .add(Overlay:) method
I have added a tapGesture on the mapView. When I click on tap I am checking the type of Feature I clicked on.

let spot = gesture.location(in: mapView)
let features = mapView.visibleFeatures(at: spot)
features.map{
print(type(of: $0))
}

I am expecting the result to be
Print:CustomPolygonFeature
Actual result:
Prints: MGLPolygonFeature

Is there anything wrong?

@1ec5
Copy link
Contributor

1ec5 commented Sep 8, 2017

@CodeKunal, this is an inherent limitation of the feature querying APIs and runtime styling in general: #6178. In #9540, we amended the documentation to note this limitation.

@1ec5
Copy link
Contributor

1ec5 commented Dec 20, 2017

This feature was implemented for in #9984, which is in iOS map SDK v3.7.0 and will be in macOS SDK v0.6.0 shortly.

@1ec5 1ec5 closed this as completed Dec 20, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
feature iOS Mapbox Maps SDK for iOS MapKit parity For feature parity with MapKit on iOS or macOS
Projects
None yet
Development

No branches or pull requests