Map | Mobile SDK | 2GIS Documentation

Map

To display a map, add a MapView to your activity:

<ru.dgis.sdk.map.MapView
    android:id="@+id/mapView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:dgis_cameraTargetLat="55.740444"
    app:dgis_cameraTargetLng="37.619524"
    app:dgis_cameraZoom="16.0"
/>

You can specify starting coordinates (cameraTargetLat for latitude and cameraTargetLng for longitude) and zoom level (cameraZoom).

MapView can also be created programmatically. In that case, you can specify starting coordinates and other settings as a MapOptions object.

To get the Map object, you can call the getMapAsync() method of MapView:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val sdkContext = DGis.initialize(applicationContext, apiKeys)
    setContentView(R.layout.activity_main)

    val mapView = findViewById<MapView>(R.id.mapView)
    lifecycle.addObserver(mapView)

    mapView.getMapAsync { map ->
        // Access map properties
        val camera = map.camera
    }
}

In some cases, to add objects to the map, you need to create a special object - a data source. Data sources act as object managers: instead of adding objects to the map directly, you add a data source to the map and add/remove objects from the data source.

There are different types of data sources: moving markers, routes that display current traffic condition, custom geometric shapes, etc. Each data source type has a corresponding class.

The general workflow of working with data sources looks like this:

// Create a data source
val source = MyMapObjectSource(
    sdkContext,
    ...
)

// Add the data source to the map
map.addSource(source)

// Add and remove objects from the data source
source.addObject(...)
source.removeObject(...)

To remove a data source and all objects associated with it from the map, call the removeSource() method:

map.removeSource(source)

You can get the list of all active data sources using the map.sources property.

To add dynamic objects to the map (such as markers, lines, circles, and polygons), you must first create a MapObjectManager object, specifying the map instance. Deleting an object manager removes all associated objects from the map, so do not forget to save it in activity.

mapObjectManager = MapObjectManager(map)

After you have created an object manager, you can add objects to the map using the addObject() and addObjects() methods. For each dynamic object, you can specify a userData field to store arbitrary data. Object settings can be changed after their creation.

To remove objects from the map, use removeObject() and removeObjects(). To remove all objects, call the removeAll() method.

To add a marker to the map, create a Marker object, specifying the required options, and pass it to the addObject() method of the object manager.

The only required parameter is the coordinates of the marker (position).

val marker = Marker(
    MarkerOptions(
        position = GeoPointWithElevation(
            latitude = 55.752425,
            longitude = 37.613983
        )
    )
)

mapObjectManager.addObject(marker)

To change the marker icon, specify an Image object as the icon parameter. You can create Image using the following functions:

val icon = imageFromResource(sdkContext, R.drawable.ic_marker)

val marker = Marker(
    MarkerOptions(
        position = GeoPointWithElevation(
            latitude = 55.752425,
            longitude = 37.613983
        ),
        icon = icon
    )
)

To change the hotspot of the icon, use the anchor parameter.

You can also set the text for the marker and other options (see MarkerOptions).

To draw a line on the map, create a Polyline object, specifying the required options, and pass it to the addObject() method of the object manager.

In addition to the coordinates of the line points, you can set the line width, color, stroke type, and other options (see PolylineOptions).

// Coordinates of the vertices of the polyline
val points = listOf(
    GeoPoint(latitude = 55.7513, longitude = 37.6236),
    GeoPoint(latitude = 55.7405, longitude = 37.6235),
    GeoPoint(latitude = 55.7439, longitude = 37.6506)
)

// Creating a Polyline object
val polyline = Polyline(
    PolylineOptions(
        points = points,
        width = 2.lpx
    )
)

// Adding the polyline to the map
mapObjectManager.addObject(polyline)

Extension property .lpx in the example above converts an integer to a LogicalPixel object.

To draw a polygon on the map, create a Polygon object, specifying the required options, and pass it to the addObject() method of the object manager.

Coordinates for the polygon are specified as a two-dimensional list. The first sublist must contain the coordinates of the vertices of the polygon itself. The other sublists are optional and can be specified to create a cutout (a hole) inside the polygon (one sublist - one polygonal cutout).

Additionally, you can specify the polygon color and stroke options (see PolygonOptions).

val polygon = Polygon(
    PolygonOptions(
        contours = listOf(
            // Vertices of the polygon
            listOf(
                GeoPoint(latitude = 55.72014932919687, longitude = 37.562599182128906),
                GeoPoint(latitude = 55.72014932919687, longitude = 37.67555236816406),
                GeoPoint(latitude = 55.78004852149085, longitude = 37.67555236816406),
                GeoPoint(latitude = 55.78004852149085, longitude = 37.562599182128906),
                GeoPoint(latitude = 55.72014932919687, longitude = 37.562599182128906)
            ),
            // Cutout inside the polygon
            listOf(
                GeoPoint(latitude = 55.754167897761, longitude = 37.62422561645508),
                GeoPoint(latitude = 55.74450654680055, longitude = 37.61238098144531),
                GeoPoint(latitude = 55.74460317215391, longitude = 37.63435363769531),
                GeoPoint(latitude = 55.754167897761, longitude = 37.62422561645508)
            )
        ),
        borderWidth = 1.lpx
    )
)

