Integrate Private Venues

The HERE SDK provides the possibility to load, show and interact with private venues on the map.

HERE Venues provide a wealth of hyperlocal information about indoor spaces, including building geometry and points of interest, spanning across multiple floors. HERE has mapped thousands of buildings globally, including, shopping malls, airports, train stations, parking garages, corporate campuses, factories, warehouses and many other types of buildings.

If you are a venue owner and are interested in leveraging HERE Venues with the HERE SDK, contact us at: venues.support@here.com.

Screenshot: Showing an airport venue with a customized floor switcher.

Note that as of now the HERE SDK provides only support for private venues: This means, that your venue data will be shown only in your app. By default, no venues are visible on the map. Each of your venues will get a uniquie venue ID that is tied to your HERE SDK credentials.

Initialize the VenueEngine

Before you start using the Venues API, the VenueEngine instance should be created and started. This can be done after a map initialization. The best point to create the VenueEngine is after the map loads a scene.

private void loadMapScene() {
    // Completion handler when loading a map scene.
    private func onLoadScene(mapError: MapError?) {
        guard mapError == nil else {
            print("Error: Map scene not loaded, \(String(describing: mapError))")
            return
        }

        // Hide an extruded buildings layer, so it will not overlaps with venues.
        mapView.mapScene.setLayerState(layerName: MapScene.Layers.extrudedBuildings,
                                       newState: MapScene.LayerState.hidden)

        // Create a venue engine object. Once the initialization is done, a completion handler
        // will be called.
        venueEngine = VenueEngine { [weak self] in self?.onVenueEngineInit() }
    }
}

Once the VenueEngine has been initialized, a completion handler will be called. From this point, there is access to the VenueService and the VenueMap. A VenueService is used for loading venues and a VenueMap controls the venues on the map. Inside the completion handler all needed delegates can be added. Afterwards, the VenueEngine needs to be started.

private func onVenueEngineInit() {
    // Get VenueService and VenueMap objects.
    let venueMap = venueEngine.venueMap
    let venueService = venueEngine.venueService

    // Add needed delegates.
    venueService.addServiceDelegate(self)
    venueService.addVenueDelegate(self)
    venueMap.addVenueSelectionDelegate(self)

    // Start VenueEngine. Once authentication is done, the authentication completion handler
    // will be triggered. Afterwards VenueEngine will start VenueService. Once VenueService
    // is initialized, VenueServiceListener.onInitializationCompleted method will be called.
    venueEngine.start(callback: {
        error, data in if let error = error {
            print("Failed to authenticate, reason: " + error.localizedDescription)
        }
    })
}

After the VenueEngine is started, it authenticates using the current credentials. If authentication is successful, the VenueEngine will start the VenueService. Once the VenueService is initialized, the VenueServiceDelegate.onInitializationCompleted() method will be called.

// Delegate for the VenueService event.
extension ViewController: VenueServiceDelegate {
    func onInitializationCompleted(result: VenueServiceInitStatus) {
        if (result == .onlineSuccess) {
            print("Venue Service initialize successfully.")
        } else {
            print("Venue Service failed to initialize!")
        }
    }

    func onVenueServiceStopped() {
        print("Venue Service has stopped.")
    }
}

Load and Show a Venue

The Venues API allows to load and visualize venues by ID's. You should know the venue ID's available for the current credentials beforehand. There are several ways to load and visualize the venues. In the VenueService there is a method to start a new venues loading queue:

venueEngine.venueService.startLoading(venueIds: /*VENUE_ID_LIST*/)

Also, there is a method to add a venue ID to the existing loading queue:

venueEngine.venueService.addVenueToLoad(venueId: /*VENUE_ID*/);

A VenueMap has two methods to add a venue to the map: selectVenueAsync() and addVenueAsync(). Both methods use getVenueService().addVenueToLoad() to load the venue by ID and then add it to the map. The method selectVenueAsync() also selects the venue.

venueEngine.venueMap.selectVenueAsync(venueId:/*VENUE_ID*/);
venueEngine.venueMap.addVenueAsync(venueId:/*VENUE_ID*/);

Once the venue is loaded, the VenueService calls the VenueDelegate.onGetVenueCompleted() method.

// Delegate for the venue loading event.
extension ViewController: VenueDelegate {
    func onGetVenueCompleted(venueId: Int32, venueModel: VenueModel?, online: Bool, venueStyle: VenueStyle?) {
        if venueModel == nil {
            print("Loading of venue \(venueId) failed!")
        }
    }
}

If the venue is loaded successfully, in case of the method addVenueAsync(), only the VenueLifecycleDelegate.onVenueAdded() method will be triggered. In case of the method selectVenueAsync(), VenueSelectionDelegate.onSelectedVenueChanged() method will be triggered as well.

// Delegate for the venue selection event.
extension ViewController: VenueSelectionDelegate {
    func onSelectedVenueChanged(deselectedVenue: Venue?, selectedVenue: Venue?) {
        if let venueModel = selectedVenue?.venueModel {
            if moveToVenue {
                // Move camera to the selected venue.
                let center = GeoCoordinates(latitude: venueModel.center.latitude,
                                            longitude: venueModel.center.longitude,
                                            altitude: 500.0)
                mapView.camera.lookAt(point: center)
            }
        }
    }
}

A Venue can be removed from the VenueMap. In this case, the VenueLifecycleDelegate.onVenueRemoved() method will be triggered.

venueEngine.venueMap.removeVenue(venue: venue)

Select Venue Drawings and Levels

