Map Library SDK

This section describes the usage of the Map Library SDK. This library allows you to visualize a map and interact with it. The Map Library SDK does not include any routing or navigation functionality like the NavApp SDK does.

Map Library SDK javadoc documentation

Map Library SDK

  • interfacing to a map view

Installing the SDK

Use Android Studio for your project and have the project use the Gradle directory structure. Depending on how recent the SDK is, there is one of two ways to incorporate it into your project: JARs and AARs.

SDK with JAR files

Copy the maplibsdk.jar and mapviewer2.jar files into your application project libs directory. The files can be found in the libs directory in the SDK zip file which you downloaded from the portal.

SDK with AAR files

Copy the AAR libraries from the SDK ZIP libs directory into the app/libs directory of your project. To make the application use them, add the following to allprojects.repository in the project (top) build.gradle file so they are referable:

1flatDir {
2 dirs 'libs'
3}

Then add the library dependencies to the module build.gradle file in app/build.gradle:

1implementation(name: 'maplib', ext: 'aar')
2implementation(name: 'maplibsdk', ext: 'aar')
3implementation(name: 'maplibsdk-assets', ext: 'aar')

After installing the app, make sure it receives the correct permissions by going to Settings/Apps/ /Permissions and enabling Location and Storage there or use requestPermissions().

Fix any run-time issues in the modules build.gradle file, located at app/build.gradle. For example, when your appcompat version does not match the one included in the AAR files:

1implementation('com.android.support:appcompat-v7:28.0.0-alpha3') {
2 exclude group: "com.android.support"
3}

Because the Map Library communicates with NavKit via a network interface, your application is required to have the INTERNET permission, even though you might not do anything with networking. So add this to your applications AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />

Defining the view in the layout xml

1...
2<com.tomtom.pnd.maplib.MapView
3 android:id="@+id/mapview"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"/>
6...

Initialising and interfacing with the SDK

The Map Library uses the same MapKit engine as the Navigation application does. The MapKit engine is used via runtime connections, which means that these connections might disappear at some point. For this reason you cannot just control your map, you need to maintain a connection to it. You are shielded from the details of this and only have to monitor two simple events to know whether or not your map is ready for use.

You need to implement the MapEventsCallbacks interface and set an instance of this callback on the MapView object. The first step is to implement the callback interface:

1public class MainActivity extends FragmentActivity
2 implements MapEventCallbacks {
3 ...
4}

Initialise the MapView object in your application and request a MapController instance with the getMapControllerAsync call:

1@Override
2protected void onCreate(final Bundle savedInstanceState) {
3 super.onCreate(savedInstanceState);
4 setContentView(R.layout.activity_main);
5
6 mMapView = (MapView) findViewById(R.id.mapview);
7 mMapView.getMapControllerAsync(this);
8 ...
9}

Use the onMapConnected(MapController) callback method to get a handle to the MapController object. The callback is triggered when the MapController is ready to be used. It provides a non-null instance of MapController.

You can use the MapController object to set the view options for the map or add markers, for example. In onMapDisconnected() you need to set your reference to the MapController to null and stop interacting with the map until the connection is restored.

1@Override
2public void onMapConnected(final MapController controller) {
3 mMapController = controller;
4}
5
6@Override
7public void onMapDisconnected() {
8 mMapController = null;
9}

The overloaded onResume() and onPause() need to inform the MapView when the activity enters these states. Otherwise MapKit will keep using resources like a foreground application.

