HERE Android SDK Premium Edition V3.6 Developer's Guide
User Manual: Pdf
Open the PDF directly: View PDF .
Page Count: 2351
HERE Android SDK
Developer's Guide
Premium Edition Version 3.6
HERE Android SDK Developer's Guide
► Notices
Important Information
Notices
Topics:
•
Legal Notices
•
Service Support
•
This section contains document notices.
Document Information
2
HERE Android SDK Developer's Guide
► Notices
Legal Notices
© 2017 HERE Global B.V. and its Affiliate(s). All rights reserved.
This material, including documentation and any related computer programs, is protected by copyright
controlled by HERE. All rights are reserved. Copying, including reproducing, storing, adapting or translating,
any or all of this material requires the prior written consent of HERE. This material also contains confidential
information, which may not be disclosed to others without the prior written consent of HERE.
Trademark Acknowledgements
HERE is trademark or registered trademark of HERE Global B.V.
Other product and company names mentioned herein may be trademarks or trade names of their respective
owners.
Disclaimer
This content is provided "as-is" and without warranties of any kind, either express or implied, including, but
not limited to, the implied warranties of merchantability, fitness for a particular purpose, satisfactory quality
and non-infringement. HERE does not warrant that the content is error free and HERE does not warrant or
make any representations regarding the quality, correctness, accuracy, or reliability of the content. You
should therefore verify any information contained in the content before acting on it.
To the furthest extent permitted by law, under no circumstances, including without limitation the negligence
of HERE, shall HERE be liable for any damages, including, without limitation, direct, special, indirect, punitive,
consequential, exemplary and/ or incidental damages that result from the use or application of this content,
even if HERE or an authorized representative has been advised of the possibility of such damages.
3
HERE Android SDK Developer's Guide
► Notices
Document Information
Product
Name:
HERE Android SDK
Version:
Premium Edition Version 3.6
Document
Name:
HERE Android SDK Developer's Guide
ID:
063f807-1513029408-3db914db
Status:
FINAL
Date:
2017-Dec-11, 22:11 (GMT)
Service Support
If you need assistance with this or any other HERE product, select one of the following options.
•
•
•
•
•
If you have a HERE representative, contact them when you have questions/issues.
If you manage your applications and accounts through developer.here.com, log into your account
and check the pages on the SLA report or API Health. If this does not clarify the issue, then check
stackoverflow.com/questions/tagged/here-api.
If you have an evaluation plan, check stackoverflow.com/questions/tagged/here-api.
If you have questions about billing or your account, Contact Us.
If you have purchased your plan/product from a HERE reseller, contact your reseller.
4
HERE Android SDK Developer's Guide
► Contents
Contents
Chapter 1: Overview............................................................................................................................................................................8
What is the HERE Android SDK?................................................................................................................................................................. 9
Feature List............................................................................................................................................................................................................. 9
Legal Requirements..........................................................................................................................................................................................11
Chapter 2: Quick Start.................................................................................................................................................................. 12
Run the Sample Application....................................................................................................................................................................... 13
Chapter 3: User Guide...................................................................................................................................................................16
System Requirements..................................................................................................................................................................................... 17
Authenticating Applications......................................................................................................................................................................... 17
Examples on GitHub........................................................................................................................................................................................ 18
HERE Map Data................................................................................................................................................................................................... 19
Embedding the Map Service....................................................................................................................................................................... 20
Maps......................................................................................................................................................................................................................... 22
Gestures.............................................................................................................................................................................................................. 27
Map Schemes.................................................................................................................................................................................................. 30
MapEngine Class............................................................................................................................................................................................ 33
Objects and Interaction.............................................................................................................................................................................34
Marker Clustering.......................................................................................................................................................................................... 46
Traffic Information....................................................................................................................................................................................... 49
Offline Maps (MapLoader)........................................................................................................................................................................ 52
Shared Map Resources.............................................................................................................................................................................. 54
Traffic History................................................................................................................................................................................................. 54
3D Landmarks................................................................................................................................................................................................. 56
Extruded Buildings........................................................................................................................................................................................57
Custom Raster Tiles.................................................................................................................................................................................... 60
Mobile Asset Management....................................................................................................................................................................... 63
Fleet Connectivity......................................................................................................................................................................................... 66
Transit................................................................................................................................................................................................................. 68
Map Customization....................................................................................................................................................................................... 78
Positioning............................................................................................................................................................................................................ 81
5
HERE Android SDK Developer's Guide
► Contents
Basic Positioning........................................................................................................................................................................................... 81
Advanced Positioning by HERE............................................................................................................................................................. 85
Map Matching.................................................................................................................................................................................................. 93
Directions.............................................................................................................................................................................................................. 95
Car and Pedestrian Routing.................................................................................................................................................................... 95
Bicycle Routing............................................................................................................................................................................................... 99
Truck Routing................................................................................................................................................................................................ 100
Transit Routing............................................................................................................................................................................................. 101
Urban Mobility Routing........................................................................................................................................................................... 103
Indoor Venue Routing.............................................................................................................................................................................. 107
Offline Routing............................................................................................................................................................................................. 107
Route Consumption................................................................................................................................................................................... 107
Route Serialization..................................................................................................................................................................................... 109
Places.....................................................................................................................................................................................................................110
Geocoding and Reverse Geocoding................................................................................................................................................... 111
Search and Discovery................................................................................................................................................................................113
Offline Search............................................................................................................................................................................................... 120
Custom Locations and Geometries....................................................................................................................................................... 121
Custom Location Extension 2............................................................................................................................................................... 121
Using CLE2 Offline...................................................................................................................................................................................... 126
Toll Cost Extension........................................................................................................................................................................................ 132
Street-Level........................................................................................................................................................................................................ 135
Street-Level Imagery................................................................................................................................................................................. 135
Street-Level Objects.................................................................................................................................................................................. 141
Turn-by-Turn Navigation.............................................................................................................................................................................145
Navigation Events....................................................................................................................................................................................... 149
Voice Instructions....................................................................................................................................................................................... 152
Traffic-Aware Navigation......................................................................................................................................................................... 155
Audio Management........................................................................................................................................................................................ 156
Urban Mobility.................................................................................................................................................................................................. 157
Coverage Search.......................................................................................................................................................................................... 158
Transit Station Search............................................................................................................................................................................. 160
Next Nearby Departures..........................................................................................................................................................................162
3D Venues........................................................................................................................................................................................................... 164
Venue Zoom................................................................................................................................................................................................... 172
Private Venues.............................................................................................................................................................................................. 174
Venue Routing............................................................................................................................................................................................... 175
LiveSight............................................................................................................................................................................................................ 180
6
HERE Android SDK Developer's Guide
► Contents
Starting and Stopping LiveSight......................................................................................................................................................... 182
Adding and Interacting with LiveSight Content......................................................................................................................... 183
Customizing LiveSight.............................................................................................................................................................................. 188
Platform Data Extension............................................................................................................................................................................. 192
Natural Language Processing (NLP)..................................................................................................................................................... 199
Chapter 4: Supplemental Information................................................................................................................203
Creating a Simple Application................................................................................................................................................................ 204
Requesting Android Permissions........................................................................................................................................................... 210
Android Emulator Support......................................................................................................................................................................... 212
Adding a MapFragment at Runtime...................................................................................................................................................... 212
3D Venues FAQ................................................................................................................................................................................................. 213
Size Management............................................................................................................................................................................................ 218
Map Rendering Order................................................................................................................................................................................... 219
Development Tips........................................................................................................................................................................................... 221
Signpost Parsing............................................................................................................................................................................................. 224
Chapter 5: Coverage Information............................................................................................................................... 227
Downloadable Maps by Country/Region........................................................................................................................................... 228
Map Label Languages................................................................................................................................................................................... 232
Navigation Voices........................................................................................................................................................................................... 234
Safety Camera Coverage............................................................................................................................................................................. 236
Chapter 6: API Reference......................................................................................................................................................239
7
HERE Android SDK Developer's Guide
► Overview
Chapter 1
Overview
Topics:
•
What is the HERE Android S...
•
Legal Requirements
•
Feature List
The articles that follow introduce the HERE Android SDK, explain
essential concepts and describe the common use cases it supports.
8
HERE Android SDK Developer's Guide
► Overview
What is the HERE Android SDK?
The HERE Android SDK provides a set of programming interfaces that enable developers to build an
immersive, geographically-aware Android applications by leveraging a powerful and flexible mapping
platform. Through this SDK, developers can add rich location features such as routing, interactive maps,
and global place search to their applications. The powerful client-side HERE Android SDK also includes
a sophisticated engine for rendering map data and calculated routes. In addition to dynamic map data
downloads, the SDK also supports offline maps using previously cached map data or downloaded map
packages.
Feature List
The main features offered by the HERE Android SDK are listed below.
Not all features are enabled by default. The features available to you are determined based on your business
plan.
Note: The HERE Android SDK is designed for standalone Android APK development. Using the HERE
SDK for platform-embedded app development (apps that ship with the device ROM) is not supported.
Mapping:
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
Dynamically download vector maps for more than 190 countries in over 60 languages
Preload maps for offline usage
Map styles: normal street map, satellite map, transit map, and more
Textured 3D landmarks
Street-level imagery
Touch gestures (including rotate, tilt, pan, flick, and pinch zoom)
Overlay objects on the map such as polylines, polygons, icons, and routes
Map marker clusters
Overlay 3D map objects
Overlay custom raster tiles on the map (for example, to display heat maps)
Ability to render raster tiles and map objects interleaved within different map layers
3D venue (indoor) maps
Show real-time traffic flow and incidents
3D Extruded Buildings
Transit object interaction
Search:
•
•
•
Search through a broad set of geographical content across the globe, (including streets, address points,
and categorized places)
Search for a specific place or explore by categories
Access rich details for a Point of Interest from third-party content sources (including images, ratings,
reviews, and editorials)
9
HERE Android SDK Developer's Guide
► Overview
•
•
Perform geocoding and reverse geocoding lookups
Offline Places search, Geocoding, Reverse Geocoding
Directions:
•
•
•
•
•
•
•
•
•
Online Car, Public Transit, Bicylce, Truck, and Pedestrian Route Directions
Routing options (Highways, Tolls, Fastest etc.)
Specify preferred route type (fastest or shortest) and route options (such as avoiding toll roads,
motorways, and parks)
Alternate routes
Saving a route as a file
Offline route calculation
Driving directions with traffic taken into account
Public Transit directions using online timetables
Indoor routing
Turn-by-turn Navigation:
•
•
•
•
•
•
•
•
Online turn-by-turn navigation for pedestrian, car, and truck routes
Offline turn-by-turn navigation for pedestrian and car routes
Natural-sounding guidance instructions, such as "turn left at the gas station" and "at the next light, turn
right"
Recorded audio and speech synthesis voices in a variety of languages. For a list of the available
languages, see the Developer's Guide.
Approximate user coordinates to the nearest road or navigation route
Approximate user tunnel position, even when there is no GPS signal
Dynamic information including signposts, the driver's current street lane, and speed
Realistic previews of certain junctions and lanes
HERE Positioning:
•
Wi-Fi, Cellular, and BT network-based positioning, including:
▫
▫
▫
▫
Online Outdoor positioning
Online-Offline Hybrid Outdoor positioning
Indoor positioning
Private Indoor positioning
LiveSight:
•
•
•
•
•
•
Track the position of a device in space and animate view accordingly
Seamless transitions from Map to LiveSight and back again
Gesture support allows the user to interact with content, custom gestures can also be defined
Content transitions and interaction are animated using hardware acceleration
"Radar" UI support, which provides the user with more context regarding their position relative to
content
Highly configurable LiveSight engine allows the user experience to be customized
Other Features:
•
Custom location and custom location geometry search
10
HERE Android SDK Developer's Guide
► Overview
•
•
•
•
•
•
Support for fleet dispatching and connectivity
Urban Mobility: transit station and departure information search
Map information for fleet vehicles and trucks
Congestion toll zones and the typical traffic patterns for a given time of the week
Ability to retrieve the toll cost of a route
Natural language interface (English only) beta
Legal Requirements
In addition to the applicable terms and conditions under which you have licensed the SDK, the following
shall apply.
Components of the HERE SDK collect certain information from your application. Such information includes
access credentials ( licenseKey, App_Id and App_Code – see also Authenticating Applications on page
17) and the types of features utilized by your application when used by end users. The information does
not identify an individual end user. However, your application's privacy policy must disclose to the end users
that you have licensed products and services from HERE and that such information is collected from your
application as it is being used by end users and that HERE collects and processes such information from the
application.
11
HERE Android SDK Developer's Guide
► Quick Start
Chapter 2
Quick Start
Topics:
•
Run the Sample Application
The example in this section provide information to help you start
using the HERE Android SDK.
12
HERE Android SDK Developer's Guide
► Quick Start
Run the Sample Application
This tutorial contains instructions on how to run the basic sample application to render a map on an
Android device. This tutorial assumes that you are using the Android Studio development environment and a
supported Android device. For more details, see System Requirements on page 17.
Development tasks for this basic application include:
•
Check HERE Credentials.
•
Import the necessary resources into the project.
•
Open the sample project in Android Studio.
Note: HERE Android SDK is now distributed as an .AAR instead of a .JAR, and the basic sample app is
also updated. If you are upgrading your existing project from an older version of the HERE SDK, be
sure to modify the project by following the instructions in Development Tips on page 221.
Check Credentials
This sample application is already configured with a set of HERE SDK credentials for evaluation
purposes. You can check these credentials by opening the BasicMapSolution/app/src/main/
AndroidManifest.xml file and inspecting the following tags:
•
•
•
"/>
Important: Typically, before developing a new HERE SDK application, you need to acquire a set of
credentials by registering your application on http://developer.here.com. Each application requires a
unique set of credentials. When you register your app, the registered bundle identifier must match the
package name in your project.
Open the Sample Project in Android Studio
The next task before running the sample HERE SDK project is to locate the project folder and open it in
Android Studio as follows:
1.
2.
In the Welcome to Android Studio dialogue box, select Open an existing Android Studio project.
In the Open File or Project dialogue box, select the BasicMapSolution folder from your file system and
click OK. The main Android Studio project window should appear with an error "Error: Failed to
resolve: :HERE-sdk:" in the Messages pane.
Import the HERE SDK Android Archive
The HERE Android SDK library is shipped as an Android Archive (.AAR) file. You can import this library by
doing the following:
1.
On the View menu, click Tool Windows > Project.
13
HERE Android SDK Developer's Guide
► Quick Start
2.
3.
4.
5.
A few tabs are available in this tool window. Select the Project tab to show a file system view of the
application structure.
Right-click on the app folder and select New > Directory to create a new folder. Use libs as the new
folder name.
In your operating system's file system, navigate to the extracted HERE SDK directory. Copy the HEREsdk.aar file and paste it into the newly created libs directory.
Open the build.gradle file under the app folder and ensure the following entries are present:
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
compile(name:'HERE-sdk', ext:'aar')
// Depending on your specific project configuration, you may have other entries here.
}
6.
Optional: To enable quick Javadoc reference within your Android Studio environment, scroll down to the
External Libraries section, right-click on HERE-sdk, and then select Library Properties. Click the + button
and locate HERE-sdk-javadoc.jar from the HERE SDK package.
Note: You can also import HERE-sdk.aar by using the menu, selecting File > Project Structure... and
clicking the "+" button. If you use this method, ensure that HERE-sdk is listed properly under the app
Module Dependencies.
Figure 1: Location of the .AAR file
14
HERE Android SDK Developer's Guide
► Quick Start
Run the Project
You can run your simple application by pressing the key combination Shift + F10 (or Ctrl + R on macOS) from
within Android Studio. The application renders a map retrieved from the HERE servers. When you are running
your application on a device, make sure a data connection is enabled.
Note: For detailed instructions on how to create a new HERE SDK app, see Create a Simple App Using
the HERE SDK on page 204
15
HERE Android SDK Developer's Guide
► User Guide
Chapter 3
User Guide
Topics:
•
System Requirements
•
Examples on GitHub
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
•
Authenticating Application...
The articles in this section provide a guide to using the HERE
Android SDK.
HERE Map Data
Embedding the Map Service
Maps
Positioning
Directions
Places
Custom Locations and Geome...
Toll Cost Extension
Street-Level
Turn-by-Turn Navigation fo...
Audio Management
Urban Mobility
3D Venues
LiveSight
Platform Data Extension
Natural Language Processin...
16
HERE Android SDK Developer's Guide
► User Guide
System Requirements
HERE Android SDK is designed and tested with Android phones and tablets in mind. SDK performance will
vary between devices, since it is primarily determined by CPU, GPU, and display resolution. Currently, Nexus 5
is a suitable reference for a device which delivers acceptable SDK performance. If your target device is not a
phone or tablet, contact us to discuss performance requirements.
•
•
•
•
•
•
Android 4.1.x "Jelly Bean" (API Level 16) or higher as the application Minimum API Level
(android:minSdkVersion).
Apps should be developed using Android Studio 2.3.2 or above
For apps that implement basic use cases such as map rendering, search, and routing, a minimum of
60MB of memory (RAM) is recommended to be available. More complex use cases, such as turn-by-turn
navigation, require more memory.
A minimum of 25MB per application should be made available for the storage of the HERE SDK libraries
A minimum of 50MB should be made available for the storage of map data
Data connectivity (WiFi or Cellular) is required to download map data and ensure map data is updated
when new versions are made available.
Note: HERE Android SDK does not support x86 Android devices.
Authenticating Applications
Developers using the HERE SDK with their app are required to register for a set of HERE credentials and to
specify these credentials (App_Id, App_Code, and licenseKey) in their app's Android manifest XML file.
Failure to do so results in blocked access to certain features and degradation in the quality of other services.
To obtain these credentials, visit the developer portal at https://developer.here.com/?create=Evaluation and
register for a free Evaluation license. Once your project is created, you can generate these credentials on
your Project Details page. If you already have a commercial (public or business) plan, you can also retrieve
these credentials from your Project Details page.
Note: Credentials are unique to your application's package namespace. Do not reuse credentials
across multiple applications.
Important: When switching from an Evaluation plan to a commercial plan, new HERE credentials must
be taken into use. Applications must not be commercially released (such as submitted to a store)
using a license key obtained as part of an Evaluation plan. Once you have upgraded to a commerical
license, you need to obtain your new license key on the Project Details page, add it to your app, and
re-deploy your app. Please contact HERE for further information.
Adding Credentials to the Manifest
You can add your HERE credentials as attributes to the AndroidManifest.xml file as
follows:
1.
In your development environment, double-click your project's AndroidManifest.xml file and ensure
that you are viewing the file in text editor mode.
17
HERE Android SDK Developer's Guide
► User Guide
2.
Within the block of tags, add the following markup directly beneath
the tag:
3.
Replace {YOUR_APP_ID}, {YOUR_APP_CODE} and {YOUR_LICENSE_KEY} with the appropriate
credentials for your application.
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android:name="android.permission.ACCESS_NETWORK_STATE" />
android:name="android.permission.INTERNET" />
android:name="android.permission.ACCESS_WIFI_STATE" />
Examples on GitHub
You can find more HERE SDK sample projects on GitHub: https://www.github.com/heremaps
18
HERE Android SDK Developer's Guide
► User Guide
HERE Map Data
Much of the functionality offered through the HERE SDK depends on HERE Map Data being downloaded and
cached on the device. This section describes different approaches that you can take to manage map data
download.
Passive Approach
The passive approach is where you allow the SDK to download map data as needed. A typical example is
where a user is panning the map, and this triggers on-demand download of the needed map data to render
the map.
Map data downloaded in this way is cached permanently and may be used for offline operation, in cases
where a network connection is not available or not desired, such as when the device is in roaming mode.
However, there is no way for you to know if sufficient data has been downloaded to enable all offline
operations, such as offline search or routing.
Active Approach
The active approach is where you explicitly preload map data. You do this by selecting from a list of
map packages. A map package may be a state (such as California), region, or a country (such as England).
Preloading map data guarantees that offline operations are possible in cases where a network connection is
not available or not desired.
Note: If map data is needed but not available in one of the preloaded map packages (for example,
if a user panned the map to a new country), the SDK dynamically downloads the needed data. This
means that the map data cache on the device contains a mixture of preloaded map packages and ondemand downloaded map data.
Keep Data Up-to-Date
Irrespective of which approach your app supports, it is important that you are aware of your responsibility
to ensure that your app is using the latest map data release. HERE releases quarterly (every three months)
updates to the map data. You must use SDK APIs to check for map data updates, and if updates are available,
trigger the update. It is recommended that your app perform such a check every time it starts. For more
information on how to check for map data updates, see the API Reference for the MapLoader class.
Important: Some SDK features may return errors if the map data is more than six months old.
Note: Incremental or patch updates are supported when upgrading from one version to the next
version, helping to reduce the amount of data downloaded. Skipping versions may result in a full
update and a large amount of data downloaded.
19
HERE Android SDK Developer's Guide
► User Guide
Embedding the Map Service
Map Service is an Android service that facilitates the use of a shared disk cache among applications that use
the HERE SDK. This service must be embedded and deployed with your HERE-enabled application; otherwise,
the MISSING_SERVICE error code is returned via the onEngineInitializationCompleted() callback.
To embed Map Service, add the following lines inside the section in your
AndroidManifest.xml file:
Incompatibility with Older Versions
Starting in v3.4, the HERE SDK is no longer compatible with pre-3.4 versions of the HERE SDK disk cache. Map
data downloaded on pre-3.4 versions of the HERE SDK cannot be used on v3.4 or later.
If your app uses the shared disk cache settings as described above, be aware of the following:
•
The required ... snippet, as described in the previous section, has changed.
•
In the case where a user has multiple HERE SDK apps on their system, pre-3.4 apps share one cache,
while post-3.4 apps share another.
•
When your users update their pre-3.4 HERE SDK apps to a newer version, their previously-downloaded
data will be unavailable. This occurs whether the app was automatically or manually updated.
If your app uses the isolated disk cache setting as described in the next section, be aware of the following:
•
•
When your users update their pre-3.4 HERE SDK apps to a newer version, their previously-downloaded
data will be unavailable. This occurs whether the app was automatically or manually updated.
You can avoid this issue by upgrading the pre-3.4 cache using the
DiskCacheUtility.migrate(String sourcePath, String destPath) method. This method
takes the same path value as setIsolatedDiskCacheRootPath(String, String), and it must be
run before MapEngine is initialized for the first time.
Note: migrate(String, String) is marked as deprecated because it is offered temporarily to
assist with the transition. It will be removed in a future release.
Using an Isolated Map Disk Cache with the Map Service
The HERE SDK supports an isolated disk cache. This allows you to set the disk cache to another location, such
as an SD Card.
Note:
20
HERE Android SDK Developer's Guide
► User Guide
•
•
•
•
Migrating the disk cache contents from one location to another is not supported.
If you are using an SD card, ensure the SD card is always present to avoid any unexpected
behavior.
You should only delete the map data cache when the app is in its early start-up stages, before
any HERE SDK calls. Otherwise, map data corruption and unexpected app errors can occur.
If you plan to support changing the storage location, such as switching between internal storage
and an SD card, be aware that this requires an app restart, as the storage location switch must
be done before initializing MapEngine or MapFragment. Also, your manifest entry for the
MapService must not contain the process attribute, so that the MapService runs in the same
process as your app. Doing so ensures it that the service is shut down properly when the app
restarts (for example, using System.exit()), and that the disk cache location change can take
effect.
The first step to use an isolated disk cache is to edit the AndroidManifest.xml with the following,
providing the service label and intent name with your custom values.
Note: Always provide custom values for {YOUR_LABEL_NAME} and {YOUR_INTENT_NAME} when you
are using an isolated disk cache. Do not reuse the HERE SDK defaults.
After editing AndroidManifest.xml, add a call to
MapSettings.setIsolatedDiskCacheRootPath(String path, String intent) with the desired
cache location and the custom intent name. This call should occur before MapEngine initialization. For
example, if you are modifying the application from the sample tutorial app, you can add the call in the
BasicMapActivity.java file before mapFragment.init().
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Search for the map fragment to finish setup by calling init().
mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.mapfragment);
boolean success = com.here.android.mpa.common.MapSettings.setIsolatedDiskCacheRootPath(
"/sdcard/foo/myservice", "{YOUR_INTENT_NAME}");
}
if (!success) {
// Setting the isolated disk cache was not successful, please check if the path is valid and
// ensure that it does not match the default location
// (getExternalStorageDirectory()/.here-maps).
// Also, ensure the provided intent name does not match the default intent name.
} else {
mapFragment.init(new OnEngineInitListener() {
...
21
HERE Android SDK Developer's Guide
► User Guide
Maps
The core feature of the HERE Android SDK is Maps. The key concepts covered in this section include adding
a map to an Android application, changing the location displayed by the map and its various properties.
The classes covered include MapFragment and Map. MapFragment and Map are parts of a Model-View-
Controller (MVC) pattern where the Model is the Map, and the View is the MapFragment. The MapFragment
is a standard Android Fragment derived component. You can create a controller class to coordinate all
interactions using custom logic.
The first step to integrate a map into an application is to insert a MapFragment to the view layout of the
application. This is accomplished by adding com.here.android.mpa.mapping.MapFragment to the
Android XML layout file as follows.
The MapFragment class handles all user interactions such as panning, tapping or pinching, as well as other
standard HERE SDK touch gestures documented in MapGestures.
Initializing MapFragment
After adding the MapFragment to the layout, the fragment must be initialized. The
MapFragment initialization is processed asynchronously. During initialization, the MapEngine
is initialized to create an instance of Map that is associated with the MapFragment. The
MapFragment.init(OnEngineInitListener) method takes in an OnEngineInitListener input
parameter to signal the caller when initialization is completed and if it was successful. The MapFragment
also initializes a MapEngine instance and creates a Map object associated with the MapFragment. The
following code illustrates the basic initialization flow when an Activity is created.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Search for the Map Fragment
final MapFragment mapFragment = (MapFragment)
getFragmentManager().findFragmentById(R.id.mapfragment);
// initialize the Map Fragment and
// retrieve the map that is associated to the fragment
mapFragment.init(new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted(
OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// now the map is ready to be used
Map map = mapFragment.getMap();
// ...
22
HERE Android SDK Developer's Guide
► User Guide
} else {
System.out.println("ERROR: Cannot initialize MapFragment");
}
}
}
});
Note: For performance reasons, com.here.android.mpa.mapping.MapFragment has set
Fragment.setRetainInstance(boolean) to true, and therefore onCreate(Bundle) is not
called again when Activity is re-created (for example, after a zoom-level change).
Note: The MapFragment class provides an asynchronous method,
getScreenCapture(OnScreenCaptureListener), for creating map snapshots of
the currently visible MapFragment area. When a snapshot has been created, an event
callback to OnScreenCaptureListener occurs, and the screenshot is provided as an
android.graphics.Bitmap object. This method of screen capture only works if the view is in
the foreground and it is rendering. If a background or viewless screen capture is required, use
MapOffscreenRenderer.
Working with Map
Once the MapFragment is initialized, you get the Map associated with the MapFragment through
MapFragment.getMap(). The Map class represents the virtual model of the world in a digital format. Key
attributes of the Map include the center GeoCoordinate, zoom level, orientation, and tilt. For example, the
following code snippet illustrates how to use Map.setCenter(GeoCoordinate, Map.Animation) to
render the Map at Vancouver, Canada.
// map fragment has been successfully initialized
...
// now the map is ready to be used
Map map = mapFragment.getMap();
// Set the map center to Vancouver, Canada.
map.setCenter(new GeoCoordinate(49.196261,
-123.004773), Map.Animation.NONE);
...
In the preceding code:
•
•
The GeoCoordinate for the map center is created by a call to the new GeoCoordinate(double,
double) constructor.
When setting the center of a map, there is an option either to animate the change or to suppress
animation by passing the constant Map.Animation.NONE as the relevant parameter.
Map Center , Tilt, Orientation, and Zoom
Here are examples of setting and getting Map attributes:
Map Center
The center of the Map is a GeoCoordinate location that your Map is focused on. You can move a Map by
redefining its center GeoCoordinate:
// Move the map to London.
map.setCenter(new GeoCoordinate(51.51,-0.11),
Map.Animation.NONE );
23
HERE Android SDK Developer's Guide
► User Guide
// Get the current center of the Map
GeoCoordinate coordinate = map.getCenter();
Zoom Level
The size of the geographical area displayed by Map can be controlled by changing the zoom level. The zoom
level ranges from getMinZoomLevel() to getMaxZoomLevel(), with the minimum value displaying the
entire world. The following code sets the zoom level to the median zoom level.
// Get the maximum,minimum zoom level.
double maxZoom = map.getMaxZoomLevel();
double minZoom = map.getMinZoomLevel();
// Set the zoom level to the median (10).
map.setZoomLevel((maxZoom + minZoom)/2);
// Get the zoom level back
double zoom = map.getZoomLevel();
Orientation
The map can be orientated in any direction. By default, the orientation is in a true North position. The
following code changes the orientation to South-up.
// Rotate 180 degrees.
map.setOrientation(180);
// Get the orientation, should be 180.
float orientation = map.getOrientation();
Tilt
The map can be tilted and rendered in a three-dimensional perspective. By default, the tilt is completely
flat. You can retrieve the minimum and maximum possible tilt values by calling getMinTilt() and
getMaxTilt()
// Set the tilt to 45 degrees
map.setTilt(45);
// Get the tilt
float tilt = map.getTilt();
Animations
The map supports three types of animations when changing attributes:
•
Map.Animation.NONE
•
Map.Animation.BOW
•
Map.Animation.LINEAR
// Move to Vancouver using bow animation
map.setCenter(new GeoCoordinate(49.0,-123.0),
Map.Animation.LINEAR);
Note: If the map changes size or the app comes to the foreground while Map.Animation.LINEAR or
Map.Animation.BOW is being used in a Map attribute setter method, then the animation aborts, and
the transition appears to fail. To avoid this behavior, use the Map.Animation.NONE animation type
or wait until the map is stable before performing the transition operation.
24
HERE Android SDK Developer's Guide
► User Guide
Setting Multiple Attributes
Map.setCenter(GeoCoordinate point, Animation animation, double level, float
orientation, float tilt) is an extended API provided for changing one or more attributes
at the same time. The zoom level, tilt and perspective can be preserved and unchanged using
Map.MOVE_PRESERVE_ZOOM_LEVEL, Map.MOVE_PRESERVE_ORIENTATION, Map.MOVE_PRESERVE_TILT
respectively.
// Move to Vancouver using a bow animation, zoom level 17, 180
//degree orientation and 45 degree tilt.
map.setCenter(new GeoCoordinate(49.0,-123.0),
Map.Animation.BOW, 17.0d, 180, 45);
Map Projection Mode
By default, the map is set to a globe projection mode. You can change it to use Mercator projection by calling
setProjectionMode(Projection). For example:
map.setProjectionMode(Map.Projection.MERCATOR);
It may be useful to set the projection modes when you need to predictably display certain types of map
information, such as custom raster tiles.
Figure 2: Globe Projection
Figure 3: Mercator Projection
MapState and Listening for Map Transform Events
MapState is an object that is a composite of the tilt, orientation, zoom level and center map attributes. Your
application can listen for updates to the MapState by using an OnTransformListener.
Map transform events are triggered by any operation that causes the MapState to change. These operations
include user interaction (for example, map gestures) as well as programmatic calls to the Map (for example,
map.setCenter(GeoCoordinate, MapAnimation)). The onMapTransformStart() method is called
before MapState begins to change, while the onMapTransformEnd(MapState) method is called after the
MapState returns to a steady value. Because of this, there can be a significant amount of time between the
two callbacks in cases such as animated map movement events and continuous user interaction.
The following code is an example of registering an OnTransformListener to listen for map transform
events:
map.addTransformListener(new OnTransformListener() {
25
HERE Android SDK Developer's Guide
► User Guide
});
@Override
public void onMapTransformStart() {
// map transform is about to start
}
@Override
public void onMapTransformEnd(MapState mapState) {
// map transform has come to an end
}
If you need to update UI widgets as the MapState changes between these two callbacks, the recommended
approach is to trigger a Runnable when onMapTransformStart() is called, which periodically checks (at
no more than 30 frames per second) the current map state via map.getMapState() and updates the UI
widgets accordingly. This Runnable can then be canceled upon a call to onMapTransformEnd(MapState).
An Android Handler can be used to trigger these Runnable objects.
Note: Do not update UI widgets in MapRenderListener.onPostDraw(boolean, long), as this
method is frequently called.
Pedestrian Features
By default, icons that indicate pedestrian access features (such as stairs or escalators)
are not displayed on the map. To display a pedestrian feature on the map view, call the
Map.setPedestrianFeaturesVisible(EnumSet) method with the desired set of PedestrianFeature.
To find out whether a feature type is enabled, call the Map.getPedestrianFeaturesVisible() method.
Figure 4: Pedestrian Feature Icons
Safety Spots
Speed cameras and traffic-light cameras are also known as safety spots in the HERE SDK. Similar to
pedestrian features, icons that indicate safety spots are not displayed on the map by default. To display
safety spots, set the Map.setSafetySpotsVisible(boolean) to true. To find out whether safety spots
are enabled, call the areSafetySpotsVisible() method.
Individual safety spots icons can be selected on the map by using SafetySpotObject, which is a subclass
of MapProxyObject. Each SafetySpotObject contains a reference to a corresponding SafetySpotInfo
26
HERE Android SDK Developer's Guide
► User Guide
object that contains the location and other properties about the camera. For more information on selecting
map proxy objects, refer to Objects and Interaction on page 34.
Map Gestures
The MapGesture interface encapsulates all user interactions and touch gestures supported by the
HERE Android SDK. The MapGesture associated with a particular fragment can be retrieved from
MapFragment.getMapGesture(). The default behavior of the map for each gesture type may be used asis, supplemented, or replaced entirely. The following table is a summary of the available gestures and their
default behavior.
To zoom the map in a fixed amount, tap the screen twice with one finger
To zoom out a fixed amount, tap the screen with two fingers
To move the map, press and hold one finger to the screen, and move it in any direction.
To tilt the map, press and hold two fingers to the screen, and move them in a vertical direction. No behavior is
predefined for other directions.
To pan the map with momentum, press and swipe one finger on the screen. The map continues to move in the
same direction, and gradually slows to a stop.
To zoom in or out continuously, press and hold two fingers to the screen, and increase or decrease the
distance between them
27
HERE Android SDK Developer's Guide
► User Guide
To rotate the map, press and hold two fingers to the screen, and rotate them together in a circle
Tap the screen with one finger. This gesture does not have a predefined map action.
Press and hold one finger to the screen. This gesture does not have a predefined map action.
The OnGestureListener Interface
The OnGestureListener interface represents a listener to provide notification upon completion of a Map
gesture event such as a single tap on a map.
For example, you can create a new OnGestureListener(), as illustrated below.
// Map gesture listener
private class MyOnGestureListener implements OnGestureListener {
@Override
public void onPanStart() {
}
@Override
public void onPanEnd() {
}
@Override
public void onMultiFingerManipulationStart() {
}
@Override
public void onMultiFingerManipulationEnd() {
}
@Override
public boolean onMapObjectsSelected(List objects) {
return false;
}
@Override
public boolean onTapEvent(PointF p) {
return false;
}
@Override
public boolean onDoubleTapEvent(PointF p) {
return false;
28
HERE Android SDK Developer's Guide
► User Guide
}
@Override
public void onPinchLocked() {
}
@Override
public boolean onPinchZoomEvent(float scaleFactor, PointF p) {
return false;
}
@Override
public void onRotateLocked() {
}
@Override
public boolean onRotateEvent(float rotateAngle) {
return false;
}
@Override
public boolean onTiltEvent(float angle) {
return false;
}
@Override
public boolean onLongPressEvent(PointF p) {
return false;
}
@Override
public void onLongPressRelease() {
}
}
@Override
public boolean onTwoFingerTapEvent(PointF p) {
return false;
}
Note: The OnGestureListener methods that mention "rotate" and "tilt", such as
onRotateEvent(float), are not supported. They are only defined here to maintain compatibility
with the Premium Edition of the HERE SDK.
To add the listener to your map, include a call to addOnGestureListener(OnGestureListener) after
the map fragment has been successfully initialized as follows:
...
mapFragment.init(new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted(OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// map fragment has been successfully initialized
mapFragment.getMapGesture().addOnGestureListener(new MyOnGestureListener());
}
}
});
...
Note: After you add an OnGestureListener to an application, remember to call
removeOnGestureListener(OnGestureListener) when you no longer need to listen for map
events to free up application resources.
The default implementation of a OnGestureListener does not affect any of the standard HERE SDK touch
gestures. Each method within the MyOnGestureListener class returns a value of false, which stipulates
29
HERE Android SDK Developer's Guide
► User Guide
that the application should not override the underlying action that a device performs upon detecting a
particular gesture.
If you want to customize an action that your application performs upon detection of a particular gesture,
you must include appropriate commands within a relevant method of the MyOnGestureListener
class and return a value of true to override the default action, as illustrated below with revisions to the
onTwoFingerTapEvent(PointF) method.
@Override
public boolean onTwoFingerTapEvent(PointF p) {
// Reset the map view
double level = map.getMinZoomLevel() + map.getMaxZoomLevel() / 2;
map.setCenter(new GeoCoordinate(49.196261, -123.004773),
Map.Animation.NONE);
map.setZoomLevel(level);
return true;
}
Note: Since the onTapEvent(PointF) event is always triggered before the
onMapObjectsSelected(List) event, you can leverage this behavior to implement
your own object selection logic. While implementing object selection, it is recommended that you
use both Map.getSelectedObject(PointF) and Map.getSelectedObject(ViewRect) and
combine the results, so that the user's tap input is interpreted over a larger area, rather than only a
single point.
After the revision, the basic application responds to each two-finger tap gesture by returning to its initial
view (the view displayed upon application launch). Other touch gestures continue to trigger standard HERE
SDK actions.
Map Schemes
The HERE Android SDK provides a variety of map appearances for your application to choose from, these
appearances are otherwise known as map schemes.
Map.Scheme defines visualization types that the HERE map service supports. There is a variety of map
schemes available that can be used, based on the specific use case:
•
Explore - Normal, Terrain, Pedestrian
•
Navigation - Car Navigation, Car Navigation with Traffic, Maneuver
•
Overlays - Grey, Transit, Reduced, Traffic
You can set a desired scheme by making a call to the Map.setMapScheme(String) method.
30
HERE Android SDK Developer's Guide
► User Guide
Examples of Map Scheme
All available schemes are defined as constant strings in the Map.Scheme class. The following are examples
of string values that you can use to set the map scheme in your application:
Figure 5: Scheme.NORMAL_DAY
Figure 6: Scheme.SATELLITE_DAY
Figure 7: Scheme.HYBRID_DAY
Figure 8: Scheme.TERRAIN_DAY
Note: Your application also needs to switch to one of the following schemes if you enable traffic
information with Map.setTrafficInfoVisible(true). These map schemes are otherwise
identical to their non-traffic counterparts.
•
Scheme.HYBRID_TRAFFIC_DAY
•
Scheme.NORMAL_TRAFFIC_DAY
•
•
Scheme.HYBRID_TRAFFIC_NIGHT
Scheme.NORMAL_TRAFFIC_NIGHT
31
HERE Android SDK Developer's Guide
► User Guide
Note: In addition to the preceding schemes, Scheme.SATELLITE_NIGHT is also available. It is
similar to Scheme.SATELLITE_DAY, but the color of the sky is different when the map is tilted.
Figure 9: Scheme.HYBRID_DAY_TRANSIT
Figure 10: Scheme.NORMAL_NIGHT_TRANSIT
Figure 11: Scheme.NORMAL_NIGHT
Figure 12: Scheme.NORMAL_DAY_TRANSIT
Navigation Schemes
The HERE SDK also offers the following schemes to be used with navigation:
•
Scheme.CARNAV_DAY
•
Scheme.CARNAV_HYBRID_DAY
•
•
•
•
•
•
Scheme.CARNAV_NIGHT
Scheme.CARNAV_HYBRID_NIGHT
Scheme.CARNAV_TRAFFIC_DAY
Scheme.CARNAV_TRAFFIC_NIGHT
Scheme.CARNAV_TRAFFIC_HYBRID_DAY
Scheme.CARNAV_TRAFFIC_HYBRID_NIGHT
32
HERE Android SDK Developer's Guide
► User Guide
•
Scheme.CARNAV_DAY_GREY
•
Scheme.PEDESTRIAN_DAY
•
•
•
•
•
•
•
•
Scheme.CARNAV_NIGHT_GREY
Scheme.PEDESTRIAN_NIGHT
Scheme.PEDESTRIAN_DAY_HYBRID
Scheme.PEDESTRIAN_NIGHT_HYBRID
Scheme.TRUCKNAV_DAY
Scheme.TRUCKNAV_NIGHT
Scheme.TRUCKNAV_HYBRID_DAY
Scheme.TRUCKNAV_HYBRID_NIGHT
If you are using a pedestrian navigation scheme, it is recommended that you also enable the pedestrian
features using the Map class. See Maps on page 22 for more details.
Note: The HERE SDK does not automatically switch map schemes during navigation mode. Before
starting car or pedestrian navigation, be sure to save the current map scheme and switch to the
appropriate navigation map scheme. When navigation has completed, your application code should
switch back to the previously saved scheme.
For more information on how to perform navigation operations, see Turn-by-Turn Navigation for Walking and
Driving on page 145.
Setting a Map Scheme
The following example demonstrates how to retrieve available map schemes and change the current map
scheme:
// Array containing string values of all available map schemes
List schemes = map.getMapSchemes();
// Assume to select the 2nd map scheme in the available list
map.setMapScheme(schemes.get(1));
Listening for MapScheme Change Events
Applications can listen for map scheme change events by way of the Map.OnSchemeChangedListener:
map.addSchemeChangedListener(new OnSchemeChangedListener() {
@Override
public void onMapSchemeChanged(String mapScheme) {
// react to map scheme change here
}
});
For information on the fleet map scheme, see Mobile Asset Management on page 63.
MapEngine Class
MapEngine is a singleton class used to manage active mapping resources for use in applications developed
with the HERE SDK. MapEngine must be initialized before Map and map-related objects, such as MapMarker
and Places, can be instantiated and retrieved from the API. A runtime exception occurs if MapEngine is not
properly initialized before map-related objects are used.
33
HERE Android SDK Developer's Guide
► User Guide
Initialization
MapEngine must be initialized before it can be used. MapEngine is automatically initialized for your
application by using MapFragment. MapFragment is a fragment class that applications can use as an UI
module in an activity for map display. However, if your application does not use MapFragment classes, then
the application should initialize the MapEngine directly before using any HERE APIs. You can do this by
calling MapEngine.init(ApplicationContext, OnEngineInitListener) as shown below:
MapEngine mapEngine = MapEngine.getInstance();
ApplicationContext appContext = new ApplicationContext(context);
mapEngine.init(appContext, new OnEngineInitListener() {
@Override
public void onEngineInitializatonCompleted(Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// Post initialization code goes here
} else {
// handle factory initialization failure
}
}
});
If map engine initialization is in progress or has failed, calling any other HERE SDK APIs fails because invalid
objects cannot be created. To avoid this problem, check for MapEngine.isInitialized() in your app
lifecycle callbacks. For example, the following example avoids problems with using the PositionManager
before an instance can be properly created:
public void onDestroy()
{
//Set initComplete using MapEngine.isInitialized()
if (initComplete) {
PositioningManager.getInstance().removeListener(this);
}
super.onDestroy();
}
For examples of typical scenarios using the MapFragment that automatically initializes the MapEngine, see
Maps on page 22.
Objects and Interaction
You can select ViewObject objects by using a single tap gesture. To enable
this in your code, create an OnGestureListener object and pass it to
MapFragment.getMapGesture().addOnGestureListener(OnGestureListener). When a single tap
occurs, the listener receives the onTapEvent(PointF) callback, and if that event is not handled, then the
listener receives the onMapObjectsSelected(List) callback. The application can then
define what to do with the selected ViewObject.
Types of ViewObject objects that are selectable are defined within the ViewObject.Type enumeration,
which includes:
•
•
USER_OBJECT - an object that the application adds to a map with a MapObject base class
(MapPolygon for example).
PROXY_OBJECT - an object that is added automatically to a map with a MapProxyObject base class.
A proxy object may contain special information about the object, depending on the type (for example,
34
HERE Android SDK Developer's Guide
► User Guide
•
TransitStopObject may provide transit stop-related information), but it cannot be created or
modified.
UNKNOWN_OBJECT - a selectable map object that is not a USER_OBJECT nor a PROXY_OBJECT
The ViewObject Abstract Class
The ViewObject abstract class represents the base implementation for all objects that are selectable on a
MapView or MapFragment. The MapFragment features user-selectable objects.
Sub-classes of the ViewObject class include MapObject and MapProxyObject .
MapObject and Geo Objects
MapObject represents an abstract class for all map-related objects that can be added on a Map. The
subclasses of this abstract class include:
•
MapContainer
•
MapPolyline
•
•
•
•
•
•
•
•
MapCircle
MapPolygon
MapRoute
MapMarker
MapLocalModel
MapGeoModel
MapLabeledMarker
MapScreenMarker
These objects can be created by calling the appropriate constructor methods. In some cases, a geo object
is required in the constructor. Geo objects (for example, GeoPolyline and GeoPolygon) are geographical
data representations that act as models to MapObjects, which act as views. Unlike map objects, geo objects
cannot be added directly to a Map. For more information on geo objects and creating map objects, see the
API Reference.
The following code snippet demonstrates how to create a MapPolyline and a GeoPolyline object:
List testPoints = new ArrayList();
testPoints.add(new GeoCoordinate(49.163, -123.137766, 10));
testPoints.add(new GeoCoordinate(59.163, -123.137766, 10));
testPoints.add(new GeoCoordinate(60.163, -123.137766, 10));
GeoPolyline polyline = new GeoPolyline(testPoints);
MapPolyline mapPolyline = new MapPolyline(polyline);
To add a MapObject to the map, use Map.addMapObject(MapObject) or
Map.addMapObjects(List). You can use the setOverlayType(MapOverlayType)
method to set the display layer for the map object. By default, map objects are assigned to the foreground.
Note: For use cases where a map object needs to be viewable in 3D space, use MapLocalModel or
MapGeoModel. Other map objects are not guaranteed to support 3D.
35
HERE Android SDK Developer's Guide
► User Guide
MapContainer
You can use MapContainer as a container for other MapObject instances. Map containers
determine the stacking order of objects displayed on a map. To add a map object, call the
MapContainer.addMapObject(MapObject) method.
Note: MapRoute and MapContainer cannot be added to a MapContainer.
Note: If a map object is a part of a MapContainer, it has the same MapOverlayType as the map
container.
MapCircle
A MapCircle represents a type of MapObject in the shape of a circle, with an assigned radius distance
and a GeoCoordinate center. It can be created by calling the constructor MapCircle(double radius,
GeoCoordinate center).
Figure 13: A MapCircle object
MapPolyline
A MapPolyline is a MapObject in the shape of a polyline with anchor points at any number of
GeoCoordinate points. It can be created via a GeoPolyline object, which can be created by calling the
GeoPolyline(List points) constructor.
36
HERE Android SDK Developer's Guide
► User Guide
Note: A MapPolyline or MapPolygon can only contain up to 65536 vertices.
Figure 14: A MapPolyline object
MapPolygon
A MapPolygon is a MapObject in the shape of a polygon. In contrast with a MapPolyline, it is assumed
that the last coordinate in the line's path is connected to the first coordinate, thereby constructing an
enclosed geometry. A MapPolygon may define separate border and fill colors. To create a MapPolygon,
use the constructor MapPolygon(GeoPolygon polygon). A GeoPolygon can be created by calling
GeoPolygon(List points).
Figure 15: A MapPolygon object
MapRoute
A MapRoute is a MapObject that displays a calculated route on a map. For more information on MapRoute,
see Routing .
37
HERE Android SDK Developer's Guide
► User Guide
MapMarker
A MapMarker is a MapObject that displays an icon at a geographical position on a map. You can create a
MapMarker with your own custom icon by calling MapMarker(GeoCoordinate, Image).
Figure 16: A MapMarker object
MapMarker instances are always placed on top of other map objects. Refer to the diagram below for more
information about z-index ordering for multiple map markers.
Figure 17: MapMarker order
You can set MapMarker to be draggable by using the MapMarker.setDraggable(true) method. To listen
for drag events, such as marker position changes, use MapMarker.OnDragListener.
MapLabeledMarker
A MapLabeledMarker is a different type of marker object that avoids overlapping with other icons and text
on the map. By default, the visual appearance of a MapLabeledMarker is similar to a point of interest. You
38
HERE Android SDK Developer's Guide
► User Guide
can choose a preset category icon (for example, IconCategory.ZOO) or set your own Image as the marker
icon.
Figure 18: A MapLabeledMarker object
Unlike MapMarker, setting the label text to a MapLabeledMarker does not require enabling an
info bubble. You can set the marker label text by providing a language and a localized string to the
MapLabeledMarker.setLabelText(String, String) method. The localized text in the language that
matches the current Map.getMapDisplayLanguage(), if available, is displayed. Otherwise, the first-added
localized text is displayed.
Note: Although a MapLabeledMarker is visually similar to a point of interest, its overlay type is set
to FOREGROUND_OVERLAY by default.
MapLocalModel
A MapLocalModel is an arbitrary 3D map object that is drawn using a local coordinate (as opposed to a
geocoordinate) mesh. You can create a custom MapLocalModel by calling MapLocalModel(), and setting
the model mesh, texture, orientation, and geographical location before adding it to the map. For example:
FloatBuffer buff = FloatBuffer.allocate(12); // Two triangles
buff.put(0- delta);
buff.put(0- delta);
buff.put(1.f);
buff.put(0 + delta);
buff.put(0 - delta);
buff.put(1.f);
buff.put(0 - delta);
buff.put(0 + delta);
buff.put(1.f);
buff.put(0 + delta);
buff.put(0 + delta);
buff.put(1.f);
// Two triangles to generate the rectangle. Both front and back face
IntBuffer vertIndicieBuffer = IntBuffer.allocate(12);
vertIndicieBuffer.put(0);
vertIndicieBuffer.put(2);
vertIndicieBuffer.put(1);
vertIndicieBuffer.put(2);
vertIndicieBuffer.put(3);
39
HERE Android SDK Developer's Guide
► User Guide
vertIndicieBuffer.put(1);
vertIndicieBuffer.put(0);
vertIndicieBuffer.put(1);
vertIndicieBuffer.put(2);
vertIndicieBuffer.put(1);
vertIndicieBuffer.put(3);
vertIndicieBuffer.put(2);
// Texture coordinates
FloatBuffer textCoordBuffer = FloatBuffer.allocate(8);
textCoordBuffer.put(0.f);
textCoordBuffer.put(0.f);
textCoordBuffer.put(1.f);
textCoordBuffer.put(0.f);
textCoordBuffer.put(0.f);
textCoordBuffer.put(1.f);
textCoordBuffer.put(1.f);
textCoordBuffer.put(1.f);
LocalMesh myMesh = new LocalMesh();
myMesh.setVertices(buff);
myMesh.setVertexIndices(vertIndicieBuffer);
myMesh.setTextureCoordinates(textCoordBuffer);
MapLocalModel myObject = new MapLocalModel();
myObject.setMesh(myMesh); //a LocalMesh object
myObject.setTexture(myImage); //an Image object
myObject.setAnchor(myLocation); //a GeoCoordinate object
myObject.setScale(20.0f);
myObject.setDynamicScalingEnabled(true);
myObject.setYaw(45.0f);
map.addMapObject(myObject);
While translating the 3D model mesh to the map, a unit of 1.0f represents 1 meter in the real world. For
example, a Vector3f(100,200,300) represents an offset of +100 meters in the x-axis (East), +200 meters
in the y-axis (North), and +300 meters in the z-axis direction (Up). You can further control the size of the 3D
model mesh by setting a scaling factor with the setScale() method.
Figure 19: A MapLocalModel object
40
HERE Android SDK Developer's Guide
► User Guide
Aside from setting a texture, a MapLocalModel can also be customized by setting its material and lighting
using the Phong reflection model. For example, the following code sets the ambient color, diffuse color, and
light source to the MapLocalModel.
// This light shines from above in the Z axis
DirectionalLight light = new DirectionalLight(new Vector3f(0, 0.5f, 1));
m_model.addLight(light);
// Give this a default color
PhongMaterial mat = new PhongMaterial();
mat.setAmbientColor(0xffffffff);
mat.setDiffuseColor(0x00000000);
m_model.setMaterial(mat);
Note:
•
•
As 3D objects consume large amounts of memory, avoid using MapLocalModel and
MapGeoModel to replace 2D map markers. Two examples of recommended uses of these classes
are adding a few 3D structures to the map, or showing a realistic car model during guidance.
If you use MapLocalModel to create a two-dimensional object, and if you use an anchor with an
undefined or zero altitude value, there is a known rendering issue with OpenGL where parts of
the object may conflict with the map layer, causing the object to flicker. To get around this issue,
use a z-coordinate offset that is greater than 0. For example, you can use a small floating point
number such as, 0.001, so that the user is unable to distinguish between the object's altitude
and the map.
MapGeoModel
A MapGeoModel is an arbitrary 3D map object that is drawn using geocoordinate vertices. You can create
a MapGeoModel by calling its constructor and setting a list of geocoordinates, a list indicating the vertex
order, a list of UV coordinates, and a texture Image. For example:
List myLocations =
new GeoCoordinate(37.783409,
new GeoCoordinate(37.785444,
new GeoCoordinate(37.774149,
Arrays.asList(
-122.439473),
-122.424667),
-122.429345));
// vertices must be specified in a counter-clockwise manner
IntBuffer vertIndicieBuffer = IntBuffer.allocate(3);
vertIndicieBuffer.put(0);
vertIndicieBuffer.put(2);
vertIndicieBuffer.put(1);
FloatBuffer textCoordBuffer = FloatBuffer.allocate(6);
textCoordBuffer.put(0.5f);
textCoordBuffer.put(0.5f);
textCoordBuffer.put(0.5f);
textCoordBuffer.put(0.5f);
textCoordBuffer.put(0.5f);
textCoordBuffer.put(0.5f);
GeoMesh meshy = new GeoMesh();
meshy.setVertices(myLocations);
meshy.setVertexIndices(vertIndicieBuffer);
meshy.setTextureCoordinates(textCoordBuffer);
MapGeoModel myGeoModel = new MapGeoModel();
myGeoModel.setMesh(meshy);
myGeoModel.setTexture(myTexture);
41
HERE Android SDK Developer's Guide
► User Guide
As with MapLocalModel, you can also set the lighting and color properties for a MapGeoModel using the
addLight(DirectionalLight) and setMaterial(PhongMaterial) methods.
Figure 20: A MapGeoModel object
MapCartoMarker
Points of interest are represented by instances of the MapCartoMarker proxy object class.
Figure 21: Examples of Points of Interest
In the above screenshot, there are four points of interests: two shops, one restaurant, and one car
dealership. Each of these points of interest may be selected by either tapping on the map.
42
HERE Android SDK Developer's Guide
► User Guide
The following is an example of how to retrieve point of interest information from a MapCartoMarker:
switch (proxyObj.getType()) {
case MAP_CARTO_MARKER:
MapCartoMarker mapCartoMarker =
(MapCartoMarker) proxyObj;
Location location = mapCartoMarker.getLocation();
String placeName =
location.getInfo().getField(Field.PLACE_NAME);
String placeCategory =
location.getInfo().getField(Field.PLACE_CATEGORY);
String placePhone =
location.getInfo().getField(Field.PLACE_PHONE_NUMBER);
//...
break;
//...
default:
Log.d(TAG, "ProxyObject.getType() unknown");
}
You can extract further Point of Interest (POI) information from a cartographic marker by using the Places
feature in the HERE SDK, since cartographic markers contain identifying data that can be passed to a Place
search request. For example:
if (mapCartoMarker.getLocation() != null &&
mapCartoMarker.getLocation().getInfo() != null)
{
LocationInfo info = mapCartoMarker.getLocation().getInfo();
String foreignSource = info.getField(Field.FOREIGN_ID_SOURCE);
String foreignId = info.getField(Field.FOREIGN_ID);
}
PlaceRequest request = new PlaceRequest(foreignSource, foreignId);
request.execute(new ResultListener() {
@Override
public void onCompleted(Place data, ErrorCode error) {
if (error == ErrorCode.NONE) {
//extract Place data
}
}
});
For more information about this feature, see the External References section.
User Interactions with MapObject
This section provides an example on handling MapObject tap events. In the following code:
•
•
addMapObject() adds the object on the Map.
List holds the objects that have been selected in this tap event. By looping through this
list of objects, your code can find the MapObject that should respond to this tap event.
Note: The onMapObjectsSelected(List) callback is triggered after the onTapEvent(PointF)
callback. For more information on this, refer to Map Gestures on page 27
// Create a custom marker image
com.here.android.mpa.common.Image myImage =
new com.here.android.mpa.common.Image();
try {
43
HERE Android SDK Developer's Guide
► User Guide
myImage.setImageResource(R.drawable.my_png);
} catch (IOException e) {
finish();
}
// Create the MapMarker
MapMarker myMapMarker =
new MapMarker(new GeoCoordinate(LAT, LNG), myImage);
map.addMapObject(myMapMarker);
...
// Create a gesture listener and add it to the MapFragment
MapGesture.OnGestureListener listener =
new MapGesture.OnGestureListener.OnGestureListenerAdapter() {
@Override
public boolean onMapObjectsSelected(List objects) {
for (ViewObject viewObj : objects) {
if (viewObj.getBaseType() == ViewObject.Type.USER_OBJECT) {
if (((MapObject)viewObj).getType() == MapObject.Type.MARKER) {
// At this point we have the originally added
// map marker, so we can do something with it
// (like change the visibility, or more
// marker-specific actions)
((MapObject)viewObj).setVisible(false);
}
}
}
// return false to allow the map to handle this callback also
return false;
}
...
};
The MapOverlay Class
The MapOverlay class represents a special type of map object that does not inherit from the MapObject
base class. Instead, it provides a way for any Android View to be displayed at a fixed geographical location on
the map.
You can add content to a map overlay by using the MapOverlay(View, GeoCoordinate) constructor.
If complex view contents are required, such as a view with subviews of its own, the content should be fully
initialized before adding it to the map overlay.
Due to the extra performance cost of Android views, it is recommended that the MapOverlay only be used
in situations where the additional functionality provided by a View, such as a button, is needed. If the map
object only needs to display a static image, use MapMarker.
Note: MapOverlay does not inherit from MapObject, but overlays are returned as a MapMarker
from a a tap gesture callback by default. To avoid this behavior and these substitute markers, the
appropriate gesture handling must be implemented either in a MapOverlay subclass, or in a custom
view that is added as a subview to a standard MapOverlay.
The following code shows how to use a simple button in a MapOverlay.
private Button button;
private void onMapFragmentInitializationCompleted() {
// retrieve a reference of the map from the map fragment
map = mapFragment.getMap();
// Set the map center coordinate to the Vancouver region (no animation)
44
HERE Android SDK Developer's Guide
► User Guide
}
map.setCenter(new GeoCoordinate(49.196261, -123.004773, 0.0),
Map.Animation.NONE);
// Set the map zoom level to the average between min and max (no
// animation)
map.setZoomLevel((map.getMaxZoomLevel() + map.getMinZoomLevel()) / 2);
// create the button
button = new Button(this);
button.setText("TEST");
// create overlay and add it to the map
map.addMapOverlay(
new MapOverlay(button,
new GeoCoordinate(37.77493, -122.419416, 0.0)));
Handling MapProxyObject objects
The following code demonstrates how to handle tap events on a MapProxyObject:
•
•
•
The onMapObjectsSelected event of the OnGestureListener listens to object selected. For more
information on OnGestureListener, refer to Map Gestures on page 27.
If the selected object is a PROXY_OBJECT then you can safely cast the ViewObject into a
MapProxyObject.
If the selected object is a USER_OBJECT then you need to find the object using the hash map; refer to
the preceding example.
private MapGesture.OnGestureListener listener =
new MapGesture.OnGestureListener.OnGestureListenerAdapter() {
...
@Override
public boolean onMapObjectsSelected(List objects) {
for (ViewObject obj : objects) {
switch (obj.getBaseType()) {
case PROXY_OBJECT:
MapProxyObject proxyObj = (MapProxyObject) obj;
switch (proxyObj.getType()) {
case TRANSIT_ACCESS:
TransitAccessObject transitAccessObj =
(TransitAccessObject) proxyObj;
Log.d(TAG, "Found a TransitAccessObject");
break;
case TRANSIT_LINE:
TransitLineObject transitLineObj =
(TransitLineObject) proxyObj;
Log.d(TAG, "Found a TransitLineObject");
break;
case TRANSIT_STOP:
TransitStopObject transitStopObj =
(TransitStopObject) proxyObj;
Log.d(TAG, "Found a TransitStopObject");
break;
default:
Log.d(TAG, "ProxyObject.getType() unknown");
}
break;
// User objects are more likely to be handled
// as in the previous example
case USER_OBJECT:
default:
Log.d(TAG,
"ViewObject.getBaseType() is USER_OBJECT or unknown");
break;
}
45
HERE Android SDK Developer's Guide
► User Guide
}
return true;
}
...
};
Marker Clustering
You can use marker clustering to reduce the visual overload caused by too much markers being displayed on
the map at once. With this feature, markers that are close together are automatically replaced by numbered
cluster markers to indicate that multiple map markers are represented.
Figure 22: Cluster Markers
Showing Cluster Markers
You can enable cluster markers on a map by using a ClusterLayer and adding map markers to it. All
markers that are on a layer are automatically clustered based on a grid-based clustering algorithm that
depends on the map zoom level.
The following steps demonstrates how to use the ClusterLayer class:
1.
Create map markers as normal:
MapMarker mm = new MapMarker();
mm.setIcon(myImage);
mm.setCoordinate(new GeoCoordinate(52.53,13.23));
2.
Create a ClusterLayer object.
ClusterLayer cl = new ClusterLayer();
46
HERE Android SDK Developer's Guide
► User Guide
3.
Add markers to the cluster layer, instead of the map directly. You can also add a Collection of
MapMarker instead of setting just adding a single marker.
cl.addMarker(mm);
4.
Add the cluster layer to the map.
mMap.addClusterLayer(cl);
5.
Note: The order of these two steps is not important. You can also add the cluster layer to the
map first and add markers to the cluster layer afterwards.
To remove a marker or collection or markers from the cluster layer again, call:
cl.removeMarker(mm);
You can also retrieve all markers on a cluster layer with the ClusterLayer.getMarkers()
method. This is useful in the case where you would like to remove all markers by using the
removeMarkers(Collection) method.
Theming
You can customize clusters by assigning a ClusterTheme object to the ClusterLayer. Every theme
consists of several styles, where a cluster style defines the look of marker cluster objects at a particular
density. Cluster density is the amount of markers that is being represented by a cluster.
Figure 23: Marker Cluster with Density of 7
There are three available cluster styles that you can use with a ClusterTheme:
•
•
•
Default cluster style - the predefined markers behavior. This is the default style that is used if you do not
set a theme. It is also used for ranges that are not covered by your own theme.
BasicClusterStyle - similar to the default style, but you can change the fill color, text color, and
stroke color for the markers.
ImageClusterStyle - use your own bitmap image as a marker.
To set a style, use the setStyleForDensityRange(int, int, ClusterStyle) or
setStyleForDensityRange(ClusterDensityRange, ClusterStyle) methods in ClusterTheme. For
example, if you want red for all clusters between density 10 to 19, and green for 20 to 49, and the default
blue for all other cases, you can use BasicClusterStyle as follows:
47
HERE Android SDK Developer's Guide
► User Guide
1.
Create a style with a red circle and a style with a green one:
BasicClusterStyle redStyle = new BasicClusterStyle();
redStyle.setFillColor(Color.RED);
BasicClusterStyle greenStyle = new BasicClusterStyle();
greenStyle.setFillColor(Color.GREEN);
2.
Create a new theme and add those styles to the theme with defining the density ranges they should be
used for:
ClusterTheme theme = new ClusterTheme();
theme.setStyleForDensityRange(10, 19, redStyle);
theme.setStyleForDensityRange(20, 49, greenStyle);
Instead of setting the integer values directly, you can also make use of the ClusterDensityRange
class.
3.
Note: Do not overlap density ranges. Overlapping ranges causes InvalidArgumentException.
Finally, add this theme to the cluster layer you use:
cl.setTheme(theme);
To use your own image as a cluster icon, set an Image to an ImageClusterStyle instance before setting
the style to the cluster theme. For example:
Image img = new Image();
try {
img.setImageResource(R.drawable.banner);
} catch (IOException e) {
e.printStackTrace();
}
ImageClusterStyle imageCluster = new ImageClusterStyle(img);
ClusterTheme theme = new ClusterTheme();
theme.setStyleForDensityRange(2, 9, imageCluster);
cl.setTheme(theme);
Although you can only set one theme per layer, you can mix styles for different densities in a single theme.
For example, you can set a BasicClusterStyle from density of 10 to 19 and an ImageClusterStyle
from 20 to 30. The default theme applies for all other densities that are not covered by the custom themes.
Cluster Marker Events
Cluster markers are similar to normal markers on the map. You can also use map object gesture listeners in a
similar manner as normal map markers. For example:
1.
Add a gesture listener to the map via:
mMapFragment.getMapGesture().addOnGestureListener(mMyGestureHandler);
2.
Next, listen for
public boolean onMapObjectsSelected(List viewObjects)
3.
to get the map click event.
Iterate over the ViewObjects and check for type PROXY_OBJECT and sub-type CLUSTER_MARKER.
Alternatively, you can also use the instanceof keyword.
@Override
public boolean onMapObjectsSelected(List viewObjects) {
48
HERE Android SDK Developer's Guide
► User Guide
for (ViewObject obj : viewObjects){
if (obj.getBaseType() == ViewObject.Type.PROXY_OBJECT){
if (proxyObj.getType() == MapProxyObject.Type.CLUSTER_MARKER) {
ClusterViewObject cv = (ClusterViewObject) proxyObj;
Log.i(TAG, "Cluster clicked: markers#"+cv.getMarkers().size());
return true;
}
}
return false;
}
Working with Clusters
The HERE SDK also provides a few other ways for you to interact with marker clusters. You can get all markers
inside one specific cluster by using the ClusterViewObject, which is a proxy object representing a cluster.
For example:
Collection ClusterViewObject.getMarkers()
You can also retrieve the bounding box around all markers that are in a cluster marker by calling the
following:
BoundingBox ClusterViewObject.getBoundingBox();
Traffic Information
Traffic information can be displayed on the Map, depending on traffic data availability, by using
Map.setTrafficInfoVisible(true) and setting the map to a traffic-enabled map scheme. Traffic
visualization requires a network data connection to download real time traffic information. However, traffic
information may continue to be displayed thereafter without a connection until the traffic events expire or
the visibility is toggled.
The current traffic-enabled map schemes are:
•
•
•
•
•
•
•
•
Map.Scheme.HYBRID_TRAFFIC_DAY
Map.Scheme.HYBRID_TRAFFIC_NIGHT
Map.Scheme.NORMAL_TRAFFIC_DAY
Map.Scheme.NORMAL_TRAFFIC_NIGHT
Map.Scheme.CARNAV_TRAFFIC_DAY
Map.Scheme.CARNAV_TRAFFIC_NIGHT
Map.Scheme.CARNAV_TRAFFIC_HYBRID_DAY
Map.Scheme.CARNAV_TRAFFIC_HYBRID_NIGHT
49
HERE Android SDK Developer's Guide
► User Guide
The following figure shows a sample traffic visualization.
Figure 24: Traffic information with color-coded lines
Traffic flow lines are color coded as follows:
Green
Normal
Amber
High
Red
Very High
Black
Blocking
Controlling Traffic Display
You can further control traffic display by calling Map.getMapTrafficLayer() and using the
MapTrafficLayer object.
With MapTrafficLayer, you can individually disable Traffic Flow, Traffic Incidents, or On-Route Traffic, as
well as filter traffic that is displayed according to the minimal severity level.
For example, you can set the map to only display traffic flow lines that are "very high" (red) or
"blocking" (black) by performing the following:
MapTrafficLayer traffic = map.getMapTrafficLayer();
//set the minimum displayed traffic level
traffic.setDisplayFilter(TrafficEvent.Severity.VERY_HIGH);
Controlling Traffic Updates
By default, traffic events are automatically loaded inside the viewport when traffic is
enabled. You can also explicitly fetch traffic around a given set of geocoordinates by using
TrafficUpdater.request(GeoCoordinate, int, Listener).
50
HERE Android SDK Developer's Guide
► User Guide
To completely customize the traffic-updating implementation in your app, first turn off automatic traffic
updates via the Map.disableTrafficAutoUpdate() method, then use the above mentioned method to
fetch traffic only where it is required.
Note: Since downloading and decoding traffic data can be computationally costly, do not fetch
traffic for the same area too frequently. For example, if you are fetching traffic only around the user
position, do not fetch more frequently than once a minute. For lower end devices, fetch data in even
less frequent intervals, such as once every five minutes.
Selecting Traffic Objects and Events
Traffic events are selectable through map gestures and
OnGestureListener.onMapObjectsSelected(List). A user selectable
TrafficEventObject contains live traffic event information and is presented on the map in different
forms. The following figures illustrate three examples:
Figure 25: TrafficEventObject example: Figure 26: TrafficEventObject example: Figure 27: TrafficEventObject example:
Roadwork
Accident
Road Closed
To acquire information about a tapped TrafficEventObject (see MapObjects), use
onMapObjectsSelected(List) as in the following:
private MapGesture.OnGestureListener listener = new MapGesture.OnGestureListener() {
...
@Override
public boolean onMapObjectsSelected(List objects) {
for (ViewObject obj : objects) {
if (obj.getBaseType() == ViewObject.Type.PROXY_OBJECT) {
MapProxyObject proxyObj = (MapProxyObject) obj;
if (proxyObj.getType() == MapProxyObject.Type.TRAFFIC_EVENT) {
TrafficEventObject trafficEventObj =
(TrafficEventObject) proxyObj;
TrafficEvent trafficEvent =
trafficEventObj.getTrafficEvent();
Toast.makeText(getApplicationContext(), trafficEvent.getEventText(),
Toast.LENGTH_LONG).show();
}
}
}
return true;
};
51
HERE Android SDK Developer's Guide
► User Guide
Offline Maps (MapLoader)
Even without an active data connection, applications developed with the HERE Android SDK let you
browse, search and interact with maps. Classes involved with providing Offline Maps functionality include
MapLoader, MapLoader.Listener, MapLoader.ResultCode and MapPackage.
An application can use MapLoader while it is performing another map operation. For example, an app can
download map data while a user is panning a map.
Offline map functionality is invoked automatically, or you can also disable connectivity for the entire HERE
SDK by using MapEngine.setOnline(false). Note that you can only set MapEngine.setOnline(true)
when the device is online. If not, then the request is ignored.
MapLoader and MapLoader.Listener
Offline maps capabilities are enabled through the use of MapLoader and its associated objects. The
MapLoader class provides a set of APIs that allow manipulation of the map data stored on the device.
Operations include:
•
getMapPackages() - To retrieve the state of the map data on the device
•
uninstallMapPackages(List packageIdList) - To uninstall and delete country or
region data that is no longer desired
•
•
•
•
installMapPackages(List packageIdList) - To download and install new country or
region data
checkForMapDataUpdate() - To check whether a new map data version is available
performMapDataUpdate() - To perform a map data version update, if available
cancelCurrentOperation() - To cancel the running MapLoader operation
To use MapLoader, you must call MapLoader.getInstance() to retrieve a MapLoader object instance.
Note that com.here.android.mpa.mapping.MapEngine must be successfully initialized before this
method can be used.
MapLoader operations are performed asynchronously. Results of the various operations are returned by way
of a MapLoader.Listener implementation that must be set to listen for notifications from the MapLoader
as in the code snippet below:
MapLoader.Listener mapLoaderListener = new MapLoader.Listener() {
public void onUninstallMapPackagesComplete(MapPackage rootMapPackage,
MapLoader.ResultCode mapLoaderResultCode) {
}
public void onProgress(int progressPercentage) {
}
public void onPerformMapDataUpdateComplete(MapPackage rootMapPackage,
MapLoader.ResultCode mapLoaderResultCode) {
}
public void onInstallationSize(long diskSize, long networkSize) {
}
public void onInstallMapPackagesComplete(MapPackage rootMapPackage,
MapLoader.ResultCode mapLoaderResultCode) {
}
public void onGetMapPackagesComplete(MapPackage rootMapPackage,
MapLoader.ResultCode mapLoaderResultCode) {
}
public void onCheckForUpdateComplete(boolean updateAvailable,
52
HERE Android SDK Developer's Guide
► User Guide
String currentMapVersion,String newestMapVersion,
MapLoader.ResultCode mapLoaderResultCode) {
}
};
MapLoader mapLoader = MapLoader.getInstance();
mapLoader.addListener(mapLoaderListener);
Also, all operations of the MapLoader are mutually exclusive. For example, if method XYZ is called before
the callback method ABC has returned a result, method XYZ returns false to indicate that the MapLoader is
busy with another operation.
The MapPackage Class
The map data packages available for download are represented as a tree structure with the root map
package representing the world map. The MapPackage class represents the model through which this tree
structure is accessed. As shown in the preceding code snippet, many of the MapLoader.Listener callbacks
returns the root MapPackage. The other MapPackage instances are accessed by recursing through the tree
structure from the root.
The MapPackage state of a particular instance is not updated dynamically to reflect changes to map data
on disk. Therefore if you retrieve MapPackage instance A, and then perform an installation operation (which
returns MapPackage instance B through onInstallMapPackagesComplete()), MapPackage instance A
does not reflect the updated map data state, but MapPackage instance B does. Therefore, always use the
new MapPackage object returned by a given operation and update the representation in your application
accordingly.
Note: The getSize() method returns the maximum install size of the map package, in kilobytes.
If this is the first MapPackage to be installed, then the package takes up the same amount of
memory storage as returned by this method. However, if other packages have already been installed,
then the required disk space for this map package is considerably less than the value returned by
getSize(), because common data between map packages does not need to be installed again. To
get an accurate representation of the disk space that is used for a given installation operation, use
the MapLoader.Listener.onInstallationSize(long, long) callback method.
Note: Map data packages may need to be reinstalled if the application crashes or is forced closed
during MapLoader installation or uninstallation.
Incremental Map Data Updates
MapLoader exposes the ability to update the map data version to provide the user with the freshest map
data available. The map data version applies not only to map data pre-installed using the MapLoader, but
also to data that is retrieved dynamically by browing new areas.
Map data version is consistent for all map data across the entire system, whether the map data is
downloaded or not. It is not possible to have some data from one map version and other data from another
map version concurrent in the disk cache. Therefore, it is important to keep the map version of the system
up to date. However, map version updating does not require re-downloading everything. Instead, only
incremental changes need to be downloaded, making typical updates small and quick. Map version updating
is exposed through the checkForMapDataUpdate() and performMapDataUpdate() methods.
53
HERE Android SDK Developer's Guide
► User Guide
Data Groups
Map packages are made up of several groups, each of which contains a different type of map
data. Some of these groups may be selected or deselected before map packages are downloaded
for offline use, depending on the needs of the application. The optional data groups are given
in the SelectableDataGroup enum. To select or deselect a data group for download, pass the
appropriate enum value to the NMAMapLoader.selectDataGroup(SelectableDataGroup) or
deselectDataGroup(SelectableDataGroup) method.
Note: This feature can only be used with an isolated disk cache. For more information, see Embedding
the Map Service on page 20.
The selected data groups of the map loader are used for all future installation operations. However, changes
to the data group selection do not affect previously installed packages. To update these packages, call
performMapDataUpdate() after changing the data group selection.
The default data group selection may not be optimal for some applications. To minimize disk space
usage, it's recommended that any applications which allow offline map downloads ensure they are only
downloading the required data groups.
Shared Map Resources
The HERE SDK utilizes a shared disk cache on external device memory where it keeps map resources. This
disk cache is shared between all applications and components that use the HERE SDK. The disk cache can be
accessed by multiple applications concurrently, even while map data is being loaded. (For more information,
see Offline Maps (MapLoader) on page 52)
Usage of map resources must be properly handled on an application level. When an application is in active
use, it must hold a reference to map resources for HERE SDK functionality to work. When an application is
not being used (for example, it has been sent to the background), it should release its reference to map
resources.
For MapFragment users, map resource usage is handled automatically, so explicit handling of map resource
references is not required.
Reference handling can be performed manually. This is useful if an application wishes to support a use
case that is outside the scope of classes that provide automatic reference handling. Some examples of this
include performing route calculations in a background service or directly using a MapView component.
•
onPause() - Decrements the reference count of map resource usage.
•
getResourceReferenceCount() - Get the current reference count of map resource usage for your
application
•
onResume() - Increments the reference count of map resource usage.
For more info on these methods and their usage, consult the API reference.
Traffic History
Traffic History allows the user to obtain map tiles that show the typical traffic pattern for
a specific time point during the week. To display Traffic History tiles, create an instance of
54
HERE Android SDK Developer's Guide
► User Guide
HistoricalTrafficRasterTileSource by specifying a day of the week and a time. For example, to show
the traffic tiles for Wednesdays at 5:40pm, add the following:
HistoricalTrafficRasterTileSource tileSource =
new HistoricalTrafficRasterTileSource(DaysOfTheWeek.WEDNESDAY, 17, 40);
map.addRasterTileSource(tileSource);
Figure 28: Traffic History in San Francisco
55
HERE Android SDK Developer's Guide
► User Guide
3D Landmarks
Figure 29: A Landmark on a Map of San Francisco
Another related feature in the Map class is 3D landmarks. By calling setLandmarksVisible(true), you
can make certain landmark structures visible. These structures are presented with textures and more details
in their 3D geometry, as seen in the screenshot above. Landmarks are not visible when the map is set to a
Hybrid or Satellite map scheme.
56
HERE Android SDK Developer's Guide
► User Guide
Extruded Buildings
Figure 30: Extruded Buildings on a Map of San Francisco
HERE Android SDK supports 3D representations of buildings and structures. This feature is called extruded
buildings, and you can display them by using the setExtrudedBuildingsVisible() method in
com.here.android.mpa.mapping.Map. Extruded buildings are available for most metropolitan areas in
North America and Europe.
The MapBuildingLayer Class
The main entry point for extruded buildings is the MapBuildingLayer class, which can be retrieved by
calling getMapBuildingLayer() from a Map. MapBuildingLayer provides methods for working with
building groups and individual buildings such as:
•
•
•
•
getBuilding()
createNewBuildingGroup()
releaseBuildingGroup()
getDefaultBuildingGroup()
These APIs provide a way for the developer to create groups of buildings (for example, to highlight them in a
blue color), or to retrieve a default group containing all possible extruded buildings on-screen.
Note: The extruded building methods are available even when buildings are invisible. For example,
calling getDefaultBuildingGroup() returns the default group, even if you have called
setExtrudedBuildingsShown(false) or if the current zoom level does not allow visible extruded
buildings.
The MapBuildingGroup Class
The MapBuildingGroup class represents a group of buildings. There are two types of groups:
•
New building groups - Created by calling createNewBuildingGroup(). No building is attached to this
•
Default building groups - Retrieved by calling getDefaultBuildingGroup(). The default building
group when it is created. An application can have a maximum of six new building groups at a time.
group is a generic group that represents all possible buildings in the entire world. There are two distinct
57
HERE Android SDK Developer's Guide
► User Guide
types of default building groups— IMPORTANT_BUILDINGS, which are textured landmark buildings, and
NORMAL_BUILDINGS, which include all other buildings.
Each MapBuildingGroup holds an EnumSet of building faces, a color, as well as a building height scaling
factor. To control the appearance of extruded buildings, you can set these attributes and add buildings to
the group. For example, to highlight a building's roof, create a new building group, set the group's roof color
as Color.RED, and then add a building to this group, as in the following code:
// retrieve a reference of the map from the map fragment
map = mapFragment.getMap();
// Create a custom building group
buildingGroup = map.getMapBuildingLayer().createNewBuildingGroup();
// Set the buildingGroup's roof color to "red"
buildingGroup.setColor(Color.RED, EnumSet.of(MapBuildingGroup.BuildingFace.ROOF));
// Set the buildingGroup's height
buildingGroup.setVerticalScale(0.90f);
buildingGroup.addBuilding(myBuildingIdentifier);
Note: Remember to call releaseBuildingGroup() to release any unused building groups.
Otherwise, users may receive a null pointer exception after the device has been rotated a few times.
Figure 31: Highlighting a Building
Note: By default, a new MapBuildingGroup has the color
MapBuildingLayer.DefaultBuildingColor.SELECTED on all building faces.
The following images show the values that can be used to highlight building faces:
58
HERE Android SDK Developer's Guide
► User Guide
MapBuildingGroup.BuildingFace.ROOF
MapBuildingGroup.BuildingFace.OUTLINE
MapBuildingGroup.BuildingFace.WALLBOTTOM
MapBuildingGroup.BuildingFace.WALLTOP
59
HERE Android SDK Developer's Guide
► User Guide
MapBuildingGroup.BuildingFace.LANDMARKS - Note that this value is
only applicable for landmarks. When this value is used, the entire landmark is
shaded.
The MapBuildingObject Class
The MapBuildingObject class represents a single building, with the following attributes:
•
a name
•
the building height, in meters
•
•
a geocoordinate position
a unique map building identifier
To detect whether a user has tapped on an extruded building, use MapGesture.OnGestureListener and
look for the selected MapBuildingObject:
private MapGesture.OnGestureListener gestureListener =
new MapGesture.OnGestureListener.OnGestureListenerAdapter() {
public boolean onMapObjectsSelected(List objects) {
for (ViewObject vo : objects) {
if (vo instanceof MapBuildingObject) {
// Remove currently selected building
buildingGroup.removeAllBuildings();
// Add this building to the group.
MapBuildingObject building = (MapBuildingObject) vo;
buildingGroup.addBuilding(building.getIdentifier());
}
}
return false;
}
};
Custom Raster Tiles
You can use the HERE Android SDK to enhance maps with custom raster tiles. Custom raster tiles are tile
images that you can add to a map to customize it with enhanced information. For example, you may wish
to use this feature to add heat maps over a map of New York City. You can store custom raster tile images
locally or on a remote server for users to access when they navigate in a map. If the application is set to
display custom raster tiles, then tiles are displayed when users view a designated geographical area at a
specified zoom level or range of zoom levels.
60
HERE Android SDK Developer's Guide
► User Guide
Dividing a Map and Tile Lookup
To use your own custom raster tile images, you need to have a scheme for dividing your map according to
the zoom level and map coordinates, and then provide map tiles according to this scheme. Your application
must then use this scheme in the implementation of one of the following classes:
•
MapRasterTileSource - Implement this class if you plan to fetch local tile images, create dynamic
•
UrlMapRasterTileSourceBase - This is a convenience child class of MapRasterTileSource.
images, or if you would like to provide your own method of retrieving images from a remote server.
Implement this if you plan to fetch tile images from a remote server using a URL over HTTP.
Note: Raster tiles can be one of the following supported image types:
•
•
•
PNG
JPEG
BMP
Once a tile source has been implemented, you can toggle its display by adding or
removing it to the map using Map.addRasterTileSource(MapRasterTileSource) or
Map.removeRasterTileSource(MapRasterTileSource).
The MapRasterTileSource Abstract Class
MapRasterTileSource is the common way for you to define your raster tile source. If your application
uses local tile images or remote images that require custom server authentication, then you should
implement this class by defining the hasTile() and getTileWithError() methods. For example:
public class MyTileSource extends MapRasterTileSource {
@Override
public boolean hasTile(int x, int y, int zoomLevel) {
return true;
}
@Override
public TileResult getTileWithError(int x, int y, int zoomLevel) {
byte[] myImageData = null;
// perform tile retrieval logic such as server authentication
// also translate the x, y, and zoomlevel to address an image
TileResult result = new TileResult(Error.NONE, myImageData);
return result;
}
}
Note: Ensure that getTileWithError() returns within a reasonable amount of time. If your
operation takes a longer period of time, launch an asynchronous operation and return the
TileResult.Error.NOT_READY error code while the operation is in progress.
The UrlMapRasterTileSourceBase Abstract Class
UrlMapRasterTileSourceBase is a child abstract class of MapRasterTileSource that you
can use if you plan to fetch tile images from a remote server using image URLs. The following
is a sample implementation of UrlMapRasterTileSourceBase. In this example, we use the
MapRasterTileSource.MapTileSystemHelper.tileXYToQuadKey() method to address our map tiles.
61
HERE Android SDK Developer's Guide
► User Guide
This helper method assumes that we are using a quadtree/quadkey scheme, where the map is divided into a
quadtree (a tree data structure where each node has exactly four children) with 20 levels. Each level of this
map quadtree has (2 ) tiles, where x represents the floor function value of the current zoom level. So for
x 2
level 0, there is 1 x 1 = 1 tile, level 1 has 2 x 2 = 4 tiles, level 2 has 4 x 4 = 16 tiles, and level 3.7 has 8 x 8 = 64
tiles—since the floor value of 3.7 is 3.
For more information about the quadkey/quadtree division scheme, see the tileXYToQuadKey() API
reference.
public class LiveMapRasterTileSource extends UrlMapRasterTileSourceBase {
private final static String URL_FORMAT =
"http://1.communitymaptiles.example.org/tilehub/live/map/png/%s";
public LiveMapRasterTileSource() {
// We want the tiles placed over everything else
setOverlayType(MapOverlayType.FOREGROUND_OVERLAY);
// We don't want the map visible beneath the tiles
setTransparency(Transparency.OFF);
// We don't want the tiles visible between these zoom levels
hideAtZoomRange(12, 20);
// Do not cache tiles
setCachingEnabled(false);
}
// Implementation of UrlMapRasterTileSourceBase
public String getUrl(int x, int y, int zoomLevel) {
String url = null;
// Utility to map the x, y coordinates easily into an equivalent
// quadkey at a specified zoomLevel
String quadKey =
MapTileSystemHelper.tileXYToQuadKey(x, y, zoomLevel);
try {
// Append the quadkey to the URL template to get a real URL
url = String.format(URL_FORMAT, quadKey);
} catch (Exception ex) {
ex.printStackTrace();
}
return url;
}
}
The example above generates a quadkey from the x, y coordinates and the zoom level and appends it to the
URL. However, this is server-specific and the method of converting x, y and zoom level to a URL can be done
in many ways. Also, it is worth noting that tiles can be cached with setCachingEnabled(true).
Changing the Overlay Rendering Order
You can choose to customize the order that raster tiles are rendered by calling
MapRasterTileSource.setOverlayType(MapOverlayType). For example
MapOverlayType.BACKGROUND_OVERLAY and MapOverlayType.BACKGROUND_REPLACEMENT are similar
to rendering raster tiles with streets rendered on top. MapOverlayType.FOREGROUND_OVERLAY renders
tiles on top of everything on the map.
62
HERE Android SDK Developer's Guide
► User Guide
Caching Tiles
Tiles can be cached to the disk by calling the following:
// Give the tile source a custom prefix so it can be cached on the disk
MapRasterTileSource.setCachePrefix(String cache)
// Give each raster tile file an expiration time in seconds.
MapRasterTileSource.setCacheExpiration( int seconds )
If no expiration time is set, then the raster tiles remains on the device. We recommend that both a cache
prefix and an expiration time be set.
Mobile Asset Management
The Mobile Asset Management (MAM) features provide useful information for logistics
companies to manage their fleet vehicles. You can enable features one at a time,
or multiple at the same time, using setFleetFeaturesVisible(EnumSet).
setFleetFeaturesVisible(EnumSet.noneOf(Map.FleetFeature.class)) disables all fleet
features.
Fleet Vehicle Map
The fleet vehicle map scheme is a scheme optimized for fleet management. These schemes can be used to
show road networks, as well as truck toll and highway exits.
To display fleet maps, pick one of the following truck map schemes:
•
•
•
•
TRUCK_DAY
TRUCK_NIGHT
TRUCK_HYBRID_DAY
TRUCK_HYBRID_NIGHT
For example:
map.setMapScheme(Map.Scheme.TRUCK_DAY);
63
HERE Android SDK Developer's Guide
► User Guide
The screenshot below shows highways with truck toll highlighted in purple and highway exit signs in Berlin.
Figure 32: Fleet Map of Berlin
For information about other map schemes, see Map Schemes on page 30.
Truck Restrictions
This fleet feature contains information about heavy vehicle route restrictions, such as height, weight, or
environmental restrictions. For example, trucks carrying flammable materials may not travel on certain
roads.
To display truck restrictions, add TRUCK_RESTRICTIONS to your fleet features as follows:
map.setFleetFeaturesVisible(EnumSet.of(Map.FleetFeature.TRUCK_RESTRICTIONS));
64
HERE Android SDK Developer's Guide
► User Guide
The screenshot below shows truck restrictions in London.
Figure 33: Truck Restrictions in London
Congestion and Environmental Zones
This fleet feature highlights congestion and environmental zones. Congestion zones are areas where certain
classes of vehicles must pay a toll to enter. Environmental Zones areas only admit certain kinds of vehicles
depending on their emissions class.
To display congestion and environmental zones, add CONGESTION_ZONES and ENVIRONMENTAL_ZONES to
your fleet features:
EnumSet features = map.getFleetFeaturesVisible();
features.add(Map.FleetFeature.CONGESTION_ZONES);
features.add(Map.FleetFeature.ENVIRONMENTAL_ZONES);
map.setFleetFeaturesVisible(features);
The screenshots below show congestion and environmental zones in London.
65
HERE Android SDK Developer's Guide
► User Guide
Figure 34: London Congestion Zones
Figure 35: London Environmental
Zones
Figure 36: London Congestion and
Environmental Zones (with Truck
Restrictions)
Fleet Connectivity
The Fleet Connectivity features allows applications that use the HERE Android SDK to receive job dispatches
from a fleet dispatcher. Each job dispatch contains custom information, such as a geocoordinate to a
destination, that can be used by your application for navigation or other purposes.
Your application, which is known as an asset in a fleet connectivity context, can also use this feature to
indicate its job status to the dispatcher. These job statuses include whether it is available to receive a job,
whether the job was accepted, and whether the job is finished.
Note: The HERE Android SDK contains APIs for you to implement a fleet connectivity client. For
instructions on how to use the fleet dispatcher features, see the Fleet Connectivity Extension
Developer's Guide at developer.here.com.
FleetConnectivityService
This singleton class holds information such as this fleet asset ID, the fleet dispatcher to connect to, the
running job, and job event polling interval.
Note: At a minimum, you must set the fleet asset and dispatcher IDs before starting the fleet
connectivity service.
FleetConnectivityEvent and FleetConnectivityService.Listener
You can set a FleetConnectivityService.Listener to the service to listen for dispatcher events.
Dispatcher events are highly customizable, with the only mandatory information being a Job ID. Otherwise,
you can define any type of information in its content payload, such as a set of geocoordinates, a Place ID,
or an address string.
There are two methods that need to be implemented in Listener:
66
HERE Android SDK Developer's Guide
► User Guide
•
•
onMessageReceived(FleetConnectivityMessage)
onEventAcknowledged(FleetConnectivityEvent, FleetConnectivityError)
Using the Fleet Connectivity Feature
The basic flow of how to use the Fleet Connectivity feature is as follows:
1.
Start the fleet connectivity service and begin listening for fleet connectivity events.
FleetConnectivityService service = FleetConnectivityService.getInstance();
service.setDispatcherId("Driver-321");
service.setAssetId("Truck-987");
if (service.start()) {
// service has started
} else {
// service has failed to start
}
2.
Implement Listener. Once a message is received, check if it is a dispatch job.
public void onMessageReceived(FleetConnectivityMessage message) {
if (message.getMessage() != null) {
// Display the optional message content
}
if (message instanceof FleetConnectivityJobMessage) {
// This message represents a job
FleetConnectivityJobMessage newDestinationMessage =
(FleetConnectivityJobMessage) message;
// Get job ID from newDestinationMessage.getJobId()
// Get location (in your preferred format) from
// newDestinationMessage.getMessage()
// Get threshold from newDestinationMessage.getEtaThreshold()
// (this threshold controls when ETA updates are sent back to dispatcher)
} else if (message instanceof FleetConnectivityCustomMessage) {
// This message does not represent a job
FleetConnectivityCustomMessage customMessage =
(FleetConnectivityCustomMessage) message;
// Get job ID from customMessage.getJobId()
// Get message from customMessage.getContent()
}
}
3.
Send an event to the dispatcher that the job has been accepted.
FleetConnectivityJobStartedEvent event =
new FleetConnectivityJobStartedEvent();
// populate event.setJobId(String)
// populate event.setEtaThreshold(long)
if (FleetConnectivityService.getInstance().sendEvent(event)) {
// job is running
// for example, your application can begin navigating to the job destination
} else {
// job has failed to start
}
Alternatively, you can also reject the the job by sending a rejection event.
FleetConnectivityJobRejectedEvent event =
new FleetConnectivityJobRejectedEvent();
// populate event.setJobId(String)
67
HERE Android SDK Developer's Guide
► User Guide
if (!FleetConnectivityService.getInstance().sendEvent(event)) {
// failed to reject job
}
4.
While a job is in the accepted state, you can tell the dispatcher that the job is canceled.
FleetConnectivityJobCancelledEvent event = new FleetConnectivityJobCancelledEvent();
if (!FleetConnectivityService.getInstance().sendEvent(event)) {
// failed to cancel job
}
5.
Upon job completion, notify the server that the job is finished. For example, you can choose to send this
event when your application has successfully finished the navigation session.
FleetConnectivityJobFinishedEvent event = new FleetConnectivityJobFinishedEvent();
if (!FleetConnectivityService.getInstance().sendEvent(event)) {
// failed to mark job as finished
}
6.
In the previous steps, after sending each event, your application receives an acknowledgment
from the dispatching server through the onEventAcknowledged(FleetConnectivityEvent,
FleetConnectivityError) callback.
public void onEventAcknowledged(FleetConnectivityEvent event, FleetConnectivityError error)
{
if (event instanceof FleetConnectivityJobStartedEvent) {
// the job start event is acknowledged
} else if (event instanceof FleetConnectivityJobRejectedEvent) {
// the job rejection event is acknowledged
} else if (event instanceof FleetConnectivityJobFinishedEvent) {
// the job completion event is acknowledged
} else if (event instanceof FleetConnectivityJobCancelledEvent) {
// the job cancellation event is acknowledged
} else if (event instanceof FleetConnectivityCustomEvent) {
// the custom event is acknowledged
}
}
7.
Stop the service by calling FleetConnectivityService.getInstance().stop().
Transit Information
Your application can use API calls from the HERE Android SDK to display transit information for users.
Note: The transit map schemes (NORMAL_DAY_TRANSIT, NORMAL_NIGHT_TRANSIT, and
HYBRID_DAY_TRANSIT) are specifically designed for displaying transit information. You can opt to
use one of these schemes when your app displays transit information.
MapTransitLayer
MapTransitLayer is a layer that displays the available transit data for a map area. To customize the transit
layer, call Map.getMapTransitLayer() to access the methods available through the MapTransitLayer
class. For example, to show all transit information available:
// Assumes map is instantiated
map.getMapTransitLayer().setMode(MapTransitLayer.Mode.EVERYTHING);
68
HERE Android SDK Developer's Guide
► User Guide
Note: MapTransitLayer settings may be affected when you change map schemes. For example,
changing the map scheme to NORMAL_DAY_TRANSIT enables the "everything" mode. It is
recommended that map scheme changes occur before changes in the MapTransitLayer.
Figure 37: MapTransitLayer set to show everything
To show only transit stops and accesses call:
// Assumes map is instantiated
map.getMapTransitLayer().setMode(MapTransitLayer.Mode.STOPS_AND_ACCESSES);
Figure 38: MapTransitLayer set to show only transit stops and accesses
To hide all transit information call:
// Assumes map is instantiated
map.getMapTransitLayer().setMode(MapTransitLayer.Mode.NOTHING);
Highlighting Transit Objects
The following four types of transit data objects are available:
69
HERE Android SDK Developer's Guide
► User Guide
•
Transit Stop data - represented by TransitStopObject
•
Transit Access data - represented by TransitAccessObject
•
•
Transit Line data - represented by TransitLineObject
Transit Line Segment data - represented by TransitLineSegmentObject
Transit objects can be selected through tap gestures. For example, to highlight one or more
TransitLineObject, you need to know the unique identifier of the line objects. Depending on the use
case, there are several ways of getting a single or a list of Identifier objects:
•
•
Call TransitLineObject.getLineId() when a user has selected a TransitLineObject by tapping
on it. It returns an Identifier of the selected transit line.
Call TransitStopObject.getTransitStopInfo().getLines() when a user has selected a
TransitStopObject via tapping. getLines() returns a list of Identifier of the lines connected to
the selected transit stop.
For details of handling tappable MapProxyObjects, see Handling MapProxyObject objects on page 45.
With a single or a list of Identifier objects, you call the following API to highlight the lines:
// Assumes map is instantiated and identifierList is
// filled with a list of Identifiers
map.getMapTransitLayer().highlightTransitLines(identifierList);
Figure 39: MapTransitLayer highlighting transit lines connected to the selected transit stop
70
HERE Android SDK Developer's Guide
► User Guide
TransitStopObject
A TransitStopObject is a type of MapProxyObject that contains information about a transit stop. The
following figures show the different types of transit stops:
Figure 40: TransitStopObject: A metro station
Figure 41: TransitStopObject: A ferry station
To acquire information about a tapped TransitStopObject (see Handling MapProxyObject objects on page
45 ) use onMapObjectsSelected(List) as follows:
private MapGesture.OnGestureListener listener = new MapGesture.OnGestureListener() {
...
@Override
public boolean onMapObjectsSelected(List objects) {
for (ViewObject obj : objects) {
if (obj.getBaseType() == ViewObject.Type.PROXY_OBJECT) {
MapProxyObject proxyObj = (MapProxyObject) obj;
if (proxyObj.getType() == MapProxyObject.Type.TRANSIT_STOP) {
TransitStopObject transitStopObj
= (TransitStopObject) proxyObj;
Log.d(TAG, "Found a TransitStopObject");
Log.d(TAG, "position is "
+ transitStopObj.getCoordinate().toString());
TransitStopInfo transitStopInfo
= transitStopObj.getTransitStopInfo();
...
}
}
return true;
}
The TransitStopObject provides two methods for getting information about the transit stop:
•
•
getCoordinate() gets the location coordinates of the transit stop.
getTransitStopInfo() gets further information about the transit stop.
71
HERE Android SDK Developer's Guide
► User Guide
TransitStopInfo
The TransitStopInfo class contains transit stop information that is accessed by calling one or more of the
following methods
•
getOfficialName() gets the official name of the transit stop
•
getId() gets the Identifier of the transit stop
•
•
•
getInformalName() gets the informal name of the transit stop
getTransitTypes() gets the transit types this transit stop belongs to; there can be more than one.
getLines() gets a list of Identifier objects for transit lines connected to this transit stop
Each Identifier is submitted to the TransitDatabase to get further information. For more details, see
TransitDatabase. Also they can be submitted to the MapTransitLayer to get highlighted on the map. (See
MapTransitLayer on page 68)
An example of getting information about the first transit line connected to the transit stop is provided
below. A TransitDatabase.OnGetTransitInfoListener needs to be implemented to receive the
TransitLineInfo. (See TransitLineInfo on page 74)
An asynchronous request is submitted to the TransitDatabase along with the
OnGetTransitInfoListener.
TransitDatabase.OnGetTransitInfoListener listener
= new TransitDatabase.OnGetTransitInfoListener() {
......
@Override
public void onTransitLineInfo(TransitLineInfo info) {
......
}
// transitStopInfo is a TransitStopInfo object
......
mTransitDatabase.getLineInfo(transitStopInfo.getLines().get(0), listener);
72
HERE Android SDK Developer's Guide
► User Guide
TransitLineObject
A TransitLineObject is a type of MapProxyObject that contains information about a transit line. The
following figure shows examples of different types of transit lines:
Figure 42: Three types of transit lines: Metro, Train and Water
To acquire information about a tapped TransitLineObject (see Handling MapProxyObject objects on page
45 ) use onMapObjectsSelected(List) as illustrated in the following code:
private MapGesture.OnGestureListener listener = new MapGesture.OnGestureListener() {
...
@Override
public boolean onMapObjectsSelected(List objects) {
for (ViewObject obj : objects) {
if (obj.getBaseType() == ViewObject.Type.PROXY_OBJECT) {
MapProxyObject proxyObj = (MapProxyObject) obj;
if (proxyObj.getType() == MapProxyObject.Type.TRANSIT_LINE) {
TransitLineObject transitLineObj
= (TransitLineObject) proxyObj;
Log.d(TAG, "Found a TransitLineObject");
Log.d(TAG, "Id is "
+ transitLineObj.getLineId().toString());
mTransitDatabase.getLineInfo(m_lineIdList.get(0),
mOnGetTransitInfoListener);
}
}
}
return true;
}
The TransitLineObject provides a single method for getting the Identifier of the transit line.
This Identifier can be submitted to the MapTransitLayer to get highlighted on the map. (For more
information, refer to MaptransitLayer)
As shown in the example above, the Identifier can also be submitted to the TransitDatabase (see
TransitDatabase) along with the OnGetTransitInfoListener to get more information about the transit
73
HERE Android SDK Developer's Guide
► User Guide
line. mOnGetTransitInfoListener is implemented to receive the TransitLineInfo object from the
TransitDatabase.
TransitDatabase.OnGetTransitInfoListener mOnGetTransitInfoListener
= new TransitDatabase.OnGetTransitInfoListener() {
...
@Override
public void onTransitLineInfo(TransitLineInfo info) {
...
}
}
TransitLineInfo
The TransitLineInfo class contains transit line information that is accessed by calling one or more of the
following methods:
•
getOfficialName() gets the official name of the transit line
•
getShortName() gets the short name of the transit line
•
•
getInformalName() gets the informal name of the transit line
getTransitType() gets the transit types this transit line belongs to.
TransitAccessObject
A TransitAccessObject is a type of MapProxyObject that contains information about a transit access. A
transit access is an entrance/exit to a transit stop. There can be multiple transit accesses to a transit stop.
Transit access is presented as a smaller transit stop with a downward triangle attached to the bottom and is
visible only in higher zoom levels. The icons presenting the transit stops and access vary between different
countries and companies. The following figures show two examples:
Figure 43: Transit Stop and Access: Metro Station with Single Access
Figure 44: Transit Stop and Access: Metro Station with Mutiple
Accesses
74
HERE Android SDK Developer's Guide
► User Guide
To acquire information about a tapped TransitAccessObject (see Handling MapProxyObject objects on
page 45) use onMapObjectsSelected(List) as in the following code:
private MapGesture.OnGestureListener listener = new MapGesture.OnGestureListener() {
...
@Override
public boolean onMapObjectsSelected(List objects) {
for (ViewObject obj : objects) {
if (obj.getBaseType() == ViewObject.Type.PROXY_OBJECT) {
MapProxyObject proxyObj = (MapProxyObject) obj;
if (proxyObj.getType() == MapProxyObject.Type.TRANSIT_ACCESS) {
TransitAccessObject transitAccessObj
= (TransitAccessObject) proxyObj;
Log.d(TAG, "position is " +
transitAccessObj.getCoordinate().toString());
TransitAccessInfo transitAccessInfo
= transitAccessObj.getTransitAccessInfo();
...
break;
}
}
}
}
return true;
The TransitAccessObject provides two methods for getting information about the transit access:
•
•
getCoordinate() gets the location coordinates of the transit access.
getTransitAccessInfo() gets further information about the transit access.
TransitAccessInfo
The TransitAccessInfo class contains transit access information that can be accessed by calling one or
more of the following methods
•
•
getTransitTypes() gets the transit types this transit access belongs to; there can be more than one.
getStopId() gets a unique Identifier of the transit stop that this transit access leads to.
In the next example, the unique identifier of the transit stop is submitted to the TransitDatabase to get
further information. For more details, see TransitDatabase.
// transitAccessInfo is a TransitAccessInfo object
Log.d(TAG, "transit type is " +
transitAccessInfo.getTransitTypes().toString());
Log.d(TAG, "Stop Id is " +
transitAccessInfo.getStopId().toString());
mTransitDatabase.getStopInfo(transitAccessInfo
.getStopId(), mOnGetTransitInfoListener);
An example of getting information about the destination transit stop of a transit access is provided below.
An OnGetTransitInfoListener needs to be implemented to receive the TransitStopInfo object. An
asynchronous request is submitted to the TransitDatabase along the OnGetTransitInfoListener. For
more information, see TransitStopInfo.
TransitDatabase.OnGetTransitInfoListener mOnGetTransitInfoListener
= new TransitDatabase.OnGetTransitInfoListener(){
......
75
HERE Android SDK Developer's Guide
► User Guide
}
@Override
public void onTransitStopInfo(TransitStopInfo info) {
......
}
// transitAccessInfo is a TransitAccessInfo object
......
mTransitDatabase.getStopInfo(transitAccessInfo.getStopId(),
mOnGetTransitInfoListener);
TransitSystemInfo
The TransitSystemInfo class contains information about a public transit system that can be accessed by
calling one or more of the following methods:
•
getSystemOfficialName() - gets the official name of the transit system
•
getCompanyOfficialName() - gets the official transit system company name
•
•
•
•
•
•
•
•
getSystemWebsitUrl() - gets the website URL of the transit system
getCompanyWebsiteUrl() - gets the website URL of the transit system company
getCompanyRoutePlannerUrl() - gets the route planner URL of the transit system company
getCompanyScheduleUrl() - gets the schedule url of the transit system company
getCompanyPhone() - gets the phone number for the transit system company
getBicycleHours() - gets the tranit system's bicycle parking hours
getSystemLogo() - gets the system logo (if present)
getCompanyLogo() - gets the companyLogo (if presents)
An example of retrieving transit system information is provided below. In this example, an
OnGetTransitInfoListener is implemented to receive the TransitSystemInfo object. For more
information, see the TransitDatabase section.
TransitDatabase.OnGetTransitInfoListener mOnGetTransitInfoListener =
new TransitDatabase.OnGetTransitInfoListener() {
...
@Override
public void onTransitSystemInfo(TransitSystemInfo systemInfo) {
String officialName = systemInfo.getSystemOfficialName();
}
...
}
// transitLineInfo is a TransitLineInfo object
mTransitDatabase.getSystemInfo(transitLineInfo.getSystemId(),
mOnGetTransitInfoListener);
TransitDatabase
The TransitDatabase class is responsible for querying transit information of various types using a unique
Identifier , with a OnGetTransitInfoListener for monitoring query results and triggering appropriate
callback methods upon completion. Applications can call the TransitDatabase constructor to activate a
TransitDatabase for querying transit information.
76
HERE Android SDK Developer's Guide
► User Guide
The OnGetTransitInfoListener interface can be used to monitor query results of the
TransitDatabase. It must be implemented within the application and submitted as part of the
asynchronous query request.
TransitDatabase.OnGetTransitInfoListener mOnGetTransitInfoListener
= new TransitDatabase.OnGetTransitInfoListener() {
@Override
public void onTransitLineInfo(TransitLineInfo info) {
//...
}
@Override
public void onTransitStopInfo(TransitStopInfo info) {
//...
}
@Override
public void onTransitAccessInfo(TransitAccessInfo info) {
//...
}
@Override
public void onTransitSystemInfo(TransitSystemInfo info) {
//...
}
@Override
public void onEnd(TransitDatabase.Error error) {
//...
}
};
The OnGetTransitInfoListener class provides five callbacks:
•
onTransitLineInfo provides a TransitLineInfo object. (See TransitLineInfo on page 74)
•
onTransitAccessInfo provides a TransitAccessInfo object. (See TransitAccessInfo on page 75)
•
•
•
onTransitStopInfo provides a TransitStopInfo object. (See TransitStopInfo on page 72)
onTransitSystemInfo provides a TransitSystemInfo object. (See TransitSystemInfo on page 76)
onEnd is a callback that signifies the asynchronous query request has completed.
Note: TransitDatabase rejects all subsequent requests unless it has completed the current
request. If the TransitDatabase is busy, INVALID_OPERATION is returned.
An asynchronous request is submitted to the TransitDatabase along with the
OnGetTransitInfoListener. Note that the TransitDatabase instance is created by calling the
TransitDatabase constructor.
The following lists the main use cases of the TransitDatabase:
•
getLineInfo() - Pass in TransitLineObject.getLineId() when a user has selected a
TransitLineObject by tapping on it. This method returns an Identifier of a selected transit line.
// transitLineObject is a TransitLineObject object
......
mTransitDatabase.getLineInfo(transitLineObject
.getLineId(), mOnGetTransitInfoListener);
77
HERE Android SDK Developer's Guide
► User Guide
•
getLineInfo() - Pass in TransitStopObject.getTransitStopInfo().getLines() when a user
has selected a TransitStopObject by tapping on it. This method returns a list of Identifiers for
the lines connected to the selected transit stop.
// transitStopInfo is a TransitStopInfo object
......
// Requesting transit line info of the first identifier on the list.
mTransitDatabase.getLineInfo(transitStopInfo
.getLines().get(0), mOnGetTransitInfoListener);
•
getStopInfo() - Pass in TransitAccessInfo.getStopId() when a user has selected a
TransitAccessObject by tapping on it. This method returns an Identifier of the stop that the
transit access leads to.
// transitAccessInfo is a TransitAccessInfo object
......
mTransitDatabase.getStopInfo(transitAccessInfo
.getStopId(), mOnGetTransitInfoListener);
Transit-related enumerations
•
•
The TransitType enum - represents values describing different transit types, such as BUS_PUBLIC,
RAIL_METRO or TRAIN_REGIONAL.
The TransitDatabase.Error enum - represents values describing possible transit database errors,
such as NONE or INVALID_PARAMETERS
Map Customization
Whether you want to optimize your map for a certain display size, use case, branding, or highlight objects
which are important to your users, the HERE SDK map customization feature allows a high degree of
customization freedom so that you can achieve fine control of your map view's rendering characteristics.
This section presents the components and concepts which you need to create your own map look-and-feel.
Map Schemes
Map customization starts by using one of the predefined schemes (such as "Normal Day" and "Normal Night")
to serve as a staring point. These predefined schemes are not customizable themselves, but provide the
initial values that a custom scheme derives from.
The following screenshots show examples of pre-defined schemes available in HERE SDK.
Figure 45: Normal Day scheme
Figure 46: Normal Night scheme
78
HERE Android SDK Developer's Guide
► User Guide
To customize the map, the first step is to obtain a Customizable Scheme object from the map view. With this
object, you can then set properties to modify the map. You can change color, icon size, width, length, and
other properties of almost all map objects such as buildings, land features, and roads. Whether property
changes are visible depends on if the map you are customizing is currently at the affected zoom level, and
the map scheme is active.
Note: A custom scheme is not permanently saved, but it lives as long as the map view object is in
memory.
Creating Your First Map Scheme Customization
After you decide on which scheme to base on, create a Customizable Scheme object with the map view
method:
CustomizableScheme scheme = map.createCustomizableScheme("newCustomScheme", Map.Scheme.NORMAL_DAY);
After creating the customizable scheme, you can retrieve it again with the following method, as long as the
map view object was not destroyed. Customizable Schemes are created and valid only to the specific Map
View from which was obtained:
CustomizableScheme scheme = map.getCustomizableScheme("newCustomScheme");
You can then get map attributes for a particular zoom level. To set attributes, specify a zoom range. A helper
ZoomRange class is provided, which takes a minimum and maximum zoom level value.
ZoomRange range = new ZoomRange (0, 20);
The HERE SDK has a class called CustomizableVariables which list all the attributes that the HERE SDK
supports for customization.
Note that in the CustomizableVariables class, map attributes are wrapped in their respective
types to identify their type. For example, CustomizableVariables.CountryBoundary.WIDTH is of
SchemeFloatProperty type which means that it is holding float value. The same applies for the others.
The SDK provides following helper classes to handle the different types of properties:
•
•
•
CustomizableColorProperty - specify color type (In Android, Color is represented as ARGB integer
color value)
SchemeFloatProperty - specify float type
CustomizableIntegerProperty - specify int type
To change map attributes for color, float, and integer types, perform the following:
// change 0M water color
CustomizableScheme.ErrorCode errorCode =
scheme.setVariableValue(CustomizableVariables.Water.COLOR_0M, Color.RED, range);
// get water color for zoom level 10
int waterColor0M = scheme.getVariableValue(CustomizableVariables.Water.COLOR_0M, 10.0f);
// change 3d landmark
CustomizableScheme.ErrorCode errorCode =
scheme.setVariableValue(CustomizableVariables.Landmark3d.ALPHA, 1, range);
// get 3d landmark for zoom level 10
int waterColor0M = scheme.getVariableValue(CustomizableVariables.Landmark3d.ALPHA, 10.0f);
// change CountryBoundary WIDTH
79
HERE Android SDK Developer's Guide
► User Guide
CustomizableScheme.ErrorCode errorCode =
scheme.setVariableValue(CustomizableVariables.CountryBoundary.WIDTH, 2.0f, range);
// get CountryBoundary WIDTH for zoom level 10
float countryBoundaryWidth = scheme.getVariableValue(CustomizableVariables.CountryBoundary.WIDTH,
10.0f);
After customizing, you should activate your scheme by calling one of following methods:
Map.setMapScheme(CustomizableScheme customizableScheme);
or
Map.setMapScheme(String scheme)
Note: You can customize the current activated custom scheme, but changes are not automatically
applied. Remember to call one of the above methods to refresh the map scheme.
The following is a more complete example:
// create a new scheme
CustomizableScheme scheme = map.createCustomizableScheme("newCustomScheme", Map.Scheme.NORMAL_DAY);
// change water color 0m
int lightYellow = Color.argb(0, 255,255,224);
CustomizableScheme.ErrorCode err = scheme.setVariableValue(CustomizableVariables.Water.COLOR_0M,
lightYellow , range);
Log.i(TAG, "Error: " + err);
// change water color 3000m
err = scheme.setVariableValue(CustomizableVariables.Water.COLOR_3000M, Color.YELLOW, range);
Log.i(TAG, "Error: " + err);
// activate scheme
80
HERE Android SDK Developer's Guide
► User Guide
map.setMapScheme(scheme);
Figure 47: Example Output
Positioning
HERE Android SDK positioning interface allows applications to choose from two different location
information sources:
•
Basic Positioning is described in Basic Positioning on page 81 and provides a simple interface to the
•
Advanded Positioning by HERE is described in Advanced Positioning by HERE on page 85. Advanced
location information provided by the Android platform. This location source is always at the developer's
disposal regardless of the HERE Android SDK license.
positioning is accessed via the same interface as the Basic Positioning, but the underlying location
source is HERE Positioning that provides developers, amongst other things, advanced offline network
positioning as well as indoor positioning.
Note: To use HERE Positioning, one or more HERE Positioning features needs to be enabled in the
HERE Android SDK license.
Basic Positioning
The HERE Android SDK provides the following interfaces for users to retrieve location updates and to display
their current location on a map:
•
•
PositioningManager
OnPositionChangedListener
81
HERE Android SDK Developer's Guide
► User Guide
•
PositionIndicator
Note: The Android permission android.permission.ACCESS_FINE_LOCATION is required when
your app calls PositioningManager.start(LocationMethod). Otherwise, the method returns
false. In addition, to ensure that the app receives location updates, the user needs to have the
Location permission enabled (toggled to "on") during runtime.
PositioningManager Class
A PositioningManager class provides information related to the device's geographical location, like the
current position and the average speed. Applications can register to receive position updates using one of
the positioning mechanisms described in the LocationMethod:
•
GPS - positioning using the real GPS available on the device.
•
NETWORK - positioning using a wireless network.
•
GPS_NETWORK - positioning is provided using a wireless network or the real GPS available on the device
Note: See Advanced Positioning by HERE on page 85 for more information about additional
Location Method types.
The current status of a particular location method is represented by the LocationStatus value returned
from the PositioningManager.getLocationStatus(LocationMethod) method.
PositioningManager can be accessed by calling PositioningManager.getInstance().
An application can start receiving real time positioning updates by calling
PositioningManager.start(LocationMethod) with one of the location methods listed above
and can stop the positioning updates by calling PositioningManager.stop(). While position
updates are being received, an application can retrieve the current position of the client device via the
PositioningManager.getPosition() method.
OnPositionChangedListener Interface
In addition to the PositioningManager's getPosition() method, applications can
subscribe to position update notifications from the PositioningManager through the
PositioningManager.OnPositionChangedListener interface. To add or remove
OnPositionChangedListener, applications can use the following methods:
PositioningManager.addListener(WeakReference)
PositioningManager.removeListener(OnPositionChangedListener)
The positioning manager enhances your application with the current position of the user's device. The
registration of the positioning listener should be performed after the MapFragment, MapView, or
MapEngine is initialized as described in the following code snippet.
// Define positioning listener
private OnPositionChangedListener positionListener = new
OnPositionChangedListener() {
public void onPositionUpdated(LocationMethod method,
GeoPosition position, boolean isMapMatched) {
// set the center only when the app is in the foreground
// to reduce CPU consumption
if (!paused) {
map.setCenter(position.getCoordinate(),
Map.Animation.NONE);
82
HERE Android SDK Developer's Guide
► User Guide
}
}
public void onPositionFixChanged(LocationMethod method,
LocationStatus status) {
}
};
// Register positioning listener
PositioningManager.getInstance().addListener(
new WeakReference(positionListener));
...
In order to avoid unnecessary position updates while the activity is in the background, you need to start or
stop the PositioningManager within your activity's onResume() and onPause() methods.
// Set this to PositioningManager.getInstance() upon Engine Initialization
private PositioningManager posManager;
...
// Resume positioning listener on wake up
public void onResume() {
super.onResume();
paused = false;
if (posManager != null) {
posManager.start(
PositioningManager.LocationMethod.GPS_NETWORK);
}
}
// To pause positioning listener
public void onPause() {
if (posManager != null) {
posManager.stop();
}
super.onPause();
paused = true;
}
// To remove the positioning listener
public void onDestroy() {
if (posManager != null) {
// Cleanup
posManager.removeListener(
positionListener);
}
map = null;
super.onDestroy();
}
PositionIndicator Class
PositionIndicator is a special map marker object that allows the current client device position to be
shown on a map. Every HERE SDK Map object has an integrated position indicator, set to invisible, by default.
The indicator can be retrieved and set to visible by calling MapFragments.getPositionIndicator() and
PositionIndicator.setVisible(), as follows:
// Display position indicator
mapFragment.getPositionIndicator().setVisible(true);
83
HERE Android SDK Developer's Guide
► User Guide
By default, the position indicator is rendered as a marker surrounded by a circle, the diameter
of which illustrates the accuracy of the indicated position. You can change this marker by calling
PositionIndicator.setMarker(Image).
Figure 48: A PositionIndicator
Note: For the position indicator to stay in the center of the map and illustrate real-time updates of
the device's position, it is necessary to update the map's center whenever a new location update is
received.
Note: PositionIndicator only works if the application has started the PositioningManager.
Position Simulation and Creating Position Logs
You can use PositionSimulator to simulate device position by injecting locations
into the Android LocationManager. Locations are read from GPX log files. After calling
PositionSimulator.startPlayback(String), the positions in the log file are processed until the end
of the log is reached or stopPlayback() is called.
You can also use the HERE SDK to create the GPX logs that can be replayed by PositionSimulator.
To do this, call setLogType(EnumSet) in PositioningManager to include
LogType.DATA_SOURCE. GPX logs are written to a "gpx" sub-directory of your appication's data directory —
for example, "/sdcard/Android/data/com.companyName.appName/files/gpx/". To disable logging,
call setLogType(EnumSet.noneOf(LogType.class)).
Note: This feature is only intended for debugging purposes. Do not use Position Logging in a
production application.
Note: PositionSimulator does not support indoor positioning.
84
HERE Android SDK Developer's Guide
► User Guide
Advanced Positioning by HERE
In addition to the basic platform positioning, HERE Android SDK provides advanced HERE Positioning with the
following key features:
•
Cellular network positioning in GSM, CDMA, WCDMA, TD-SCDMA and LTE networks
•
High accuracy indoor positioning with building and floor detection using wifi and Bluetooth radios
•
•
•
•
•
Wifi network positioning
Automatic, on-demand download of radio positioning data for positioning without network connection
(offline)
Note: Offline functionality is not supported for CDMA positioning.
Automatic positioning method switching between satellite-based (GNSS), wifi, Bluetooth, and cellular
network positioning, providing the best possible position information using the available methods.
HERE Indoor Positioning supports both private and public data. You can have a private venue that is
mapped through HERE Private Venues on page 174 or your own custom indoor map, with the indoor
location information being only available for your applications. In contrast, indoor location information
for public venues is available for all HERE SDK users with an appropriate license.
Global positioning coverage and data hosting infrastructure for the optimal availability, reliability, and
user experience.
HERE Positioning Feature Groups
HERE Positioning is split into features groups. Depending on your business plan, you may have access to one
or more of the following:
•
Online Positioning
This feature group provides online positioning by sending anonymous network measurements
(cellular and wifi) to the HERE Positioning servers to resolve the device location based on the provided
measurements. Online positioning offers limited offline positioning capabilities through caching:
•
measurements and resolved positions are stored into a local cache from which they can be later used
without network connection.
Offline Positioning
This feature group provides offline network positioning (cellular and wifi) by utilizing downloaded
positioning assistance data, i.e. radiomaps, for the position estimation within the device. In this context
offline means that network connectivity is only required for the radiomap download after which no
connectivity is needed unless further radiomap tiles are needed. This may happen due to the device
movement or radiomap expiration.
Radiomap download is handled by the on-demand downloader, which automatically downloads
radiomap tiles in the vicinity of the device. The downloader also handles the maintenance of the
radiomaps in the device by updating the radiomap tiles at regular intervals as needed, and by removing
the oldest radiomap tiles in case the storage consumed by the radiomap tiles exceed the quota. This
quota is defined per technology and is configured as speficied in the table below. The table also defines,
how often the HERE Android SDK attempts to update the radiomaps.
85
HERE Android SDK Developer's Guide
► User Guide
Public Indoor
•
This feature group provides high accuracy indoor positioning for public venues from which indoor radio
data (wifi or Bluetooth) has been collected using the HERE Indoor Radio Mapper tool. The feature uses
HERE Indoor Positioning community radiomaps that are accessible by all HERE Android SDK users having
access to this feature. Further details can be found in the HERE Indoor Positioning Installation Guide.
Similarly to Offline Positioning, HERE Public Indoor Positioning works in offline mode. The storage quota
and the radiomap tile update interval is specified in the table below.
Private Indoor
•
This feature group provides high accuracy indoor positioning for private venues from which indoor
radio data (wifi or Bluetooth) has been collected using the HERE Indoor Radio Mapper tool. In contrast
to the Public Indoor feature, the Private Indoor feature provides indoor positioning capability that
is accessible only by the owner of the radiomap. Further details can be found in the HERE Indoor
Positioning Installation Guide.
Similarly to the Offline Positioning, also HERE Private Indoor Positioning works in the offline mode. The
storage quota and the radiomap tile update interval is specified in the table below.
Table 1: Maximum Storage Consumption and Update Interval Per Technology
Technology
Quota
Update interval
Description
Offline cellular
32MB
23 days
Radiomaps for cellular network positioning (all network technologies).
Offline wifi
32MB
23 days
Radiomaps for wifi network positioning.
Indoor wifi and
32MB
7 days
Radiomaps (private and community) for high accuracy indoor wifi and
Bluetooth
Bluetooth positioning.
Using HERE Positioning
To start using HERE Positioning in the Android applications, complete the following steps:
1.
Embed the HERE Positioning service in your application
3.
Set the positioning data source to HERE Positioning and start the Positioning Manager
2.
4.
Add the required Android permissions to your application
Receive and handle location updates
In detail, the following actions need to be taken:
1) Embedding the HERE Positioning service
To embed the HERE Positioning service, add the following lines to the
section in AndroidManifest.xml:
2) Add the Android permissions to your application
86
HERE Android SDK Developer's Guide
► User Guide
Add the following permissions to AndroidManifest.xml:
android:name="android.permission.BLUETOOTH" />
android:name="android.permission.BLUETOOTH_ADMIN"/>
android:name="android.permission.CHANGE_NETWORK_STATE" />
android:name="android.permission.CHANGE_WIFI_STATE" />
android:name="android.permission.READ_PHONE_STATE" android:maxSdkVersion="22" />
android:name="android.permission.WAKE_LOCK" />
Note: If your application uses Android target SDK version level 23 or above, you must add code to
request permissions at runtime. For more detailed instructions and example code, see Requesting
Android Permissions on page 210.
3) Change the positioning data source and start PositioningManager
After completing the previous configuration steps, change the Positioning Manager data source to HERE
Positioning and start the Positioning Manager with the suitable location method to start receiving HERE
Positioning location updates:
m_hereDataSource = LocationDataSourceHERE.getInstance();
if (m_hereDataSource != null) {
PositioningManager pm = PositioningManager.getInstance();
pm.setDataSource(m_hereDataSource);
pm.addListener(new WeakReference(this));
if (pm.start(PositioningManager.LocationMethod.GPS_NETWORK_INDOOR)) {
// Position updates started successfully.
}
}
For detailed description of the location methods available for the HERE Positioning location data source,
please see the section Location Methods on page 88.
Note:
•
•
•
•
LocationDataSourceHERE.getInstance() cannot be instantiated if no HERE Positioning
features are enabled in the SDK license.
You cannot start PositioningManager with a location method containing indoor positioning
without first setting location data source to HERE
You can only start PositioningManager with a location method that is enabled by your
business plan.
When using a location method containing indoor positioning, the indoor positioning mode can
be controlled. Please see the section Indoor Positioning Mode on page 89 below for further
information.
4) Receive and handle location updates
The location updates are handled by onPositionUpdated() (when the device location
changes) and onPositionFixChanged() (when location method changes) methods from
OnPositionChangedListener:
@Override public void onPositionUpdated(final PositioningManager.LocationMethod locationMethod,
final GeoPosition geoPosition, final boolean mapMatched) {
// new position update received
}
@Override public void onPositionFixChanged(PositioningManager.LocationMethod locationMethod,
PositioningManager.LocationStatus locationStatus) {
// positioning method changed
87
HERE Android SDK Developer's Guide
► User Guide
}
Note:
•
•
•
PositioningManager.OnPositionChangedListener represents an interface for the
position update listeners.
GeoPosition carries GeoCoordinate that contains the device WGS84 Latitude/Longitude
coordinates and altitude with double precision. In addition, GeoPosition carries location
uncertainty estimate as well as speed and heading information.
For indoor positioning, GeoPosition carries building information (building name and building
ID) as well as floor level information.
Location Methods
The Location methods refers to the set of technologies used in the location determination. HERE Positioning
supports a plethora of location methods. The following table introduces the location methods that the
PositioningManager can be started with, when the location data source is LocationDataSourceHERE.
Table 2: Supported Location Methods
Method
Description
GPS
Uses the satellite-based positioning (GNSS) as provided by the Android platform.
NETWORK
Uses the online or offline wifi or cellular network positioning methods, depending upon the device
INDOOR
Uses either wifi or Bluetooth radio to provide indoor location estimates, with building and floor information,
GPS_NETWORK
Uses satellite-based positioning and network positioning, always selecting the best technology for optimal
capabilities, always selecting the best radio technology for location estimation.
always selecting the best radio technology in case both are available.
location estimation.
GPS_NETWORK_INDOOR Uses satellite-based positioning, network positioning and indoor positioning, always selecting the best
technology for optimal location estimation.
Pre-downloading radiomaps to the device
The RadioMapLoader interface provides a mechanism to pre-download radiomaps surrounding a specified
center point to the device. Having pre-downloaded radiomaps to the device, the device can be located
without data connectivity. In addition to the center point, the interface needs to be provided with the type of
the radiomap to be downloaded. The available types are:
Table 3: Radiomap types available for pre-download
Mode
Description
SPARSE
Configures the download of the cellular radiomaps for coarse positioning (accuracy hundreds of meters) within
COARSE
Configures the download of the cellular radiomaps and the highly compressed wifi radiomaps to balance data
DETAILED
Configures the download of the cellular radiomaps and the less compressed wifi radiomaps to favor accuracy
INDOOR
Configures the download of the radiomaps necessary for indoor positioning. Radiomaps are downloaded within
15 kilometers from the center point.
consumption and positioning accuracy. Radiomaps are downloaded within 5 km from the center point.
over data consumption. Radiomaps are downloaded within one km from the center point.
500 m from the center point.
88
HERE Android SDK Developer's Guide
► User Guide
Instead of defining the center point and the radiomap type to the interface, it is also possible to provide the
Venue Map information as com.here.android.mpa.venues3d.Venue to the interface. In this case the job
downloads the radiomaps necessary for indoor positioning at that particular venue.
The interface also provides a mechanism to delete all the downloaded radiomaps as well as to observe the
status and the progress of the download job.
Indoor Positioning Mode
The indoor positioning mode refers to the set of radiomaps used by the indoor positioning engine. For
detailed discussion on the different radiomaps, please refer to HERE Indoor Positioning Installation Guide.
Indoor positioning can be configured to run in four different modes, which are defined by
the LocationDataSourceHERE.IndoorPositioningMode enumeration. Use the method
setIndoorPositioningMode() from the LocationDataSourceHERE class to change the indoor
positioning mode. Use the method getIndoorPositioningMode() from the same class to check the
current mode.
The following table introduces the four indoor positioning modes.
Table 4: Indoor Positioning Modes Supported by the HERE Positioning
Mode
Description
AUTOMATIC
HERE Positioning automatically chooses which mode to apply. Requires that the SDK license has both public
and private indoor positioning features enabled. If both features are enabled in the SDK license, then this
positioning mode is set as the default.
COMMUNITY
Community indoor radiomap is used by the indoor positioning engine. Requires that the SDK license has the
public indoor positioning feature enabled. If only the public indoor positioning feature is enabled in the SDK
license, then this indoor positioning mode is set as the default.
PRIVATE
The customer's private indoor radiomap is used by the indoor positioning engine. Requires that the SDK license
has private indoor positioning feature enabled. If only the private indoor positioning feature is enabled in the
SDK license, then this indoor positioning mode is set as the default.
DRAFT
Mode used for testing the draft indoor radiomap before publishing the draft to production servers. Requires
that either public or private indoor feature is enabled in the SDK license. Please see the Using the Draft Indoor
Positioning Mode on page 89 section below for further details.
Using the Draft Indoor Positioning Mode
A Draft Radiomap is a sandbox for the system operators and developers to test and try out positioning
without affecting the applications using HERE Indoor Positioning. When radio data is collected and tested in
the HERE Indoor Radio Mapper, the tool always uses a Draft Radiomap. Once the Draft Radiomap shows good
performance, the radio data is published to the production radiomap (private or public one, depending upon
the venue type) for the use by HERE Android SDK.
In the production configuration, HERE Android SDK fetches positioning data from the production radiomaps.
However, for R&D purposes, HERE Android SDK can be configured to use a draft radiomap. The DRAFT
indoor positioning mode allows testing the unpublished draft indoor radiomap in the actual application
(as opposed to testing only in the HERE Indoor Radio Mapper) before publishing the Draft Radiomap to
production. However, since this is an R&D feature, this mode is limited to a maximum of ten devices that
have to be defined before compiling the application.
89
HERE Android SDK Developer's Guide
► User Guide
For detailed discussion on the radiomaps and data flows, please refer to HERE Indoor Positioning Installation
Guide.
To enable the DRAFT indoor positioning mode for a specific device, do the following:
1.
Find out the Android ID of the device that you want to enable the DRAFT indoor positioning mode for.
a.
b.
Run command adb shell getprop | grep net.hostname via the Android debug bridge
Inspect the command output. For example:
[net.hostname]: [android-1234567812345678]
c.
2.
3.
The Android ID is what appears after the string "android-".
Please note that the Android ID may change when the device factory settings are returned or when
switching the user profile in a device with multi-user support
Add a new XML resource to your project using the Android Studio and name it, for example, as
draft_enabled_devices.xml
Add the Android ID to the string-array within resources as follows in
draft_enabled_devices.xml:
- 1234567812345678
4.
Note: Up to ten Android IDs can be defined in the string-array. If more than ten Android IDs
are defined, none of them will work.
Add a new metadata element defining the draft-enabled devices to your AndroidManifest.xml:
Note: This field must be added at the same level with the HERE Application ID, Application Code
and the SDK License.
Note: The resource name must match to the string-array name created in the step 3.
Indoor Positioning-specific Methods
HERE Positioning has three indoor positioning-specific methods, which are methods of geoPosition class:
•
String getBuildingName()
Returns the BuildingName, if known. If the BuildingName is not present, null is returned.
For HERE Venue Maps the BuildingName is assigned by HERE, when creating the Venue Map. This
method returns that name.
For custom indoor maps, the BuildingName is the one given when the indoor map was imported using
•
HERE Indoor Radio Mapper. However, illegal characters are removed from the name.
String getBuildingId()
Returns the BuildingID, if known. If the BuildingID is not present, null is returned.
90
HERE Android SDK Developer's Guide
► User Guide
For HERE Venue Maps, the BuildingID is the globally unique ID assigned by HERE, when creating the
Venue Map. An example of a HERE Venue Map BuildingID is DM_8213.
For custom indoor maps, the BuildingID is the customer-defined BuildingName, prefixed with BM_.
If the position estimate is outdoors, but indoor positioning is used for producing the location estimate,
•
the string OUTDOOR is returned.
Integer getFloorId()
Returns the floor level. If the FloorId is not present, null is returned.
For HERE Venue Maps, the floor levels are returned as defined for the particular the HERE Venue Map.
For custom indoor maps, the FloodId follows the customer-defined floor levels specified in HERE
Indoor Radio Mapper, when the indoor maps were imported.
Receiving Detailed Status Reports and Diagnostics
Status Listener
StatusListener provides information on the few most typical issues that prevent the usage of HERE
Indoor Positioning. An instance of StatusListener is given as an input parameter, when creating an
instance of HERE Positioning: LocationDataSourceHERE.getInstance(StatusListener).
The following table details the return codes as defined in StatusListener.PositioningError.
Table 5: Status messages provided by StatusListener
Status message
Description
INJECT ERROR
Coarse location could not be resolved. HERE Indoor Positioning requires coarse location to retrieve the correct
NO COVERAGE
No radiomaps were found in the vicinity of the resolved coarse location. Please ensure you have created indoor
NOT FOUND
HERE Indoor Positioning coverage exists for the area, but resolving position failed. Please make sure that your
positioning data from the HERE positioning servers.
positioning coverage with HERE Indoor Radio Mapper and that the data has been published for use.
latest data has been published in HERE Indoor Radio Mapper.
Diagnostics Listener
DiagnosticsListener provides more detailed information on the most typical issues preventing
the usage of HERE Indoor Positioning. The diagnostics listener can be set up via the method
setDiagnosticsListener.
The following table lists the events that can be received through the DiagnosticsListener.
Table 6: Diagnostics messages provided by DiagnosticsListener
Diagnostics message
Category
Description
List of the enabled positioning
info
Lists the positioning methods at your disposal.
List of the requested and
info
Lists the requested positioning methods in addition to the allowed positioning
methods
enabled positioning methods
methods. In case you are missing a method you would like to use, please contact
your HERE representative.
91
HERE Android SDK Developer's Guide
► User Guide
Diagnostics message
Category
Description
No license to use any of the
error
In case none of the requested methods is enabled in your license, positioning
Dynamic permissions are not
error
Please set the permissions as instructed in Using HERE Positioning on page 86
No reference position
error
The same as INJECT ERROR above.
No coverage
error
The same as NO COVERAGE above.
Not found
error
The same as NOT FOUND above.
requested positioning methods
set for the application and
cannot be performed. Please use a method allowed by your license.
service cannot be started
Device Settings
The device settings may have a major impact on positioning accuracy and may even prevent HERE
Positioning from functioning altogether—for example, disabling GPS/GNSS, network location support, or
radio technologies prevent HERE Positioning from functioning normally. As a developer you should detect
these situations, let the application user know about the situation, and inform the user about the possible
corrective action. The following table lists the situations that may impact positioning, and the corrective
action for each case.
Table 7: Device Settings and Corrective Actions
Situation
Impact
Device Only Location mode is selected from HERE Positioning is not allowed to perform
the device location settings.
network or indoor positioning.
Battery Saving Location mode is selected
HERE Positioning is not able to use
from the device location settings.
Airplane mode is enabled.
satellite-based positioning.
Notify the user about the situation and
inform the user to switch the Location
mode to High accuracy.
Notify the user about the situation and
inform the user to switch the Location
mode to High accuracy.
By default, the airplane mode shuts down
Notify the user about the situation. Do not
network measurements or only cellular
mode without warning, because there
device radios preventing either all the
measurements, depending on how the user
has modified device settings after enabling
the airplane mode.
wifi is disabled.
Corrective Action
Disabling wifi scans prevents wifi-based
outdoor and indoor positioning.
inform the user to turn off the airplane
might be a valid reason why airplane mode
is enabled.
Notify the user about the situation and
inform the user to switch the wifi radio on,
or to turn on Scanning always available
option from the wifi advanced settings or
from the location settings.
Bluetooth is disabled.
Disabling Bluetooth prevents Bluetoothbased indoor positioning.
Notify the user about the situation and
inform the user to switch the Bluetooth
radio on, or to turn on Scanning always
available option from the Bluetooth
advanced setting or from the location
settings.
92
HERE Android SDK Developer's Guide
► User Guide
Power Consumption Considerations
HERE Positioning power consumption depends heavily on the used LocationMethod. As a rule of thumb,
network (wifi or cellular) positioning consumes less power than GNSS and indoor positioning. If your
application does not require the highest possible accuracy, you should consider using network positioning to
save energy.
Online network positioning consumes more energy than offline network positioning. In a typical setting
offline positioning uses up to 80-90% less energy than online positioning. The difference is mainly because
of the network connection usage in online network positioning.
In order to save energy, you should also consider pausing position updates when your application is moved
to the background, unless your application is dependent on frequent location updates even when the
application is in the background.
Troubleshooting HERE Positioning
If your application does not receive any position updates while using HERE Positioning, try the following:
•
•
•
Check that the Android permissions are set as defined in this document
Check that the positioning service is correctly defined in the application manifest
Make sure that the relevant network settings are enabled:
•
•
Check that the high accuracy location settings is enabled:
•
•
▫ Go to System settings and check that Wifi, Cellular and Bluetooth (if the indoor feature is used) are
enabled.
Make sure that the device has network connection
▫ Device-only location setting prevents network and indoor positioning
▫ battery-saving location setting prevents GNSS positioning
Make sure the indoor positioning mode is correctly set
Log events from StatusListener and DiagnosticsListener
If your application receives inaccurate position estimates while using HERE Positioning, try the following:
•
•
Make sure that you have wifi and Bluetooth (for indoor positioning) network measurements enabled
•
If you are trying to use indoor positioning, make sure that the location estimates really originate
from the indoor positioning engine by checking the position source through GetPositionSource
on GeoPosition. Moreover, please check that the positioning technology is as expected through
GetPositionTechnology.
•
•
Use the best-suited LocationMethod for your applications when requesting location updates
Check that the high accuracy location settings is enabled
Force the download of the latest indoor positioning data by deleting the folder sdcard/Android/
data/{your_package_name}/files/rmb and restarting the application.
Map Matching
The HERE Android SDK performs Map Matching automatically when it needs to match a raw position to the
road network, such as during drive guidance, where there may be inaccuracies in the road rendering or GPS
data. Map matching also supports instances where the GPS signal is lost while entering a road tunnel. The
position is extrapolated and updated based on the driver’s speed and knowledge of the tunnel layout.
93
HERE Android SDK Developer's Guide
► User Guide
Automotive Map Matching
The HERE Android SDK supports high-accuracy map matching through the
LocationDataSourceAutomotive class. As a requirement to use this class, you must have positioning
data input from a GNSS module that supports dead reckoning. This ensures a continuous and reliable stream
of position updates even in cases where the GPS becomes unavailable (for example, when the user is driving
in a tunnel). It is strongly recommended that position updates are provided, at a constant rate of 10 Hz,
together with standard deviations of the following:
•
Horizontal radial error (large component)
•
Course
•
•
•
Horizontal radial error (small component)
Speed
Elevation
Note:
•
•
Automotive Map Matching is currently offered as a beta feature. APIs may change without notice.
Automotive Map Matching does not support tunnel extrapolation.
Custom Data Sources
In general, you can use any custom positioning data by implementing the LocationDataSource class and
set it by calling PositioningManager.setDataSource(LocationDataSource) before starting the
positioning manager.
Note: While LocationDataSource.start(LocationMethod) requires a
LocationMethod parameter, it is not necessary for your data source to support all
PositioningManager.LocationMethod types.
You can support tunnel extrapolation while using a Custom Data Source by following these steps:
1.
When the data source location data is unavailable, check to see if the device is currently in a tunnel:
RoadElement roadElement = getPositioningManager().getRoadElement();
if (roadElement != null
&& roadElement.getAttributes().contains(RoadElement.Attribute.TUNNEL)) {
return true;
}
return false;
2.
3.
If the device is in a tunnel, submit a null location through
LocationDataSource.onLocationUpdated(LocationMethod, Location) at an interval of once
per second. The HERE SDK then provides an extrapolated position in the tunnel.
Continue to periodically send null positions to the positioning manager while the device is in a tunnel.
You can implement a timer so that onLocationUpdated(LocationMethod, Location) calls stop
after a period of time (after one minute, for example), and re-enable updates when location data is
available again.
94
HERE Android SDK Developer's Guide
► User Guide
Directions
This section provides an overview of the Directions feature in the HERE SDK. The Directions feature allows
developers to define and display routes between a start and a destination point within their application. It
supports many navigation options such as toll road preference and transport type.
Car and Pedestrian Routing
The HERE Android SDK supports route calculation with multiple waypoints, optimized for walking or driving. A
route describes a path between at least two waypoints, the starting point and the destination, with optional
intermediate waypoints in between. Applications can provide route information to users in two ways:
•
•
A line rendered on a map that displays a connecting path between all waypoints
Turn-by-turn directions in text format
Route Calculation Classes
This section introduces the following classes that are used for route calculations:
•
•
•
•
CoreRouter
RoutePlan
RouteWaypoint
RouteOptions
The CoreRouter class is responsible for calculating car, pedestrian, and truck routes. An application
can initiate a route calculation by calling the CoreRouter.calculateRoute(RoutePlan,
CoreRouter.Listener) method, providing options and waypoints through RoutePlan, and receive
progress events through the Router.Listener instance.
Note: The CoreRouter can also calculate routes with traffic taken into account by using
Route.TrafficPenaltyMode and setDynamicPenalty(DynamicPenalty) before calling
calculateRoute(). For more information on the DynamicPenalty class, see Dynamic Routing
Penalty on page 98.
RoutePlan is a waypoint container that is used for route calculation. A RoutePlan object is comprised of
a list of waypoint objects and an optional RouteOptions. If RouteOptions is not specified, default values
are used.
You can use RouteWaypoint to add more waypoint details to a route calculation. These details include
whether a waypoint is a deliberate stopover or a via point that the route must pass through. This affects
routing, as routes containing stopovers or via waypoints may be different. For example, a calculated route
may suggest a U-turn maneuver after a stopover, while a route containing the same location as a via
waypoint suggests continuing on the same street. The via waypoint type is only supported in car routes, and
it is not supported in other route types.
The RouteOptions class is a model of the parameters required to calculate one route. It encapsulates
"building block" parameters for a route such as:
•
The desired number of routes
95
HERE Android SDK Developer's Guide
► User Guide
•
•
•
•
The direction of travel that the route should start in
The routing type, such as fastest travel time or shortest distance
The departure time
The allowed types of route segments, such as dirt roads or highways
The HERE SDK supports alternate routes between two waypoints. The alternate route feature allows more
than one route to be returned after a route calculation. You can use the RouteOptions class to set the
desired number of routes, and the HERE SDK then returns different routes according to this limit. Note that
the first element of the returned list of RouteResult is the main route, and the rest of the returned routes
are not listed in any specific order.
Note: Do not set more than three desired routes in offline mode, as these route calculations take
significantly more time to complete.
Note: In some cases, specifying a higher route count than a previous working query can result in
a GRAPH_DISCONNECTED error. This is due to a limit on the complexity and size of the calculated
routes that can be processed. The higher the route count and the more complex a route is, the more
likely that this limit is exceeded.
RouteResult and Route
The RouteResult class represents a route calculation result. Applications can retrieve a Route
object and the corresponding set of violated routing conditions. Violated routing options are the
conditions that a routing result was unable to adhere to. For example, after specifying a route calculation
that avoids tolls and ferries, you may get a RouteResult that contains a Route object along with
RouteResult.ViolatedOption.AVOID_TOLL_ROADS. This indicates that although a route was found, this
route goes through at least one toll road—violating a condition of your route request.
The Route class is a distinct calculated path connecting two or more waypoints, consisting of
a list of maneuvers and route links. By using CoreRouter.calculateRoute(RoutePlan,
CoreRouter.Listener) to trigger a route calculation, your application can use the
CoreRouter.Listener to monitor the calculation and trigger callback methods. These callback methods
have parameters that include a list of the calculated RouteResult objects. Using these RouteResult
objects, you can call getRoute() to retrieve the routes.
The MapRoute map object
The MapRoute class is a type of MapObject that displays a calculated route on a map. Typically, an
application creates a MapRoute after a route calculation, passing the relevant Route object as a
parameter to the MapRoute(Route) constructor before adding the MapRoute to the map by calling
Map.addMapObject(MapRoute).
Note: MapRoute.setRenderType(RenderType) must be called after the MapRoute is added to a
Map.
96
HERE Android SDK Developer's Guide
► User Guide
For example, if you want to render a route that connects two waypoints (start and destination), you can add
the following application logic:
Figure 49: Calculate Route
1.
Declare a CoreRouter instance.
// Declare the variable (the CoreRouter)
CoreRouter router = new CoreRouter();
2.
Create a RoutePlan and add two GeoCoordinate waypoints.
// Create the RoutePlan and add two waypoints
RoutePlan routePlan = new RoutePlan();
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(49.1966286, -123.0053635)));
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(49.1947289, -123.1762924)));
3.
Create a new RouteOptions object, set its Type and TransportMode values by calling appropriate
RouteOptions methods, and then add it to RoutePlan.
// Create the RouteOptions and set its transport mode & routing type
RouteOptions routeOptions = new RouteOptions();
routeOptions.setTransportMode(RouteOptions.TransportMode.CAR);
routeOptions.setRouteType(RouteOptions.Type.FASTEST);
routePlan.setRouteOptions(routeOptions);
4.
To make sure route calculation can handle returning a Route object that in turn can be used
to create a MapRoute instance for rendering on the map, add an inner class by implementing
CoreRouter.Listener in the appropriate activity class.
private class RouteListener implements CoreRouter.Listener {
97
HERE Android SDK Developer's Guide
► User Guide
// Method defined in Listener
public void onProgress(int percentage) {
// Display a message indicating calculation progress
}
// Method defined in Listener
public void onCalculateRouteFinished(List routeResult, RoutingError error) {
// If the route was calculated successfully
if (error == RoutingError.NONE) {
// Render the route on the map
mapRoute = new MapRoute(routeResult.get(0).getRoute());
map.addMapObject(mapRoute);
}
else {
// Display a message indicating route calculation failure
}
}
}
5.
After adding the inner listener class (named RouteListener for this example), calculate the route
by calling CoreRouter.calculateRoute(RoutePlan, CoreRouter.Listener). Note that
CoreRouter.Listener extends Router.Listener, RoutingError>.
// Calculate the route
router.calculateRoute(routePlan, new RouteListener());
Dynamic Routing Penalty
You can use DynamicPenalty to create a policy of roads and area restriction factors that are applied during
routing calculations. For example, you can use this class to indicate that the travel speed in an area is 50
percent slower than usual. The DynamicPenalty class also allows you to set the mode used for handling
traffic events in a route calculation through the setTrafficPenaltyMode(TrafficPenaltyMode)
method.
You can change the active policy by calling CoreRouter.setDynamicPenalty(DynamicPenalty). The
policy must be set before a route calculation for the restrictions to be taken into account.
Retrieving Traffic Event Objects
You can use the TrafficUpdater class to retrieve traffic events such as road closures or congestions.
Before using the traffic updater, it must be be enabled via TrafficUpdater.enableUpdate(boolean
update) for the traffic requests to take effect.
Using the TrafficUpdater is a two-step process. First, create a TrafficUpdater.Listener and use it
with one of the following methods:
•
request(GeoCoordinate center, Listener listener) - request traffic with given center
•
request(GeoCoordinate center, int radius, Listener listener) - request traffic with
•
•
coordinates and the default radius
given center coordinates and radius
request(Route route, Listener listener) - request traffic for the given route and default
radius around each waypoint
request(Route route, int radius, Listener listener) - request traffic for given route and
radius around each waypoint
98
HERE Android SDK Developer's Guide
► User Guide
•
request(RouteElements elements, Listener listener) - request traffic for a route element
(RouteElement) object
Upon the successful callback from TrafficUpdater.Listener (with the state
TrafficUpdater.RequestState.DONE), you should retrieve a list of traffic events that affect the given
route or route element by using one of the following methods:
•
•
•
•
getEvents(Route route, GetEventsListener listener)
getEvents(RouteElement element, GetEventsListener listener)
getEvents(List elements, GetEventsListener listener)
getEvents(RouteElements elements, GetEventsListener listener)
TrafficUpdater.GetEventsListener contains one callback method,
onComplete(List events, Error error), which provides a newly updated list of
traffic events.
Routing-related Enumerations
Route calculations make use of HERE SDK enumerations that include:
•
•
•
•
•
The Route.TrafficPenaltyMode enum - represents values describing how the CoreRouter should
handle traffic events in a route calculation, such as DISABLED, AVOID_LONG_TERM_CLOSURES, or
OPTIMAL.
The RouteOptions.Type enum - represents values describing different routing types, such as
FASTEST or SHORTEST
The RouteOptions.TransportMode enum - represents values describing different transport modes,
such as CAR, TRUCK, TRACK (which connects waypoints using straight lines), or PEDESTRIAN
The RoutingError enum - represents values describing possible route calculation errors, such as NONE
or VIOLATES_OPTIONS
The RouteResult.ViolatedOption enum - represents values describing possible route option
violations, such as AVOID_HIGHWAYS or AVOID_FERRIES
Bicycle Routing
The bicycle routing feature provides route calculation using car and pedestrian roads with bicycle-specific
speed estimations. This type of routing can be performed online or offline, with elevation data being
available in an online request.
Note:
•
•
Bicycle routing is currently offered as a beta feature. APIs may change without notice. Do not use
this feature in a commercial application.
Bike-specific roadways are not yet supported.
Bicycle routing includes pedestrian-only roads and road segments that require traversing a one-way road
opposite the allowed direction of travel. When a road is not open for driving in the travel direction, the
routing algorithm assumes that the user must walk the bicycle, and therefore it uses the pedestrian walking
speed for such segments. As a special exception to this rule, pedestrian segments located in parks are
assumed to be open for bicycles, so full bicycle speed is used there. Generally, such walk-only segments
are used in bicycle routing only when they provide a big shortcut, or when a waypoint is located on such a
segment.
99
HERE Android SDK Developer's Guide
► User Guide
Performing a Bicycle Routing Request
You can perform bicycle routing by using the CoreRouter class and
RouteOptions.TransportMode.BICYCLE, as shown in the following example:
CoreRouter router = new CoreRouter();
// Create the RoutePlan and add two waypoints
RoutePlan routePlan = new RoutePlan();
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(49.276271, -123.113224)));
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(49.1947289, -123.1762924)));
RouteOptions routeOptions = new RouteOptions();
routeOptions.setTransportMode(RouteOptions.TransportMode.BICYCLE);
routeOptions.setRouteType(RouteOptions.Type.FASTEST);
routePlan.setRouteOptions(routeOptions);
// Calculate the route, assuming that you have already implemented RouteListener.
// See the Car Routing section for more details.
router.calculateRoute(routePlan, new RouteListener());
Route Elevation
In an online bicycle routing session, the HERE SDK considers elevation changes when determining what
speed should be used on the given road. When going uphill, speed decreases, possibly down to the
pedestrian speed. When going downhill, speed increases.
Note: Elevation-based speed estimations may change in the future.
You can also create an elevation profile of a route, similar to the following screenshot, by using altitude data
of the points on a calculated route.
Figure 50: Plotted Chart of Elevations
To retrieve this elevation data, call the Route.getRouteGeometryWithElevationData() method and
inspect the altitude on each returned GeoCoordinate object. If the altitude is not known at that location,
calling GeoCoordinate.getAltitude() returns the GeoCoordinate.UNKNOWN_ALTITUDE value.
Note: Route altitude data is available in online calculated routes for cars, pedestrians, trucks, and
bicycles.
Truck Routing
The Truck Routing feature in the HERE SDK allows users to calculate routes that can be specifically traveled
by trucks and commercial vehicles. Commercial vehicles typically have different regulations for their
transportation routes. For example, a government may have laws that restrict trucks carrying flammable
materials from traveling in a residential area. By using the Truck Routing feature, you can launch a route
calculation that specifically adheres to these restrictions.
100
HERE Android SDK Developer's Guide
► User Guide
Truck Routing and the RouteOptions Class
The RouteOptions class contains a number of truck-specific methods and enums that you should use
before performing a route calculation. To perform a truck route calculation, use the TransportMode.TRUCK
enum with the setTransportMode(TransportMode) method to specify the route transportation type. You
can also set the following truck-specific route options before performing the route calculation:
•
The number of truck trailers
•
The truck length
•
•
•
•
•
•
•
The truck height
The truck width
The maximum allowed truck weight
Hazardous goods that are transported by the truck
The category of tunnels that the truck can travel on
The truck's weight per axle
Difficult turns
Note: Truck routing only supports the RouteOptions.Type.FASTEST routing type. Other routing
types are not supported.
A Route Calculation Example
1.
As with the previous routing example, retrieve the CoreRouter, create a RoutePlan and set its
2.
Create a new RouteOptions object. The TransportMode should be set to TRUCK.
waypoints.
// Create the RouteOptions and set its transport mode & routing type
RouteOptions routeOptions = new RouteOptions();
routeOptions.setTransportMode(RouteOptions.TransportMode.TRUCK);
routeOptions.setRouteType(RouteOptions.Type.FASTEST);
3.
Set other truck routing options.
routeOptions.setTruckTunnelCategory(TunnelCategory.E)
.setTruckLength(25.25f)
.setTruckHeight(2.6f)
.setTruckTrailersCount(1);
4.
Set the RouteOptions to the RoutePlan.
routePlan.setRouteOptions(routeOptions);
5.
Create a CoreRouter.Listener and then calculate the route by calling
CoreRouter.calculateRoute(RoutePlan, Listener).
Transit Routing
Transit Routes are routes calculated by using CoreRouter with the the RouteOptions transport mode set
to PUBLIC_TRANSPORT. With the transit routing feature, you can calculate transit routes by using known
online timetable information.
101
HERE Android SDK Developer's Guide
► User Guide
Note:
•
•
RouteOptions.TransportMode.PUBLIC_TRANSPORT is no longer deprecated as of HERE SDK
v3.4.
To use this feature, your application must include the Gson library (release 2.2.4 or a compatible
version) on its class path.
Before displaying transit routing, you should set the map scheme to include transit so that the MapRoute
shows the color of the transit lines.
// sets the map scheme to include transit.
map.setMapScheme(Map.Scheme.NORMAL_DAY_TRANSIT);
The following is an example of a transit route using CoreRouter:
...
CoreRouter router = new CoreRouter();
// Select routing options
RoutePlan routePlan = new RoutePlan();
RouteOptions routeOptions = new RouteOptions();
routeOptions.setTransportMode(RouteOptions.TransportMode.PUBLIC_TRANSPORT);
routeOptions.setRouteType(RouteOptions.Type.FASTEST);
routePlan.setRouteOptions(routeOptions);
// Select Waypoints for your routes
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(49.1966286, -123.0053635)));
routePlan.addWaypoint(new RouteWaypoint(new GeoCoordinate(49.1947289, -123.1762924)));
router.calculateRoute(routePlan, new RouterListener());
...
private final class RouterListener implements CoreRouter.Listener {
// Method defined in Listener
public void onProgress(int percentage) {
// Display a message indicating calculation progress
}
// Method defined in Listener
public void onCalculateRouteFinished(List routeResult, RoutingError error) {
// If the route was calculated successfully
if (error == RoutingError.NONE) {
// Render the route on the map
mapRoute = new MapRoute(routeResult.get(0).getRoute());
map.addMapObject(mapRoute);
// Get the bounding box containing the route and zoom in (no animation)
GeoBoundingBox gbb = routeResult.get(0).getRoute().getBoundingBox();
map.zoomTo(gbb, Map.Animation.NONE, Map.MOVE_PRESERVE_ORIENTATION);
}
else {
// Display a message indicating route calculation failure
}
}
102
HERE Android SDK Developer's Guide
► User Guide
}
Figure 51: Transit Route
The TransitRouteElement Class
Transit route elements, which are route element objects specific to public transit, can be retrieved from the
getTransitElement() method in the RouteElement class.
Urban Mobility Routing
With the Urban Mobility Routing feature, you can calculate transit routes and fare costs by using real time
online timetable information.
To perform this type of transit routing, use UMRouter in a similar manner to CoreRouter, while specifying
options through the UMRouteOptions class.
Notes and Disclaimers
Before using the Transit Routing feature, be aware of the following:
•
•
•
Transit routing is currently offered as a beta feature. APIs may change without notice.
Access to the Transit Routing feature is restricted. Contact a HERE representative (https://
developer.here.com/contact-us?interest=mobile-sdk#contact-sales) for more information and to request
for access.
To use this feature, your application must include the Gson library (release 2.2.4 or a compatible version)
on its class path.
103
HERE Android SDK Developer's Guide
► User Guide
•
Urban Mobility routing is now offered as an additional feature outside of the evaluation package.
Urban Mobility Routing Example
The following is an example of how to calculate a public transit route that connects two waypoints (start and
destination):
1.
Declare a UMRouter instance.
// Declare the variable (the UMRouter)
UMRouter router = new UMRouter();
2.
Create a RoutePlan and add two GeoCoordinate waypoints.
// Create the RoutePlan and add two waypoints
RoutePlan plan = new RoutePlan();
// add start point to route plan
plan.addWaypoint(new RouteWaypoint(new GeoCoordinate(40.750488, -73.993546)));
// add end point to route plan
plan.addWaypoint(new RouteWaypoint(new GeoCoordinate(40.749877, -73.845853)));
3.
Note: Destinations that are too close to the starting waypoint are not supported. Use a
pedestrian route calculation instead.
Create a new UMRouteOptions object and set the desired options. Add the route options to
RoutePlan.
// Create the UMRouteOptions and set its options
UMRouteOptions options = new UMRouteOptions();
// setup walk parameters
options.setTransitWalkMaxDistance(1000);
options.setTransitWalkTimeMultiplier(0.75f);
plan.setRouteOptions(options);
4.
Create a UMRouter.Listener instance by implementing the
onCalculateRouteFinished(UMCalculateResult, ErrorCode) and onProgress(int)
methods. Note that the returned object, UMCalculateResult, may contain multiple route results
(UMRouteResult), each of which contains a single UMRoute.
UMRoute can be used to create a MapRoute instance for rendering purposes.
UMRouter.Listener listener = new UMRouter.Listener() {
@Override
public void onCalculateRouteFinished(UMCalculateResult response, ErrorCode error) {
if (error == ErrorCode.NONE) {
for (UMRouteResult result: response.getResults()) {
int changesCount = result.getUMRoute().getChangesCount();
List tariffs =
result.getUMRoute().getTariffs();
for (RouteSection section: result.getUMRoute().getSections()) {
Departure departure = section.getDeparture();
Collection alerts = section.getAlerts();
}
// ...
MapRoute mapRoute = new MapRoute(result.getUMRoute());
// show mapRoute on the map
}
if (response.isSubsequentRouteSupported()) {
// UMRouter.calculateSubsequentRoute() can be used to calculate later routes
}
} else {
104
HERE Android SDK Developer's Guide
► User Guide
String errorMessage = response.getErrorMessage();
// display error
}
}
};
5.
6.
@Override
public void onProgress(int percentage) {
// update progress
}
To perform subsequent route calculations, check if the returned UMCalculateResult object
supports subsequent routes, and call calculateSubsequentRoute(UMCalculateResult,
SubsequentRouteType, int, UMRouter.Listener) as needed. For more information on
subsequent routes, see the Subsequent Routes section in this chapter.
After adding the listener class (named listener for this example), calculate the route by calling
UMRouter.calculateRoute(RoutePlan, UMRouter.Listener). Note that UMRouter.Listener
extends Router.Listener, RoutingError>.
// Calculate the route
router.calculateRoute(routePlan, listener);
7.
8.
You are required to display the source attribution, retrieved through the
Route.getTransitRouteSourceAttribution() method somewhere near the route results.
Geometry, line color, and other types of display information can be retrieved through
TransitRouteElement objects, available through the UMRoute. You can retrieve and display ticket
fare information by using the Ticket objects inside UMRoute.
Subsequent Routes
The UMRouter class supports subsequent route calculations based on an initial set of route results.
For example, you can use UMRouter to launch a route calculation, and then use the returned
UMCalculateResult to launch a subsequent route calculation for three routes that have the same
waypoints, but have an earlier departing time.
You do not need to specify a timestamp for the subsequent calculation. The HERE SDK will automatically
return your desired number of results based on whether you specified earlier or later. In other words, you
can "slide" the window of interested route results by specifying SubsequentRouteType.EARLIER or
(SubsequentRouteType.LATER) and the desired number of results.
The method to perform this calculation is
UMRouter.calculateSubsequentRoute(UMCalculateResult, SubsequentRouteType, int,
UMRouter.Listener).
Displaying Transit Routes
Before displaying transit routing, you should set the map scheme to include transit so that the MapRoute
shows the color of the transit lines.
// sets the map scheme to include transit.
105
HERE Android SDK Developer's Guide
► User Guide
map.setMapScheme(Map.Scheme.NORMAL_DAY_TRANSIT);
Figure 52: Transit Route
The TransitRouteElement Class
Transit route elements, which are route element objects specific to public transit, can be retrieved from the
getTransitElement() method in the RouteElement class.
Online Timetables and Fallback Scenarios
Wherever possible, the HERE SDK performs route calculations using the latest online timetable data from
municipalities. This is performed automatically when you launch a transit routing request, but the following
conditions must be fulfilled:
•
•
Your application must have an active data connection. If there is no data connection, an estimated
routing is performed instead.
The route must be between two endpoints—an origin and a destination. Multiple endpoints are not
supported.
This type of calculation only returns a maximum of ten route results, even if the limit set in the
RouteOptions.setRouteCount(int) exceeds it.
Note: The following route options are ignored in this route calculation mode:
•
•
setTransitMinimumChangeTime(int)
setTransitWalkTimeMultiplier(int)
In addition, the setTransitMaximumChanges(int) option is supported in the primary route result,
but the maximum number of changes is capped at ten.
106
HERE Android SDK Developer's Guide
► User Guide
Note: Contractual limitations exist for Online Timetable coverage in Paris, France and Germany.
Contact your HERE SDK Sales, Marketing, or Product representative for additional details.
Indoor Venue Routing
For more information on 3D venues and indoor routing, see 3D Venues on page 164 and Venue Routing on
page 175.
Offline Routing
Even without an active data connection, the applications developed with the HERE Android SDK are able to
request routing information to assist travelling from one location to another.
Your application's users do not need to maintain active, data connections to calculate routes and render
them on a map. It is possible to pre-download updated maps and database information for initiating routing
requests while offline. For example, if a user has downloaded offline maps of California and Oregon, a route
from San Diego to Portland can be created without any data connection.
For more information about downloading offline maps, refer to Offline Maps (MapLoader) on page 52.
Force Online or Offline
You can launch online or offline routing without changing the device or the HERE SDK connectivity by using
the setConnectivity(Connectivity) method on a CoreRouter instance. Connectivity has three
possible values:
•
•
•
If you launch a request using the DEFAULT connectivity mode, the request is performed according to
the MapEngine connectivity setting. If the device is offline while MapEngine is set to online mode, the
request fails.
If you launch a request using the ONLINE connectivity mode, an online request is performed, regardless
of the MapEngine connectivity setting.
If you launch a request using the OFFLINE connectivity mode, an offline request is performed using
cached data, regardless of the MapEngine connectivity setting.
In all cases, if the request fails, no fallback action is automatically performed.
To ensure that the connectivity mode is applied, call setConnectivity(Connectivity) before launching
a CoreRouter calculation request. If a Connectivity.ONLINE route calculation request fails due to
connection issues, the HERE SDK returns the RoutingError.NETWORK_COMMUNICATION error code. If a
Connectivity.OFFLINE route calculation request fails due to not enough cached data, the HERE SDK
returns the RoutingError.GRAPH_DISCONNECTED error code.
Note: This feature is only applicable to car, bicycle, truck, and pedestrian routing through the
CoreRouter class.
Route Consumption
Route consumption calculation is a beta feature which allows the consumption of fuel resources, such as
gasoline or electricity, to be calculated for a given, already calculated route.
107
HERE Android SDK Developer's Guide
► User Guide
Vehicles have a limited range depending on the amount of fuel remaining and how much fuel the vehicle
consumes per kilometre under different driving conditions. This is especially important for electric vehicles,
which can typically travel less distance on a full charge than a gasoline powered vehicle could travel on a full
tank.
In order to ensure that a vehicle is able to complete a calculated route, the HERE Android SDK can calculate
the consumption for each element in that route given certain consumption parameters. This allows the
developer to, firstly, determine how much fuel the vehicle will have left at the end of the route and, secondly,
check where vehicle would run out of fuel in the case that the final destination is not reachable.
Since different vehicles consume different types of fuel (such as gasoline and electricity) and also have
different rates of consumption, the consumption calculation must be configured with consumption
parameters for a given vehicle before the calculation is performed.
An important concept for consumption calculation is the capacity unit. The capacity unit is how fuel is
measured for a given vehicle. Because consumption calculation is general, the unit is effectively defined
by the developer. For an electric vehicle the capacity unit is generally kilowatt hours (kWh), for a gasoline
powered vehicle it could be liters of gasoline.
A vehicle will have a maximum capacity and current capacity, which indicates how much fuel it can hold in
total and the current amount respectively.
In the following description, we will assume that we are calculating consumption for an electric vehicle, since
this is the most common use case. The capacity unit used will be kilowatt hours (kWh).
The consumption parameters are grouped onto the ConsumptionParameters class. The API
documentation provides descriptions of each of the individual values that can be modified. Here we will
describe the more complex and important values. To being, we'll create an empty consumption parameters
object
ConsumptionParameters consumptionParams = new ConsumptionParameters();
The speed consumption table is the basis for much of the consumption calculation, this is stored as a table,
represented as list of ConsumptionForSpeed objects. The table represents the consumption of the vehicle
per meter in kilowatt hours (the capacity unit) for ranges of speeds.
Consider a very simple example with three ranges of speeds and a consumption for each.
Table 8:
Speed range (km/h)
Consumption per meter (kWh)
0 - 30
38.82
31 - 90
18.20
> 90
27.41
From this table, we see that our vehicle consumes 38.82 kWh of capacity per metre when traveling between
0 and 30 km/h, 18.20 kWh of capacity per metre when traveling between 31 and 90 km/h and 27.41 kWh of
capacity per metre when traveling at 90 km/h or faster. Of course this example is very simple, a real world
speed consumption table would have finer grained values.
108
HERE Android SDK Developer's Guide
► User Guide
To use this table in our consumption parameters, call the
setSpeedParameters(List) method, with the upper bound of each range as
the key and the consumption per metre as the value. The final key (for > 90 km/h) should use the key 250.
List consumptionSpeed = new ArrayList();
consumptionSpeed.add(new ConsumptionForSpeed(30, 38.82));
consumptionSpeed.add(new ConsumptionForSpeed(90, 18.20));
consumptionSpeed.add(new ConsumptionForSpeed(250, 27.41));
consumptionParams.setSpeedParameters(consumptionSpeed);
The speed consumption table is enough to calculate simplistic consumption for a route. Assuming that we
already have a Route object called route, which contains a calculated route, calculate the consumption for
this route.
RouteConsumption consumption =
route.getConsumption(consumptionParameters, null);
Once we have the route consumption we can determine the last reachable position on the same route given
current capacity of our vehicle. In this example we'll suppose that our vehicle has 100,000 kWh of remaining
capacity at the beginning of the route.
GeoCoordinates lastPosition =
route.getLastReachablePosition(consumption, 100000);
The variable lastPosition will now contain the last point that our vehicle will be able to reach before the
capacity reaches zero. In the case that the final destination of the route is reachable, lastPosition will be
nil.
We can also use the route consumption to obtain the consumption for each route element that comprise the
route itself. The following code snippet prints out the starting and ending geo-coordinates for each route
element as well as the consumption for that route element.
List routeElements = route.getRouteElements().getElements();
for (int i = 0; i < routeElements.size(); i++) {
RouteElement element = routeElements.get(i);
List geometry = element.getGeometry();
double startLat = geometry.get(0).getLatitude();
double startLong = geometry.get(0).getLongitude();
double endLat = geometry.get(geometry.size() - 1).getLatitude();
double endLong = geometry.get(geometry.size() - 1).getLongitude();
Log.d(TAG, String.format("Route element (%.4f, %.4f) ---> (%.4f, %.4f) consumption %d",
startLat, startLong, endLat, endLong, consumption.getConsumption(i)));
}
As mentioned above, setting only the consumptionSpeed property is sufficient for basic consumption
calculation. There are more specialised values which can be set to more closely model the consumption of a
real vehicle across different road and traffic conditions. For more details on these properties, please see the
API documentation.
Route Serialization
Route serialization is a feature that allows users to use the Route methods to serialize a route into binary
data, which can then be saved as a file. You can also use Route class to generate a route from a previously
109
HERE Android SDK Developer's Guide
► User Guide
serialized route without going through the route calculation process. This is useful when a user wants to
recover from a crash during navigation or when a user wants to transfer a route from another device.
Note: Route serialization is currently offered as a beta feature. APIs may change without notice.
Route serialization currently only supports car, bike, truck, and pedestrian routes. Public Transit, Indoor
Venue, and Urban Mobility routes cannot be serialized. Route serialization also does not work when the
map version from which a route is serialized does not match the current map version. Route serialization
also fails if the binary data containing the serialized route is tempered with or corrupted. In these cases, a
specific SerializerError error code is returned.
Both asynchronous and synchronous operations are supported. To asynchronously serialize a route, perform
the following:
Route route;
//assume that route calculation was already performed
Route.SerializtionCallback sCallback = new SerializationCallback() {
@Override
public void onSerializationComplete(Route.SerializationResult result) {
//do something with data
byte[] data = result.data;
}
};
Route.serializeAsync(route, sCallback);
To asynchronously deserialize a route:
byte[] data;
//assume 'data' is a previously-serialized route
Route.DeserializationCallback dCallback = new DeserializationCallback() {
@Override
public void onDeserializationComplete(Route.Deserialization result){
//do something with the route
Route route = result.route;
}
};
Route.deserializeAsync(data, dCallback);
Places
This section provides an overview of the Places feature in the HERE SDK. The Places feature enables
developers to build rich, location-aware applications by adding point of interest search, discovery,
interaction, and information retrieval.
For example, when an application submits a place discovery request using this API, the application receives
a response that contains a list of links to places resources (among other information). By accessing one of
the linked Place resources, the application can get detailed information about that place, including ratings,
images, reviews, editorials, and owner content. This detailed place response also contains references to
other related places, allowing the application's users to discover other places relevant or related to their
original search.
110
HERE Android SDK Developer's Guide
► User Guide
Note: To use this feature, your application must include the Gson library (release 2.2.4 or a
compatible version) on its class path.
Geocoding and Reverse Geocoding
Geocoding and reverse geocoding APIs from the HERE Android SDK allow application developers to offer
search functionality for requesting location information and structured addresses. Geocoding APIs resolve
to a GeoCoordinate from a text query, while reverse geocoding APIs resolve to a geographic data, such as
Address, from a GeoCoordinate. Address provides textual address information, which includes house
number, street name, city, country, and district. It encompasses everything about an address or a point on
the map. Location represents a physical point on the map where additional attributes can be retrieved.
These additional attributes include a unique identifier, label, Address, GeoCoordinate positions, and
GeoBoundingBox for the Location.
The GeocodeRequest Class
GeocodeRequest represents an extended Request. GeocodeRequest can be created using a one-box
(free-formatted text) search using a combination of a text query string and geographical area arguments. The
following shows the method used to create a one-box request:
GeocodeRequest request = new GeocodeRequest(String).setSearchArea(GeoCoordinate, int)
The preceding method returns a GeocodeRequest object. To begin the search, call
GeocodeRequest.execute(). This method requires a ResultListener as an argument. When the search
is completed, the ResultListener.onCompleted() method is called with a result status and a list of
found locations.
After a request is invoked, it can be canceled using the GeocodeRequest.cancel() method, which returns
true if the request was cancelled successfully. For GeocodeRequest, a list of Location objects are
expected at the completion of the request.
The following code example demonstrates how to perform a GeocodeRequest:
// Implementation of ResultListener
class GeocodeListener implements ResultListener> {
@Override
public void onCompleted(List data, ErrorCode error) {
if (error != ErrorCode.NONE) {
// Handle error
...
} else {
// Process result data
...
}
}
}
// Instantiate a GeoCoordinate object
GeoCoordinate vancouver = new GeoCoordinate( 49.2849,- 123.1252);
// Example code for creating a OneBox Request
ResultListener> listener = new GeocodeListener();
GeocodeRequest request = new GeocodeRequest("Granville").setSearchArea(vancouver, 5000);
if (request.execute(listener) != ErrorCode.NONE) {
// Handle request error
...
111
HERE Android SDK Developer's Guide
► User Guide
}
The ReverseGeocodeRequest2 Class
The ReverseGeocodeRequest2 class represents an extended Request used to retrieve Location data.
The request is created using a GeoCoordinate as shown below:
new ReverseGeocodeRequest2(GeoCoordinate)
The above method returns a ReverseGeocodeRequest2 object. To invoke the request, you can then call
the execute() method of the returned ReverseGeocodeRequest2 object and pass in a ResultListener
to retrieve the information about the completion of the request. Once a request is invoked, the request can
be canceled via the ReverseGeocodeRequest2.cancel() method. cancel() returns true if the request
was cancelled successfully. For ReverseGeocodeRequest2, a structured Location is expected at the
completion of the request.
The following is an example of creating ReverseGeocodeRequest2 using new
ReverseGeocodeRequest2(GeoCoordinate):
// Implementation of ResultListener
class ReverseGeocodeListener implements ResultListener {
@Override
public void onCompleted(Location data, ErrorCode error) {
if (error != ErrorCode.NONE) {
// Handle error
...
} else {
// Process result data
...
}
}
}
// Instantiate a GeoCoordinate object
GeoCoordinate vancouver = new GeoCoordinate( 49.2849,- 123.1252);
// Example code for creating ReverseGeocodeRequest2
ResultListener listener = new ReverseGeocodeListener();
ReverseGeocodeRequest2 request = new ReverseGeocodeRequest2(vancouver);
if (request.execute(listener) != ErrorCode.NONE) {
// Handle request error
...
}
By default, the reverse geocode request above searches for the closest street address. Alternatively, you can
also create a reverse geocoding request in one of the following modes (ReverseGeocodeMode):
•
RETRIEVE_ADDRESSES - Search for the closest street address or addresses (same as above)
•
RETRIEVE_LANDMARKS - Search for landmarks like parks and lakes in the proximity provided in the
request
•
•
•
RETRIEVE_AREAS - Retrieve the administrative area information for the position provided in the
request
RETRIEVE_ALL - Search for streets, administrative areas and landmarks. This mode aggregates the
results of the previous three modes in one call
TRACK_POSITION - Retrieve street and address information based on a position and bearing
112
HERE Android SDK Developer's Guide
► User Guide
See the following for an example of how to use this type of request. Note that the bearing parameter is
only used when you use TRACK_POSITION.
ResultListener listener = new ReverseGeocodeListener();
ReverseGeocodeRequest2 request = new ReverseGeocodeRequest2(geoCoord, null, RETRIEVE_ADDRESSES,
bearing);
if (request.execute(listener) != ErrorCode.NONE)
{
// Handle request error ...
}
Offline Geocoding
Applications developed with the HERE Android SDK can perform offline geocoding, which allows geocode
and reverse geocode requests to be performed without an active data connection. This is performed
automatically when an active data connection is not available and when the map and database information
have already been downloaded and cached.
Search and Discovery
The HERE Android SDK provides application developers the Places API, which allows places discovery and
information retrieval.
Steps for performing a search
1.
Implement the ResultListener interface to handle the completion of the search
3.
Invoke the request by calling Request.execute(ResultListener)
2.
4.
Create a request and apply request options
The ResultListener.onCompleted() callback is triggered when the request is finished
Note: Applications that use the Places API must honor the following prescribed workflow:
1.
Search
3.
Perform Actions
2.
Request for Details
Do not preload results linked from a response to improve performance, as doing so violates HERE's
guidelines. For more information about usage restrictions, consult the API Implementation Check List
in the REST HERE Places API documentation.
The Place Class
The Place class represents a detailed set of data about a physical place, acting as a container for various
attributes, collections of media about a place, and key-value pairs of related places. A Place object can
belong to a specific Category, and has attributes such as:
•
•
•
A unique identifier (ID)
A name
•
•
A URL to the icon that best represents the place
Optional information, such as related places, user ratings, reviews, and other editorial media.
•
A Location object representing the physical location of the place, including access locations
A List of Category objects that link to the categories assigned to the place
113
HERE Android SDK Developer's Guide
► User Guide
For more information, please see the API Reference.
Discovery Requests
The HERE Places Search API supports the following discovery requests:
Request
HERE SDK class
Purpose
Search
SearchRequest
Finds places that match user-provided search terms.
Explore
ExploreRequest
Finds interesting places nearby, or in the map viewport, sorted by popularity.
Use this type of request if you are trying to answer the question "What are the
interesting places near here?" The results may be optionally restricted to a given
set of categories, which acts as a filter in terms of what places get returned.
Here
HereRequest
Helps users identify places at the given location by finding places of interest
near a given point, sorted by distance. Use this type of request if you are trying
to answer the question "What is near this location?" or "Where am I?" You can
use this endpoint to implement features like "check-in" (by identifying places at
the user's current position) or "tap to get more information about this place of
interest".
Note: Normally, the closest known places are returned with the Here
Discovery request, but if the uncertainty in the given position is high,
then some nearer places are excluded from the result in favor of more
popular places in the area of uncertainty.
Around
AroundRequest
Allows users to request places near a given point, based on a location precision
parameter. The places around that point are returned in order of proximity.
This type of request is intended for applications that employ features such as
augmented reality, where places around the user's location are displayed on a
device. It is intended to provide places that are likely to be visible to the user as
well as important places that are farther away. The Around request is considered
experimental, and its behavior and functionality are still evolving. Check future
documentation for updates to this feature.
The following code example demonstrates how to perform a search discovery request:
// Example Search request listener
class SearchRequestListener implements ResultListener {
}
@Override
public void onCompleted(DiscoveryResultPage data, ErrorCode error) {
if (error != ErrorCode.NONE) {
// Handle error
...
} else {
// Process result data
...
}
}
// Create a request to search for restaurants in Seattle
try {
GeoCoordinate seattle
= new GeoCoordinate(47.592229, -122.315147);
DiscoveryRequest request =
new SearchRequest("restaurant").setSearchCenter(seattle);
114
HERE Android SDK Developer's Guide
► User Guide
// limit number of items in each result page to 10
request.setCollectionSize(10);
ErrorCode error = request.execute(new SearchRequestListener());
if( error != ErrorCode.NONE ) {
// Handle request error
...
}
} catch (IllegalArgumentException ex) {
// Handle invalid create search request parameters
...
}
The result of a discovery request is a DiscoveryResultPage. The DiscoveryResultPage represents a
paginated collection of items from which the following can be retrieved:
•
•
Next page and previous page requests - discovery requests used to retrieve additional pages of search
items
Items for the current page - a List of DiscoveryResult
When additional pages of search results are needed, retrieve and invoke the DiscoveryRequest returned
by DiscoveryResultPage.getNextPageRequest(). If the next page request is null, no additional
results are available.
The following is an example:
DiscoveryResultPage mResultPage = null;
// Example Search request listener
class SearchRequestListener implements ResultListener {
@Override
public void onCompleted(DiscoveryResultPage data, ErrorCode error) {
if (error != ErrorCode.NONE) {
// Handle error
...
} else {
// Store the last DiscoveryResultPage for later processing
mResultPage = data;
...
}
}
}
...
// When the next page of results is needed...
DiscoveryRequest nextPageRequest = mResultPage.getNextPageRequest();
if (nextPageRequest != null) {
// More data is available if the nextPageRequest is not null
ErrorCode error = nextPageRequest.execute(new SearchRequestListener());
if( error != ErrorCode.NONE ) {
// Handle request error
...
}
}
Calling DiscoveryResultPage.getItems(), returns a List containing one of the following types of
objects, which are DiscoveryResult instances. DiscoveryResult is a collection of Link subtypes.
•
PlaceLink - Represents discovery information about a Place. The PlaceLink contains a brief
summary about a place. Details about a place are available from the Place that the PlaceLink
references.
115
HERE Android SDK Developer's Guide
► User Guide
•
DiscoveryLink - Represents a discovery-related API link used to retrieve additional
DiscoveryResultPage. This type of Link can be a result item in an Explore or Here type of search.
The DiscoveryLink references refined discovery requests resulting in more specific results. For
example, the DiscoveryLink may link to a discovery request to search for 'Eat & Drink', 'Going Out',
'Accommodation', and so on.
Since there may be new types of Link items in the future, it is recommended that each type of
DiscoveryResult be checked before it is used (as shown in the following code snippet). In the following
example, it is shown how a Place is retrieved through a PlaceLink:
// Implement a search result listener
ResultListener searchListener = new ResultListener() {
@Override
public void onCompleted(DiscoveryResultPage results, ErrorCode error) {
if (error == ErrorCode.NONE) {
// The results is a DiscoveryResultPage which represents a
// paginated collection of items.
List items = results.getItems();
// Iterate through the found place items.
for (DiscoveryResult item : items) {
// A Item can either be a PlaceLink (meta information
// about a Place) or a DiscoveryLink (which is a reference
// to another refined search that is related to the
// original search; for example, a search for
// "Leisure & Outdoor").
if (item.getResultType() == ResultType.PLACE) {
PlaceLink placeLink = (PlaceLink) item;
// PlaceLink should be presented to the user, so the link can be
// selected in order to retrieve additional details about a place
// of interest.
...
} else if (item.getResultType() == ResultType.DISCOVERY) {
DiscoveryLink discoveryLink = (DiscoveryLink) item;
// DiscoveryLink can also be presented to the user.
// When a DiscoveryLink is selected, another search request should be
// performed to retrieve results for a specific category.
...
}
}
} else {
// Handle search request error.
}
}
};
...
// Implement a Place listener for handling user interaction with a displayed PlaceLink
class PlaceListener implements ResultListener {
@Override
public void onCompleted(Place data, ErrorCode error) {
if (error != ErrorCode.NONE) {
// Handle error
...
} else {
// Present the place details to the user.
String placeName = data.getName();
List placeCategories = data.getCategories();
...
116
HERE Android SDK Developer's Guide
► User Guide
}
}
}
// Retrieve the place details when the user selects a displayed PlaceLink.
private void onPlaceLinkSelected(PlaceLink placeLink) {
PlaceRequest placeRequest = placeLink.getDetailsRequest();
if( placeRequest.execute(new PlaceListener()) == ErrorCode.NONE ) {
// Request successful. Additional work can be done here, however, place details will
// be returned in PlaceListener.onCompleted().
...
} else {
// Handle the error
...
}
}
Text AutoSuggestion Requests
The HERE Places Search API also supports text autosuggestion requests. This type of request is used for
retrieveing a list of instant results (AutoSuggestPlace) and refined search links (AutoSuggestSearch)
that are related to a specified location context and a partial search term. For example, if you make a request
with the String "rest" in Berlin, the results contain search terms such as "Restaurant", "Rest area", and
"Restorf, Höhbeck, Germany".
Note: Text AutoSuggestion is currently offered as a beta feature. APIs may change without notice.
Offline requests are not supported.
To use text suggestions, implement a listener to handle a list of AutoSuggest objects and call new
TextAutoSuggestionRequest(String) as follows:
// Example request listener
class AutoSuggestionQueryListener implements ResultListener> {
}
@Override
public void onCompleted(List data, ErrorCode error) {
for (AutoSuggest r : data) {
try {
String term = "rest";
TextAutoSuggestionRequest request = null;
request = new TextAutoSuggestionRequest(term).setSearchCenter(myMap.getCenter());
if (request.execute(new AutoSuggestionQueryListener()) !=
ErrorCode.NONE ) {
//Handle request error
//...
}
} catch (IllegalArgumentException ex) {
//Handle invalid create search request parameters
}
}
}
You can retrieve the results of a TextAutoSuggestionRequest by first checking the autosuggest object
type, as shown in the following example. Note that it is possible for AutoSuggestSearch to contain
additional paginated results through the DiscoveryRequest object. If the object is AutoSuggestPlace,
you can request for more details through its PlaceRequest object.
//assume autoSuggestList contains the list of results
try {
AutoSuggest autoSuggest = autoSuggestList.get(index);
117
HERE Android SDK Developer's Guide
► User Guide
// set
String
// get
String
title
title = autoSuggest.getTitle();
highlightedTitle
highlightedTitle = Html.fromHtml(autoSuggest.getHighlightedTitle()).toString();
if (autoSuggest instanceof AutoSuggestPlace) {
AutoSuggestPlace autoSuggestPlace = (AutoSuggestPlace)autoSuggest;
// vicinity
if (autoSuggestPlace.getVicinity() != null) {
String vicinity = autoSuggestPlace.getVicinity();
}
// set category
if (autoSuggestPlace.getCategory() != null) {
String category = autoSuggestPlace.getCategory();
}
// set position
if (autoSuggestPlace.getPosition() != null) {
String position = autoSuggestPlace.getPosition().toString();
}
// set boundaryBox
if (((AutoSuggestPlace)autoSuggest).getBoundingBox() != null) {
String boundingBox = ((AutoSuggestPlace)autoSuggest).getBoundingBox().toString();
}
} else if (autoSuggest instanceof AutoSuggestSearch) {
AutoSuggestSearch autoSuggestSearch = (AutoSuggestSearch)autoSuggest;
// set category
if (autoSuggestSearch.getCategory() != null) {
String category = autoSuggestSearch.getCategory();
}
// set position
if (autoSuggestSearch.getPosition() != null) {
String position = autoSuggestSearch.getPosition().toString();
}
// set boundaryBox
if (autoSuggestSearch.getBoundingBox() != null) {
String boundingBox = autoSuggestSearch.getBoundingBox().toString();
}
DiscoveryRequest myDiscoveryRequest = autoSuggestSearch.getSuggestedSearchRequest();
myDiscoveryRequest.execute(myDiscoveryResultPagelistener);
} catch (Exception e) {
Log.e("ERROR: ", e.getMessage());
}
External References
A place of interest can contain a reference to a foreign system outside of the HERE SDK. For example, a
Place representing a restaurant contains an external reference to an entry in a restaurant reviews website.
Each external reference is tied to a single reference source. However, each reference can have one or
multiple identifiers.
The following external reference sources are supported in PlaceRequest and DiscoveryRequest:
118
HERE Android SDK Developer's Guide
► User Guide
•
•
Request.PVID_ID_REFERENCE_NAME - Source for HERE Core Maps POI data
"yelp" - Source for Yelp IDs
An external reference is returned in the form of one or multiple String identifiers in Place, PlaceLink, or
Location. To retrieve a reference, add a source to the PlaceRequest or DiscoveryRequest, as shown in
the following example:
// Create a request to search for restaurants in Vancouver
GeoCoordinate vancouver =
new GeoCoordinate(48.263392, -123.12203);
DiscoveryRequest request =
new SearchRequest("restaurant").setSearchCenter(vancouver);
// We also want to retrieve the Yelp ID external reference
request.addReference("yelp");
request.setCollectionSize(10);
ErrorCode error = request.execute(new SearchRequestListener());
After the request execution is complete, you can retrieve the results by using the getReference(String)
method. If an external reference returns multiple results, such as the case where a single Place is
associated with multiple identifiers, use the getAlternativeReferenceIds(String) method in Place
or PlaceLink to retrieve the remaining result items.
//...
class SearchRequestListener implements ResultListener {
@Override
public void onCompleted(DiscoveryResultPage data, ErrorCode error) {
if (error != ErrorCode.NONE) {
// Handle error
//...
} else {
//...
mResultPage = data;
for (PlaceLink link : mResultPage.getPlaceLinks())
{
String yelpReferenceID = link.getReference("yelp");
}
//...
}
}
}
//...
Additional external reference sources are supported through details requests, as shown in the next example.
For example, you can use getDetailsRequest() from a PlaceLink object and retrieve reference IDs by
executing the details request.
Note: The following external references can be retrieved through a details request:
•
Request.PVID_ID_REFERENCE_NAME - Source for HERE Core Maps POI data
•
Request.VENUES_CONTENT_ID_REFERENCE_NAME - Source for HERE Venue Content IDs
•
•
•
Request.VENUES_VENUE_ID_REFERENCE_NAME - Source for HERE Venue IDs
Request.VENUES_DESTINATION_ID_REFERENCE_NAME - Source for HERE Venue Destination
IDs
Request.BUILDING_ID_REFERENCE_NAME - Source for HERE Building IDs
119
HERE Android SDK Developer's Guide
► User Guide
•
"tripadvisor" - Source for TripAdvisor IDs, to be used with TripAdvisor Content API. [*]
•
"yelp" - Source for Yelp IDs, to be used with Yelp Business API. [*]
•
"facebook" - Source for Facebook unique page IDs, to be used with Facebook Graph API. [*]
•
"opentable" - Source for OpenTable restaurant unique IDs, to be used with OpenTable
reservation widget. [*]
//...
class SearchRequestListener implements ResultListener {
@Override
public void onCompleted(DiscoveryResultPage data, ErrorCode error) {
if (error != ErrorCode.NONE) {
// Handle error
//...
} else {
//...
mResultPage = data;
for (PlaceLink link : mResultPage.getPlaceLinks())
{
PlaceRequest detailsRequest = link.getDetailsRequest();
detailsRequest.addReference("tripadvisor");
//assuming you have created DetailsListener()
if( placeRequest.execute(new DetailsListener()) == ErrorCode.NONE ) {
// Request successful.
// Check the returned Place objects in DetailsListener.onCompleted().
...
} else {
// Handle the error
...
}
}
//...
}
}
}
//...
Note: Sources marked with [*] cannot be used with the PlaceRequest(String, String)
constructor.
You can also use an external reference in the reverse scenario to retrieve a particular Place by using a
PlaceRequest created using the PlaceRequest(String, String) constructor. For example:
PlaceRequest placeRequest =
new PlaceRequest(Request.BUILDING_ID_REFERENCE_NAME, "12345");
Offline Search
When internet connectivity is unavailable, the HERE Places supports offline search by providing results using
available cached map data. The search results in this offline mode may be limited.
The main differences compared to online search results are:
•
•
Fewer POIs (Point of Interests) are available
No detailed POI's information is available (for example: ratings, reviews, editorials, images, and so on)
120
HERE Android SDK Developer's Guide
► User Guide
Force Online or Offline
You can launch online or offline searches without changing the device or HERE SDK connectivity by using
the setConnectivity(Connectivity) method on a Request instance. This property is applicable to all
Request subclasses, except TextAutoSuggestionRequest, which can only be used online.
Connectivity has three possible values:
•
•
•
If you launch a request using the DEFAULT connectivity mode, the request is performed according to
the MapEngine connectivity setting. If the device is offline while MapEngine is set to online mode, the
request fails.
If you launch a request using the ONLINE connectivity mode, an online request is performed, regardless
of the MapEngine connectivity setting.
If you launch a request using the OFFLINE connectivity mode, an offline request is performed,
regardless of the MapEngine connectivity setting.
In all cases, if the request fails, no fallback action is automatically performed.
To ensure that the connectivity mode is applied, setConnectivity(Connectivity) before executing a
Request.
•
•
•
•
If a Connectivity.ONLINE search request fails due to connection issues, HERE SDK returns the
ErrorCode.UNKNOWN error code.
If a Connectivity.ONLINE Geocoding or Reverse Geocoding request fails due to connection issues,
HERE SDK returns the ErrorCode.NETWORK_COMMUNICATION error code.
If a Connectivity.OFFLINE search request fails due to not enough cached data, HERE SDK returns
with zero results.
If you attempt to execute a TextAutoSuggestionRequest with the Connectivity.OFFLINE
connectivity mode, HERE SDK returns the ErrorCode.SERVICE_UNAVAILABLE error code, since auto
suggestions are only supported online.
Custom Locations and Geometries
This section describes the Custom Locations feature. This feature allow user-defined locations and
geometries to be retrieved through different search requests.
You can find more information about using Custom Locations, including how to import locations and how
to manage location layers, by using the Custom Locations API Developer's Guide and the Custom Location
Extension User Guide on http://developer.here.com.
Note: To use this feature, your application must include the Gson library (release 2.2.4 or a
compatible version) on its class path.
Custom Location Extension 2
Custom Location Extension 2 (CLE2) allows you to easily distribute custom geospatial information in your
mobile applications. Through CLE2, you can programatically insert spatial content to the local database and
upload them to the server for data-sharing purposes. You can also also perform online or offline searches.
These features effectively turns the HERE Android SDK into a lightweight spatial storage solution that enables
insertion and query for geospatial information using optimized algorithms.
121
HERE Android SDK Developer's Guide
► User Guide
The classes that support this feature are located under com.here.android.mpa.customlocation2.
Instead of having specific interfaces for location and geometry requests, CLE2 unifies all use cases
in one flexible approach: the returned value always contains one of the geometry types (such as
CLE2PointGeometry), along with a set of 0 to N user-defined attributes that can represent any information.
There is no implied structure in these attributes. These attributes are made available as a Map of keys and
attribute values.
Some examples of how you can use these CLE2 features include:
•
•
•
•
•
•
Show all users' custom Points of Interest (POIs) within a 2km radius.
Online or offline search for all customer offices within Germany using an area defined by a polygon, then
display the office's reception phone numbers, employee counts, and other details.
Edit geometry shapes in real time in offline mode and perform queries against it to get notifications
when such shapes intersect with other existing fixed shapes and other basic Geofencing solutions. For
example, this can be a ‘moving platform’, such as ships near ship docks, where locations are relative to
GPS movements.
Sharing Points of Interest that are not officially available as a part of HERE map data, such as a city's
facilities and outdoor basketball courts.
Persist GPS data that is tied to arbitrary data, such as hiking trails with speed, even during offline mode.
Search for specific types of objects that are near a given route.
Layers and Filtering
All data is organized in the form of layers. When uploading, storing or search for information, a layer name
string is specified and can be used to better filter relevant information.
A further filtering is possible by checking the geometry's attributes. These attributes are user-defined fields
that are linked to a geometry, such as CLE2PointGeometry, and can be text or number fields.
Note: CLE2 search is restricted per layer by app credentials. To manage the access restriction of a
Custom Location layer, contact your Custom Location administrator. If you do not have one, contact
your HERE representative.
Inserting and Uploading Data
To upload data to the CLE2 servers, you can use the web interface or REST APIs. Refer to the following User
Guide for more details: https://developer.here.com/documentation/versions#custom-location.
It is also possible to insert data locally and to the server via HERE Android SDK. The HERE SDK makes
it straightforward to generate any location-referenced data, even when storing it locally offline, and
sequentially sharing that information to other devices when a connection is established.
Performing Spatial Searches
To perform a search, choose one of the search types as shown below. A common input parameters to all
requests is the searched layer's name.
Table 9: Search Classes
Search Type
Description
Class Name
Proximity
Retrieve geometries that are within a given radius from
CLE2ProximityRequest
a center.
122
HERE Android SDK Developer's Guide
► User Guide
Search Type
Description
Class Name
Corridor
Retrieve geometries along a route specified by a
CLE2CorridorRequest
Bounding box
Retrieve geometries within a specified rectangular
CLE2BoundingBoxRequest
Quadkey
Retrieve geometries that falls within a specified
CLE2QuadkeyRequest
Attribute
Retrieve all geometries that matches with a specified
CLE2AttributeRequest
sequence of coordinates.
geographic area.
QuadKey.
query. This type of search is only available online.
Each of the search request types supports some common parameters, as listed below.
Table 10: Common CLE2Request Members
Setter Method
Description
setGeometry(CLE2GeometryType) Specifies the geometry type to be given in the result
(online only), see details below on "Understanding the
search results"
setCachingEnabled(boolean)
Default is false. If enabled, geometries received from
setQuery(String)
Currently available for online requests only. This
Example Values
•
•
•
CLE2GeometryType.FULL
CLE2GeometryType.LOCAL
CLE2GeometryType.NONE
such online search request will be stored locally.
variable allows a query filter to be specified on the
user's geometry attributes so that only geometries that
passes the filter are returned. Free form text with simple
equality and comparison operators.
Once you have a search request object created and set up according to your needs, call its
execute(CLE2ResultListener) method. The result of the search will be delivered to the provided
listener. You can get the geometries that matched search criteria from a CLE2Result object by calling
getGeometries(). This list of geometry results may contain objects of the following types:
Table 11: Geometry Return Types
Class
Geometry Description
Relevant Getter Methods
CLE2Geometry
Base class for all other geometry return values,
Map
containing user-defined attributes.
getAttributes()
CLE2PointGeometry
Represents a point in coordinates. Relates to a Point in GeoCoordinate getPoint()
CLE2MultiPointGeometry
Represents a multi-point as a coordinates array. Relates
List
CLE2PolylineGeometry
Represents a polyline as an GeoPolyline. Relates to a
GeoPolyline getPolyline()
CLE2MultiPolylineGeometry
Represents a multi-polyline as an array of
List
WKT.
to a MultiPoint in WKT.
WKT LineString object.
GeoPolyline. Relates to a WKT MultiLineString
object.
getMultiPoint()
getPolylines()
123
HERE Android SDK Developer's Guide
► User Guide
Class
Geometry Description
Relevant Getter Methods
CLE2PolygonGeometry
Represents a polygon with a GeoPolygon for the outer
GeoPolygon getOuterRing(),
Relates to a WKT polygon object containing all rings of
getInnerRings()
ring, and an array of GeoPolygon for inner holes.
this geometry.
Represents a multi-polygon as an array of
CLE2MultiPolygonGeometry
CLE2GeometryPolygon. Relates to a MultiPolygon
object in WKT.
List
List
getPolygons()
In the OpenGIS (the implementation standard for Geographic Information) and WKT representation formats,
the concept of a polygon is defined by one outer ring polygon plus zero or more inner hole polygons. This is
the reason that the class CLE2PolygonGeometry contains a GeoPolygon and a secondary GeoPolygon
array.
Understanding Local and Full Search Results
While processing user-uploaded data, CLE2 creates a look-up index where geometries are divided by an
internal fixed grid. If a geometry spans across several grid tiles, then the search index may contain smaller
slices of this uploaded geometry. This behavior allows for better search performance, as well as optimized
return values, since it is possible to only return the relevant part of the originally submitted geometry, and
thus reducing the response size and processing time.
Before executing a search, you can specify if you are only interested in the part of the geometry that falls
within the tiles around the search area (in other words, the tiled "local" geometry), or if you would like to
receive the full geometry as originally uploaded.
Note: By default, HERE SDK returns full geometries.
Figure 53: Local and Full Geometry
You can use the following three options to define whether you are requesting for full or local results. These
options are available for all search types:
CLE2GeometryType value
Meaning
FULL
The result contains the original geometry (as uploaded).
LOCAL
The result contains the processed geometries that falls within the search area for the tiles in reach.
NONE
No geometry is returned at all, only the properties/attributes of the geometries that match the given
search are returned.
124
HERE Android SDK Developer's Guide
► User Guide
Proximity Search Request Example
To perform a custom location search, you need to create a CLE2ProximityRequest using the
CLE2ProximityRequest(String layerId, GeoCoordinate center, int radius) or
CLE2ProximityRequest(List layerIds, GeoCoordinate center, int radius)
constructor methods.
A proximity search returns a list of custom locations that fall within a specified radius of a GeoCoordinate
location. For example, the following code shows how to perform a search for all locations in the previouslymentioned stores layer that exists within a 0.5 kilometer radius of Frankfurt Central Station:
String layerId = "HERE_SITES";
int radius = 8000; // 8 km
GeoCoordinate location = new GeoCoordinate(49.196261, -123.004773);
CLE2ProximityRequest req = new CLE2ProximityRequest(layerId, location, radius);
req.execute(new CLE2Request.CLE2ResultListener() {
@Override
public void onCompleted(CLE2Result result, String error)
{
//if CLE2Error.NONE.equals(error) is true, the request was successful
if (error.equals(CLE2Error.NONE)) {
List gemetry = result.getGeometries();
for (CLE2Geometry geo : gemetry) {
java.util.Map attributeMap = geo.getAttributes();
String name = attributeMap.get("NAME1");
double distance = geo.getDistance();
}
}
}
});
The layerId parameter represents a set of custom locations. For example, layerID="HERE_SITES"
represents an example layer that contains HERE locations in Germany. You can also perform a proximity
search on different layers at the same time:
List layerIds = new ArrayList():
layerIds.add("LAYER_1");
layerIds.add("LAYER_2");
int radius = 500; // 500 meters
GeoCoordinate location = new GeoCoordinate(50.113905,8.677608);
CLE2ProximityRequest req = new CLE2ProximityRequest(layerIds, location, radius);
req.execute(new CLE2Request.CLE2ResultListener() {
@Override
public void onCompleted(CLE2Result result, String error)
{
//if CLE2Error.NONE.equals(error) is true, the request was successful
if (error.equals(CLE2Error.NONE)) {
List gemetry = result.getGeometries();
for (CLE2Geometry geo : gemetry) {
java.util.Map attributeMap = geo.getAttributes();
String name = attributeMap.get("NAME1");
double distance = geo.getDistance();
}
}
}
});
After creating a request object, you can call the execute(ResultListener) method to launch the search
request and listen for search results.
125
HERE Android SDK Developer's Guide
► User Guide
You can also add a filter to the request. A filter is a JavaScript-like expression that is evaluated for each
location-matching search query. When specified, only locations where the expression evaluates to true are
returned. For example, if you want to filter for location results that have the custom location parameter of
rating that is greater than 3 and the name "MyPlace23", perform the following:
String layerId = "HERE_SITES";
int radius = 8000; // 8 km
GeoCoordinate location = new GeoCoordinate(49.196261, -123.004773);
CLE2ProximityRequest req = new CLE2ProximityRequest(layerId, location, radius);
String filter = "CITY == 'Burnaby' && NAME1 != 'MyPlace'";
req.setQuery(filter);
req.execute(new CLE2Request.CLE2ResultListener() {
@Override
public void onCompleted(CLE2Result result, String error)
{
//if CLE2Error.NONE.equals(error) is true, the request was successful
if (error.equals(CLE2Error.NONE)) {
List gemetry = result.getGeometries();
for (CLE2Geometry geo : gemetry) {
java.util.Map attributeMap = geo.getAttributes();
String name = attributeMap.get("NAME1");
double distance = geo.getDistance();
}
}
}
});
Iterating Over Results
The CLE2Result object contains a list of geometries that are a result of the search and make them available
with getGeometries() method. Since different types of geometry can be returned, it is recommended to
test for the type before using it. For example, you can use CLE2ResultListener in a similar manner as the
following example:
CLE2ResultListener resultListener = new CLE2Request.CLE2ResultListener() {
@Override
public void onCompleted(CLE2Result result, String error) {
if (!error.equals(CLE2Request.CLE2Error.NONE)) {
// process search results
for (CLE2Geometry geometry : result.getGeometries()) {
if (geometry instanceof CLE2PointGeometry) {
CLE2PointGeometry point = (CLE2PointGeometry) geometry;
// work with point geometry data
}
}
} else {
// handle error
}
}
};
Using CLE2 Offline
You can perform search requests online to the server or offline to the local device. To enable offline
searches against local data, the HERE SDK provides different ways for you to pre-fetch data.
Use offline mode as much as possible, since it provides the following advantages:
126
HERE Android SDK Developer's Guide
► User Guide
•
•
•
•
•
Resilience to network instability.
More efficient use of network bandwidth. Instead of sending one request per object, you can aggregate
requests locally and transmit data in batches.
Savings in network bandwidth. Your app can cache and update data that is only near the user's current
location or pre-download a layer only when a WiFi network becomes available.
Potentially making the application more responsive and improving the user experience and interface
interactions, since the data is already available locally on the device.
Create or modify geometries with the HERE SDK, and then store them locally, effectively using the HERE
SDK as a data source and a storage of information.
The offline CLE2 feature is designed to be simple to use. Since all database synchronization and geospatialrelated complexities are handled by the SDK, you can focus on other parts of app development.
Querying the Local Storage
To search using locally stored data, set the connectivity mode to OFFLINE for a desired CLE2Request and
perform the request:
CLE2ProximityRequest proximitySearch =
new CLE2ProximityRequest("HERE_TEST", map.getCenter(), 1000);
proximitySearch.setConnectivityMode(CLE2Request.CLE2ConnectivityMode.OFFLINE);
proximitySearch.execute(new CLE2Request.CLE2ResultListener() {
@Override
public void onCompleted(CLE2Result result, String error) {
if (!error.equals(CLE2Request.CLE2Error.NONE)) {
// process search results
} else {
// handle error
}
}
});
You can configure the search request to hybrid or automatic mode, indicating that if during an
online request the connection drops or there is a network error, then the request automatically
falls back to an offline operation. You can see whether the search was performed online or offline
by checking the connectivity mode that was used to perform the search. This can be done by calling
getConnectivityModeUsed() on the CLE2Result object.
CLE2ProximityRequest proximitySearch
= new CLE2ProximityRequest("HERE_TEST", map.getCenter(), 1000);
proximitySearch.setConnectivityMode(CLE2Request.CLE2ConnectivityMode.AUTO);
proximitySearch.execute(new CLE2Request.CLE2ResultListener() {
@Override
public void onCompleted(CLE2Result result, String error) {
if (!error.equals(CLE2Request.CLE2Error.NONE)) {
// check if data came from online or offline search
boolean isDataFromServer = result.getConnectivityModeUsed() ==
CLE2Request.CLE2ConnectivityMode.ONLINE;
} else {
// handle error
}
}
});
127
HERE Android SDK Developer's Guide
► User Guide
Ways to Populate the Local Storage
By default, the offline feature is disabled and the local storage contains no data. There are three ways to add
geometries to make them available for offline searches:
1.
2.
3.
Caching search results after performing one or more requests, such as CLE2ProximityRequest. Note
that you cannot cache attribute requests.
Download one or more layers.
Direct insertion of data into the local database.
After populating the database, you can query for the data in offline mode as usual by switching the
connectivity mode of the respective request to CLE2ConnectivityMode.OFFLINE.
CLE2DataManager and CLE2Task
The CLE2DataManager object is the central interaction point with the local storage. With it, it is possible to:
•
•
•
•
•
Download all geometries of a specific layer
Check how many geometries are currently stored in total, or in a specific layer
Delete geometries belonging to a specific layer
Purge the local storage by deleting all items
Create, update, or delete a local or remote geometry
All the operations relating to data management that CLE2DataManager exposes make use of a CLE2Task
that represent a unit of work. Since all of the data management operations involve database access,
network communication or both, CLE2Task runs asynchronously. You can obtain a CLE2Task object from
CLE2DataManager.
With CLE2Task, you can:
•
•
•
•
•
•
•
•
•
Pass it to other parts of your code. CLE2Task is a self-contained unit of work.
Subscribe for results of the operation. Multiple subscribers are supported and they are called on the
main thread.
Start execution of the task. Tasks are reusable. You can run them repeatedly multiple times, which makes
retrying a failed operation very easy.
Cancel a running task.
Check if the task is started.
Check if the task has finished.
Wait for the task to finish.
Retrieve the status of a finished operation directly from the task (check for errors).
Retrieve the result of a successfully finished operation directly from the task.
Storing Data by Caching Search Results
When caching is enabled in a CLE2Request, any returned geometries are automatically stored locally. To
activate it, call setCachingEnabled(true) before performing the request:
// set query
String query = "CITY=='Berlin'";
request.setQuery(query);
// set Geometry type
request.setGeometry(CLE2GeometryType.LOCAL);
128
HERE Android SDK Developer's Guide
► User Guide
// to cache response
request.setCachingEnabled(true);
request.execute(new CLE2Request.CLE2ResultListener() {
@Override
public void onCompleted(CLE2Result result, String error) {
if (!error.equals(CLE2Request.CLE2Error.NONE)) {
// request succeeded, which means that the results are now stored locally
} else {
// handle error
}
}
});
//Now some geometries are in local storage.
//At a later point in time if you'd like to make an offline search,
//switch the connectivity mode to offline
Storing Data by Downloading Layers
The second option is to use CLE2DataManager to insert data to the local storage using the
newDownloadLayerTask() method.
The following is an example of how to download a whole layer from the CLE2 server:
CLE2DataManager.getInstance().newDownloadLayerTask("MYLAYER").start(
new CLE2Task.Callback() {
@Override
public void onTaskFinished(CLE2OperationResult result, CLE2Error error) {
if (error.getErrorCode() == CLE2ErrorCode.NONE) {
// download succeeded
} else {
// handle download error
}
}
});
It is also possible to delete individual layers from local storage or completely wiping out all data stored
locally:
// fire and forget method of running tasks (no callback)
CLE2DataManager.getInstance().newDeleteLayersTask(Arrays.asList(new String[]{"MYLAYER"}),
StorageType.LOCAL).start();
// by specifying StorageType.REMOTE, it is possible to delete the layers from CLE2 server, so be
careful
// wipe out all local data
CLE2DataManager.getInstance().newPurgeLocalStorageTask().start(new
CLE2Task.Callback() {
@Override
public void onTaskFinished(CLE2OperationResult result, CLE2Error error) {
if (error.getErrorCode() == CLE2ErrorCode.NONE) {
// notify user that all his data is gone
}
}
});
129
HERE Android SDK Developer's Guide
► User Guide
Storing Data by Inserting Geometries
You can generate location-based data and persist it locally, remotely, or both, by using the method
newGeometryTask() from the CLE2DataManager class. This factory method returns a CLE2Task object
that can be used to start, cancel, or to fetch results of operations at any given time.
public CLE2Task newGeometryTask(
OperationType operationType,
String layerId,
List geometryData,
StorageType storageType)
•
The first parameter in this method describes the operation type, which can be one of the following:
▫ OperationType.CREATE
▫ OperationType.UPDATE
▫ OperationType.DELETE
Note that querying for geometries is accomplished through the respective CLE2Request specialized
classes, so there is no "read" opreation here.
The second parameter is the layer the operation should be applied to.
The third parameter is a list with the geometries themselves.
•
•
•
The last parameter defines whether to operate on local storage (StorageType.LOCAL), or remote
storage (StorageType.REMOTE) using the HERE CLE2 server.
Note: While this section covers usage of this method for the local option, all operations (create,
update, delete) can also be used to change remote layers.
The following is an example on how to create a geometry and store it locally:
final int geometryCount = 100;
Random rand = new Random();
List geometries = new ArrayList<>(geometryCount);
// generate random point across the globe
for (int i = 0; i < geometryCount; i++) {
GeoCoordinate newPoint = new GeoCoordinate(
(rand.nextFloat()) * 180 - 90,
(rand.nextFloat()) * 360 - 180);
}
CLE2PointGeometry point = new CLE2PointGeometry(newPoint);
point.setAttribute("i", Integer.toString(i));
point.setAttribute("COLOR", rand.nextInt() % 2 == 0 ? "BLUE" : "RED");
geometries.add(point);
// create task for storing new geometries locally
// if the layer does not exist already, it is created,
// otherwise the geometries is added to existing layer
CLE2Task createLocal = CLE2DataManager.getInstance().newGeometryTask(
OperationType.CREATE, "RED_VS_BLUE", geometries, StorageType.LOCAL);
createLocal.start(new CLE2Task.Callback() {
@Override
public void onTaskFinished(CLE2OperationResult result, CLE2Error error) {
if (error.getErrorCode() == CLE2ErrorCode.NONE) {
// success
}
}
});
130
HERE Android SDK Developer's Guide
► User Guide
Uploading a Local Layer
It is possible to upload a locally-stored layer to the server. Since this requires two operations (fetch from
local storage and upload), it's a good candidate to run individual tasks in synchronous manner to avoid
callback hell creeping in. Of course, this needs to be done on it's own thread, for example using AsyncTask.
AsyncTask localToRemoteAsyncTask = new AsyncTask() {
@Override
protected Boolean doInBackground(String... strings) {
CLE2DataManager dataManager = CLE2DataManager.getInstance();
boolean success = false;
for (String layerId : strings) {
// grab data from local storage
CLE2Task> fetchLocalTask = dataManager
.newFetchLocalLayersTask(Arrays.asList(new String[] {layerId}))
.start()
.waitForResult(5, TimeUnit.SECONDS);
success = fetchLocalTask.isFinished()
&& fetchLocalTask.getError().getErrorCode() == CLE2ErrorCode.NONE;
if (!success) {
break;
}
// upload the data to the server, creating a new layer or replacing existing one
CLE2Task uploadTask = dataManager
.newUploadLayerTask(layerId, fetchLocalTask.getResult())
.start()
.waitForResult(15, TimeUnit.SECONDS);
success = uploadTask.isFinished()
&& uploadTask.getError().getErrorCode() == CLE2ErrorCode.NONE;
if (!success) {
break;
}
};
}
}
return success;
Data Management Considerations
The following are a few tips to help with data management when using CLE2 in an offline context.
Local-only Geometries
All CLE2Geometry objects have the following properties:
1.
2.
Geometry ID, accessible with getGeometryId()
Locality flag, accessible with isLocal()
The geometry ID is unique to a layer. If a geometry object has just been created, its geometry ID is null and
the locality flag is false.
The locality flag tells whether this geometry belongs to a local context only, meaning it was not retrieved
or passed through the CLE2 server. A geometry with a true locality flag has a locally generated unique
geometry ID. Otherwise, it contains a server-provided ID. This server-provided ID is not related to the locally
generated IDs of geometries stored directly in the database created via newGeometryTask().
131
HERE Android SDK Developer's Guide
► User Guide
Note: The functionality of locally storing geometries without passing through the server is provided
so that you do not need to manage data persistence on these objects when a connection is not
available.
For simplicity, when saving geometries directly to the local database, keep them using a separate
layer name. If at a later desired point in time these geometries should be shared with the server, fetch
all local geometries using newFetchLocalLayersTask() method of CLE2DataManager and then
upload them either using newUploadLayerTask() or newGeometryTask() with a create operation
(OperationType.CREATE). This avoids the requirement to check for the isLocal() property.
By using these concepts, you can move geometries to different layers, contexts, and use these tools to
organize data.
Data Consistency
Use of newUploadLayer() should be primarily restricted to administrative users, because this method
deletes all existing geometries in the server and recreate the layer with the provided ones. If the user does
not have the latest information for this layer, data loss may occur, as it can overwrite another user's upload.
Therefore, for a scenario with continuous or concurrent geometry upload, use the newGeometryTask()
method with OperationType.CREATE or OperationType.UPDATE. Operating in a "append only"
manner or only updating the existing geometries avoids data loss, even if users are uploading geometries
concurrently to the server.
Current Limitations
Currently, individualized user account management for the CLE2 server is not available. For security reasons,
care must be taken that your app credentials are kept well hidden. Please contact HERE if your application
requires a user account access feature.
Note: Since geospatial queries are the focus of CLE2, HERE SDK does not support attribute searches in
offline mode. You can filter the data using one of the geospatial queries (such as proximity) to narrow
down the results to a small enough number that most applications do not suffer performance impact
by iterating the geometry’s attributes key-value dictionary to filter results further.
Toll Cost Extension
Toll Cost Extension provides you the possibility to easily access Toll Cost Extension API from the HERE SDK.
The HERE Toll Cost Extension (TCE) allows you to determine the toll costs for a specified route for a defined
vehicle profile.
TCE Classes
Class
Description
TollCostOptions
Specify all input parameters of TollCostRequest which includes
TollCostRequest
Allows you to determine the toll costs for a specified route for a
TollCostResult
Represents a result from a TCE request.
vehicle profile currency and departure date.
defined vehicle profile.
132
HERE Android SDK Developer's Guide
► User Guide
Class
Description
TollCostVehicleProfile
Specify different vehicle parameters and optional input
parameters.
Requesting the Toll Cost Data
To use Toll Cost Extension, you need to first have a route calculated in online mode, as the Toll Cost
Extension requires permanent directed link IDs. After having a route from the core router, you can then
retrieve the toll cost of the route.
// Create the core router
CoreRouter router = new CoreRouter();
// We need link IDs, Route.getPermanentLinkIds(), so we force online routing
router.setConnectivity(Connectivity.ONLINE);
You can provide options for the toll cost request through an instance of TollCostOptions. If the
vehicle needs non-default settings, such as if this toll request is for a specific type of vehicle, create an
TollCostVehicleProfile object.
Note: It is your responsibility to provide a compatible route and toll cost options. When setting the
toll cost options, make sure that they match the route options. For example, if the toll cost option
vehicle type is set as truck, but the route is created for a car, they are incompatible. In this case, the
toll cost result may not be valid.
133
HERE Android SDK Developer's Guide
► User Guide
Next, create a TollCostRequest request object. If the request object is valid and the route contains
permanent directed linkids, then you can execute the toll cost request via a result listener. When the result is
received, first it is checked for any error. If there is no error, its toll cost contents are retrieved.
Figure 54: A Toll Cost Example
// Step 1: Provide input and calculate toll cost.
Route route = null;
// Required
Date departureTime = null; // Optional
// Step 2: Wrap all input.
// Vehicle profile -> Type Bus, EmissionType EURO_IV, TrailerType NONE. Optional.
TollCostVehicleProfile vehicleProfile = new TollCostVehicleProfile();
134
HERE Android SDK Developer's Guide
► User Guide
vehicleProfile.setVehicleType(TollCostVehicleProfile.VehicleType.BUS);
vehicleProfile.setEmissionType(TollCostVehicleProfile.EmissionType.EURO_IV);
vehicleProfile.setTrailerType(TollCostVehicleProfile.TrailerType.NONE);
TollCostOptions options = new TollCostOptions();
parameter.setDeparture(departureTime);
parameter.setVehicleProfile(vehicleProfile);
// Step 3: Create request
TollCostRequest request = new TollCostRequest(route, parameter); // request created, can not be
modify.
// Step 4: Execute with result listener
request.execute(new TollCostRequest.Listener() {
@Override
public void onComplete(TollCostResult result, TollCostError error) {
// Step 5: process result
if(error.getErrorCode() != TollCostError.ErrorCode.SUCCESS) {
// what is the error?
error.getErrorMessage();
return;
}
// if success
// get total toll cost of a route
result.getTotalTollCost();
// get toll cost by country
java.util.Map tollCostByCountry = result.getTollCostByCountry();
// get toll cost by toll system name
java.util.Map tollCostByTollSystem = result.getTollCostByTollSystemName();
}
});
Street-Level
This section provides an overview of Street-Level Imagery and Street-Level Objects in the HERE Android
SDK. With Street-Level Imagery, developers can add an immersive street visualization experience to their
application; with Street-Level Objects, developers can add objects, such as billboards and markers, to
custom locations in the Street-Level View.
Street-Level Imagery
The HERE Android SDK allows application developers to offer users panoramic street-level imagery for many
cities.
135
HERE Android SDK Developer's Guide
► User Guide
Street-level imagery is a collection of interconnected 360-degree panoramas. You can navigate from one
panorama to another by tapping on links that appear on-screen as arrows pointing in various navigable
directions.
Figure 55: Street-level imagery at San Francisco
The key concepts covered in this section include:
•
•
•
•
how to display the street-level coverage on a map
how to display the street-level imagery in an Android application
how to select the displayed street-level location
how to add, remove and interact with components of the street-level scene
The street-level imagery APIs follow a similar design as the mapping classes: StreetLevelFragment acts
as a View, and StreetLevelModel act as a Model in a Model-View-Controller (MVC) pattern. You can create
a controller class to coordinate interactions using custom logic.
Coverage
Street-level imagery is available in more than ninety cities. To display the street-level imagery
coverage area on a map, the Map.setStreetLevelCoverageVisible(boolean) method from the
com.here.android.mpa.mapping.Map class can be used as described in the code snippet below. The
area where the imagery is available is highlighted on the map.
//Assume that map is instantiated
136
HERE Android SDK Developer's Guide
► User Guide
map.setStreetLevelCoverageVisible(true);
Figure 56: Map of San Francisco
Using Street-level Imagery
This section describes how to enhance an application by displaying the appropriate street-level imagery
when a user taps on a location on a map. Map data for a specified location must be already downloaded and
available before the imagery is requested for that location. This is achieved by displaying the map for that
location first, or by pre-downloading the map data for that region.
The first step to integrate street-level imagery into an application is to insert
a StreetLevelFragment into a view layout. This is accomplished by adding
com.here.android.mpa.streetlevel.StreetLevelFragment to the Android XML layout file as per
the following example. StreetLevelFragment is a standard Android Fragment-derived component that
enables the display and gesture interactions with street-level scenes.
...
After adding a StreetLevelFragment to the layout, the fragment must be initialized by calling the
function StreetLevelFragment.init(OnEngineInitListener). During initialization, a default
StreetLevelModel is created and associated to the street-level fragment. Street-level imagery can be
137
HERE Android SDK Developer's Guide
► User Guide
enhanced by displaying an arrow to show the possible navigation directions. This is achieved by adding an
image (for example: an arrow) to the street-level model and making it visible, as shown below:
...
streetLevelFragment = (StreetLevelFragment) getFragmentManager()
.findFragmentById(R.id.streetlevelfragment);
streetLevelFragment.init(new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted(OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
com.here.android.mpa.common.Image arrow = new com.here.android.mpa.common.Image();
try {
arrow.setImageResource(R.drawable.panoarrow);
} catch (IOException e) {
//handle error
e.printStackTrace();
}
streetLevelFragment.getStreetLevelModel().setNavigationArrow(arrow);
streetLevelFragment.getStreetLevelModel()
.setNavigationArrowVisible(true);
streetLevelFragment.getStreetLevelModel().setOverlayTransparency(
0.5f);
}
}
});
...
The logic required to launch street-level imagery for a particular user-selected map location
is illustrated in the following MapGesture.OnGestureListener.onTapEvent(PointF
p) method. First, the street-level scene for the specified location is retrieved by calling the
StreetLevelModel.getStreetLevel(GeoCoordinate center,int searchRadius) method. This
method searches for the street level that is closest to the specified coordinate. The search area around the
coordinate is defined by the searchRadius which is expressed in meters. The street-level imagery for the
specified location is then rendered by calling StreetLevelModel.moveTo(StreetLevel streetLevel,
boolean animation, float heading, float pitch, float zoom).
// Map event listener
private MapGesture.OnGestureListener mapGestureListener =
new MapGesture.OnGestureListener() {
...
@Override
public boolean onTapEvent(PointF p) {
final StreetLevelFragment streetLevelFragment =
(StreetLevelFragment) getFragmentManager()
.findFragmentById(R.id.streetlevelfragment);
if (streetLevelFragment.getStreetLevelModel() != null) {
GeoCoordinate coordinate = map.pixelToGeo(p);
// Get street level meta data
StreetLevel level =
streetLevelFragment.getStreetLevelModel()
.getStreetLevel(coordinate, 100);
if (level != null) {
// Render street level imagery
streetLevelFragment.getStreetLevelModel().moveTo(
level, false, map.getOrientation(), 0, 1.0f);
}
}
return false;
}
...
138
HERE Android SDK Developer's Guide
► User Guide
};
Issue-Reporting Link
To comply with government regulations, street-level imagery in the HERE SDK is always accompanied by a
link to report incorrect imagery and privacy concerns. By default, the issue-reporting link is displayed in
English. If the user's device language is set to French or German, then the issue-reporting link appears in
these languages.
Note: It is a requirement for the issue-reporting link to be unobscured while street-level imagery is
visible.
Using Gestures in a Street-level Scene
Street-level imagery supports the following default gestures:
•
Rotate: Pan the Street-level view to rotate or change the pitch
•
Single Tap: Select and highlight the object tapped
•
Pinch/Spread to Zoom: Zoom out or zoom in by respectively, pinching or spreading two fingers being
held on the screen
•
Double Tap: Move to a new location with the center in the vicinity of where the screen was tapped.
The StreetLevelGesture class allows an app to enable, disable, and handle gestures in a street-
level scene. StreetLevelGesture is used in a similar manner as the MapGesture class. To use it, call
StreetLevelFragment.getStreetLevelGesture() to retrieve the associated object instance, then use
setter methods (such as setPinchEnabled(false)) to disable any unwanted gestures.
Note: By default, rotation, pinch, single-tap and double-tap are enabled in StreetLevelGesture.
As with MapGesture.OnGestureListener, you can add custom logic to handle street-level gestures by
implementing StreetLevelGesture.OnGestureListener. The OnGestureListener class contains
methods such as onPinchZoom that are called when a user performs the particular gesture. As with
MapGesture.OnGestureListener, you can allow or prevent the default gesture behavior to be performed
after your implemented callback by returning a boolean value.
private StreetLevelGesture.OnGestureListener myStreetLevelGestureListener =
new StreetLevelGesture.OnGestureListener() {
...
@Override
public boolean onObjectsSelected(List selectedObjects) {
boolean consumed = false;
for (StreetLevelSelectedObject o : selectedObjects) {
if (o != null) {
ViewObject vo = (ViewObject) o.getObject();
if (vo instanceof StreetLevelBuilding) {
StreetLevelBuilding b = (StreetLevelBuilding) vo;
b.setHighlight(0.5f);
consumed = true;
break;
}
}
}
return consumed;
}
139
HERE Android SDK Developer's Guide
► User Guide
}
...
// implement the other gesture callbacks
To add the listener to your street-level view, include a call to
addOnGestureListener(StreetLevelGesture.OnGestureListener) after the street-level fragment
has been successfully initialized as follows:
...
streetLevelFragment.init(new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted(OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// fragment has been successfully initialized
if (streetLevelFragment.getStreetLevelGesture() != null) {
streetLevelFragment.getStreetLevelGesture().addOnGestureListener(
myStreetLevelGestureListener);
}
}
}
});
...
Note: Remember to call removeOnGestureListener(OnGestureListener) when you no longer
need to listen for street-level gesture events to free up application resources.
OnEventListener Interface
The StreetLevelModel.OnEventListener interface represents a listener to provide notification
upon completion of a StreetLevel event such as user interaction with a street-level object. Relevant
StreetLevelModel methods that control adding and removing this kind of listener include:
•
•
StreetLevelModel.addStreetLevelModelListener(OnEventListener);
StreetLevelModel.removeStreetLevelModelListener(OnEventListener)
Registration of the OnEventListener should be performed after the StreetLevelFragment is initialized
as described in the code snippet below.
...
streetLevelFragment = (StreetLevelFragment) getFragmentManager().findFragmentById(
R.id.streetlevelfragment);
streetLevelFragment.init(new FragmentInitListener() {
@Override
public void onFragmentInitializationCompleted(InitError error) {
if (error == InitError.NONE) {
StreetLevelModel model =
streetLevelFragment.getStreetLevelModel();
if (model != null) {
model.addStreetLevelModelListener(onEventListener);
}
...
}
}
});
...
Removing the street-level listener can be performed as described in the code snippet below.
140
HERE Android SDK Developer's Guide
► User Guide
...
StreetLevelFragment streetLevelFragment =
(StreetLevelFragment) getFragmentManager()
.findFragmentById(R.id.streetlevelfragment);
streetLevelFragment.getStreetLevelModel()
.removeStreetLevelModelListener(onEventListener);
...
Compass Map
By default, a circular map view called a Compass Map is presented with street-level imagery. Users
can view it by panning a street-level view downward towards the ground. Compass Map provides users
with their direction and location context while they are viewing the street-level scene. Users cannot
directly manipulate a Compass Map, but changes from panning or navigating the street-level imagery are
immediately reflected in it.
To disable the Compass Map, use the setCompassMapVisible(boolean) method in StreetLevelModel.
Street-Level Objects
The street-level API exposes access and allows interaction with two different types of objects that are part of
the street-level scene:
•
•
PROXY_OBJECT - provided automatically in the street-level scene when it is displayed.
USER_OBJECT - provided by the application and can be added in to the street-level scene.
The supported street-level proxy objects are:
•
StreetLevelBuilding - a building that is visible in the street-level scene
141
HERE Android SDK Developer's Guide
► User Guide
•
StreetLevelLink - a navigation arrow that can be clicked by the user to navigate from one panorama
to another
The following code snippet highlights the selected building from a street-level scene. All the logic is
implemented in the
StreetLevelGesture.OnGestureListener.onObjectsSelected(List
selectedObjects) method.
private StreetLevelGesture.OnGestureListener listener
= new StreetLevelGesture.OnGestureListener() {
...
@Override
public boolean onObjectsSelected
(List selectedObjects) {
boolean consumed = false;
for (StreetLevelSelectedObject object : selectedObjects) {
if (object!= null) {
ViewObject viewObject = (ViewObject) object.getObject();
if (viewObject instanceof StreetLevelBuilding) {
StreetLevelBuilding building
= (StreetLevelBuilding) viewObject;
building.setHighlight(0.5f);
consumed = true;
break;
}
}
}
return consumed;
}
...
};
The supported street-level user objects are:
•
•
StreetLevelIcon - an image that has a specified location on the street level map
StreetLevelBillboard - a billboard that has a specified location on the street-level map
Note: Both objects can also be attached to a StreetLevelBuilding.
The major differences between these objects are the way the size is specified and how they are rendered
when a zoom operation is performed. The billboard has the size specified in meters so it is always rendered
relative to the size of the building, while the StreetLevelIcon has the size specified in pixels and has
a StreetLevelIconSize.ScalePolicy to define the rendering size. Relevant StreetLevelModel
methods that control adding and removing street-level objects are:
•
•
StreetLevelModel.addStreetLevelObject(StreetLevelObject streetLevelObject)
StreetLevelModel.removeStreetLevelObject(StreetLevelObject streetLevelObject)
The easiest way to enhance an application by displaying a street-level user object is to create the object, set
the content (desired image) and properties (image size and building identifier) and add it to the street-level
model as described in the code snippet below.
@Override
public boolean onObjectsSelected(List selectedObjects) {
boolean consumed = false;
142
HERE Android SDK Developer's Guide
► User Guide
for (StreetLevelSelectedObject object : selectedObjects) {
if (object != null) {
ViewObject viewObject = (ViewObject) object.getObject();
if (viewObject instanceof StreetLevelBuilding) {
StreetLevelBuilding building = (StreetLevelBuilding) viewObject;
building.setHighlight(0.5f);
consumed = true;
// Create Image
com.here.android.mpa.common.Image streetLevelImage =
new com.here.android.mpa.common.Image();
try {
streetLevelImage.setImageResource(
R.drawable.streetLevelIconImage);
} catch (Exception io) {
System.out.println(
"ERROR: Cannot create street " + "level icon image");
}
// Create Icon and set properties
StreetLevelIcon streetLevelIcon = new
StreetLevelIcon(building.getPosition(),streetLevelImage);
}
StreetLevelIconSize size = new StreetLevelIconSize(100,100);
streetLevelIcon.setSize(size);
streetLevelIcon.setAttachmentIdentifier(building.getIdentifier());
// Add icon to the street level
streetLevelModel.addStreetLevelObject(streetLevelIcon);
break;
}
}
}
return consumed;
Note: It is not necessary to have both the StreetLevelFragment and the MapFragment in the
same activity. However, before a StreetLevelFragment can be used, the area to be displayed in
the StreetLevelFragment must be previously shown on the Map in order for the related data to be
downloaded onto the device.
Placing Street-level Objects
The HERE Android SDK also allows you to control the placement of a street-level object. For example, you
may wish to put a large billboard to cover the entire side of a building, or you may want a small icon in the
middle of a road. Whether it is a billboard or an icon, you can use the StreetLevelIconPlacement class
to control how your street-level object is presented to the user.
The first step to place your object is to determine whether your street-level object is attached to a building,
or placed according a geocoordinate location.
•
•
For objects attached to a building, call setAttachmentIdentifier(Identifier) with a building Id.
For objects not attached to a building, the HERE SDK places the object at its geocoordinate position. You
can optionally call setAnchorPoint(PointF) to indicate which part of the billboard is used as the
anchor, hovering exactly over the specified geocoordinate position.
Note: Using the StreetLevelIconPlacement.HorizontalPlacement.DEFAULT placement
mode places the object at its geocoordinate position, even if setAttachmentIdentifier() has
been set.
143
HERE Android SDK Developer's Guide
► User Guide
Next, call the StreetLevelIconPlacement constructor to create a StreetLevelIconPlacement
object, and call setPlacementMode() to set it to the street-level object. See the following tables to see
descriptions of the valid vertical and horizontal placement modes.
VerticalPlacement value
Notes
TERRAIN
The vertical placement height parameter is evaluated as meters above the current terrain.
ATTACHMENT
Used with objects that are attached to buildings. The height parameter is evaluated as a ratio of the
FACADE
Used with HorizontalPlacement.FACADE. Refer to the table below. (If this value is used with
DEFAULT
The vertical placement height parameter is evaluated as meters above sea level.
HorizontalPlacement value
Notes
FACADE
•
Used for objects attached to buildings
•
Object placement is updated as the camera moves
building height, with 0 indicating the ground level, and 1 being the top of the building.
another horizontal placement mode, the HERE SDK treats it as HorizontalPlacement.TERRAIN.)
•
•
SURFACE
•
•
•
TRACK_CAMERA
the HERE SDK, whichever is higher
Used for objects attached to buildings
"Snaps" object to the closest point on the building, as determined by the object's
geocoordinate position
Object is viewable through the attached building's faces (For example, an object that has
"snapped" to building face A is viewable through building face B, even if A is obstructed)
Used for objects not attached to buildings
•
VerticalPlacement mode is ignored, regardless of the set value
•
Object is placed at the camera location. The object's geocoordinate is ignored
Vertical placement height is evaluated as meters above or below the camera
•
Used for objects attached to buildings
•
When used, icon is placed as if it is in the center of the attached building
•
•
DEFAULT
Using it with VerticalPlacement.FACADE places the object at a height as defined by you or
•
•
CENTROID
Places the street-level object on the side of the building that is most visible to the camera
•
Can only be used for icons, not billboards
You can see the icon through the faces of the attached building
The object is placed at its geocoordinate position, even if it is associated with a building.
Orienting Street-level Objects
You can control the orientation of street-level billboards by using the
StreetLevelBillboardOrientation class. To use this class, call its constructor to create an object
instance, and then set it to your billboard object using StreetLevelBillboard.setOrientation().
The StreetLevelBillboardOrientation constructor has three arguments: an orientation mode enum, a
normal vector indicating how the billboard is oriented, and an up vector indicating how the billboard stands.
Vectors may be ignored depending on the orientation mode.
The following is a list of the possible orientation mode values:
144
HERE Android SDK Developer's Guide
► User Guide
•
•
•
FIXED - Orients the billboard according to a normal and up vector.
VERTICAL_FIXED - The billboard is always upright. The horizontal orientation of the billboard is set
according to a normal vector.
BILLBOARD - The billboard is always upright and oriented towards the camera.
Turn-by-Turn Navigation for Walking and Driving
Note: [Important] Application developers using the Turn-by-turn Guidance APIs are required to
thoroughly test their applications in all expected usage scenarios to ensure safe and correct behavior.
Application developers are responsible for warning their users of the following obligations:
1.
Do not follow instructions that may lead to an unsafe or illegal situation
3.
Be aware that using a mobile phone or some of its features while driving may be prohibited.
2.
4.
5.
Obey all local laws.
Always keep hands free to operate the vehicle while driving.
The first priority while driving should be road safety.
The HERE Android SDK supports navigation on pedestrian , truck, and car routes. Using this feature, your
app can check the current device position against a calculated route and get just-in-time navigational
instructions. Both visual and audio instructions are supported.
The HERE Android SDK can also track the current position and display it on the map without a calculated
route.
145
HERE Android SDK Developer's Guide
► User Guide
Note: Your application should switch to the navigation-specific map schemes while performing
navigation. For more information on using these schemes, see Map Schemes on page 30.
Figure 57: Turn-by-turn Navigation with Speed Warning
Note: MapEngine.getInstance().onResume() should be called when the engine is required
for headless navigation operations. (For example, if you are providing your own map tiles.) This puts
the engine in the "active state". MapEngine.getInstance().onPause() puts the engine into a
suspended state and release cached in memory data. The onResume() and onPause() methods are
reference-counted and should be called an equal number of times.
The NavigationManager Class
The NavigationManager class is responsible for providing voice and visual instructions to
the user while driving or walking. An instance of NavigationManager can be retrieved using
NavigationManager.getInstance(). It should be associated with a map with the setMap(Map) method
before navigation is started.
Navigation can be started in three different modes:
•
Simulation Mode - simulate(Route, long) - This mode does not require any GPS data from the
•
Navigation Mode - startNavigation(Route) - This mode takes the calculated route and matches the
•
device, as the position is simulated along the given route.
position against the route. Before using this mode, PositioningManager is required to be running
using GPS data.
Tracking Mode - startTracking() - This mode does not require a calculated route, as it only tracks
and shows the current position on the map.
146
HERE Android SDK Developer's Guide
► User Guide
Note: The Android permission android.permission.ACCESS_FINE_LOCATION is required to use
the NavigationManager. Otherwise, the class returns Error.POSITIONING_FAILED. In addition,
to ensure that the app receives location updates, the user needs to have the Location permission
enabled (toggled to "on") during runtime.
The following is an example of performing navigation on a calculated route:
NavigationManager navigationManager = NavigationManager.getInstance();
//set the map where the navigation will be performed
navigationManager.setMap(getMap());
// if user wants to start real navigation, submit calculated route
// for more information on calculating a route, see the "Directions" section
NavigationManager.Error error = navigationManager.startNavigation(route);
Note: During a navigation session, it is recommended that you handle gestures by calling
NavigationManager.setMapUpdateMode(MapUpdateMode.NONE). This stops the map from
moving, but it does not affect position indicator movement and voice navigation. To re-enable map
movement, call NavigationManager.setMapUpdateMode(MapUpdateMode) with the previous
update mode enum, such as MapUpdateMode.ROADVIEW.
Background Navigation
If you are using the Turn-by-Turn Navigation Mode for driving, you can also set the HERE SDK to perform
guidance (including voice instructions and event callbacks) while the app is in the background. However,
unlike the foreground navigation scenario, HERE SDK does not stream map data during background
navigation. To properly support background navigation, HERE SDK requires your app to preload map data
(such as for the current city or state) using the MapLoader class.
Map Matching During Navigation
Map Matching is automatically enabled in both navigation mode and tracking mode. In simulation mode, the
map-matched position is simulated along the route with a user-defined speed.
Tunnel Extrapolation
Tunnel extrapolation is also performed internally by NavigationManager. It uses the last
available position data, which contains speed, orientation and GeoCoordinate, to estimate
the position of your vehicle inside a tunnel. If your application listens to position updates from
NavigationManager.PositionListener, there are regular position updates during tunnel
extrapolation. However, if your application also listens to the onPositionFixChanged() callback from
PositioningManager.OnPositionChangedListener, then it receives this callback, signifying a position
fix change. (For more information on the OnPositionChangedListener, see the Positioning section.) If
the road element at position contains the tunnel attribute, it implies that the current location is provided
by tunnel extrapolation. Position updates during tunnel extrapolation should be treated the same way as
regular updates.
The following sample code demonstrates how to determine if tunnel extrapolation is active:
// At startup, have a member variable position manager setup
// and listening to events
positioningManager = PositioningManager.getInstance();
147
HERE Android SDK Developer's Guide
► User Guide
positioningManager.addListener(
new WeakReference(this);
positioningManager.start(LocationMethod.GPS_NETWORK);
......
// upon callback, determine if tunnel extrapolation is active
public void onPositionFixChanged(LocationMethod method, LocationStatus status) {
if (method == LocationMethod.GPS) {
boolean isExtrapolated =
((positioningManager.getRoadElement() != null) &&
((positioningManager.getRoadElement().getAttributes()
.contains(RoadElement.Attribute.TUNNEL))));
boolean hasGps = status == LocationStatus.AVAILABLE;
}
}
Natural Guidance
The NavigationManager.setNaturalGuidanceMode() method can be used to enable natural guidance.
Natural guidance refers to a type of dynamic information available during navigation where route guidance
instructions contain contextual elements around a decision point. These contextual elements may include
services, catographic features, traffic signals, and stop signs. Some examples of natural guidance instructions
are:
•
"Go past the park on your right, then turn left at Anderson school on Bayview street"
•
"Continue on your route, passing the dome building on your right"
•
"Go through the traffic light and turn right before the petrol station"
Three options are available for natural guidance, and they are defined by the
NavigationManager.NaturalGuidanceMode enumerations. Note that STOP_SIGN,
TRAFFIC_LIGHT and JUNCTION can be used together. To disable natural guidance, call the
setNaturalGuidanceMode(EnumSet) method with an empty EnumSet.
•
TRAFFIC_LIGHT - Natural guidance for traffic lights
•
JUNCTION - Natural guidance for junctions such as landmarks
•
STOP_SIGN - Natural guidance for stop signs
To use the natural guidance feature, set your voice package to the map display language. Also, to get the
best results, use the appropriate voice package for the current location. For example, use the French voice
package in the city of Paris. Natural guidance currently supports the following text-to-speech voice packages:
•
U.S. English
•
Italian
•
•
•
•
U.K. English
Spanish
German
French
148
HERE Android SDK Developer's Guide
► User Guide
Navigation Events
NavigationManager Listeners
The NavigationManager contains a number of listeners that are responsible for for monitoring navigation
status and getting instructions during navigation. The following table shows the name of the available
listeners and the information provided them.
Listener Name
Purpose
PositionListener
Whether the current position has updated
NavigationManagerEventListener
Whether the navigation session has started, updated, or ended
NewInstructionEventListener
Whether a new navigation instruction is available to be fetched
GpsSignalListener
Whether the system has lost its GPS signal
RerouteListener
Whether a route recalculation has begun as a result of the current position deviating
TrafficRerouteListener
Whether a route recalculation to avoid traffic has begun
SpeedWarningListener
Whether the user has exceeded the speed limit
SafetySpotListener
Whether safety spots, such as speed cameras and red light cameras, are upcoming
LaneInformationListener
Whether lane information should be presented
RealisticViewListener
Listens for events related to a realistic view image
AudioFeedbackListener
Whether a voice command or vibration alert is available
from the original route
Listener instances can be added to NavigationManger through their respective add and remove
methods. For example, the LaneInformationListener can be added and removed by using
addLaneInformationListener(WeakReference) and
removeLaneInformationListener(WeakReference).
Note: NavigationManager.SpeedWarningListener only returns car speed warnings. Truck
speed warnings are not currently supported.
New Instructions and Maneuvers
The Maneuver class represents the action required to go from one segment to the next within a calculated
Route. Each Maneuver object provides information such as:
•
Location of the maneuver
•
Distance between maneuvers
•
•
•
•
•
Action required to complete the maneuver
Current road
Next road
Estimated times of the maneuver
Signposts (if any) indicating entrance, exit, or merge information
149
HERE Android SDK Developer's Guide
► User Guide
The NavigationManager provides a new Maneuver object after every onNewInstructionEvent()
callback. You can implement NewInstructionEventListener and implement this callback to provide
display logic. For example:
@Override
public void onNewInstructionEvent() {
Maneuver maneuver = navigationManager.getNextManeuver();
if (maneuver != null) {
if (maneuver.getAction() == Maneuver.Action.END) {
//notify the user that the route is complete
}
//display current or next road information
//display maneuver.getDistanceToNextManeuver()
}
}
Rerouting
While navigation is in progress, the following three types of rerouting can occur:
•
Basic Route Recalculation - This is performed automatically by the guidance engine. The guidance
engine checks the current position to see if it is on the route and approaching the target
destination. If it is not, then it triggers a route recalculation and updates the navigation session. The
RerouteListener.onRerouteBegin() and RerouteListener.onRerouteEnd() callbacks also
•
•
occur.
Dynamic Traffic Reroute - This mode is enabled by default. In this mode, the HERE SDK regularly requests
a traffic-aware route recalculation from the server, and the navigation manager switches to this route
automatically. For more information, see Traffic-Aware Navigation on page 155.
Manual Traffic Reroute - This mode can be optionally enabled. In this mode, the HERE SDK also requests
a traffic-aware route recalculation from the server, but it notifies the client before using the new route in
the navigation manager. For more information, see Traffic-Aware Navigation on page 155.
Lane Information
NavigationManager.LaneInformationListener provides the
onLaneInformation(List, RoadElement) callback method. This
callback occurs when the user has arrived at a point in the route where lane information should
be presented, such as before a highway exit. The LaneInformation class represents a lane turn
direction and whether this lane is on the current route. For example, an application may receive the
onLaneInformation(List, RoadElement) callback as the user navigates to an
intersection. If the route requires a left turn, and the current road has three lanes—a left-turn lane and two
straight lanes—then the callback returns with three LaneInformation objects. Since LaneInformation
objects are always returned in the callback method in a left-to-right order, the first LaneInformation has
a direction of LEFT and LaneInformation.getRecommendationState() returns a recommendation on
whether the lane can be taken for the current route. If there isn't enough data to determine whether the lane
is on or off-route, getRecommendationState() returns NOT_AVAILABLE.
150
HERE Android SDK Developer's Guide
► User Guide
Realistic View: 2D Signposts and Junction View
In addition to the data offered through the Lane Info feature, the HERE Android SDK also offers image
previews of signposts and junctions on certain highways. These two features together are known as Realistic
View.
Figure 58: An Example of a 2D Signpost
Figure 59: An Example of a Junction View
The 2D Signpost feature provides images that illustrate road signposts. For example, as a user approaches
a fork on a highway, your application can use show a preview of the instruction signs above the highway.
The Junction View feature provides images that illustrate road junctions. For example, as a user approaches
a junction on a highway, your application can show the lanes near the junction, with indicator arrows
highlighting the correct lane to take. Signpost and junction view images are provided as SVG images through
the following RealisticViewListener callback methods, which occur just after the previous maneuver
and before entering the junction:
•
•
onRealisticViewShow(AspectRatio, Image, Image)
onRealisticViewNextManeuver(AspectRatio, Image, Image)
Realistic view is disabled by default. To enable it, call
NavigationManager.setRealisticViewMode(RealisticViewMode) and set the view mode to
RealisticViewMode.DAY or RealisticViewMode.NIGHT. Next, register the desired image aspect
ratios by using addRealisticViewAspectRatio(AspectRatio ratio). After adding your listener
implementation, your application begins to receive the above event callbacks as the HERE SDK arrives to a
highway section that supports Realistic View.
Note: It is possible to add multiple aspect ratios and receive multiple images of the same signpost or
junction view.
The following is an example of onJunctionViewShow() and enabling realistic view:
// In the RealisticViewListener implementation
public void onRealisticViewShow(AspectRatio ratio, Image junction, Image signpost) {
if (junction.getType() == Image.Type.SVG) {
// full size is too big (will cover most of the screen), so cut the size in half
Bitmap bmpImage = junction.getBitmap((int) (junction.getWidth() * 0.5),
(int) (junction.getHeight() * 0.5));
if (bmpImage != null) {
//
// show bmpImage on-screen
//
}
151
HERE Android SDK Developer's Guide
► User Guide
}
}
...
navigationManager.setRealisticViewMode(NavigationManager.RealisticViewMode.DAY);
navigationManager.addRealisticViewAspectRatio(AspectRatio.AR_4x3);
navigationManager.addRealisticViewListener(
new WeakReference(viewListener));
Voice Instructions
You can use voice instructions with turn-by-turn navigation. Voice instructions are offered in a variety of
languages, which are available through downloadable text-to-speech (TTS) and pre-recorded voice skins.
Pre-recorded voice skins provide basic maneuver instructions, such as "turn right in 300 meters", while textto-speech voices also supports spoken street names, such as "turn right in 300 meters onto Granville Street".
You can check Navigation Voices on page 234 for a list of the supported voices in the HERE SDK.
Voice skins information and voice skins downloads can be managed through the voice catalog. The following
section describes how you can use the voice catalog, download voice skins, and use a voice skin with turnby-turn navigation.
Note: Voice instructions are only supported in Navigation Mode for driving. Users of the pedestrian
Navigation Mode receive audio beeps and vibrations alerts at the change of each maneuver.
Note: Voice navigation with routes that contain via waypoints is only supported through pre-recorded
voices.
The VoiceCatalog Class
The VoiceCatalog class is used to access voice skin files from the local device. A VoiceCatalog
object instance can be retrieved by calling VoiceCatalog.getInstance(). Then, using
getLocalVoiceSkins() method, you can fetch a list of VoiceSkin files that are stored on the device.
Be sure to check if VoiceCatalog.getCatalogList() is empty. Since the voice catalog is downloaded
based on the current device language, changing the device language causes an empty list of downloadable
voice skins. When this happens, the user needs to re-download the voice catalog.
Note: A set of sample voice skins are packaged with the HERE Android SDK in the {HERE
Android SDK}.zip/misc directory. To deploy these voice skins into your test device, extract the
voiceskins.tar.gz file, then call the following ADB command from the parent folder of the
voices folder:
adb push voices /sdcard/Android/data/{YourAppNamespace}/files/voices-download
The VoicePackage Class
The VoicePackage class represents an entry within the voice catalog. Each voice package shares a common
ID value with a downloadable voice skin. You can use this class to display information about the voice skin
before launching a download.
A list of VoicePackage can be accessed by using the VoiceCatalog.getCatalogList() method.
152
HERE Android SDK Developer's Guide
► User Guide
The VoiceSkin Class
The VoiceSkin class encapsulates voice-generation scripts. The scripts are used to generate voice
instructions for navigation. A voice skin is language-specific and can either support Text-to-Speech or voice
audio files. Multiple voice skins can be loaded to the device, but only one can be selected for navigation
voice playback.
A list of loaded VoiceSkin instances can be accessed by using the VoiceCatalog singleton instance.
Each VoiceSkin can be fetched by the getLocalVoiceSkins() method. Voice skins can be passed to
NavigationManager by calling NavigationManager.setVoiceSkin(VoiceSkin).
Selecting a Voice Skin and Starting Navigation
The following is an example of how to start navigation using a calculated route and an English text-to-speech
voice skin:
1.
Get a NavigationManager by calling NavigationManager.getInstance()
// Declare the navigationManager member variable
private NavigationManager navigationManager = null;
...
...
// Get the NavigationManager
navigationManager = NavigationManager.getInstance();
2.
3.
Get a calculated Route from CoreRouter. Refer to the code samples in the Routing section.
Declare NewInstructionEventListener and PositionListener member variables
// declare the listeners
// add application specific logic in each of the callbacks.
private NavigationManager.NewInstructionEventListener instructListener
= new NavigationManager.NewInstructionEventListener() {
@Override
public void onNewInstructionEvent() {
// Interpret and present the Maneuver object as it contains
// turn by turn navigation instructions for the user.
navigationManager.getNextManeuver();
}
};
private NavigationManager.PositionListener positionListener
= new NavigationManager.PositionListener() {
@Override
public void onPositionUpdated(GeoPosition loc) {
// the position we get in this callback can be used
// to reposition the map and change orientation.
loc.getCoordinate();
loc.getHeading();
loc.getSpeed();
// also remaining time and distance can be
// fetched from navigation manager
navigationManager.getTta(TrafficPenaltyMode.DISABLED, true);
navigationManager.getDestinationDistance();
}
};
153
HERE Android SDK Developer's Guide
► User Guide
4.
Add the listeners to NavigationManager to listen to the callbacks
// start listening to navigation events
navigationManager.addNewInstructionEventListener(
new WeakReference(instructListener));
// start listening to position events
navigationManager.addPositionListener(
new WeakReference(positionListener));
5.
Retrieve the VoiceCatalog and download the latest updates.
VoiceCatalog voiceCatalog = VoiceCatalog.getInstance();
voiceCatalog.downloadCatalog(new OnDownloadDoneListener() {
@Override
public void onDownloadDone(Error errorCode) {
if (errorCode == Error.NONE) {
// catalog download successful
}
}
});
6.
Using the voice catalog, find the desired voice package. In this example, choose "English TTS" from the
voice catalog.
// Get the list of voice packages from the voice catalog list
List voicePackages = VoiceCatalog.getInstance().getCatalogList();
long id = -1;
// select
for (VoicePackage vPackage : voicePackages) {
if (vPackage.getMarcCode().compareToIgnoreCase("eng") == 0) {
if (vPackage.isTts()) {
id = vPackage.getId();
break;
}
}
}
7.
Note: Some voice packages in the voice catalog may not be supported by the
installed TTS engine. You can check whether the language is supported by using the
isLanguageAvailable(Locale) method in android.speech.tts.TextToSpeech.
If the ID of the voice package does not match an item in the list of installed voice skins, install it. When
the installation operation completes, a callback to the onDownloadDone(Error) method occurs.
if (!voiceCatalog.isLocalVoiceSkin(id))
{
voiceCatalog.downloadVoice(id, new OnDownloadDoneListener() {
@Override
public void onDownloadDone(Error errorCode) {
if (errorCode == Error.NONE){
//voice skin download successful
}
}
});
}
8.
Start navigation according to user selection. Navigation can be started in three different modes, but only
one of them can be started at a time:
154
HERE Android SDK Developer's Guide
► User Guide
// if user wants to start simulation,
// submit calculated route and a simulation speed in meters per second
error = navigationManager.simulate(route, 60);
// set the voice skin for use by navigation manager
navigationManager.setVoiceSkin(voiceCatalog.getLocalVoiceSkin(id));
9.
If the user decides to abort navigation, call stop().
// abort navigation
navigationManager.stop();
Traffic-Aware Navigation
With the HERE SDK, developers can enable turn-by-turn route navigation that takes live traffic information
into account. NavigationManager.setTrafficAvoidanceMode() can be used to set the way in which
traffic should be handled during navigation.
Three modes are available for traffic avoidance, and they are defined by the following
NavigationManager.TrafficAvoidanceMode enumerations. The default mode is DISABLE.
•
DYNAMIC - Performs traffic-aware rerouting without user input.
In this mode, the guidance engine performs periodic route calculations while the device is online.
A route calculation is a server request where the server finds the most optimal route by avoiding
traffic congestions and calculating speed limits. If the calculated route is different from the
current route, the navigation manager automatically switches to the new route. It also triggers the
NavigationManager.NavigationManagerEventListener.onRouteUpdated(Route) callback
method.
Note: You can set the frequency of the route request by using
NavigationManager.setRouteRequestInterval().
•
MANUAL - Provides notifications about upcoming traffic incidents and requires user confirmation before
•
DISABLE - Disables traffic-based rerouting.
rerouting. For more information, see the next section.
Manual Traffic-Based Rerouting
If the device is online and NavigationManager.TrafficAvoidanceMode.MANUAL is selected,
the guidance engine considers all incoming traffic event within proximity and checks if the event
is on the current route route, and whether the route is reroutable. If these conditions are true, the
engine triggers a route calculation. You can listen to this recalculation event by implementing the
onTrafficRerouteBegin(TrafficNotification) method in TrafficRerouteListener.
Route recalculation is a server request where the server finds the most optimal route
by avoiding live traffic congestions and calculating road speed limits. If the calculated
route is different from the current route, the new route is returned through the
NavigationManager.TrafficRerouteListener.onTrafficRerouted(Route) callback
method. A voice note is also played by the guidance engine. You can then set the new Route to the
NavigationManager manually.
155
HERE Android SDK Developer's Guide
► User Guide
The TrafficWarner Class
The TrafficWarner class is responsible for enabling and handling traffic notifications. Traffic notifications
occur if there is a traffic event on the current route and the user's current position is near the event.
To retrieve an instance of the TrafficWarner object, call NavigationManager.getTrafficWarner().
You then can call TrafficWarner.init() to initialize and start the TrafficWarner.
One or more of the following methods can be used to operate traffic warner, or to retrieve further
information about traffic notifications:
•
•
•
•
•
•
•
isAhead(TrafficNotification) - determines whether or not a traffic notification is ahead of the
last callback position
isOnRoute(Route, TrafficNotification) - determines if a traffic notification is on a given route
isValid() - determines if the traffic warner is valid
setAskAvoidOutput(TrafficNotification) - sets the output of the traffic notification to "Do you
want to avoid.. ?"
setInformAvoidOutput(TrafficNotification) - sets the output of the traffic notication to "You
are rerouted because of..."
stop() - stops the traffic warner
start() - starts the traffic warner
To listen for traffic notifications, a listener must be added via TrafficWarner.addListener().
The TrafficNotification and TrafficNotificationInfo Classes
TrafficWarner.Listener provides a callback that returns a TrafficNotification object
that is relevant to the current navigation session. This TrafficNotification contains a list of
TrafficNotificationInfo instances associated with the traffic notification, retrievable through
TrafficNotification.getInfoList().
The TrafficNotificationInfo class encapsulates the details of a traffic notification.
TrafficNotificationInfo.Type defines the type of traffic notification with regards to the current route.
The following methods can be used to retrieve details about a TrafficNotificationInfo instance:
•
getType() - gets the type of traffic notification info
•
getDistanceInMeters() - gets the distance from the last callback position to the traffic notification
•
getEvent() - gets the traffic event (TrafficEvent) associated with the traffic notification info
Audio Management
Manage Voice Volume
If your application uses voice navigation, it is recommended that you implement volume ducking and volume
control through hardware keys.
Volume ducking is the practice of manipulating volume based on audio focus. It allows another app, such as
a phone call, to flag its audio as having higher priority and "takes over" the current device audio. To grant
156
HERE Android SDK Developer's Guide
► User Guide
or request audio focus, call NavigationManager.AudioPlayer.getStreamId() to retrieve the current
audio stream, and then use it with the Android AudioManager. For example:
int result = audioManager.requestAudioFocus(afChangeListener,
NavigationManager.getInstance().getAudioPlayer().getStreamId(),
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
By default, the HERE SDK uses AudioManager.STREAM_MUSIC as the audio stream. For more information
on volume ducking, consult this article: "Managing Audio Focus" .
To control voice navigation volume through hardware keys, call
Activity.setVolumeControlStream(NavigationManager.AudioPlayer.getStreamId()) at an
early point in your app's lifecycle. This ensures that presses on the hardware keys modify the volume for the
navigation manager's audio stream. For more information on hardware keys and application audio volume,
consult this article: "Controlling Your App's Volume and Playback" .
Overriding Default Audio Playback
The HERE SDK provides a way for you to take over audio playback by the
NavigationManager. To do this, implement the AudioPlayerDelegate class, and call
NavigationManager.AudioPlayer.setDelegate(AudioPlayerDelegate). Note that setting a
delegate stops all audio and text-to-speech playback by the SDK.
The AudioPlayerDelegate interface contains two callback methods. When you are implementing your own
delegate, follow these recommendations:
AudioPlayerDelegate.playText(String)
•
If you are using your own text-to-speech engine, the locale used in the engine should match up with the
•
In most cases, the text can be directly submitted to the engine's playback API. For example, for the
•
VoiceSkin.
Android system text-to-speech engine, you can submit the text to TextToSpeech.speak(String,
int, HashMap).
For the best user experience, the speech rate and pitch should be adjusted.
AudioPlayerDelegate.playFiles(String[])
•
•
The list of file paths come in a sequence. The order in the array indicates the exact order the files should
be played. The files are single words used for composing commands with numbers and units.
Since recordings may contain padding in the end, do not play the files in a sequence, as
this sounds too slow and robotic. Instead, you can add each file into individual instances of
android.Media.MediaPlayer, and at almost the end of playback for one file, start the next one in
parallel. In this manner, the resulting sentence sounds more natural. The finite timing before the end of
playback can be adjusted and experimented to achieve the best user experience.
Urban Mobility
This section provides an overview of the Urban Mobility features in the HERE SDK. These features allow
developers to query and use advanced information related to public transit systems.
157
HERE Android SDK Developer's Guide
► User Guide
Note: Before using Urban Mobility, be aware of the following:
•
•
All Urban Mobility features is currently offered as a beta feature. APIs may change without notice.
Access to the Next Nearby Departures, All Next Departures, and Transit Routing features are
restricted. Please contact a HERE representative (https://developer.here.com/contact-us?
interest=mobile-sdk#contact-sales) for more information.
Coverage Search
Urban Mobility Coverage Search provides detailed information about the regions and cities with public
transit data that are supported by HERE SDK. For example, you can get a list of cities that have transit
data coverage by using a query request with a search area. You can create this type of request by using
RequestManager.createCityCoverageRequest(ResponseListener) to
generate a city coverage request object.
// Geo coordinate of a reference point
GeoCoordinate coordinates = new GeoCoordinate(40.750488, -73.993546);
//somewhere in NY
// listener that will be notified after request is completed
RequestManager.ResponseListener responseListener = new
RequestManager.ResponseListener() {
@Override public void onSuccess(CityCoverageResult searchResult) {
List foundCities = searchResult.getCities();
for (City city : foundCities)
{
String cityName = city.getDisplayName();
float percentCovered = city.getQuality();
// ...
}
}
@Override public void onError(ErrorCode errorCode, String errorMessage) {
// report error
}
};
// creating and executing the request
// search coverage by city name
CityCoverageRequest request = new RequestManager()
.createCityCoverageRequest(responseListener).setLocation(coordinates) // if not specified,
returns all cities
.setRadius(150000) // meters
.setUpdateType(CityCoverageRequest.UpdateType.ALL) // all cities
.setTime(null); // works together with the UpdateType parameter
request.execute();
Another supported type of coverage search uses a query string to look for cities with
names that begin with this substring. You can create this type of request by using
RequestManager.createCitySearchRequest(String, ResponseListener).
// name of the city to search
String name = "New York";
// listener that will be notified after request is completed
RequestManager.ResponseListener responseListener = new
RequestManager.ResponseListener() {
@Override public void onSuccess(CitySearchResult citySearchResult) {
for (City city: citySearchResult.getCities())
{
String name = city.getName();
158
HERE Android SDK Developer's Guide
► User Guide
}
}
GeoCoordinate coordinates = city.getLocation();
int population = city.getPopulation();
// ...
@Override public void onError(ErrorCode errorCode, String errorMessage) {
// report error
}
};
// creating and executing the request
// search coverage by city name
CitySearchRequest request = new RequestManager().createCitySearchRequest(
name, responseListener);
request.execute();
You can also use RequestManager.createNearbyCoverageRequest(GeoCoordinate,
ResponseListener) to search for coverage information around a given
location. This type of search returns information of the city that contains this location. If there is no transit
stop within 2 kilometers of the location, the request returns the first five nearest transit stops outside of the
2 kilometers area. This type of search result is also known as an "Explored Coverage". The following code
demonstrates how to retrieve both nearby and explored coverage results.
GeoCoordinate coordinates = new GeoCoordinate(40.750488, -73.993546);
//somewhere in NY
// listener that will be notified after request is completed
RequestManager.ResponseListener responseListener = new
RequestManager.ResponseListener() {
@Override public void onSuccess(NearbyCoverageResult nearbyCoverage) {
City city = nearbyCoverage.getCity();
ExploredCoverage exploredCoverage = nearbyCoverage.getExploredCoverage();
...
}
@Override public void onError(ErrorCode errorCode, String errorMessage) {
// report error
}
};
// creating and executing the request
// search coverage nearby
NearbyCoverageRequest request = new RequestManager().createNearbyCoverageRequest(coordinates,
responseListener);
request.execute();
159
HERE Android SDK Developer's Guide
► User Guide
Transit Station Search
Figure 60: Station Search by Coordinates
The Transit Stations Search feature allows users to discover transit stations by searching for stations around
a specified location. For example, your application can look for the nearest subway station by retrieving all
subway stations at the current location with the specified radius.
The following code demonstrates how to trigger a station search by location:
GeoCoordinate centerCoordinates = new GeoCoordinate(40.750488, -73.993546);
//somewhere in NY
// listener that will be notified after request is completed
RequestManager.ResponseListener responseListener = new
RequestManager.ResponseListener() {
@Override public void onSuccess(StationSearchResult searchResult) {
List foundStations = searchResult.getStations();
Collection transports = searchResult.getTransports();
for (Station station: foundStations) {
String stationName = station.getAddress().getName();
// ...
}
// ...
}
@Override public void onError(ErrorCode errorCode, String errorMessage) {
// report error
}
160
HERE Android SDK Developer's Guide
► User Guide
};
// creating and executing the request
// search stations nearby
StationSearchRequest requestNearby = new RequestManager().createStationSearchRequest(
centerCoordinates, "", responseListener)
.setMaximumResults(3)
.setRadius(1000);
requestNearby.execute();
Figure 61: Station Search by Name
You can also search for public transit stations by providing its name. For example, a user searching for
"Penn" retrieves all stations that match that name. This search is location-aware, so results are sorted by
proximity to the current location.
The following code demonstrates how to perform a station search and filter by the specified name:
// search stations nearby with given name filter, returning transports information
StationSearchRequest requestName = new RequestManager().createStationSearchRequest(
centerCoordinates, "Penn", responseListener)
.setStationNameMatchingMethod(StationSearchRequest.NameMatchingMethod.FUZZY)
.setRequestStationDetailsEnabled(true);
requestName.execute();
You can also retrieve a station directly by specifying the station IDs while creating the
StationSearchRequest, as demonstrated in the following:
Set ids = new HashSet();
ids.add("717010002");
161
HERE Android SDK Developer's Guide
► User Guide
ids.add("717001746");
// search stations by given IDs
StationSearchRequest requestIds = new RequestManager().createStationSearchRequest(
ids, responseListener);
requestIds.execute();
Next Nearby Departures
You can use the HERE SDK to query for the next transit departures for a particular station. Next nearby
departure information is based on the timetable information provided by transit agencies, and it includes all
types and times of departures from one station at a given time.
Note: Next Nearby Departures and All Next Departures are currently offered as beta features. APIs
may change without notice.
Figure 62: Station Departures
To query for the departures, create a request by using the stationId and stationCoordinates and
implement a ResponseListener. The returned DepartureBoard contains a list of Departure objects
that is sorted by the departure times.
// ID of a station, can be obtained from station search
String stationId = "717010002";
// Geocoordinate of a station
GeoCoordinate stationCoordinates = new GeoCoordinate(40.750488, -73.993546);
//somewhere in NY
// listener that will be notified after request is completed
162
HERE Android SDK Developer's Guide
► User Guide
RequestManager.ResponseListener responseListener = new
RequestManager.ResponseListener() {
@Override
public void onSuccess(DepartureBoard departureBoard) {
List departures = departureBoard.getDepartures();
for (Departure departure : departures) {
Station station = departure.getStation();
Date departureTime = departure.getTime();
Transport line = departure.getTransport();
String direction = departure.getTransport().getDirection();
// ...
}
}
};
@Override public void onError(ErrorCode errorCode, String errorMessage) {
// Handle error
}
// creating and executing the request
DepartureBoardRequest request = new RequestManager().createDepartureBoardRequest(
stationCoordinates, stationId, responseListener);
request.execute();
All Next Departures
To query for departure information of stations in a given area, create a request by using the coordinates
and implement a ResponseListener. The returned MultiBoardResult contains a list of
StationWithDepartureBoard objects that contain departure information, which are sorted by the
departure times. StationWithDepartureBoard is a child implementation of the Station class.
// Geocoordinate of a reference point
GeoCoordinate coordinates = new GeoCoordinate(40.750488, -73.993546);
//somewhere in NY
// listener that will be notified after request is completed
RequestManager.ResponseListener responseListener = new
RequestManager.ResponseListener() {
@Override
public void onSuccess(MultiBoardResult multiBoard) {
Collection stations = multiBoard.getStations();
Collection transports = multiBoard.getTransports();
for (StationWithDepartureBoard station: stations) {
String stationName = station.getAddress().getName();
List departures = station.getDepartureBoard().getDepartures();
for (Departure departure : departures) {
Date departureTime = departure.getTime();
Transport transport = departure.getTransport();
String direction = departure.getTransport().getDirection();
// etc.
}
}
}
};
@Override public void onError(ErrorCode errorCode, String errorMessage) {
// Handle error
}
// creating and executing the request
MultiBoardRequest requestNearby = new RequestManager().createMultiBoardRequest(
coordinates, responseListener);
163
HERE Android SDK Developer's Guide
► User Guide
// You can set other search parameters here before calling execute()
requestNearby.execute();
You can call requestNearby.setRadius(int) to limit the search area before executing the search. You
can also limit the search to specific stations by using an overloaded createMultiboardRequest.
3D Venues
This section gives an overview of the classes and interfaces associated with the 3D Venues feature. Examples
of available 3D venues include shopping malls and airports. This section also explores three use cases:
searching for a venue, opening a venue, and getting a notification when a venue is visible in the viewport.
The classes covered in this section include:
•
•
•
•
•
•
VenueMapFragment
VenueMapView
VenueService
VenueMapFragment.VenueListener
VenueLayerAdapter
VenueService.VenueServiceListener
Note: To use this feature, your application must include the Gson library (release 2.2.4 or a
compatible version) on its class path.
Figure 63: 3D venue map of a Berlin shopping center
The 3D Venues feature can be used with or without a map. To use it with a map, use the VenueMapFragment
or VenueMapView. To use 3D Venues without a map, use the VenueService class.
164
HERE Android SDK Developer's Guide
► User Guide
Using VenueMapFragment
VenueMapFragment provides developers with access to all 3D venue-related features. As with
MapFragment, VenueMapFragment needs to be added to the layout file of the application, for example:
The fragment must then be initialized in the same manner as MapFragment.
VenueListener
To receive venue-related events, implement VenueMapFragment.VenueListener and add
it to the VenueMapFragment, similar to the following code example. As with a MapFragment,
you can mark VenueMapFragment initialization as successfully completed by looking for the
OnEngineInitListener.Error.NONE error status code.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Search for the VenueMapFragment
final VenueMapFragment mapFragment = (VenueMapFragment)
getFragmentManager().findFragmentById(R.id.mapfragment);
}
// initialize the Map Fragment and
// retrieve the map that is associated to the fragment
mapFragment.init(new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted( OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// add listeners
mapFragment.addListener(myVenueListener);
mapFragment.getVenueService().addListener(myVenueServicelistener);
map = mapFragment.getMap();
} else {
System.out.println("ERROR: Cannot initialize VenueMapFragment");
}
}
});
In this example, myVenueListener is assumed to implement VenueMapFragment.VenueListener, and
myVenueServiceListener implements VenueService.VenueServiceListener. For more information
about these listener classes, see VenueService and VenueServiceListener on page 168.
Using VenueMapView
VenueMapView provides developers with similar features as VenueMapFragment, but as a MapView-based
class. Like MapView, VenueMapView needs to be added to the layout file of the application:
The view must then be initialized in the same manner as MapView. As with a MapView,
you can mark VenueMapView initialization as successfully completed by looking for the
OnEngineInitListener.Error.NONE error status:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Search for the VenueMapView
final VenueMapView mVenueMapView = (VenueMapView)findViewById(R.id.venuemapview);
// get and initialize the MapEngine and
// assign the VenueMapView to it
ApplicationContext context = new ApplicationContext(this);
MapEngine.getInstance().init(context, new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted( OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// create and assign map
mMap = new Map();
mVenueMapView.setMap(mMap);
mVenueMapView.init(this, mVenueServiceListener);
mVenueMapView.addListener(mVenueListener);
} else {
System.out.println("ERROR: Cannot initialize VenueMapView");
}
}
});
}
Working with 3D Venue Models
Once a VenueMapFragment or VenueMapView is correctly initialized, 3D-enabled venues become visible
on the map. These venues can be distinguished by their colors and icons, as in the screenshot below. This
screenshot was taken from an app that uses VenueMapFragment, but the same visuals also appear on a
VenueMapView.
Figure 64: A 3D venue on the Map
166
HERE Android SDK Developer's Guide
► User Guide
VenueMapFragment offers two ways to select a venue and open the indoor map view. When a
user taps the venue, the onVenueTapped(VenueController, float, float) method in
VenueMapFragment.VenueListener is called with a Venue object as a parameter. The venue can then be
opened by giving the Venue object to selectVenue(Venue). For example:
public void onVenueTapped(Venue venue, float x, float y) {
mapFragment.selectVenue(venue);
}
When the venue is selected, the onVenueSelected(Venue) callback of the
VenueMapFragment.VenueListener interface is called. A venue can also be selected and opened by
giving its identifier by using the selectVenueAsync(String venueId) method of VenueLayerAdapter,
which is implemented by VenueMapFragment. For example, a typical scenario is a venue search where a
successful search results in an opened venue.
The selectVenue(Venue) method opens the venue right away in a synchronous manner by taking
a downloaded Venue object as a parameter. However, the selectVenueAsync(String) and
selectVenueAsync(String, String) methods may involve downloading the venue from the
backend, asynchronously, while the venue is selected and opened. You can get a notification for when
asynchronous loading is complete by listening to the onGetVenueCompleted(Venue) callback in
VenueService.ServiceListener.
It is also possible to receive a notification when there is a venue in the viewport. You can use this callback to
implement a feature, such as drawing user attention to the venue when it is visible.
The triggering area for this is a rectangle at the center of the viewport. The width of the area is two-thirds
of the screen width, and the height is equal to the width. When the center point of the venue enters
this triggering area, such as during map panning, onVenueVisibleInViewport(Venue, boolean)
of VenueMapFragment.VenueListener is called. The boolean parameter indicates whether the
venue is entering or exiting from the triggering area. Note that to get the notifications, you must first set
setVenuesInViewportCallback(boolean) to true. For example:
// enabling onVenueVisibleInViewport notification
mapFragment.setVenuesInViewportCallback(true);
//...
@Override
public void onVenueVisibleInViewport(Venue venue, boolean visible) {
if (visible) {
// venue entered triggering area
} else {
// venue disappeared from triggering area
}
}
Note: This feature is not processor-intensive, as checking only occurs once as map movement stops.
The notification is not sent during continuous movement, even when there is a venue in the triggering
area.
To change the current floor for a given venue, retrieve a VenueController object
by using the getVenueController(String venueId) method, and then call the
VenueController.selectLevel(Level) method.
167
HERE Android SDK Developer's Guide
► User Guide
You can enable animations for venue selection and floor transitions by calling
setFloorChangingAnimation(true) and setVenueEnteringAnimation(true).
VenueService and VenueServiceListener
In the case where you are not using a VenueMapFragment, you can use the
VenueService instead. The service initialization status is provided as a parameter
to the onInitializationCompleted(InitStatus) callback method in the
VenueService.VenueServiceListener interface. You can also retrieve the status using the
getInitStatus() method in VenueService.
boolean cacheInUse = false
//...
@Override
public void onInitializationCompleted(InitStatus result) {
if (result == InitStatus.ONLINE_SUCCESS) {
// init ok, online content available
} else if (result == InitStatus.OFFLINE_SUCCESS) {
// cached content available
cacheInUse = true;
} else if (result == InitStatus.ONLINE_FAILED && cacheInUse) {
// failed to authenticate, but cached content available
} else {
// something else has gone wrong
}
}
VenueService offers methods for searching and loading venues without using a map. For example, the
code below retrieves the closest venue inside a given radius near a given location. The area can also be
defined by GeoBoundingBox rather than a radius.
private void loadClosestVenue() {
VenueService venueService = VenueService.getInstance(getActivity().getApplicationContext());
GeoCoordinate myLocation = new GeoCoordinante(60.43704, 22.21710);
float radiusInMeters = 5000.0f;
VenueInfo closestVenue = venueService.getVenuesAt(myLocation, radiusInMeters);
venueService.getVenueAsync(closestVenue);
}
@Override
public void onVenueLoadCompleted(Venue venue, VenueInfo venueInfo, VenueLoadStatus loadStatus) {
// closest venue available through the venue object
}
Note: The VenueService is also invoked when VenueMapFragment is used. As such, some venue
features can be used in a common manner between these classes.
Venue Objects
The following is a list of venue objects (as Java classes) in the com.here.android.mpa.venues3d
package. These objects are presented here from the lowest to the highest level of conceptual detail, and
each level is related in a has-a relationship. For example, each Venue object may have multiple Level
member objects, which contain multiple OuterArea objects.
168
HERE Android SDK Developer's Guide
► User Guide
Java class
Description
com.here.android.mpa.venues3d.Venue
Represents a building which may contain one or more structures and
com.here.android.mpa.venues3d.Level
Represents a floor or horizontal layer within a Venue.
com.here.android.mpa.venues3d.OuterArea
Represents a part of one Level, contained within one exterior wall of a
com.here.android.mpa.venues3d.Space
Represents a spatial area on a Level, like a store or a facility, such as an
levels.
structure.
escalator. It may contain subspaces which represent the next level of
detail about a space.
com.here.android.mpa.venues3d.Content
Point of interest information about a spatial area within the venue.
A Venue object consists of one or more Level objects. The Level objects represents physical levels of the
venue. Each Level object consist of one or more OuterArea objects. For example, a building with common
ground level areas can contain two separate towers on top of that common area, and so there would be two
OuterArea objects in higher levels in that venue. An OuterArea consist of one or more Space objects.
Note that both Venue and Space classes have a member Content class, which holds point-of-interest
information. This allows for a venue (such as a museum) and a space (such as a shop within a shopping mall)
to hold separate address and phone numbers. Your app can use methods such as getPhoneNumber(),
getWebsite() and getOpeningTimes() to retrieve the relevant directory information from Content
objects.
Working with Venues
Venue, OuterArea and Space objects can be interacted by the user through tapping. For an opened venue,
use the VenueMapFragment.VenueListener.onVenueSelected(Venue) callback. For a selected space,
use the callback VenueMapFragment.VenueListener.onSpaceSelected(Venue, Space). Selected
spaces are highlighted with different color in an opened venue.
Both Venue and Space objects contain Content objects. Content encapsulates information related to
the object, such as name, address, other contact information and category of the venue or space. There is
169
HERE Android SDK Developer's Guide
► User Guide
also a concept of selected floor, which is the same as the visible floor. This is demonstrated in the following
screenshot. Note that the floor selection widget in this screenshot is not a part of the HERE SDK.
Figure 65: Selected Space
The following example shows how to add a MapMarker to a space upon a onSpaceSelected event, and
how to remove it when the space is deselected.
@Override
public void onSpaceSelected(Venue venue, Space space) {
removeMarker();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.pin_start);
Image image = new Image();
image.setBitmap(bitmap);
m_marker = new MapMarker(space.getCenter(), image);
m_marker.setAnchorPoint(new PointF(image.getWidth() / 2f, 0.9f * image.getHeight()));
m_marker.setOverlayType(MapOverlayType.FOREGROUND_OVERLAY);
m_marker.setZIndex(100);
getMap().addMapObject(m_marker);
}
@Override
public void onSpaceDeselected(Venue venue, Space space) {
removeMarker();
}
private void removeMarker() {
if (m_marker != null) {
getMap().removeMapObject(m_marker);
m_marker = null;
}
}
Open Mode
When open mode is enabled, venues that are in the viewport are opened automatically, rather than requiring
the user to click on the venue to open it. The venue closest to the center of the screen is always selected.
Open mode can be enabled on VenueMapLayer, VenueMapView, VenueMapFragment, or
VenueMapAdapter.
venueMapLayer.setOpenMode(true);
170
HERE Android SDK Developer's Guide
► User Guide
boolean isOpenMode = venueMapLayer.getOpenMode();
Dynamic Styles
StyleSettings encapsulates the parameters that have an impact on the visual appearances of opened
venues. You can set space names, icons, and colors by using this object. The fill and outline colors can be
also set separately to selected and unselected spaces.
The following example shows how to set the name, label, fill color, and outline color to the given Space
object. The code snippet does not contain completed code but assumes that variables have been initialized.
import com.here.android.mpa.common.Image;
//...
private void updateStyles(Venue venue, Space space) {
Integer selectedColor = 0xFFFF0000; // red, format 0xAARRGGBB
Integer unselectedColor = 0xFFFFFF00; // yellow
Integer outlineColor = 0xFF0000FF; // blue
VenueController controller = m_venueMapFragment.getVenueController(venue);
StyleSettings settings = new StyleSettings();
settings.setLabelName("My Space");
Image img = new Image();
img.setImageResource(com.example.android.UnitTest.R.drawable.png);
settings.setLabelImage(img);
settings.setSelectedFillColor(selectedColor);
settings.setFillColor(unselectedColor);
settings.setOutlineColor(outlineColor);
}
controller.setStyleSettings(settings, space);
Nearby Spaces
You can find all spaces in a radius around a given position by using a Level or an OuterArea object. The
position needs to be given as a geocoordinate, and the radius in meters. The returned list of spaces contains
all spaces that fall within or intersect the radius.
GeoCoordinate myLocation = new GeoCoordinante(60.43704, 22.21710);
List nearbySpaces = level.getNearbySpaces(myLocation, 10.0);
Area at Position
You can retrieve areas in a level by specifying a position. The area returned will either be a Space or an
OuterArea. Similarly, you can also get spaces in a OuterArea.
In case of nested spaces, the innermost nested space encompassing the position is be returned.
GeoCoordinate myLocation = new GeoCoordinante(60.43704, 22.21710);
Area area = level.getAreaAtPosition(myLocation);
Space space = outerArea.getSpaceAtPosition(myLocation);
171
HERE Android SDK Developer's Guide
► User Guide
Frequently Asked Questions
You can find additional information about 3D Venues in the 3D Venues FAQ on page 213.
Venue Zoom
Certain 3D venues may have fine details that are not visible even at the maximum map zoom level. The HERE
SDK offers a way to activate a venue-focused extended zoom mode to show a venue in a closer view. These
fine details are not available for all venues.
Figure 66: A venue at the max zoom level
Figure 67: A venue with Venue Zoom enabled
To enable this extended Venue Zoom feature, call VenueService.enableVenueZoom(true) at any time.
// Get an instance of VenueService:
VenueService service = m_mapFragment.getVenueService();
// To enable Venue Zoom:
service.enableVenueZoom(true);
//...
// To disable Venue Zoom:
service.enableVenueZoom(false);
After enabling Venue Zoom, you can use one of the following ways to activate the feature. If the application
input is based on gestures, the most convenient way is to register an instance of VenueGestureListener,
which implements OnGestureListener to receive gesture events. When the map is zoomed using gestures
172
HERE Android SDK Developer's Guide
► User Guide
to the maximum level and Venue Zoom is enabled, 3D venues are automatically shown in an enlarged
mode. Note that VenueGestureListener only handles pinch zoom events and activates Venue Zoom in a
supported area. In all other situations it returns false for the gesture events, and thus has effectively no
impact on gesture handling.
The example code below shows how the gesture handler can be used with Venue Zoom:
private VenueGestureListener m_gestureListener = null;
// to be done in onCreate/onResume:
if ( myExtendedZoomLevel == false ) {
// Max map zoom level is depending on display metrics
DisplayMetrics metrics = getResources().getDisplayMetrics();
m_gestureListener = new VenueGestureListener(m_mapFragment, m_zoomLevelText,
metrics.densityDpi);
m_mapFragment.getMapGesture().addOnGestureListener(m_gestureListener);
service.enableVenueZoom(true);
} else if (m_gestureListener != null) {
m_mapFragment.getMapGesture().removeOnGestureListener(m_gestureListener);
m_gestureListener = null;
service.enableVenueZoom(false);
}
Note: You can also use VenueMapFragment.VenueZoomListener to determine whether the Venue
Zoom feature was successfully activated.
Another way to activate Venue Zoom is to use the useVenueZoom(true) method of VenueController to
immediately and activate Venue Zoom in a supported area.
// Get VenueController:
venueController = m_mapFragment.getVenueController(venue);
// To activate Venue Zoom (only works in a supported Venue)
venueController.useVenueZoom(true);
// To return to a normal zoom level
venueController.useVenueZoom(false);
While venue zoom is activated, only 3D Venues are shown, and the base map becomes hidden. If venue zoom
is activated while a venue route is being displayed, only the indoor portions of the routes are shown. You can
use VenueZoomListener to determine whether the feature was successfully activated.
Once activated, the visible map becomes two zoom levels larger than they would appear without Venue
Zoom. For example, if Venue Zoom is activated at map zoom level 18, the venues are shown as if the map is
at zoom level 20. Note that this behavior only occurs when the map is at zoom level 18 or higher.
Scaling Custom Map Objects
For custom polygon-based map objects, the HERE SDK offers scaling methods to scale them
to be used with Venue Zoom. Each point of the polygon need to be scaled to be used in Venue
Zoom using VenueController.getScaledGeoCoordinate(GeoCoordinate). If the
reverse operation is desired, where the starting point is a scaled polygon or geolocation, the
getNormalGeoCoordinate(GeoCoordinate) method returns geocoordinates for a non-scaled
geolocation.
173
HERE Android SDK Developer's Guide
► User Guide
Note: While both MapGeoModel and MapLocalModel can be scaled by these scaling methods,
it is recommended to use MapGeoModel, since only the anchor point location can be scaled in a
MapLocalModel.
In most cases it makes most sense to create two map objects, one for used in normal zoom
mode and one used in Venue Zoom mode. Use Map.addMapObject(MapObject) and
Map.removeMapObject(MapObject) to swap between objects when changing between normal and Venue
Zoom mode.
Enlarged 3D Models and Venue Zoom
Venue Zoom displays enlarged venue models that are automatically generated. Model generation happens in
the following situations:
•
•
Venue Zoom is enabled and a venue is downloaded from the backend.
Venue Zoom is enabled and a venue is opened, and there is no enlarged model generated yet.
Note that if a venue is opened in 3D mode and then Venue Zoom is enabled, Venue Zoom would not
successfully activate if the enlarged venue model has not been generated. In this case, the Venue Zoom can
be used after the venue is closed and then opened again. If an enlarged model has already been generated
earlier, then Venue Zoom can be used immediately after it has been enabled.
Private Venues
This feature allows you to use a different source of venue data in addition to or instead of the default HERE
backend. The private content backend must be configured by HERE, which is an operation transparent to a
developer using the HERE Android SDK. Access to private venue data is at the discretion of its legal owner,
and by definition, it is not public.
Note: For more information about configuring a private venue data backend, contact your HERE
representative.
If a private backend has been configured, call the setPrivateContent(boolean) method on the
VenueService class at MapFragment initialization to indicate that you want your application to use it.
The code below demonstrates a call to this method. Note that the code does not show the entire
MapFragment initialization sequence.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize the Map Fragment to have a map created and attached to
// the fragment
m_mapFragment.init(new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted(Error error) {
if (error == Error.NONE) {
m_map = (Map) m_mapFragment.getMap();
// Set access to private venue data:
m_mapFragment.getVenueService().setPrivateContent(true);
// Remember to start or restart Venue Service after setting private content
} else {
System.out.println("ERROR: Cannot initialize Map Fragment" + error.toString());
}
}
});
174
HERE Android SDK Developer's Guide
► User Guide
}
Dynamic Content
By default, the HERE SDK uses public HERE 3D venue content. It is possible to use customer-specific content
instead by calling the VenueService.setPrivateContent(boolean) method, as shown in the examples
below. It is also possible use both private and public content together, and define which one has priority. If
this kind of combined content is needed, use VenueService.setIsCombinedContent(boolean).
// Obtain VenueService
VenueService service = m_mapView.getVenueService();
// Use only HERE SDK content (this is the default behavior)
Service.setPrivateContent(false);
Service.setIsCombinedContent(false);
// Use only private content
Service.setPrivateContent(true);
Service.setIsCombinedContent(false);
// Prefer HERE SDK content and use private as an alternative
Service.setPrivateContent(false);
Service.setIsCombinedContent(true);
// Prefer private content and use HERE SDK as an alternative
Service.setPrivateContent(true);
Service.setIsCombinedContent(true);
Multiple VenueService objects can run at the same time. For example, if some part of an application
requires access to only private content, and another part requires HERE SDK content, two VenueService
objects can be instantiated and configured differently. Activities in one service do not have an impact
on another service. For example, notifications related to loading (onVenueLoadCompleted(Venue,
VenueInfo, VenueLoadStatus)) are sent only to the client that initiated the load. To obtain an
additional VenueService object, use the static createAdditionalService(Context) method.
VenueService mMainInstance;
VenueService mAdditionalInstance;
mMainInstance =
VenueService.getInstance(getActivity().getApplicationContext());
mAdditionalInstance =
VenueService.createAdditionalService(getActivity().getApplicationContext());
For more information, see the API Reference.
Note: VenueService is also invoked when VenueMapFragment or VenueMapView is used. As such,
some venue features can be used in a common manner between these classes.
Venue Routing
The HERE Android SDK extends its 3D venue maps functionality to provide indoor routing. The SDK supports
the following use cases:
•
•
•
•
Routing from store A to store B within a venue
Routing from an outside point to a point in a venue
Routing from a point in a venue to an outside point
Routing from a venue to another venue, with endpoints being a store or a point in a venue
175
HERE Android SDK Developer's Guide
► User Guide
Both on-line and off-line routing are supported. The classes that support this feature can be found in the
com.here.android.mpa.venues3d and com.here.android.mpa.routing packages.
Note that if HERE has no routing information for an area between the outdoor part of the route and the
venue entry point, the route visualization represents the unknown section of the route with a dotted line.
Routing Between Locations in a Venue
This section demonstrates how to calculate and display an indoor route by using a code example. The
example is based on a scenario where a device user wants to find out how to reach another location in the
same venue. In real life, the user would select the starting point and destination for the route by tapping on
the map of the venue. However, for the sake of simplicity, the code below calculates a route by assuming the
spaces are already selected.
Note: See the Getting Indoor Location Based on a Tap Point section for a code example of how to
handle tap events to get a location for indoor venue routes.
The code below shows the implementation and is assumed to be part of an application. Previous
initialization steps are assumed.
// Add the application as a listener for route-calculation-completed:
m_venueMapFragment.getRoutingController().addListener(m_activity);
// Route start and end set-up – they are spaces in a user-selected 3D venue.
// Set route start. You can choose an item from all spaces associated with the venue, or
// allow the application user to select an item and then use the callback onSpaceSelected().
SpaceLocation startLocation = new SpaceLocation(startSpace,
m_venueMapFragment.getVenueController(venue));
// Set route end.
SpaceLocation endLocation = new SpaceLocation(endSpace,
m_venueMapFragment.getVenueController(venue));
// Get route option objects
VenueRouteOptions venueRouteOptions = new VenueRouteOptions();
RouteOptions options = venueRouteOptions.getRouteOptions();
// Set route type, transport mode, number of routes to calculate:
options.setRouteType(Type.values()[m_routingOptionType.getSelectedItemPosition()]);
options.setTransportMode(
TransportMode.values()[m_routingOptionMode.getSelectedItemPosition()]);
options.setRouteCount(1);
// Set route options:
venueRouteOptions.setRouteOptions(options);
// Calculate route - this is an asynchronous call, once the calculation is done
// onCombinedRouteCompleted() is called – see below.
routingController.calculateCombinedRoute(startLocation, endLocation,
venueRouteOptions);
//...
// Callback invoked when the route calculation is done to display the route passed
// to it as an argument.
public void onCombinedRouteCompleted(CombinedRoute route) {
// Use RoutingController to show route:
m_venueMapFragment.getRoutingController().showRoute(route);
176
HERE Android SDK Developer's Guide
► User Guide
Routing Between Venues
You can perform routing between venues by using separate VenueController objects. For example, in the
following, two different venues are used to retrieve VenueController objects:
// In this example m_startVenue and m_endVenue are assumed to be initialized
// with proper references.
SpaceLocation startLocation = new SpaceLocation(startSpace,
m_venueMapFragment.getVenueController(m_startVenue));
SpaceLocation endLocation = new SpaceLocation(endSpace,
m_venueMapFragment.getVenueController(m_endVenue));
}
// Other parts like in the previous example
Routing Using an Arbitrary Indoor Location
It is also possible to use an arbitrary indoor location that is not at a store or designated space as a route
endpoint. An example of this kind of location is a point in a corridor. The next code snippet demonstrates the
initialization of such an endpoint.
// Create a free point location to be used as a start location
// Here we assume that both current level and geo position is available in
// m_currentPosition. This can be obtained from, for example, some indoor positioning service.
LevelLocation startLocation = new LevelLocation(m_currentPosition.getLevel(),
m_currentPosition.getGeoCoordinate(),
m_venueMapFragment.getVenueController(venue));
The LevelLocation class extends BaseLocation and can be used as start or end location in
calculateCombinedRoute() method of RoutingController, similar to SpaceLocation and
OutdoorLocation.
The next example demonstrates route calculation from an indoor location, using type LevelLocation, to
some location outside the venue, represented by an OutdoorLocation class.
// Create a free point location to be used as a start location
// Here we assume that both current level and geo position is available in
// m_currentPosition. This could be obtained from some indoor positioning
// service (not covered in this section).
LevelLocation startLocation = new LevelLocation(m_currentPosition.getLevel(),
m_currentPosition.getGeoCoordinate(),
m_venueMapFragment.getVenueController(venue));
// Create an outdoor location.
GeoCoordinate endPosition = new GeoCoordinate(52.517072, 13.411232);
OutdoorLocation endLocation = new OutdoorLocation(endPosition);
// Get route options objects
VenueRouteOptions venueRouteOptions = new VenueRouteOptions();
RouteOptions options = venueRouteOptions.getRouteOptions();
// Set route type, transport mode, number of routes to calculate:
options.setRouteType(Type.FASTEST);
options.setTransportMode(TransportMode.CAR);
options.setRouteCount(1);
// Set route options:
venueRouteOptions.setRouteOptions(options);
177
HERE Android SDK Developer's Guide
► User Guide
// Calculate route - this is an asynchronous call, once the calculation is done
// onCombinedRouteCompleted() is called – see below.
routingController.calculateCombinedRoute(startLocation, endLocation,
venueRouteOptions);
//...
// Callback invoked when the route calculation is done to display the route passed
// to it as an argument.
public void onCombinedRouteCompleted(CombinedRoute route) {
// Use RoutingController to show route:
m_venueMapFragment.getRoutingController().showRoute(route);
}
Venue Route Options
VenueRouteOptions encapsulate options used in indoor routing. It is possible to set many parameters
related to visualization of the route line (for example color, line width, visibility of start and end flags) as
well as parameters related to how the route is calculated (for example if elevators are allowed, if stairs are
allowed, if corridors are preferred). The next example shows route calculation from one level to another level
while avoiding stairs. Initialization steps for used variables are assumed.
// set up start and end locations, assuming they're on different levels
// ...
// Set venue route options, including flag to avoid stairs:
VenueRouteOptions venueRouteOptions = new VenueRouteOptions();
venueRouteOptions.setStairsAllowed(false);
venueRouteOptions.setCorridorsPreferred(true);
// Set other route options
RouteOptions options = venueRouteOptions.getRouteOptions();
options.setRouteType(Type.SHORTEST);
options.setTransportMode(TransportMode.PEDESTRIAN);
options.setRouteCount(1);
venueRouteOptions.setRouteOptions(options);
// Calculate route - this is an asynchronous call, once the calculation is done
// onCombinedRouteCompleted() is called
routingController.calculateCombinedRoute(startLocation, endLocation,
venueRouteOptions);
//...
Getting Indoor Location Based on a Tap Point
The next code example shows how to add a route point to an indoor route using the onTapEvent(PointF
point) method of com.here.android.mpa.mapping.MapGesture.OnGestureListener.
public boolean onTapEvent(PointF point) {
// convert tap point to GeoGoordinate
Map map = m_venueLayer.getMap();
GeoCoordinate tapPoint = map.pixelToGeo(point);
if (tapPoint == null || !tapPoint.isValid()) {
return false;
}
// If any venue is selected, get related VenueController
Venue venue = m_venueLayer.getSelectedVenue();
178
HERE Android SDK Developer's Guide
► User Guide
VenueController venueController = null;
if (venue != null) {
venueController = m_venueLayer.getVenueController(venue);
}
// If no venue was selected, consider tapped location as OutdoorLocation
// and add it as route point.
if (venueController == null) {
BaseLocation location = new OutdoorLocation(tapPoint);
addRoutePoint(location);
return false;
}
// Otherwise consider tapped location as SpaceLocation and add it as route point.
BaseLocation location = venueController.getLocation(point, m_preferSpaceSelection);
addRoutePoint(location);
}
}
return false;
private void addRoutePoint(BaseLocation location) {
//Logic for saving route points.
}
Calculating Route Length
The following code example shows how the total length of a route can be calculated:
@Override
public void onCombinedRouteCompleted(CombinedRoute combinedRoute) {
};
double distance = 0.0;
final List routeSections = route.getRouteSections();
for (IRouteSection section : routeSections) {
switch (section.getRouteSectionType()) {
case VENUE:
List maneuvers = ((VenueRoute)section).getVenueManeuvers();
distance += maneuvers.get(maneuvers.size() - 1).getDistanceFromStart();
break;
case LINK:
GeoCoordinate from = ((LinkingRoute)section).getFrom();
GeoCoordinate to = ((LinkingRoute)section).getTo();
distance += from.distanceTo(from);
break;
case OUTDOOR:
com.here.android.mpa.routing.Route route = ((OutdoorRoute)section).getRoute();
distance += route.getLength();
break;
default:
break;
}
}
}
// do something with distance information
Natural Guidance for Venue Maneuvers
Venue maneuvers provide the names of the closest POIs for natural guidance purposes. For each maneuver,
this is the closest POI within a natural guidance radius around the position of the maneuver. If no POI exists
179
HERE Android SDK Developer's Guide
► User Guide
within this radius, an empty string is returned. The natural guidance radius is a global parameter common to
all maneuvers that may be set and queried by the user.
Note: For more information about natural guidance, see Turn-by-Turn Navigation for Walking and
Driving on page 145.
VenueManeuver.setNaturalGuidanceRadius(10.0);
floar naturalGuidanceRadius = VenueManeuver.getNaturalGuidanceRadius();
String naturalGuidance = myManeuver.getNaturalGuidancePOI();
Bounding Boxes for Parts of Venue Routes
Venue and outdoor route sections provide axis-aligned bounding boxes for the route. Axis-aligned bounding
boxes are provided for individual route segments for each level. These methods return null if a route has
no segment on the given level.
GeoBoundingBox obb = outdoorRoute.getBoundingBox();
GeoBoundingBox vbb = venueRoute.getBoundingBox();
GeoBoundingBox lbb = venueRoute.getBoundingBox(level);
The bounding box for the venue route (vbb in the example) also provides altitude information that may be
extracted using the getTopLeftFront() and getBottomRightBack() methods of GeoBoundingBox.
LiveSight
LiveSight enables user experiences that use the real world as an interface. With LiveSight, developers can
overlay geospatial content on the real world, which is displayed using the device's camera. Additionally, an
immersive experience is created by using the device's sensors to track movement in space and update the
view accordingly.
The key concepts covered in this section include adding LiveSight to an Android application, transitioning
from Map Mode to LiveSight Mode, and customizing the LiveSight experience. The classes covered include
CompositeFragment and ARController.
Note: LiveSight requires the following sensors enabled:
•
•
•
•
GPS
Compass
Accelerometer
Gyroscope
LiveSight does not function properly, or does not function at all, if these sensors are not working or
incorrectly calibrated.
CompositeFragment
The Fragment subclass related to LiveSight functionality is the CompositeFragment. It is called
"composite" because it exposes both Map and LiveSight functionality in one Android UI component,
with an easy way to switch between the two; therefore, in addition to methods related to LiveSight
functionality, the CompositeFragment also includes all of the methods found in the MapFragment.
The CompositeFragment is useful in the situation where an application wants to include both map and
LiveSight functionality.
180
HERE Android SDK Developer's Guide
► User Guide
The remainder of this section uses the CompositeFragment in code samples and discussions.
Note: The CompositeFragment.getScreenCapture(OnScreenCaptureListener) method has
the same functionality as MapFragment.getScreenCapture(OnScreenCaptureListener). It
does not support taking screen snapshots of the LiveSight Camera View or AR Objects on a map.
Adding and Initializing the CompositeFragment
The first step to integrate LiveSight functionality into an application is to insert a CompositeFragment into
the view layout. This is accomplished by adding com.here.android.mpa.ar.CompositeFragment to the
Android XML layout file as follows:
Note: When using the CompositeFragment, you do not have to also use the MapFragment. The
CompositeFragment is a superset of the MapFragment.
After adding the CompositeFragment to the layout, the fragment must be initialized by calling the
CompositeFragment.init(OnEngineInitListener) method. During this asynchronous initialization,
the MapEngine is initialized to create an instance of Map that is associated with the CompositeFragment.
The ARController is also be created. For more information about ARController, see Customizing
LiveSight on page 188.
The following code example illustrates the basic initialization flow when an Activity is created. The
init(OnEngineInitListener) method uses the OnEngineInitListener parameter to signal the caller
when the asynchronous initialization is completed.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Search for the Composite Fragment
final CompositeFragment compositeFragment = (CompositeFragment)
getFragmentManager().findFragmentById(R.id.compositefragment);
// initialize the Composite Fragment and
// retrieve the map that is associated to the fragment
compositeFragment.init(new OnEngineInitListener() {
@Override
public void onEngineInitializationCompleted(
OnEngineInitListener.Error error) {
if (error == OnEngineInitListener.Error.NONE) {
// now the map is ready to be used
map = compositeFragment.getMap();
// the arController is also ready to be used now
arController = compositeFragment.getARController();
} else {
System.out.println(
"ERROR: Cannot initialize CompositeFragment");
}
}
});
}
181
HERE Android SDK Developer's Guide
► User Guide
Note: com.here.android.mpa.ar.CompositeFragment has
Fragment.setRetainInstance(boolean) set to true; therefore, onCreate(Bundle) should not
be called again when Activity is re-created (for example, after an orientation change).
Starting and Stopping LiveSight
It is important to make a distinction between the two operating modes provided by the
CompositeFragment, Map Mode and LiveSight Mode. Map Mode is the mode of operation that is the same
as that provided by the MapFragment. As with MapFragment, Map Mode behavior and functionality are as
described under Maps. LiveSight Mode is the mode of operation that provides the LiveSight experience and
has separate functionality and behavior.
Note: It may be confusing that the map is still displayed even when LiveSight Mode is enabled. While
this map view is a part of the LiveSight experience, it has different functionality and behavior than
Map Mode.
Figure 68: Map Mode
Figure 69: LiveSight Mode
To switch between Map Mode and LiveSight Mode, two methods from the ARController, start() and
stop(), are used. The start() method triggers the transition from Map Mode to LiveSight Mode, which
includes a short cinematic transition animation by default. Calling start() while already in LiveSight Mode
results in the Error.INVALID_OPERATION error code being returned. Use the stop() method to transition
from LiveSight Mode to Map Mode. By default the CompositeFragment starts in Map Mode.
// Triggers the transition from Map Mode to LiveSight Mode
Error error = arController.start();
...
// Exits LiveSight Mode and returns to Map Mode
Error error = arController.stop(true);
182
HERE Android SDK Developer's Guide
► User Guide
Reading the Current Pose
The ARController provides a convenient way to retrieve the current positional and directional
(pose) values of your LiveSight session. By calling ARController.getPose(), you can retrieve the
ARPoseReading instance, which contains the following values:
•
Heading
•
Roll
•
•
•
•
•
Pitch
Latitude
Longitude
Altitude
Timestamp
Although these ARPoseReading values are derived from device sensors, they are interpolated and
smoothed by the LiveSight engine.
Adding and Interacting with LiveSight Content
This section covers how to add content to be displayed in LiveSight and how to handle user interactions with
that content. The classes covered in this section are ARObject and ARIconObject. Additionally, several
ARController methods are used:
•
addARObject(ARObject)
•
addOnTapListener(OnTapListener)
•
•
•
•
•
•
removeARObject(ARObject)
press(PointF)
focus(ARObject)
defocus()
getObjects(PointF)
getObjects(ViewRect)
The LiveSight Object Model
A LiveSight object has several visual representations. The representation to be displayed is determined
based on the current state of the LiveSight view, which is defined as a function of the device's pitch by
default, as well as the state of the object itself. The object state that influences display is the Focus state.
The Focus state is discussed in detail later.
The LiveSight view states are the "Down" state and the "Up" state. By default, when the view is pitched
downwards (for example, if the device screen is face-up), then LiveSight is in the Down state and set to the
default Map view. As the device is pitched further upwards (angled negatively around the x-axis), LiveSight
transitions to the Up state, which has the Camera view as its default view.
•
Down Object Representation
183
HERE Android SDK Developer's Guide
► User Guide
While in the Down state, a LiveSight object is represented by an icon associated with the Down state.
When transitioning to the Up state, a fly-in transition animation occurs, and the larger front icon is
displayed.
Figure 70: An Icon in the Down State
•
Up Object Representation
While the Down state representation consists of just a single icon, the Up state representation is more
complex. While in the Up state, there are two planes where an object can be displayed, and the object
representation is different in each. The planes are the "Front" plane and the "Back" plane. By default,
objects that are geographically closer to the LiveSight center are displayed in the Front plane, and
184
HERE Android SDK Developer's Guide
► User Guide
objects that are further away are displayed in the Back plane. Objects can be moved from one plane to
the other using the vertical pan gesture.
Figure 71: Icons in the Up State
Figure 72: Visualization of the Front and Back Planes
While in the Front plane, a LiveSight object is represented by both its icon and an information view
(Info View) that extends out from the side of the icon. This information view is intended to act as a
mechanism for displaying more detailed information about the object. In the Back plane, an object is
initially represented by a single icon. It is possible to have an object in the Back plane display its Info
View by putting it in focus. The icon for the Front plane and the Back plane can be different, and by
default, the transition from one plane to the other is animated.
The ARObject Abstract Class
ARObject is the base implementation for all other objects that can be added to LiveSight in order to be
displayed. It contains methods common to all LiveSight objects, enabling the following operations:
•
•
•
•
•
Set and retrieve the object's current position
Set and retrieve the object's down, front, or back icon
Set and retrieve the object's down, front, and back icon sizes
Set and retrieve the size, image, and extension state of the information view
Set and clear an image texture on the object's down, front, back icons and information view
185
HERE Android SDK Developer's Guide
► User Guide
The ARIconObject Class
Currently, the single concrete ARObject is the ARIconObject. ARIconObject represents the object model
described in The LiveSight Object Model on page 183. Because it is the only concrete ARObject, all of its
functions reside in the ARObject.
Adding and Interacting with ARObjects
Adding an ARObject to LiveSight is accomplished with the ARController.addARObject(ARObject)
method:
arIconObject = new ARIconObject(
new GeoCoordinate(49.276744, -123.112049, 2.0), view, image);
arController.addARObject(arIconObject);
Similarly, ARObjects can be removed using the ARController.removeObject(ARObject) method:
boolean success = arController.removeARObject(arIconObject);
To facilitate interactivity with ARObjects, an ARController.OnTapListener can be registered with
the ARController.addOnTapListener(OnTapListener) method. When a tap event occurs, the
ARObject at the tap point can be found through the press(PointF) method. Calling press(PointF)
also causes an animation on the ARObject. Additionally, the ARObject can be put into focus with the
ARController.focus(ARObject) method. While in focus, an ARObject that is in the back plane displays
its info pane. Only one ARObject may have focus at a time.
arController.addOnTapListener(new ARController.OnTapListener() {
@Override
public boolean onTap(PointF point) {
// retrieve ARObject at point (if one exists)
// and trigger press animation
ARObject arObject = arController.press(point);
if (arObject != null) {
// focus object
arController.focus(arObject);
}
});
}
return false;
To defocus an ARObject, call focus() on another ARObject. You can also call the
ARController.defocus() method to defocus from the currently focused ARObject.
In addition to event driven ARObject retrieval, the ARController.getObjects(PointF) and
ARController.getObjects(ViewRect) methods can be used to programmatically get ARObject at a
screen location:
PointF point = new PointF(50, 50);
List objectsAtPoint = arController.getObjects(point);
ViewRect viewRect = new ViewRect(50, 50, 25, 25);
186
HERE Android SDK Developer's Guide
► User Guide
List objectsInViewRect = arController.getObjects(viewRect);
Selecting ARObjects
After retrieving an ARObject, you can choose to select and unselect it by calling the select() and
unselect() methods. Selecting an object causes objects to change their properties (such as size and
opacity) according to ARObject.SelectedItemParams. Only one ARObject may be selected at a time.
Note: A single ARObject cannot be focused and selected simultaneously. However, it is possible to
have one ARObject focused and another ARObject selected at the same time.
ARObject Occlusion
Figure 73: Occluded Objects in the Map View
Occlusion refers to whether a certain ARObject is behind a building, with respect to the user's point of view.
If the point represented by the ARObject is not visible in real life because it is blocked by a building, then
that point is considered occluded. Because occlusion is dependent of the user's point of view, this feature is
dependant on having accurate building data for the user's location.
With the ARController, you can check for occluded LiveSight objects and change their opacity by using the
following methods:
•
ARController.isOccluded(ARObject arObject)
•
ARController.setOcclusionEnabled(boolean enable)
•
ARController.setOcclusionOpacity(float opacity)
3D Objects
LiveSight also supports two types of 3D objects: ARBillboardObject and ARMeshObject
. Both of these classes are the derivatives of ARModelObject, which provides the
187
HERE Android SDK Developer's Guide
► User Guide
ability to control basic properties such as object opacity, scale, and rotation. Because
ARModelObject classes do not derive from the ARObject class, you cannot use them with
some ARController methods such as focus(ARObject) or press(ARObject). To add or
remove an ARModelObject, use the ARController.addARObject(ARModelObject) and
ARController.removeARObject(ARModelObject) methods.
An ARBillboardObject can be oriented in the Up state in two ways. In the FIXED orientation mode, it may
be "attached" to a surface by specifying the up and normal vectors using the setUpDirection(Vector3f)
and setSurfaceNormal(Vector3f) methods. In the BILLBOARD orientation mode, a billboard is set to
be always upright and facing the camera. An ARBillboardObject can be positioned in one of the following
ways:
•
•
Anchored to a specific geo-location using constructors ARBillboardObject(GeoCoordinate)
and ARBillboardObject(GeoCoordinate, Image), as well as the
setGeoPosition(GeoCoordinate) method.
Anchored relative to the camera using constructors ARBillboardObject(Vector3f) and
ARBillboardObject(Vector3f, Image), as well as the setLocalPosition(Vector3f) method.
The Vector3f represents the location of the center of the billboard in meters away from the camera.
An ARMeshObject represents a 3D object mesh. As with an ARBillboardObject, a mesh object may be
anchored to a geo location or relative to the screen. You can control the orientation of the ARMeshObject
by providing a GeoCoordinate that the object can point towards.
Customizing LiveSight
LiveSight is highly configurable, allowing developers and designers to create many different and immersive
experiences. The ARController class serves as a facade for overall LiveSight functionality, containing all of
the methods and callbacks available for controlling and customizing LiveSight behavior.
The methods used to customize LiveSight reside in the ARController and also the following inner classes:
•
•
•
•
•
•
•
•
•
ARController.UpViewParams
ARController.UpViewTransitionParams
ARController.DownViewParams
ARController.IntroAnimationParams
ARController.IconParams
ARController.InfoParams
ARController.CameraParams
ARController.FilterParams
ARController.SelectedItemParams
UpViewParams, UpViewTransitionParams, and DownViewParams
UpViewParams and DownViewParams encapsulate the customizable parameters that are applicable for the
Up and Down views. UpViewTransitionParams encapsulates customizable parameters that are applicable
when you move up from the down view and enter the camera view.
188
HERE Android SDK Developer's Guide
► User Guide
IntroAnimationParams
IntroAnimationParams encapsulates parameters about various intro animations. Intro animation refers
to the animation that is used while the app is entering LiveSight. During LiveSight entry, a few transition
operations happen simultaneously, and each of these can be assigned a different animation style. These
customizable transition operations are: map zoom (the map zooms in towards the ground level), heading
change (the current heading changes to the current device heading), position change (the current position
changes to the current device position or a LiveSight-specific position), and pitch change (the pitch changes
according to the 3D orientation of the device).
IconParams and InfoParams
IconParams encapsulates customizable parameters for the front, back, and down icons. In addition to
setting icon sizes, you can also set how icons animate when they first appear ("pop-up"), receive a tap, or
appear while transitioning from the Down to Up view ("fly"). For more information on down, front, and back
icons, consult the section Adding and Interacting with LiveSight Content on page 183.
InfoParams allow you to customize how Info Views animate when they first appear ("pop-up"), receive a
tap, or appear while transitioning from the Down to Up view ("fly").
CameraParams
CameraParams encapsulates parameters that are related to the camera-enabled Up view. The
CameraParams.setSize(Size) method allows you to set the camera resolution to be used for the
LiveSight camera view. Note that using a high camera resolution may cause performance degradation. The
default camera resolution is 680x480.
HeadingFilterParams, PitchFilterParams, and ZoomFilterParams
HeadingFilterParams, PitchFilterParams, and ZoomFilterParams are all instances of the
ARController.FilterParams class. These objects encapsulate the customizable parameters for the
heading, pitch, and zoom data sampling. Methods in Filter allow you to customize how data samples are
read from the device sensors.
SelectedItemParams
SelectedItemParams encapsulates parameters that control behavior when an ARObject is in the selected
or unselected state. For example, you can increase the size of a selected icon. By default, objects are neither
in the selected or unselected state. When an ARObject is selected, all other ARObjects are changed to the
unselected state. When selection is canceled, all objects are returned to the neutral state.
Other ARController Settings
In addition to the previous parameter classes, you can use methods in ARController to customize the
following areas in LiveSight:
•
Alternative Center Location — LiveSight is not only limited to the current device location. It is possible to
use the ARController to set an alternative location ("space shift") for your LiveSight experience.
189
HERE Android SDK Developer's Guide
► User Guide
•
•
•
Icon Display Behavior — Adjust settings related to the icon display, such as as only showing Front items,
or using Down icons in the map.
Device Orientation Behavior — Set whether the Down view is updated with the device sensor's
orientation data.
Layout Updates — By default, LiveSight icons are set to update dynamically according to the current
device position. However, you can set ARController so that the Layout (containing the front and back
icons) does not update until the device position has changed significantly past a threshold.
External Sensor Data
LiveSight also provides the ability to switch from using integrated device sensor data to one or more data
feeds provided by external sources. You may need to do this when more accuracy is required for object
projection results in the Up state, or when you require a distributed LiveSight solution where data from
multiple sensors are provided over TCP/IP.
ARController.ExternalSensors is the entry point for using external sensor data. You can enable
or disable input from one or more external sensors by using this class with the values defined in the
ARController.SensorType enum, with the exception of CAMERA. For example:
arController.ExternalSensors.utilize(SensorType.GPS, true);
Note: This call should only be made when ARController is stopped or paused.
After the external sensor is enabled, the integrated device sensor data is no longer used, and you must start
providing sensor data into the LiveSight engine using the pushData(SensorType, double, double,
double, long) method. Note that the parameters are treated differently if a different SensorType is
used. For more information on providing data using this method, see the API reference.
Animation Interpolators
The visual appearance of many of the LiveSight animations can be changed by using different animation
interpolators. The available interpolator types include:
•
LINEAR - Linear interpolation
•
DECELERATE - Starts quick, and then decelerates
•
•
•
•
•
•
ACCELERATE - Starts slow, and then accelerates
ACCELERATE_DECELERATE - Starts and ends slowly, but accelerates through the middle
OVERSHOOT - Flings forward and overshoots the last value, then comes back
ANTICIPATE - Starts backward, then flings forward
ANTICIPATE_OVERSHOOT - Starts forward, then flings forward and overshoots the target value, and
finially goes back to the final value
BOUNCE - Rate of change 'bounces' at the end
Listeners provided by ARController
The ARController class provides a variety of listener classes that can be used to trigger event-driven code
in your application. The listeners provided are:
190
HERE Android SDK Developer's Guide
► User Guide
Listener Name
Purpose
OnCameraEnteredListener
Listener for the Camera view-entered event. This event is triggered just before the
OnCameraExitedListener
Listener for the Camera view-exited event. This event is triggered just after the camera
camera frame is displayed.
frame is exited.
OnCompassCalibrationChangedListenerListener for compass calibration changed event. This event is triggered by the system
when the compass calibration status changes.
OnMapEnteredListener
Listener for the "Map view entered" event. This event is triggered just before the Map
OnMapExitedListener
Listener for the "Map view exited" event. This event is triggered just after the Map view is
OnObjectTappedListener
Listener for object selection events.
OnPanListener
Listener for pan events.
OnPoseListener
Listener for pose update events.
OnPreDrawListener
Listener for pre-draw event. This event is triggered just before a draw is performed. This
view is displayed.
exited.
listener is useful in case a client wants to update things while in LiveSight Mode and
serialize the update action with the LiveSight draw cycle. This callback is performed in
both Map view and Camera view.
OnPreDrawMapListener
Listener for the map pre-draw event. This event is triggered just before the map is being
drawn. This listener is useful in the case where the client wants to update things on the
map and serialize the update action with the draw cycle.
OnPrePresentListener
Listener for the event that occurs before the frame is composited. This event occurs after
OnPostPresentListener
Listener for the event that occurs after the frame is composited.
OnLivesightStatusListener
Listener for hardware and component errors related to LiveSight.
OnPitchFunction
Listener for pitch changes. Allows you to override the default LiveSight pitch/zoom curve.
OnRadarUpdateListener
Listener for radar update events.
the draw event.
OnSensorCalibrationChangedListener Listener for sensor calibration events.
OnTapListener
Listener for tap events.
OnTouchDownListener
Listener for touch "down" events.
OnTouchUpListener
Listener for touch "up" events.
Note: OnPreDrawMapListener is a replacement for the OnMapRenderListener.onPreDraw()
callback, which is not triggered while in LiveSight Mode.
All of these Listeners are added and removed by way of their respective addOnXYZListener and
removeOnXYZListener methods provided by the ARController.
Draw and Present Phases
To understand how to use OnPreDrawMapListener, OnPrePresentListener, and
OnPostPresentListener, it is important to understand the order and differences between Present and
Draw. The order of LiveSight phases and events are as follows:
1.
Pre-Draw event
191
HERE Android SDK Developer's Guide
► User Guide
2.
Draw phase
4.
Present phase
3.
5.
Pre-Present event
Post-Present event
The Draw phase is the action of filling graphics buffers with the various components of the scene, which are
applicable for the current LiveSight View. In the Draw phase, the LiveSight engine is composing different
layers in a frame, such as a map and icons for the Down view, and the camera feed and icons for the Up view.
This phase occurs before the Present phase, and since the Pre-Draw event is triggered before this phase, you
should use the Pre-Draw event if you want, for example, for an icon to change for the next frame.
Note the absence of the Post-Draw event. If you want to do something just before presenting, use the PrePresent event, which is the same as Post-Draw, since the Pre-Present event occurs after "Draw" but before
the "Present" phase.
The Present phase refers to the composition of the buffers filled in the draw phase into the frame. In other
words, this phase takes buffers and blends their contents into one buffer to be displayed. For example, this
is how icons are placed on top of the camera frame. After the "Present" phase, the Post-Present event is
triggered.
Platform Data Extension
Platform Data Extension (PDE) provides the ability to easily access the Platform Data Extension API from
the HERE Android SDK. You can use this extension to access a wide range of data that can be later used for
different use cases. Some examples include displaying road elevation, slopes, and traffic signs. For more
information about use cases and the types of data that can be accessed through PDE, check the Platform
Data Extension API Developer's Guide .
PDE Thematic Layers
PDE divides map content across many thematic layers. Each thematic data layer serves a specific use case
and only contains the data required for it, such as road elevation. To use PDE, you need to first decide on the
required data for your app and select the PDE thematic layers accordingly. The available thematic layers can
be found via the PDE Layers API. You can then check the targeted thematic layers via the individual Layer
API. Before starting to use PDE in your app, you need to select the correct thematic layers, and then decide
on what data to use and how to use it. This is a crucial step.
Note: It is common for routes to start on smaller roads, climb to bigger roads, stay on motorways
for the main part, and finally steps down to smaller roads again when approaching the destination.
Since retrieving all information about smaller roads along the entire route requires a large amount
of data, road link-related thematic layers are actually split into five layers each, corresponding to
the functional road classes in the HERE map. Functional Class 1 roads are generally motorways,
while Functional Class 5 roads are small roads that are only used near a destination. To use these
layers, you need to specify the tile layer by appending the functional class suffix "_FCx", where x is
a number from 1 to 5. For example, ROAD_GEOM_FC1. If a layer isn't related to road links, such as the
PSTLCB_GEN layer, you don't need to append the "_FCx" suffix and specify the tile layer.
192
HERE Android SDK Developer's Guide
► User Guide
PDE Classes
Class
Description
PlatformDataRequest
Used to create and execute a PDE data request with the specified layers and
PlatformDataResult
The result of running a PDE data request. Contains data for requested layers,
GeoBoundingBox object.
which are accessible by the layer name. This implements the java.util.Map
interface for ease of use and interoperability, although objects of this class
are unmodifiable. Provides extract method to convert the result into an
Map>> which is a raw representation
of the underlying data.
PlatformDataItemCollection
Represents layer data in the form of an array of PlatformDataItem objects.
This is what you get by asking PlatformDataResult for a specific layer—
for example, by calling platformDataResult.get("ROAD_GEOM_FC1").
PlatformDataItemCollection implements the java.util.List
interface for ease of use and interoperability, although objects of this class are
unmodifiable. It also provides an extract() method to convert the collection
into an List