Map Selection ¶
In this guide you will learn how to select an item on an interactive map, such as an alternate route, if a route computation resulted in two or more routes, or a POI (point of interest), such as a park, a cafe, a mountain or an archeological site.
Setup ¶
First, get an API key token, see the Getting Started guide.
Download the Maps & Navigation SDK for Android archive file
Download the
MapSelection
project
archive file or clone the project with Git
See the Configure Android Example guide.
Run the example ¶
In Android Studio, from the
File
menu, select
Sync
Project
with
Gradle
Files
|
|
|
|
How it works ¶

You can open the MainActivity.kt file to see how the route is computed and drawn on the map.
1private fun calculateRoute() {
2 val waypoints = arrayListOf(
3 Landmark("London", Coordinates(51.5073204, -0.1276475)),
4 Landmark("Paris", Coordinates(48.8566932, 2.3514616))
5 )
6 routingService.calculateRoute(waypoints)
7}
calculateRoute()
function, 2
Landmark
instances are
defined, one for the departure, and one for the destination
coordinates of the route endpoints. A route must have at least 2
Landmark instances(waypoints), but optionally can have more,
for any optional additional waypoints along the route.
Coordinates
in the 2
Landmark
instances.
routingService
to calculate the route.
1private val routingService = RoutingService(
2 onStarted = {
3 progressBar.visibility = View.VISIBLE
4 },
5 onCompleted = { routes, errorCode, _ ->
6 progressBar.visibility = View.GONE
7 when (errorCode) {
8 GemError.NoError -> {
9 routesList = routes
10 SdkCall.execute {
11 gemSurfaceView.mapView?.presentRoutes(routes, displayBubble = true)
12 gemSurfaceView.mapView?.preferences?.routes?.mainRoute?.let { selectRoute(it) }
13 }
14 flyToRoutesButton.visibility = View.VISIBLE
15 }
16 GemError.Cancel -> {
17 // The routing action was cancelled.
18 }
19 else -> {
20 // There was a problem at computing the routing operation.
21 showDialog("Routing service error: ${GemError.getMessage(errorCode)}")
22 }
23 }
24 }
25)
RoutingService
is instantiated to compute a route and render
it on the map. The
onStarted
and
onCompleted
callbacks are
implemented, to detect when the route computation is started and when
it is completed.
presentRoutes()
.
displayBubble
flag is set to true to show the distance in km,
and the time in hours, next to each route.
1override fun onCreate(savedInstanceState: Bundle?) {
2 super.onCreate(savedInstanceState)
3 setContentView(R.layout.activity_main)
4 progressBar = findViewById(R.id.progressBar)
5 gemSurfaceView = findViewById(R.id.gem_surface)
6 overlayContainer = findViewById(R.id.overlay_container)
7 name = findViewById(R.id.name)
8 description = findViewById(R.id.description)
9 image = findViewById(R.id.image)
10 followCursorButton = findViewById(R.id.follow_cursor)
11 flyToRoutesButton = findViewById<FloatingActionButton?>(R.id.fly_to_route).also {
12 it.setOnClickListener {
13 SdkCall.execute {
14 gemSurfaceView.mapView?.let { mapView ->
15 mapView.deactivateAllHighlights()
16 mapView.preferences?.routes?.mainRoute?.let { mainRoute ->
17 selectRoute(mainRoute)
18 }
19 }
20 }
21 }
22 }
23 imageSize = resources.getDimension(R.dimen.image_size).toInt()
24 SdkSettings.onMapDataReady = onMapDataReady@{ isReady ->
25 if (!isReady) return@onMapDataReady
26 // Defines an action that should be done when the world map is ready (Updated/ loaded).
27 calculateRoute()
28 // Set GPS button if location permission is granted, otherwise request permission
29 SdkCall.execute {
30 val hasLocationPermission = PermissionsHelper.hasPermission(this,
31 Manifest.permission.ACCESS_FINE_LOCATION)
32 if (hasLocationPermission)
33 {
34 Util.postOnMain { enableGPSButton() }
35 }
36 else
37 {
38 requestPermissions(this)
39 }
40 }
onCreate()
function
calls the
calculateRoute()
function shown above,
once the map is ready, that is, instantiated and loaded.
onCreate()
also uses
findViewById()
to get pointers/handles
to each of the various graphical elements on which something is
rendered, or which are used for user input, such as the button in
the lower left to fly to the calculated routes, or the other button
to fly to the position of the device, also referred to as GPS position,
also appearing in the lower left of the viewport.
followCursorButton
,
is shown only if the camera is not already following the position
of the device, indicated by a green arrow on the map.
flyToRoutesButton
,
is used to fly back to the currently selected precalculated route,
after panning away from it for example;
this button is shown after the route is calculated and rendered.
1private fun requestPermissions(activity: Activity): Boolean
2{
3 val permissions = arrayListOf(
4 Manifest.permission.INTERNET,
5 Manifest.permission.ACCESS_NETWORK_STATE,
6 Manifest.permission.ACCESS_FINE_LOCATION,
7 Manifest.permission.ACCESS_COARSE_LOCATION
8 )
9 return PermissionsHelper.requestPermissions(
10 REQUEST_PERMISSIONS, activity, permissions.toTypedArray()
11 )
12}
requestPermissions()
function asks the user for internet
and location permissions.
onRequestPermissionsResult()
function is overridden, to receive
the results of the permission requests.
1 // onTouch event callback
2 gemSurfaceView.mapView?.onTouch = { xy ->
3 // xy are the coordinates of the touch event
4 SdkCall.execute {
5 // tell the map view where the touch event happened
6 gemSurfaceView.mapView?.cursorScreenPosition = xy
7 gemSurfaceView.mapView?.deactivateAllHighlights()
8 val centerXy = Xy(gemSurfaceView.measuredWidth / 2, gemSurfaceView.measuredHeight / 2)
9 val myPosition = gemSurfaceView.mapView?.cursorSelectionSceneObject
10 if (myPosition != null
11 && isSameMapScene(myPosition, MapSceneObject.getDefPositionTracker().first!!))
12 {
13 showOverlayContainer(
14 GemUtil.getUIString(EStringIds.eStrMyPosition),
15 "",
16 GemUtilImages.asBitmap(ImageDatabase().getImageById(SdkImages.UI.SearchForCurrentLocation.value),
17 imageSize, imageSize)
18 )
19 myPosition.coordinates?.let {
20 gemSurfaceView.mapView?.centerOnCoordinates(
21 it,
22 -1,
23 centerXy,
24 Animation(EAnimation.Linear),
25 Double.MAX_VALUE,
26 0.0
27 )
28 }
29 return@execute
30 }
1
onTouch
listener is defined for the
mapView
to get
the
cursorScreenPosition
. Once the x,y position of the user
touch event has been set in the mapView, the code verifies
what map element was selected.
val
myPosition
=
gemSurfaceView.mapView?.cursorSelectionSceneObject
myPosition.coordinates?.let
{
get the coordinates of the scene object (it), and use the
gemSurfaceView.mapView?.centerOnCoordinates(it,
function to
move the camera with a flying animation
Animation(EAnimation.Linear),
to the selected object.
1 val landmarks = gemSurfaceView.mapView?.cursorSelectionLandmarks
2 if (!landmarks.isNullOrEmpty())
3 {
4 val landmark = landmarks[0]
5 landmark.run {
6 showOverlayContainer(
7 name.toString(),
8 description.toString(),
9 image?.asBitmap(imageSize, imageSize)
10 )
11 }
12 val contour = landmark.getContourGeographicArea()
13 if (contour != null && !contour.isEmpty())
14 {
15 contour.let {
16 gemSurfaceView.mapView?.centerOnArea(
17 it,
18 -1,
19 centerXy,
20 Animation(EAnimation.Linear),
21 )
22 val displaySettings = HighlightRenderSettings(
23 EHighlightOptions.ShowContour,
24 Rgba(255, 98, 0, 255),
25 Rgba(255, 98, 0, 255),
26 0.75
27 )
28 gemSurfaceView.mapView?.activateHighlightLandmarks(landmark, displaySettings)
29 }
30 }
31 else
32 {
33 landmark.coordinates?.let {
34 gemSurfaceView.mapView?.centerOnCoordinates(
35 it,
36 -1,
37 centerXy,
38 Animation(EAnimation.Linear),
39 Double.MAX_VALUE,
40 0.0
41 )
42 }
43 }
44 return@execute
45 }
1
val
landmarks
=
gemSurfaceView.mapView?.cursorSelectionLandmarks
val
contour
=
landmark.getContourGeographicArea()
gemSurfaceView.mapView?.centerOnArea(
function to move the camera with a flying animation to the
selected region, such that the bounding box of
the selected region fits snugly in the viewport.
landmark.coordinates?.let
{
gemSurfaceView.mapView?.centerOnCoordinates(
1 val trafficEvents = gemSurfaceView.mapView?.cursorSelectionTrafficEvents
2 if (!trafficEvents.isNullOrEmpty())
3 {
4 hideOverlayContainer()
5 openWebActivity(trafficEvents[0].previewUrl.toString())
6
7 return@execute
8 }
val
trafficEvents
=
gemSurfaceView.mapView?.cursorSelectionTrafficEvents
openWebActivity(trafficEvents[0].previewUrl.toString())
at index 0, in case there are more than one.
1 val overlays = gemSurfaceView.mapView?.cursorSelectionOverlayItems
2 if (!overlays.isNullOrEmpty())
3 {
4 val overlay = overlays[0]
5 if (overlay.overlayInfo?.uid == ECommonOverlayId.Safety.value)
6 {
7 hideOverlayContainer()
8 openWebActivity(overlay.getPreviewUrl(Size()).toString())
9 }
10 else
11 {
12 overlay.run {
13 showOverlayContainer(
14 name.toString(),
15 overlayInfo?.name.toString(),
16 image?.asBitmap(imageSize, imageSize)
17 )
18 }
19 overlay.coordinates?.let {
20 gemSurfaceView.mapView?.centerOnCoordinates(
21 it,
22 -1,
23 centerXy,
24 Animation(EAnimation.Linear),
25 Double.MAX_VALUE,
26 0.0
27 )
28 }
29 }
30 return@execute
31 }
val
overlays
=
gemSurfaceView.mapView?.cursorSelectionOverlayItems
overlay.coordinates?.let
{
gemSurfaceView.mapView?.centerOnCoordinates(
1 // get the visible routes at the touch event point
2 val routes = gemSurfaceView.mapView?.cursorSelectionRoutes
3 // check if there is any route
4 if (!routes.isNullOrEmpty())
5 {
6 // set the touched route as the main route and center on it
7 val route = routes[0]
8 selectRoute(route)
9
10 return@execute
11 }
12 }
13 }
14 }
val
routes
=
gemSurfaceView.mapView?.cursorSelectionRoutes
and if there is any route under the cursor, get a list of routes
with the
cursorSelectionRoutes
function.
selectRoute()
is called on this route to center it
with an animated motion.
1private fun selectRoute(route: Route)
2{
3 gemSurfaceView.mapView?.apply {
4 route.apply {
5 showOverlayContainer(
6 summary.toString(),
7 "",
8 GemUtilImages.asBitmap(
9 ImageDatabase().getImageById(SdkImages.UI.RouteShape2.value),
10 imageSize,
11 imageSize
12 )
13 )
14 }
15 preferences?.routes?.mainRoute = route
16 }
17
18 gemSurfaceView.mapView?.centerOnRoutes(routesList)
19}
selectRoute()
function uses
centerOnRoutes()
to fly the camera with an animated motion to the set
of computed routes, if there are more than one in the result,
such that their bounding box fits in the viewport,
so that all alternate routes are visible.
mainRoute
in order to highlight that particular
route.
gemSurfaceView.mapView?.centerOnRoute(route)
1private fun showOverlayContainer(name: String,
2 description: String, image: Bitmap?) = Util.postOnMain {
3 if (!overlayContainer.isVisible)
4 {
5 overlayContainer.visibility = View.VISIBLE
6 }
7 this.name.text = name
8 if (description.isNotEmpty())
9 {
10 this.description.apply {
11 text = description
12 visibility = View.VISIBLE
13 }
14 }
15 else
16 {
17 this.description.visibility = View.GONE
18 }
19 this.image.setImageBitmap(image)
20}
showOverlayContainer()
function is used to
show for example the outline of the selected building,
or park, or the border of the selected country, etc.
1 SdkSettings.onApiTokenRejected = {
2 showDialog("TOKEN REJECTED")
3 }
4 if (!Util.isInternetConnected(this))
5 {
6 showDialog("You must be connected to the internet!")
7 }
8}
onCreate()
function also checks if internet access is available
and if not, shows a dialog that internet connection is required
(because it is used to download map data).