Integrate Indoor Maps
HERE Indoor Map provides functionality to load, show, and interact with private venues on a map. For more information on HERE Indoor Map, see the HERE Indoor Map Guide.
Note
If you are a venue owner and are interested in leveraging HERE Indoor Map with the HERE SDK, contact us at
venues.support@here.com.
An airport venue with a customized floor switcher.
Currently, the HERE SDK only supports private venues, therefore your venue data will only be shown in your app. By default, no venues are visible on the map. Each of your venues receives a unique venue ID that is tied to your HERE SDK credentials.
Initialize the VenueEngine
Before you can begin using the HERE Indoor Map API, the VenueEngine
instance must be created and started. This can be done after a map initialization, however The best time to create the VenueEngine
is after the map loads a scene:
private void loadMapScene() {
private func onLoadScene(mapError: MapError?) {
guard mapError == nil else {
print("Error: Map scene not loaded, \(String(describing: mapError))")
return
}
mapView.mapScene.disableFeatures([MapFeatures.extrudedBuildings])
do {
try venueEngine = VenueEngine { [weak self] in self?.onVenueEngineInit() }
} catch {
print("SDK Engine not instantiated: \(error)")
}
}
}
Once the VenueEngine
has been initialized, a completion handler is called. From this point on, there is access to both the VenueService
and the VenueMap
. A VenueService
is used to load venues, and a VenueMap
controls the venues on the map. Inside the completion handler, all required delegates can be added, and then the VenueEngine
must be started. The platform map catalog HRN
must be set once the VenueEngine
is started.
Note
Setting HRN is optional. If a user does not set HRN, default collection HRN is automatically selected. If a user want to use any other collection, respective HRN can be set. An error log is generated that indicates missing or invalid HRN values. For more information on how you can receive a valid HRN string for your project, see the HERE Indoor Map Guide.
private func onVenueEngineInit() {
let venueMap = venueEngine.venueMap
let venueService = venueEngine.venueService
venueService.addServiceDelegate(self)
venueService.addVenueDelegate(self)
venueMap.addVenueSelectionDelegate(self)
venueEngine.start(callback: {
error, data in if let error = error {
print("Failed to authenticate, reason: " + error.localizedDescription)
}
})
if (hrn != "") {
venueService.setHrn(hrn: hrn)
}
}
Once the VenueEngine
is started, it authenticates using the current credentials, and then starts the VenueService
. Once the VenueService
is initialized, the VenueServiceDelegate.onInitializationCompleted()
method is called:
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.")
}
}
Start the VenueEngine using a token
The VenueEngine
can be started using a valid HERE Indoor Platform project token. For additional information on the HERE platform project management and project workflows refer to HERE Platform Project management
private func onVenueEngineInit() {
let venueMap = venueEngine.venueMap
let venueService = venueEngine.venueService
venueService.addServiceDelegate(self)
venueService.addVenueDelegate(self)
venueMap.addVenueSelectionDelegate(self)
venueEngine.start(token:"TOKEN_GOES_HERE")
}
List All Indoor Maps
The HERE Indoor Maps API allows you to list all private venues that are accessible for your account and the selected collection. VenueMap
contains a list which holds VenueInfo
elements containing venue Identifier, venue ID and venue Name.
let venueInfo:[VenueInfo]? = venueEngine?.venueMap.getVenueInfoList()
if let venueInfo = venueInfo {
for venueInfo in venueInfo {
print("Venue Identifier: \(venueInfo.venueIdentifier)." + " Venue Id: \(venueInfo.venueId)." + " Venue Name: \(venueInfo.venueName).")
}
}
Load and Show a Venue
The HERE Indoor Map API allows you to load and visualize venues by ID. You must know the venue's ID for the current set of credentials. 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: )
You can also add a venue ID to the existing loading queue:
venueEngine.venueService.addVenueToLoad(venueId: );
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:
extension ViewController: VenueDelegate {
func onGetVenueCompleted(venueId: Int32, venueModel: VenueModel?, online: Bool, venueStyle: VenueStyle?) {
if venueModel == nil {
print("Loading of venue \(venueId) failed!")
}
}
}
Once the venue is loaded successfully, if you are using the addVenueAsync()
method, only the VenueLifecycleDelegate.onVenueAdded()
method will be triggered. If you are using the selectVenueAsync()
method, the VenueSelectionDelegate.onSelectedVenueChanged()
method will also be triggered.
extension ViewController: VenueSelectionDelegate {
func onSelectedVenueChanged(deselectedVenue: Venue?, selectedVenue: Venue?) {
if let venueModel = selectedVenue?.venueModel {
if moveToVenue {
let center = GeoCoordinates(latitude: venueModel.center.latitude,
longitude: venueModel.center.longitude,
altitude: 500.0)
mapView.camera.lookAt(point: center)
}
}
}
}
A Venue
can also be removed from the VenueMap
, which triggers the VenueLifecycleDelegate.onVenueRemoved()
method:
venueEngine.venueMap.removeVenue(venue: venue)
Label Text Preference
You can override the default label text preference for a venue.
Once the VenueEngine
is initialized, a callback is called. From this point on, there is access to the VenueService
. The optional method setLabeltextPreference()
can be called to set the label text preference during rendering. Overriding the default style label text preference provides an opportunity to set the following options as a list where the order defines the preference:
- "OCCUPANT_NAMES"
- "SPACE_NAME"
- "INTERNAL_ADDRESS"
- "SPACE_TYPE_NAME"
- "SPACE_CATEGORY_NAME"
These can be set in any desired order. For example, if the label text preference does not contain "OCCUPANT_NAMES" then it will switch to "SPACE_NAME" and so on, based on the order of the list. Nothing is displayed if no preference is found.
private func onVenueEngineInit() {
let venueMap = venueEngine.venueMap
let venueService = venueEngine.venueService
venueService.addServiceDelegate(self)
venueService.addVenueDelegate(self)
venueMap.addVenueSelectionDelegate(self)
venueEngine.start(callback: {
error, data in if let error = error {
print("Failed to authenticate, reason: " + error.localizedDescription)
}
})
if (hrn != "") {
venueService.setHrn(hrn: hrn)
}
venueService.setLabeltextPreference(labelTextPref: LabelPref)
}
Select Venue Drawings and Levels
A Venue
object allows you to control the state of the venue.
The property Venue.selectedDrawing
allows you to get and set a drawing which is visible on the map. When a new drawing is selected, the VenueDrawingSelectionDelegate.onDrawingSelected()
method is triggered.
The following provides an example of how to select a drawing when an item is clicked in a UITableView
:
extension DrawingSwitcher: UITableViewDelegate {
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let drawingIndex: Int = indexPath.row
if let venue = venueMap?.selectedVenue {
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 is triggered.
The following provides 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) {
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 IndoorMap example app, available on GitHub.
Customize the Style of a Venue
You can change the visual style of VenueGeometry
objects. Geometry style and/or label style objects must be created and provided to the Venue.setCustomStyle()
method:
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)
Select Space by Identifier
The ID of spaces, levels and drawings can be extracted using getIdentifier()
, e.g. for spaces call: spaces.getIdentifier()
. Then, for using those id values, a specific space can be searched in a level or a drawing with getGeometryById(id:)
.
var geometriesID : [String] = [];
var geometries : [VenueGeometry] = [];
for id in geometriesID
{
VenueGeometry geometry = selectedVenue?.getSelectedDrawing().getGeometryById(id);
geometries.append(geometry);
}
geometryStyle = VenueGeometryStyle(
mainColor: selectedColor, outlineColor: selectedOutlineColor, outlineWidth: 1)
labelStyle = VenueLabelStyle(
fillColor: selectedTextColor, outlineColor: selectedTextOutlineColor, outlineWidth: 1, maxFont: 28)
selectedVenue.setCustomStyle(geometries: geometries, style: geometryStyle, labelStyle: labelStyle)
Handle Tap Gestures on a Venue
You can select a venue object by tapping it. First, set the 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
if let position = mapView.viewToGeoCoordinates(viewCoordinates: origin) {
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 let currentMarker = marker {
mapView.mapScene.removeMapMarker(currentMarker)
}
}
func onGeometryPicked(venue: Venue,
geometry: VenueGeometry) {
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)
}
}
}
}
HERE recommends that you 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 showing usage of the map tap event with venues is available in the IndoorMap example app, available on GitHub.