1@Override
2protected void onResume() {
3 super.onResume();
4 if (mMapView != null) {
5 mMapView.onResume();
6 }
7...
8
9@Override
10protected void onPause() {
11 super.onPause();
12 if (mMapView != null) {
13 mMapView.onPause();
14 }
15...

Accessing the API functionality

Below you will find some brief examples on how to achieve some of the functionality for your app.

Enabling and disabling zooming interaction

1mMapController.setZoomEnabled(true); // Allow zooming interaction.
2mMapController.setZoomEnabled(false); // Disallow zooming interaction.
3if (mMapController.isZoomEnabled()) {
4 // Zooming is enabled for the user.
5 ...
6}

Zooming the map programmatically

1final int minZoomLevel = mMapController.getMinZoomLevel();
2final int maxZoomLevel = mMapController.getMaxZoomLevel();
3final int zoomLevel = mMapController.getZoomLevel();
4setZoomLevel(6);

Getting the map scale

The scale of a map is the ratio between a distance on the screen to that distance in the world. So a scale of 1 means that there is a 1:1 ratio which in turn means that a centimeter on screen is a centimeter in reality. A scale of 500 means that there is a 1:500 ratio which means that a centimeter on screen is 500 centimeters in reality. The same goes for any unit: 1:500 means 1 inch is 500 inches in reality, for example. In short, the higher the scale value, the farther zoomed out the map will look on screen.

The zoom levels indicate how far zoomed in the map is, which means the higher the zoom level, the lower the scale value. For example, zoom level 20 is defined as scale 1:500, while zoom level 13 is scaling the map to 1:64,000. The default scale value is 1:8000, which is equivalent to zoom level 16.

Finding out at which scale the map is drawn, can be done using the getMapScale() function:

final int scale = mMapController.getMapScale();

Setting the map scale

(Please see the previous paragraph for an explanation of what a scale value means.)

Setting the scale of the map is fairly simple. You call setMapScale(scale) and the map will be drawn in that scale.

mMapController.setMapScale(8000);
final int actualScale = mMapController.getMapScale();

The scale value must fall within a range that can be supported by both the map renderer and the map itself; a typical range could be 500 to 225.966.736. The value that you're trying to set might therefore not be the scale that will be used in the end. To verify what scale you ended up with, check getMapScale() afterwards. Values that fall outside of the supported range, will be clamped to the nearest possible value. So trying to set the scale to 100 when the map only supports 500 and higher, will scale the map to 1:500.

Enabling and disabling panning interaction

1mMapController.setPanEnabled(true); // Allow panning interaction.
2mMapController.setPanEnabled(false); // Disallow panning interaction.
3if (mMapController.isPanEnabled()) { // Panning is enabled for the user.
4 ...
5}

Enabling and disabling autocenter

1mMapController.setAutoCenterEnabled(true); // Enables autocenter.
2mMapController.setAutoCenterEnabled(false); // Disables autocenter.
3if (mMapController.isAutoCenterEnabled()) { // Autocenter is enabled.
4 ...
5}

Change map orientation

From version 16 (release 18.6) onward, you can determine the orientation of the map. Up until version 15, the map is always facing due North by default. Version 16 makes it possible to have the map align with the driving direction.

1mMapController.setMapOrientation(MapController.MAP_ORIENTATION.DRIVING_DIRECTION); // Align map to heading.
2...
3if (mMapController.getMapOrientation() == MapController.MAP_ORIENTATION.DRIVING_DIRECTION) { // Following device heading.
4 ...
5}
6mMapController.setMapOrientation(MapController.MAP_ORIENTATION.NORTH_UP); // Back to default behaviour.

Also note that with the map orientation set to DRIVING_DIRECTION it is not possible to pan the map, and disabling autocenter will be ignored. Panning is only possible while the map is oriented NORTH_UP.

Please also be aware that the options to set map orientation are only available on NDS builds. On earlier builds these calls will have no effect, or simply return default values.

Panning and setting the map center

If the requested location is near to the currently viewed location, the map will pan towards the location. If the location is too far away for smooth panning, it will instantly display the location. You can center on a Location object or use latitude and longitude directly, as well as centering the map to the current GPS location.

Getting the current map center

This will return the current location the map view has been panned to. This is not necessarily the current location of the device.

final Location location = mMapController.getMapCenter();

Set map center using Location class

1final Location location = new Location("TomTom");
2location.setLatitude(52.3764293);
3location.setLongitude(4.908397);
4mMapController.setMapCenter(location);

Set map center using latitude and longitude

mMapController.setMapCenter(52.3764293, 4.908397);

Set map center using current location

mMapController.setMapCenterToCurrentLocation();

Auto-centering map

1mMapController.setAutoCenterEnabled(true); // Map will follow my current position.
2if (mMapController.isAutoCenterEnabled()) {
3 // The map is currently following my current position.
4 ...
5}

Using map markers

Markers are locations on the map that have a visual indicator, also known as push pins. Map library provides a means to add default and custom markers to the MapView that the user can interact with. You will be notified of touch events and can act on them using the information of the selected marker, which includes world location and screen position.

Markers will pan with the map to keep indicating the location they were placed at. When the map is zoomed, marker icons will stay the same size.

Adding a default marker

To add a marker using the default marker icon, you call the createMarkerAt function.

final Marker marker = mMapController.createMarkerAt(52.3764293, 4.908397);

Removal of markers is done through this call:

mMapController.removeMarker(marker);

Adding a marker

Markers can be grouped into layers, which can be individually hidden and shown. This allows for choosing which markers to show at which time. So in order to create a Marker you'll first need a MarkerLayer. With that layer you can create the markers that should be part of that layer.

final MarkerLayer layer = mMapController.createLayer();
final Marker marker = layer.createMarkerAt(52.3764293, 4.908397);

Removal of markers is done through this call:

layer.removeMarker(marker);

Giving a marker a custom icon

If the default icon is not what you want, you have the option to give each marker a different one. The Map library can read image files from internal/external storage and also accepts Drawable's.

The anchor point is the point on the icon that sticks to the location, or points to where the location of the marker is. The center of the icon is at (0, 0). The right side equals 1, while the left side equals -1; similar for the bottom and top sides. So, the bottom left corner of the icon would be at (-1, 1).

1// Using a file path.
2final String iconPath = "/absolute/path/to/an/icon.png";
3marker.setIcon(iconPath);
4marker.setAnchorPoint(-1, 1);
5...
6// Using a Drawable.
7marker.setIcon(getResources().getDrawable(R.drawable.marker_icon));

Query for markers

To obtain a list of all markers currently on the map, you call getMarkers. This will return a list of all(!) markers, not just the ones that are currently visible.

final List<Marker> markers = mMapController.getMarkers();

Query for markers

To obtain a list of all markers of a MarkerLayer, you call getMarkers. This will return a list of all the markers of that layer, not just the ones that are currently visible.

final List<Marker> markers = layer.getMarkers();

Using map layers

Layers are groups of visual elements, either stock or custom. Layers can be hidden or shown individually and they are considered as an ordered set for visualization and selection.

Stock layers

Stock layers are those defined in the StockLayers class. These layers contain predefined elements such as the route, traffic, etc. By default these stock layers are hidden. To change visibility of a stock layer, use the showLayer method.

1final MapController controller = mMapView.getMapController();
2controller.setLayerVisibility(StockLayers.ROUTE_LAYER, true);
3controller.setLayerVisibility(StockLayers.TRAFFIC_LAYER, true);

Renderable layers

A RenderableLayer is where Renderables, like Polylines, are stored so they will be drawn onto the map. MapController has one RenderableLayer that is always present and can be obtained by calling getRenderableLayer(). Since RenderableLayers are composites, it's possible to place one or more RenderableLayers inside another. Just create a new RenderableLayer and add it to an existing RenderableLayer.

Renderables have a Z-index property. The Z-index determines where in the layer this Renderable will be drawn, relative to other Renderables. A higher value means it will get drawn above any Renderable with a lower Z-index. The order in which Renderables with the same Z-index are drawn is undefined; if order is important, use the Z-index property to control the order. The default Z-index is zero.

When you do not need a Renderable any more, you need to free its allocated resources by calling Renderable.release().

Polylines

The Maplib SDK offers the ability to draw polylines on the map. A polyline is a continuous line composed of one or more line segments defined in GeoPoint’s, consisting of a latitude and a longitude value.

A PolylineBuilder object is used to create a new Polyline. You can add the points to it, and set other properties like line width and color. Next you add the polyline to a RenderableLayer. The line segments are drawn between the points in the order in which you added them to the PolylineBuilder.

The following code snippet illustrates how to add a polyline to a map:

1final RenderableLayer renderableLayer = mMapController.getRenderableLayer();
2renderableLayer.add(new PolylineProperties()
3 .color(Color.RED)
4 .width(5f)
5 .add(new GeoPoint(52.375663, 4.907869))
6 .add(new GeoPoint(52.377307, 4.900070))
7 .add(new GeoPoint(52.378617, 4.902816))
8 .build()
9 );

This will produce a red polyline on the map, 5 pixels wide, in the Amsterdam area. Note that this polyline will be opaque since the color is not given an alpha value. Transparent polylines need an alpha value less than the maximum. See the Android Color class for details.

To remove a Polyline from the map, use RenderableLayer.remove(Renderable) and use your Polyline as an argument.

Polygons

The Maplib SDK offers the ability to draw polygons on the map. A polygon is an enclosed shape that can be used to highlight areas on the map. The outline is a continuous line composed of one or more line segments defined in GeoPoint’s, consisting of a latitude and a longitude value. Polygons are self closing, which means that there is no need to repeat the first point at the end in order to get a closed shape; the line segment from the last point back to the first is implied.

Currently supported polygon types are convex and (weakly) simple concave polygons. Please note that self-intersecting polygons will not be filled but drawn as closed polylines.

A PolygonBuilder object is used to create a new Polygon. You can add the points to it, and set other properties like stroke width and fill color. The points should be added in counter clockwise order; the result is undefined when they are not. Next you add the polygon to a RenderableLayer.

To briefly explain counter clockwise points: if you imagine the hours on the face of a clock to be 12 points of a polygon, then the list of points will have to be added counter clockwise: 12, 11, ..., 2, 1. The starting point is irrelevant as long as the direction is correct. Another way to look at it is that the left side of each line segment is the interior of the polygon.

The following code snippet illustrates how to add a polygon to a map:

1final RenderableLayer renderableLayer = mMapController.getRenderableLayer();
2renderableLayer.add(new PolygonProperties()
3 .fillColor(Color.argb(127, 80, 80, 80))
4 .strokeColor(Color.BLUE)
5 .strokeWidth(7f)
6 .zIndex(2)
7 .add(new GeoPoint(52.376217, 4.907824))
8 .add(new GeoPoint(52.376225, 4.907883))
9 .add(new GeoPoint(52.376315, 4.908585))
10 .add(new GeoPoint(52.376466, 4.908602))
11 .add(new GeoPoint(52.376536, 4.908104))
12 .add(new GeoPoint(52.376476, 4.907687))
13 .build()
14 );

This will produce a gray polygon with a blue outline on the map, the maximum of 7 pixels wide, in the Amsterdam area. Because it is given a Z-index of 2, it will be drawn on top of any other Renderable with a Z-index of 1 or lower.

Also note that we get 50% transparency, by giving our fillColor an alpha value of 50%; 127 is about half of 255, the maximum value. The same can be done for the strokeColor.

To remove a Polygon from the map, use RenderableLayer.remove(Renderable) and use your Polygon as an argument.

Tiling images to fill polygons

By default, polygons are filled using a color but one can also use an image. This texture is a Drawable from your app's resources. Possible applications are zoning and spatial planning, or any other reason to indicate land use like no-go areas, range indication, permit zones, etc.

Textures are tiled horizontally and vertically, like the background of an HTML page, and they have the size of their original source, the Drawable. When zooming the map in or out, this size will remain the same; so the texture is not resized when the zoom level changes! This ensures that the tiles are always recognizable, instead of them getting smaller and smaller when you zoom out, for example.

The texture has its own alpha channel, with which you can make the polygon interior (partially) transparent. Changing it with setTextureAlpha(190) for example, which is 75% of 255, the fully opaque value, will let the map underneath your polygon shine through somewhat. Using a partially transparent Drawable will do the same, giving you the option to have completely transparent areas between partially transparent areas.

The texture can be made (partially) transparent. Changing it with setTextureAlpha(190) for example, which is 75% of 255, the fully opaque value, will let the map underneath your polygon shine through somewhat. The texture is drawn taking the source image's transparency information into account. This means that any transparent pixels in the original image will be just as transparent when used as a texture.

1final Polygon polygon = new PolygonBuilder()
2 .add(52.373620, 4.908018)
3 .add(52.373545, 4.909971)
4 .add(52.374750, 4.910250)
5 .add(52.374973, 4.908222)
6 .texture(this, R.drawable.dots)
7 .textureAlpha(190)
8 .build();
Image overlay

Polylines and polygons provide a way to draw shapes onto the map, that are relatively easy to define, since we are going from point to point which all have the same color. What if we want to show something more complex over an area on the map, like weather radar images, full featured floor plans for big buildings like shopping malls, or contour lines like isobars including gradients in between. Then we need to overlay that image on the map and dispense with points and lines.

The image is locked in place by two coordinates: the North-West and the South-East corner of the image. The image is rectangular and will be stretched across the map between the given locations. Non-rectangular shapes can be obtained by using transparency in the image itself, which is a separate value from the overall transparency of the entire image.

Like with the Polyline and Polygon, an ImageOverlay is constructed by using a builder: ImageOverlayBuilder. You set the required properties in the builder and let it create the ImageOverlay for you, which you can then add to a RenderableLayer so it will be displayed.

1final ImageOverlay overlay = new ImageOverlayBuilder()
2 .nw(52.3031428, 4.9479324)
3 .se(52.30177, 4.9510746)
4 .image(this, R.drawable.weather)
5 .alpha(190)
6 .build();

Listen for Map events

Touch events are processed by views. Therefore, listening for touch events in the Map Library is done by registering a MapListener with the MapView.

mMapView.addMapListener(new MyMapListener());

Your MapListener implementation can then respond to touch events on the map.

1private class MyMapListener implements MapListener {
2 @Override
3 void onMarkerSelected(final Marker marker, final TouchType touchType) {
4 ...
5 }
6
7 @Override
8 void onRenderableSelected(final Renderable renderable, final int x, final int y,
9 final TouchType touchType) {
10 ...
11 }
12
13 @Override
14 void onMapTouched(final int x, final int y, final TouchType touchType) {
15 ...
16 }
17
18 @Override
19 public void onDrag(final int prevX, final int prevY, final int newX, final int newY) {
20 ...
21 }
22}

onMarkerSelected, onRenderableSelected, onMapTouched functions will be called with:

TouchTypeWhen
SINGLE_TAPA single tap on a marker, renderable or map.
LONG_PRESS_BEGIN

The user is touching a marker, renderable or map for a prolonged time.

LONG_PRESS_END

The user stopped touching the marker, renderable or map for a prolonged time.

In order to obtain the screen position of that marker, you can use the LocationConverter. The fromLocation function, giving it the world location of that marker, will return a Point with the on screen position. You can use this to position popup menu's, for example.

Similiarly, to get the geopoints from screen position (x, y), also use the LocationConverter. For example:

final Location location = mMapController.getLocationConverter().toLocation(x, y);
final GeoPoint geoPoint = new GeoPoint(location);
  • onMarkerSelected() callback has the highest priority. If your marker and renderable (Polygon, Polyline, Image) overlap, then you will receive an onMarkerSelected() callback when you touch the overlapping area.
  • onRenderableSelected() is received only when the renderable is made selectable by setting setSelectable(true); Renderable's are not selectable by default. When selectable renderables overlap, the topmost renderable is returned by this callback.
  • onMapTouched() is received when there is no marker or renderable at the touch point.

Using Sensor Location

There are two ways to obtain the current location in your application. First is a map matched location which is calculated with a location provider's data and a map. Second is a non map matched location (Sensor Location) which is your absolute location without map influence.

Enabling Sensor Location

By default your application uses the map matched location. To enable Sensor Location you need use the following MapController method:

void setSensorLocationEnable(final boolean enable, final Drawable icon);
  • enabled - When true then map sensor location is enabled; when false, the matched location is used
  • icon - An icon to show the location as reported by the sensor. The icon must point to due north at the top of the image.

You can provide your own icon using a Drawable. This icon will change its orientation depending on the bearing, the top of the image pointing to the direction of travel.

Always disable the Sensor Location when not needed since listening to the location consumes more battery power.

1@Override
2public void onStart() {
3 super.onStart();
4 final MapController controller = mMapView.getMapController();
5 controller.setSensorLocationEnable(true);
6}
7...
8@Override
9public void onStop() {
10 super.onStop();
11 final MapController controller = mMapView.getMapController();
12 controller.setSensorLocationEnable(false);
13}

Retrieving Sensor Location State

To know the state of the current location provider you can use this MapController method:

boolean isSensorLocationEnabled();
  • true - The current location is continously set using the GNSS sensor information.
  • false - The current location is never set using the GNSS sensor information.