Make sure you have all submodules initialized and updated. To do this, use
git submodule init
git submodule update
The bridging interface between Kotlin and C++ are defined in the djinni files under djinni. After modifying those files the new bridging code can be generated by running
make clean djinni
in the folder djinni. This generates the Kotlin bindings, the C++ header files as well as all the JNI glue code.
The Android library can be opened as an Android Studio project in the android folder. Alternatively, you can build the library using gradle from the command line using
./gradlew assemble
and you find the generated .aar library under build/outputs/aar/. For development, it is easiest to include this library directly as a module into a wrapping Android application project, that can be run on a device.
This library is available on MavenCentral. To add it to your Android project, add the following line to your build.gradle
dependencies {
implementation 'io.openmobilemaps:mapscore:1.3.3'
}
Make sure you have mavenCentral() listed in your project repositories.
Alternatively, either include the android folder as a module in the Android project or move the generated .aar to e.g. the app/libs folder of the project and include it in the project via the apps build.gradle:
implementation fileTree(dir: 'libs', include: ['*.aar'])
When Open Maps Mobile is included as .aar (and not as JCenter dependency), the following dependencies in the apps build.gradle are necessary:
implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2"
implementation 'com.squareup.okhttp3:okhttp:4.7.2' // used for the default TextureLoader
To use the library, it needs to be initialized as early as possible, e.g. in the oOnCreate(), of the hosting Application by calling:
MapsCore.initialize()The main ui element to use Open Mobile Maps is the MapView provided with it. To use it, the following steps are necessary to set it up:
val mapView = findViewById<MapView>(R.id.mapView)
mapView.setupMap(MapConfig(...))
mapView.registerLifecycle(lifecycle)The map needs to be initialized with a MapConfig, which specifies the coordinate system used and the respective bounds and zoom levels that the camera operates in. Additionally, the MapView needs to be registered to a lifecycle that also provides a coroutine context, in which the maps tasks can be scheduled (e.g. for tile loading).
A standard use-case is to display content from a tile-server as a layer in the map, e.g. one containing data from OpenStreetMap. In this example case, the map is a projection in the EPSG:3857 system. Thus the MapView is recommended to be initialized with a matching MapConfig.
mapView.setupMap(MapConfig(CoordinateSystemFactory.getEpsg3857System()))To display the tiles, a Tiled2dMapRasterLayer must be created with both a Tiled2dMapLayerConfig and the implementation of a TextureLoader.
val tiledLayer = Tiled2dMapRasterLayerInterface.create(layerConfig, textureLoader)Open Maps Mobile provides a default implementation for a TextureLoader, which uses OkHttp to load a bitmap from a given URL. Of course, a custom implementation of the TextureLoaderInterface can be used as well.
val textureLoader = TextureLoader(this, cacheDir, 50L * 1024L * 1024L)The LayerConfig contains the information needed for the layer to compute the visible tiles in the current camera configuration, as well as to load and display them.
val layerConfig = object : Tiled2dMapLayerConfig() {
// Defines the bounds of the layer and implicitly the coordinate system used by the layer as well
val epsg3857Bounds: RectCoord = RectCoord(
Coord(CoordinateSystemIdentifiers.EPSG3857(), -20037508.34, 20037508.34, 0.0),
Coord(CoordinateSystemIdentifiers.EPSG3857(), 20037508.34, -20037508.34, 0.0)
)
// Defines to map coordinate system of the layer
override fun getCoordinateSystemIdentifier() : String = CoordinateSystemIdentifiers.EPSG3857()
// Name of the layer
override fun getLayerName(): String = "OSMLayer"
// Defines the url-pattern to load tiles. Enter a valid OSM tile server here
override fun getTileUrl(x: Int, y: Int, zoom: Int): String =
"https://add-osm-web-server-address-here/$zoom/$x/$y.png"
// Defines both an additional scale factor for the tiles, as well as how many
// layers above the ideal one should be loaded an displayed as well.
override fun getZoomInfo(): Tiled2dMapZoomInfo = Tiled2dMapZoomInfo(1.2f, 2)
// List of valid zoom-levels and their target zoom-value, the tile size in
// the layers coordinate system, the number of tiles on that level and the
// zoom identifier used for the tile-url (see getTileUrl above)
override fun getZoomLevelInfos(): ArrayList<Tiled2dMapZoomLevelInfo> = ArrayList(
listOf(
Tiled2dMapZoomLevelInfo(500000000.0, 40075016f, 1, 1, 0, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(250000000.0, 20037508f, 2, 2, 1, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(150000000.0, 10018754f, 4, 4, 2, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(70000000.0, 5009377.1f, 8, 8, 3, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(35000000.0, 2504688.5f, 16, 16, 4, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(15000000.0, 1252344.3f, 32, 32, 5, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(10000000.0, 626172.1f, 64, 64, 6, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(4000000.0, 313086.1f, 128, 128, 7, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(2000000.0, 156543f, 256, 256, 8, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(1000000.0, 78271.5f, 512, 512, 9, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(500000.0, 39135.8f, 1024, 1024, 10, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(250000.0, 19567.9f, 2048, 2048, 11, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(150000.0, 9783.94f, 4096, 4096, 12, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(70000.0, 4891.97f, 8192, 8192, 13, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(35000.0, 2445.98f, 16384, 16384, 14, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(15000.0, 1222.99f, 32768, 32768, 15, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(8000.0, 611.496f, 65536, 65536, 16, epsg3857Bounds, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(4000.0, 305.748f, 131072, 131072, 17, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(2000.0, 152.874f, 262144, 262144, 18, epsg3857Bounds),
Tiled2dMapZoomLevelInfo(1000.0, 76.437f, 524288, 524288, 19, epsg3857Bounds)
)
)
}
}Finally, the layer can be added to the MapView.
mapView.addLayer(tiledLayer.asLayerInterface())Open Mobile Maps supports the WMTS standard and can parse their Capability XML file to generate raster layer configurations.
val ressource = WmtsCapabilitiesResource.create(xml)The created resource object is then capable of creating a layer object with a given identifier.
val layer = ressource.createLayer("identifier", textureLoader)
mapView.addLayer(layer.asLayerInterface())This feature is still being improved to support a wider range of WMTS capabilities.
Open Mobile Maps provides a simple interface to create a polygon layer. The layer handles the rendering of the given polygons and calls the callback handler in case of user interaction.
val polygonLayer = PolygonLayerInterface.create()
polygonLayer.add(
PolygonInfo(
"Polygon",
/* Coordinates */,
/* holes */,
false,
Color(1.0f, 0.0f, 0.0f, 1.0f),
Color(1.0f, 0.4f, 0.4f, 1.0f),
)
)
polygonLayer.setCallbackHandler(object : PolygonLayerCallbackInterface(){
override fun onClickConfirmed(polygon: PolygonInfo) {
// React
}
})
mapView.addLayer(polygonLayer.asLayerInterface())A simple icon layer is implemented as well. This supports displaying textures at the given coordinates. A scale parameter has to be provided which specifies how the icon should be affected by camera movements. In case of user interaction, the given callback handler will be called.
val iconLayer = IconLayerInterface.create()
val texture = BitmapTextureHolder(/* drawable or bitmap */)
val iconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48f, resources.displayMetrics)
val icon = IconFactory.createIcon(
"Icon",
coordinate,
texture,
Vec2F(iconSize, iconSize),
IconType.INVARIANT)
iconLayer.add(icon)
iconLayer.setCallbackHandler(object : IconLayerCallbackInterface(){
override fun onClickConfirmed(icons: ArrayList<IconInfoInterface>): Boolean {
// React and return true if handled
return true
}
})
mapView.addLayer(iconLayer.asLayerInterface())A line layer can be added to the mapView as well. Using the MCLineFactory a LineInfo object can be created. The width can be specified in either SCREEN_PIXEL or MAP_UNIT.
val lineLayer = LineLayerInterface.create()
val line = LineFactory.createLine(
"lineIdentifier",
lineCoordinates,
LineStyle(
ColorStateList(normal = Color(1.0f, 0.0f, 0.0f, 0.5f), highlighted = Color(1.0f, 0.5f, 0.0f, 0.5f)),
SizeType.SCREEN_PIXEL,
50.0f
)
)
lineLayer.add(line)
mapView.addLayer(lineLayer.asLayerInterface())The camera position and zoom can easily be adjusted by manipulating the Camera2dInterface received from the map. For example, to set a custom location:
mapView.getCamera().moveToCenterPositionZoom(
Coord(CoordinateSystemIdentifiers.EPSG4326(), 8.378232525377973, 46.962592372639634, 0.0),
10000000.0, false)In the camera, one can also override the default zoom limits with:
mapView.getCamera().setMinZoom(5000000.0)
mapView.getCamera().setMaxZoom(300.0)This project is licensed under the terms of the MPL 2 license. See the LICENSE file.