jingo_man
jingo_man

Reputation: 529

Mapbox visibleFeatures(at: styleLayerIdentifiers:) - No results

The goal is to drop annotations (MGLPointAnnotation) on the map, and where it is dropped, detect if it is within another feature (initial testing is if its within instances of MGLPolygon).

I got some insight from the following question: Mapbox iOS SDK - visibleFeaturesAtPoint returns empty array

That code runs as expected, moving the code to the mapView(:didFinishLoadingMap:) as stated in their own answer / comment. I have tried alternate GPS coordinates as well, like Wembley Stadium.

The difference with my scenario is using my own custom GeoJSON data which are a set of specific polygons I have drawn on the map (created in Mapbox's Studio Datasets, but downloaded and not published as a Style), which I bundle with the app. These features load as expected on the mapView.

So I am trying to use the alternative visibleFeatures(at: styleLayerIdentifiers:) method instead, but I am returning an empty / nil result.

This is my ViewController currently:

import UIKit
import Mapbox

class ViewController: UIViewController {

    @IBOutlet weak var mapView: MGLMapView!

    // 3 points to check. 3rd not inside airport boundaries
    var locations = [CLLocationCoordinate2D(latitude: 53.865841, longitude: -1.655929),
                         CLLocationCoordinate2D(latitude: 53.866524, longitude: -1.661572),
                         CLLocationCoordinate2D(latitude: 53.863542, longitude: -1.672380)]

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.styleURL = MGLStyle.satelliteStyleURL()

        // LBIA - 53.866524, -1.661572
        let coord = CLLocationCoordinate2D(latitude: 53.866524, longitude: -1.661572)
        mapView.setCenter(coord, zoomLevel: 17, animated: true)

    }

    func addAnnotations() {

        let layerIdentifiers : Set = ["features"]


        for shot in shotLocations {
            let annotation = MGLPointAnnotation()
            annotation.coordinate = shot
            mapView.addAnnotation(annotation)


            let ptTest = mapView.convert(annotation.coordinate, toPointTo: mapView)

            print("features: \(mapView.visibleFeatures(at: ptTest, styleLayerIdentifiers: layerIdentifiers))")
            //print("vis features: \(mapView.visibleFeatures(at: ptTest))")

        }

    }
}

extension ViewController: MGLMapViewDelegate {

    func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
        let url = URL(fileURLWithPath: Bundle.main.path(forResource: "features", ofType: "geojson")!)

        guard let shape = try? MGLShape(data: Data(contentsOf: url), encoding: String.Encoding.utf8.rawValue) else { return }
        let source = MGLShapeSource(identifier: "features", shape: shape, options: nil)
        //let source = MGLShapeSource(identifier: "features", url: url, options: nil)
        style.addSource(source)
        print(source)

        let layer = MGLFillStyleLayer(identifier: "features", source: source)

        layer.sourceLayerIdentifier = "features"
        layer.fillColor = MGLStyleValue(rawValue: .green)

        // If just want to show "airports"...
        //layer.predicate = NSPredicate(format: "feature == %@", "airport")

        style.addLayer(layer)

        addAnnotations()
    }

    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
        // Always allow callouts to popup when annotations are tapped.
        return true
    }

    func mapView(_ mapView: MGLMapView, fillColorForPolygonAnnotation annotation: MGLPolygon) -> UIColor {
        return .green
    }
}

Any help would be appreciated...

Upvotes: 0

Views: 953

Answers (1)

jingo_man
jingo_man

Reputation: 529

Resolved myself, mostly. Enough for the next problem...

Using other published Examples on the Mapbox site, I used the content here: https://www.mapbox.com/ios-sdk/examples/select-layer/

I added a UITapGestureRecognizer in viewDidLoad(), so I could drop new annotations where I was tapping on the screen.

let gesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
mapView.addGestureRecognizer(gesture)

Added new Obj-C function for the tap handler, and moved the layerIdentifiers variable into global scope within the class:

@objc func handleTap(_ gesture: UITapGestureRecognizer) {

        // User tapped location
        let spot = gesture.location(in: mapView)
        print("spot: \(spot)")

        let features = mapView.visibleFeatures(at: spot, styleLayerIdentifiers: layerIdentifiers)
        print("features: \(features)")

}

This worked! It displays my custom features from the GeoJSON Dataset file.

I dropped more (manually tapped) annotations next to my coded annotations, and the CGPoint location was totally different. Investigating further, it relates to the currently visible map on the iPhone's screen, which is why it wasn't returning anything. With my own code, I had actually set a center GPS slightly to the left, so always had to scroll slightly to see my custom features / MGLPolygons, so they were initially off-screen.

After this, for testing purposes, I changed the code so that it worked on button clicks.

The first button uses the setVisibleCoordinateBounds(bounds: animated:) function to move the visible part of the map over a location that has my custom features.

The second button is where I drop my previous coded annotations (contained in a simple function). Now, with the map moved and updated / re-loaded, the annotations can check if they are over features from my specified layers.

Upvotes: 1

Related Questions