Integrate Indoor Maps

The HERE Indoor Map feature provides the possibility to load, show and interact with private venues on the map. For more information about Here Indoor Map, refer to the Introduction to HERE Indoor Map.

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.

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 Indoor Map 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() {
    // Load a scene from the HERE SDK to render the map with a map scheme.
    mapView.getMapScene().loadScene(MapScheme.NORMAL_DAY, mapError -> {
        if (mapError == null) {
            // Hide extruded buildings layer, so it will not overlaps with venues.
            List<String> mapFeatures = new ArrayList<>();
            mapFeatures.add(MapFeatures.EXTRUDED_BUILDINGS);
            mapView.getMapScene().disableFeatures(mapFeatures);        

            // Create a venue engine object. Once an initialization is done, the callback
            // will be called.
            try {
                venueEngine = new VenueEngine(this ::onVenueEngineInitCompleted);
            } catch (InstantiationErrorException e) {
                Log.e(TAG, "SDK Engine instantiation failed");
                e.printStackTrace();
            }
        } else {
            Log.d(TAG, "Loading map failed: mapError: " + mapError.name());
        }
    });
}

Once the VenueEngine is initialized, a callback 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 callback, all needed listeners can be added. Afterwards, the VenueEngine needs to be started. The platform map catalog HRN must be set once VenueEngine is started.

Note

It is required to set a valid HRN value, otherwise the VenueService will not work - and an error log indicates the missing or invalid HRN value. This link contains more information on how you can get a valid HRN string for your project.

private void onVenueEngineInitCompleted() {
    // Get VenueService and VenueMap objects.
    VenueService service = venueEngine.getVenueService();
    VenueMap venueMap = venueEngine.getVenueMap();

    // Add needed listeners.
    service.add(serviceListener);
    service.add(venueListener);
    venueMap.add(venueSelectionListener);

    // Start VenueEngine. Once authentication is done, the authentication callback
    // will be triggered. After, VenueEngine will start VenueService. Once VenueService
    // is initialized, VenueServiceListener.onInitializationCompleted method will be called.
    venueEngine.start((authenticationError, authenticationData) -> {
        if (authenticationError != null) {
            Log.e(TAG, "Failed to authenticate, reason: " + authenticationError.value);
        }
    });

    // Set platform catalog HRN
    service.setHrn(HRN);
}

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 VenueServiceListener.onInitializationCompleted() method will be called.

// Listener for the VenueService event.
private final VenueServiceListener serviceListener = new VenueServiceListener() {
    @Override
    public void onInitializationCompleted(@NonNull VenueServiceInitStatus result) {
        if (result == VenueServiceInitStatus.ONLINE_SUCCESS) {
            Log.d(TAG, "VenueService initialization is successful.");
        } else {
            Log.e(TAG, "Failed to initialize venue service.");
        }
    }

    @Override
    public void onVenueServiceStopped() {}
};

Load and Show a Venue

The Indoor Map 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.getVenueService().startLoading(/*VENUE_ID_LIST*/);

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

venueEngine.getVenueService().addVenueToLoad(/*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.getVenueMap().selectVenueAsync(/*VENUE_ID*/);
venueEngine.getVenueMap().addVenueAsync(/*VENUE_ID*/);

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

// Listener for the venue loading event
private final VenueListener venueListener = (venueId, venueModel, online, venueStyle) -> {
    if (venueModel == null) {
        Log.e(TAG, "Failed to load the venue: " + venueId);
    }
};

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

// Listener for the venue selection event.
private final VenueSelectionListener venueSelectionListener =
    (deselectedVenue, selectedVenue) -> {
        if (selectedVenue != null) {
            // Move camera to the selected venue.
            GeoCoordinates venueCenter = selectedVenue.getVenueModel().getCenter();
            final double distanceInMeters = 500;
            MapMeasure mapMeasureZoom = new MapMeasure(MapMeasure.Kind.DISTANCE, distanceInMeters);
            mapView.getCamera().lookAt(
                    new GeoCoordinates(venueCenter.latitude, venueCenter.longitude),
                    mapMeasureZoom);
        }
    };

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

venueEngine.getVenueMap().removeVenue(venue);

Label Text Preference

You can override the default label text preference for the venue.

Once the VenueEngine is initialized, a callback will be called. From this point, there is access to the VenueService. The optional method setLabeltextPreference() can be called for setting the label text prererence for rendering. Overriding the default style label text preference provides the opportunity to set the following options as a list where the order defines the preference: "OCCUPANT_NAMES", "SPACE_NAME", "INTERNAL_ADDRESS", "SPACE_TYPE_NAME" and "SPACE_CATEGORY_NAME". These can be set in any desired order. 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 in the list. If no preference is found then nothing will be shown.