A Venue object allows to control a state of the venue.

The property Venue.selectedDrawing allows to get and set a drawing which will be visible on the map. If a new drawing is selected, the VenueDrawingSelectionDelegate.onDrawingSelected() method will be triggered. See an example of how to select a drawing when an item is clicked in an UITableView:

extension DrawingSwitcher: UITableViewDelegate {
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let drawingIndex: Int = indexPath.row
        if let venue = venueMap?.selectedVenue {
            // Set the selected drawing when a user clicks on the item in the table view.
            let drawing: VenueDrawing = venue.venueModel.drawings[drawingIndex]
            venue.selectedDrawing = drawing
            ...
        }
    }
}

The properties Venue.selectedLevel, Venue.selectedLevelIndex and Venue.selectedLevelZIndex allow you to get and set a level which will be visible on the map. If a new level is selected, the VenueLevelSelectionDelegate.onLevelSelected() method will be triggered. See an example of how to select a level based on a reversed levels list from UITableView:

extension LevelSwitcher: UITableViewDelegate {
    public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // Rows in the LevelSwitcher's table view are presented in the reversed way
        currentLevelIndex = Int32(levels.count - indexPath.row - 1)
        updateLevel(currentLevelIndex)
    }
}
func updateLevel(_ levelIndex: Int32) {
    if let venue = venueMap?.selectedVenue {
        venue.selectedLevelIndex = currentLevelIndex
    }
}

A full example of the UI switchers to control drawings and levels is available in the venue example app you can find on GitHub.

Customize the Style of a Venue

It is possible to change the visual style of the VenueGeometry objects. Geometry style and/or label style objects need to be created and provided to the Venue.setCustomStyle() method.

// Create geometry and label styles for the selected geometry.
geometryStyle = VenueGeometryStyle(
    mainColor: selectedColor, outlineColor: selectedOutlineColor, outlineWidth: 1)
labelStyle = VenueLabelStyle(
    fillColor: selectedTextColor, outlineColor: selectedTextOutlineColor, outlineWidth: 1, maxFont: 28)
venue.setCustomStyle(geometries: [geometry], style: geometryStyle, labelStyle: labelStyle)

Handle Tap Gestures on a Venue

You can select a venue object by performing a tap gesture on it. First, set the tap delegate:

// Create a venue tap handler and set it as default tap delegate.
mapView.gestures.tapDelegate = VenueTapHandler(venueEngine: venueEngine,
                                               mapView: mapView,
                                               geometryLabel: geometryNameLabel)

Inside the tap delegate, you can use the tapped geographic coordinates as parameter for the VenueMap.getGeometry() and VenueMap.getVenue() methods:

public func onTap(origin: Point2D) {
    deselectGeometry()

    let venueMap = venueEngine.venueMap
    // Get geo coordinates of the tapped point.
    if let position = mapView.viewToGeoCoordinates(viewCoordinates: origin) {
        // If the tap point was inside a selected venue, try to pick a geometry inside.
        // Otherwise try to select an another venue, if the tap point was on top of one of them.
        if let selectedVenue = venueMap.selectedVenue, let geometry = venueMap.getGeometry(position: position) {
            onGeometryPicked(venue: selectedVenue, geometry: geometry)
        } else if let venue = venueMap.getVenue(position: position) {
            venueMap.selectedVenue = venue
        }
    }
}

func deselectGeometry() {
    // If a map marker is already on the screen, remove it.
    if let currentMarker = marker {
        mapView.mapScene.removeMapMarker(currentMarker)
    }
}

func onGeometryPicked(venue: Venue,
                      geometry: VenueGeometry) {
    // If the geomtry has an icon, add a map marker on top of the geometry.
    if geometry.lookupType == .icon {
        if let image = getMarkerImage() {
            marker = MapMarker(at: geometry.center,
                               image: image,
                               anchor: Anchor2D(horizontal: 0.5, vertical: 1.0))
            if let marker = marker {
                mapView.mapScene.addMapMarker(marker)
            }
        }
    }
}

It is good practice to deselect the tapped geometry when the selected venue, drawing or level has changed:

public init(venueEngine: VenueEngine, mapView: MapView, geometryLabel: UILabel) {
    ...
    let venueMap = venueEngine.venueMap
    venueMap.addVenueSelectionDelegate(self)
    venueMap.addDrawingSelectionDelegate(self)
    venueMap.addLevelSelectionDelegate(self)
}

deinit {
    let venueMap = venueEngine.venueMap
    venueMap.removeVenueSelectionDelegate(self)
    venueMap.removeDrawingSelectionDelegate(self)
    venueMap.removeLevelSelectionDelegate(self)
}
extension VenueTapHandler: VenueSelectionDelegate {
    public func onSelectedVenueChanged(deselectedVenue: Venue?, selectedVenue: Venue?) {
        self.deselectGeometry()
    }
}

extension VenueTapHandler: VenueDrawingSelectionDelegate {
    public func onDrawingSelected(venue: Venue, deselectedDrawing: VenueDrawing?, selectedDrawing: VenueDrawing) {
        self.deselectGeometry()
    }
}

extension VenueTapHandler: VenueLevelSelectionDelegate {
    public func onLevelSelected(venue: Venue, drawing: VenueDrawing, deselectedLevel: VenueLevel?, selectedLevel: VenueLevel) {
        self.deselectGeometry()
    }
}

A full example of the usage of the map tap event with venues is available in the venue example app you can find on GitHub.

results matching ""

    No results matching ""