Skip to main content

Clickable Points

Contents

In this example, icons are displayed at the locations obtained from the JSON file, and when tapped on, custom data is displayed.

Clickable Points

Sample Code

import Foundation
import UIKit
import MapKit
import TrimbleMaps
import TrimbleMapsAccounts

class ClickablePointsViewController: UIViewController, AccountManagerDelegate, TMGLMapViewDelegate {
    
    internal var mapView: TMGLMapView!
    
    private let SOURCE_ID = "tristatepoints"
    private let LAYER_ID = "tristatepoints"
    private let ICON_ID = "ic_location"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let apiKey =  "Your-API-key-here"
        let account = Account(apiKey: apiKey, region: Region.northAmerica)
        AccountManager.default.account = account
        AccountManager.default.delegate = self
        
        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(backButtonPressed))
    }
    
    func stateChanged(newStatus: AccountManagerState) {
        if newStatus == .loaded {
            DispatchQueue.main.async {
                // Create a map view
                self.mapView = TMGLMapView(frame: self.view.bounds)
                self.mapView.delegate = self

                // Set the map location
                let center = CLLocationCoordinate2D(latitude: 41.36290180612575, longitude: -74.6946761628674)
                self.mapView.setCenter(center, zoomLevel: 13, animated: false)

                // Add the map
                self.view.addSubview(self.mapView)
            }
        }
    }
    
    func mapViewDidFinishLoadingMap(_ mapView: TMGLMapView) {
        // In this example a .json file is being used as the source

        let filePath = Bundle.main.path(forResource: "tristate", ofType: "json")!
        let fileUrl = URL(fileURLWithPath: filePath)

        // Create a source and add it to the style. Important to note, sources are linked to styles.
        // If you change the style you may need to re-add your source and layers
        let shapeSource = TMGLShapeSource(identifier: SOURCE_ID, url: fileUrl, options: .none)
        mapView.style?.addSource(shapeSource)
        
        // Add the icon image to the style
        if let image = UIImage(named: ICON_ID) {
            mapView.style?.setImage(image, forName: ICON_ID)
        }

        // Create a SymbolLayer to display the icons
        let symbolLayer = TMGLSymbolStyleLayer(identifier: LAYER_ID, source: shapeSource)
        symbolLayer.iconImageName = NSExpression(forConstantValue: ICON_ID)
        
        // Add the layer
        mapView.style?.addLayer(symbolLayer)
        
        // Add tap gesture recognizer
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleMapTap(_:)))
        mapView.addGestureRecognizer(tapGestureRecognizer)
    }
    
    @objc func handleMapTap(_ sender: UITapGestureRecognizer) {
		
		// Get the point where the user tapped on the map and convert it to map coordinates
	    let point = sender.location(in: mapView)
        let coordinate = mapView.convert(point, toCoordinateFrom: mapView)

		// Get the features visible at the tapped point, check if the first feature is a TMGLPointFeature, and show an alert with its coordinates and state
        let features = mapView.visibleFeatures(at: point, styleLayerIdentifiers: Set([LAYER_ID]))
        if let feature = features.first as? TMGLPointFeature, let properties = feature.attributes as? [String: Any] {
            let state = properties["state"] as? String ?? "Unknown"
            let coordinates = feature.coordinate
            let message = "Coordinates: \(coordinates.longitude), \(coordinates.latitude)\nState: \(state)"
            showAlert(message: message)
        }
    }
    
    func showAlert(message: String) {
		// Displays the message
	
        let alert = UIAlertController(title: "Location Info", message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        present(alert, animated: true, completion: nil)
    }

    @objc func backButtonPressed() {
        dismiss(animated: true, completion: nil)
    }
}
Last updated February 11, 2025.
Contents