private void onVenueEngineInitCompleted() {
    // Get VenueService and VenueMap objects.
    VenueService service = venueEngine.getVenueService();
    VenueMap venueMap = venueEngine.getVenueMap();

    // Add needed listeners.
    service.add(serviceListener);
    service.add(venueListener);
    venueMap.add(venueSelectionListener);

    // Start VenueEngine. Once authentication is done, the authentication callback
    // will be triggered. After, VenueEngine will start VenueService. Once VenueService
    // is initialized, VenueServiceListener.onInitializationCompleted method will be called.
    venueEngine.start((authenticationError, authenticationData) -> {
        if (authenticationError != null) {
            Log.e(TAG, "Failed to authenticate, reason: " + authenticationError.value);
        }
    });

    // Set platform catalog HRN
    service.setHrn(HRN);

    // Set label text preference
    service.setLabeltextPreference(LabelPref);
}

Select Venue Drawings and Levels

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

The methods getSelectedDrawing() and setSelectedDrawing() allow to get and set a drawing which will be visible on the map. If a new drawing is selected, the VenueDrawingSelectionListener.onDrawingSelected() method will be triggered. See an example of how to select a drawing when an item is clicked in a ListView:

@Override
public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
    VenueModel venueModel = venue.getVenueModel();
    // Set the selected drawing when a user clicks on the item in the list.
    venue.setSelectedDrawing(venueModel.getDrawings().get(position));
}

The methods getSelectedLevelIndex() and setSelectedLevelIndex() allows you to get and set a level based on the location in the list of levels. If a new level is selected, the VenueLevelSelectionListener.onLevelSelected() method will be triggered. See an example of how to select a level based on a reversed levels list from ListView:

listView.setOnItemClickListener((parent, view, position, id) -> {
    if (venueMap.getSelectedVenue() != null) {
        // Revers an index, as levels in LevelSwitcher appear in a different order
        venueMap.getSelectedVenue().setSelectedLevelIndex(maxLevelIndex - position);
    }
});

A full example of the UI switchers to control drawings and levels is available in the indoor-map 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.

private final VenueGeometryStyle geometryStyle = new VenueGeometryStyle(
        SELECTED_COLOR, SELECTED_OUTLINE_COLOR, 1);
private final VenueLabelStyle labelStyle = new VenueLabelStyle(
        SELECTED_TEXT_COLOR, SELECTED_TEXT_OUTLINE_COLOR, 1, 28);
ArrayList<VenueGeometry> geometries =
        new ArrayList<>(Collections.singletonList(geometry));
venue.setCustomStyle(geometries, geometryStyle, labelStyle);

Handle Tap Gestures on a Venue

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

mapView.getGestures().setTapListener(tapListener);

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

private final TapListener tapListener = origin -> {
    deselectGeometry();

    // Get geo position of the tap.
    GeoCoordinates position = mapView.viewToGeoCoordinates(origin);
    if (position == null) {
        return;
    }

    VenueMap venueMap = venueEngine.getVenueMap();
    // Get VenueGeometry under the tapped position.
    VenueGeometry geometry = venueMap.getGeometry(position);

    if (geometry != null) {
        // If there is a geometry, put a marker on top of it.
        marker = new MapMarker(position, markerImage, new Anchor2D(0.5f, 1f));
        mapView.getMapScene().addMapMarker(marker);
    } else {
        // If no geometry was tapped, check if there is a not-selected venue under
        // the tapped position. If there is one, select it.
        Venue venue = venueMap.getVenue(position);
        if (venue != null) {
            venueMap.setSelectedVenue(venue);
        }
    }
};

private void deselectGeometry() {
    // If marker is already on the screen, remove it.
    if (marker != null) {
        mapView.getMapScene().removeMapMarker(marker);
    }
}

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

private final VenueSelectionListener venueSelectionListener =
        (deselectedController, selectedController) -> deselectGeometry();

private final VenueDrawingSelectionListener drawingSelectionListener =
        (venue, deselectedController, selectedController) -> deselectGeometry();

private final VenueLevelSelectionListener levelChangeListener =
        (venue, drawing, oldLevel, newLevel) -> deselectGeometry();

void setVenueMap(VenueMap venueMap) {
    if (this.venueMap == venueMap) {
        return;
    }

    // Remove old venue map listeners.
    removeListeners();
    this.venueMap = venueMap;

    if (this.venueMap != null) {
        this.venueMap.add(venueSelectionListener);
        this.venueMap.add(drawingSelectionListener);
        this.venueMap.add(levelChangeListener);
        deselectGeometry();
    }
}

private void removeListeners() {

    if (this.venueMap != null) {
        this.venueMap.remove(venueSelectionListener);
        this.venueMap.remove(drawingSelectionListener);
        this.venueMap.remove(levelChangeListener);
    }
}

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

results matching ""

    No results matching ""