mapObjectManager.addObject(polygon)

To add markers to the map in clustering mode, you must create a MapObjectManager object using MapObjectManager.withClustering(), specifying the map instance, distance between clusters in logical pixels, maximum value of zoom-level, when MapObjectManager in clustering mode, and user implementation of the protocol SimpleClusterRenderer. SimpleClusterRenderer is used to customize clusters in MapObjectManager.

val clusterRenderer = object : SimpleClusterRenderer {
    override fun renderCluster(cluster: SimpleClusterObject): SimpleClusterOptions {
        val textStyle = TextStyle(
            fontSize = LogicalPixel(15.0f),
            textPlacement = TextPlacement.RIGHT_TOP
        )
        val objectCount = cluster.objectCount
        val iconMapDirection = if (objectCount < 5) MapDirection(45.0) else null
        return SimpleClusterOptions(
            icon,
            iconWidth = LogicalPixel(30.0f),
            text = objectCount.toString(),
            textStyle = textStyle,
            iconMapDirection = iconMapDirection,
            userData = objectCount.toString()
        )
    }
}

mapObjectManager = MapObjectManager.withClustering(map, LogicalPixel(80.0f), Zoom(18.0f), clusterRenderer)

You can control the camera by accessing the map.camera property. See the Camera object for a full list of available methods and properties.

You can change the position of the camera by calling the move() method, which initiates a flight animation. This method has three parameters:

  • position - new camera position (coordinates and zoom level). Additionally, you can specify the camera tilt and rotation (see CameraPosition).
  • time - flight duration in seconds as a Duration object.
  • animationType - type of animation to use (CameraAnimationType).

The call will return a Future object, which can be used to handle the animation finish event.

val mapView = findViewById<MapView>(R.id.mapView)

mapView.getMapAsync { map ->
    val cameraPosition = CameraPosition(
        point = GeoPoint(latitude = 55.752425, longitude = 37.613983),
        zoom = Zoom(16.0),
        tilt = Tilt(25.0),
        bearing = Arcdegree(85.0)
    )

    map.camera.move(cameraPosition, Duration.ofSeconds(2), CameraAnimationType.LINEAR).onResult {
        Log.d("APP", "Camera flight finished.")
    }
}

You can use the .seconds extension to specify the duration of the flight:

map.camera.move(cameraPosition, 2.seconds, CameraAnimationType.LINEAR)

For more precise control over the flight, you can create a flight controller that will determine the camera position at any given moment. To do this, implement the CameraMoveController interface and pass the created object to the move() method instead of the three parameters described previously.

The current state of the camera (i.e., whether the camera is currently in flight) can be obtained using the state property. See CameraState for a list of possible camera states.

val currentState = map.camera.state

You can subscribe to changes of camera state using the stateChannel property.

// Subscribe to camera state changes
val connection = map.camera.stateChannel.connect { state ->
    Log.d("APP", "Camera state has changed to ${state}")
}

// Unsubscribe when it's no longer needed
connection.close()

The current position of the camera can be obtained using the position property (see CameraPosition).

val currentPosition = map.camera.position

Log.d("APP", "Coordinates: ${currentPosition.point}")
Log.d("APP", "Zoom level: ${currentPosition.zoom}")
Log.d("APP", "Tilt: ${currentPosition.tilt}")
Log.d("APP", "Rotation: ${currentPosition.bearing}")

You can subscribe to changes of camera position using the positionChannel property.

// Subscribe to camera position changes
val connection = map.camera.positionChannel.connect { position ->
    Log.d("APP", "Camera position has changed (coordinates, zoom level, tilt, or rotation).")
}

// Unsubscribe when it's no longer needed
connection.close()

You can add a special marker to the map that will be automatically updated to reflect the current location of the device. To do this, create a MyLocationMapObjectSource data source and add it to the map.

// Create the data source
val source = MyLocationMapObjectSource(
    sdkContext,
    MyLocationDirectionBehaviour.FOLLOW_SATELLITE_HEADING,
    createSmoothMyLocationController()
)

// Add the data source to the map
map.addSource(source)

You can get information about map objects using pixel coordinates. For this, call the getRenderedObjects() method of the map and specify the pixel coordinates and the radius in screen millimeters. The method will return a deferred result (Future) containing information about all found objects within the specified radius on the visible area of the map (a list of RenderedObjectInfo).

An example of a function that takes tap coordinates and passes them to getRenderedObjects():

override fun onTap(point: ScreenPoint) {
    map.getRenderedObjects(point, ScreenDistance(5f)).onResult { renderedObjectInfos ->
        // First list object is the closest to the coordinates
        for (renderedObjectInfo in renderedObjectInfos) {
            Log.d("APP", "Arbitrary object data: ${renderedObjectInfo.item.item.userData}")
        }
    }
}