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
- 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.MapView3 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 FragmentActivity2 implements MapEventCallbacks {3 ...4}
Initialise the MapView object in your application and request a MapController instance with the getMapControllerAsync call:
1@Override2protected void onCreate(final Bundle savedInstanceState) {3 super.onCreate(savedInstanceState);4 setContentView(R.layout.activity_main);56 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@Override2public void onMapConnected(final MapController controller) {3 mMapController = controller;4}56@Override7public 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@Override2protected void onResume() {3 super.onResume();4 if (mMapView != null) {5 mMapView.onResume();6 }7...89@Override10protected 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 Renderable
s, like Polyline
s, 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 RenderableLayer
s are composites, it's possible to place one
or more RenderableLayer
s inside another. Just create a new RenderableLayer
and add it to an
existing RenderableLayer
.
Renderable
s have a Z-index property. The Z-index determines where in the layer this Renderable
will be drawn, relative to other Renderable
s. A higher value means it will get drawn above
any Renderable
with a lower Z-index. The order in which Renderable
s 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 @Override3 void onMarkerSelected(final Marker marker, final TouchType touchType) {4 ...5 }67 @Override8 void onRenderableSelected(final Renderable renderable, final int x, final int y,9 final TouchType touchType) {10 ...11 }1213 @Override14 void onMapTouched(final int x, final int y, final TouchType touchType) {15 ...16 }1718 @Override19 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:
TouchType | When |
---|---|
SINGLE_TAP | A 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 anonMarkerSelected()
callback when you touch the overlapping area.onRenderableSelected()
is received only when the renderable is made selectable by settingsetSelectable(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; whenfalse
, 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@Override2public void onStart() {3 super.onStart();4 final MapController controller = mMapView.getMapController();5 controller.setSensorLocationEnable(true);6}7...8@Override9public 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.