Skip to content

Commit

Permalink
Merge pull request #597 from openmobilemaps/2.0-RC
Browse files Browse the repository at this point in the history
2.0 rc
  • Loading branch information
maerki authored Feb 16, 2024
2 parents a907f7b + 33a370d commit 852a1d0
Show file tree
Hide file tree
Showing 41 changed files with 1,474 additions and 344 deletions.
221 changes: 137 additions & 84 deletions android/readme.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.openmobilemaps.mapscore.map.layers

import android.content.Context
import io.openmobilemaps.mapscore.map.loader.DataLoader
import io.openmobilemaps.mapscore.shared.map.layers.tiled.DefaultTiled2dMapLayerConfigs
import io.openmobilemaps.mapscore.shared.map.layers.tiled.Tiled2dMapLayerConfig
import io.openmobilemaps.mapscore.shared.map.layers.tiled.raster.Tiled2dMapRasterLayerInterface
import io.openmobilemaps.mapscore.shared.map.loader.LoaderInterface
import java.util.UUID

class TiledRasterLayer(layerConfig: Tiled2dMapLayerConfig, loaders: ArrayList<LoaderInterface>) {

/**
* Creates a default web-mercator (EPSG:3857) tiled raster layer with a custom LoaderInterface (e.g. a custom DataLoader)
*
* @param tileUrl Url to the tiles, formatted with placeholders. E.g. https://www.sample.org/{z}/{x}/{y}.png
* @param loader A loader interface for loading the layer tiles. E.g an instance of a DataLoader
* @param layerName Name of the created layer
*/
constructor(tileUrl: String, loader: LoaderInterface, layerName: String = UUID.randomUUID().toString()) : this(
DefaultTiled2dMapLayerConfigs.webMercator(layerName, tileUrl), arrayListOf(loader)
)

/**
* Creates a default web-mercator (EPSG:3857) tiled raster layer with a default DataLoader.
*
* @param context Android contex used for the default DataLoader
* @param tileUrl Url to the tiles, formatted with placeholders. E.g. https://www.sample.org/{z}/{x}/{y}.png
* @param layerName Name of the created layer
*/
constructor(context: Context, tileUrl: String, layerName: String = UUID.randomUUID().toString()) : this(
tileUrl, DataLoader(context, context.cacheDir, 50 * 1024 * 1024L), layerName
)

private val layer: Tiled2dMapRasterLayerInterface = Tiled2dMapRasterLayerInterface.create(layerConfig, loaders)

fun layerInterface() = layer.asLayerInterface()
fun rasterLayerInterface() = layer
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.openmobilemaps.mapscore.map.layers

import android.content.Context
import io.openmobilemaps.mapscore.map.loader.DataLoader
import io.openmobilemaps.mapscore.map.loader.FontLoader
import io.openmobilemaps.mapscore.shared.map.layers.tiled.vector.Tiled2dMapVectorLayerInterface
import io.openmobilemaps.mapscore.shared.map.loader.FontLoaderInterface
import io.openmobilemaps.mapscore.shared.map.loader.LoaderInterface
import java.util.UUID

class TiledVectorLayer(
styleUrl: String,
loaders: ArrayList<LoaderInterface>,
fontLoader: FontLoaderInterface,
name: String = UUID.randomUUID().toString()
) {

constructor(
context: Context,
styleUrl: String,
fontAssetFolder: String,
loaders: ArrayList<LoaderInterface> = arrayListOf(DataLoader(context, context.cacheDir, 50 * 1024L * 1024L))
) : this(styleUrl, loaders, FontLoader(context, fontAssetFolder))

constructor(
context: Context,
styleUrl: String,
loaders: ArrayList<LoaderInterface> = arrayListOf(DataLoader(context, context.cacheDir, 50 * 1024L * 1024L)),
fontLoader: FontLoaderInterface = FontLoader(context)
) : this(styleUrl, loaders, fontLoader)

private val layer = Tiled2dMapVectorLayerInterface.createFromStyleJson(name, styleUrl, loaders, fontLoader)

fun layerInterface() = layer.asLayerInterface()
fun vectorLayerInterface() = layer

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ open class DataLoader(
context: Context,
private var cacheDirectory: File,
private var cacheSize: Long,
private var referrer: String,
private var referrer: String = "",
private var userAgent: String = RequestUtils.getDefaultUserAgent(context)
) : LoaderInterface() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.openmobilemaps.mapscore.map.loader

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
Expand All @@ -10,29 +11,49 @@ import io.openmobilemaps.mapscore.shared.graphics.common.Quad2dD
import io.openmobilemaps.mapscore.shared.graphics.common.Vec2D
import io.openmobilemaps.mapscore.shared.map.loader.*

open class FontLoader(context: Context, private val dpFactor: Float) : FontLoaderInterface() {

private val moshi = Moshi.Builder().build();
private val moshiFontAdapter = moshi.adapter(FontDataJson::class.java)

private val fontMap: MutableMap<String, FontDataHolder> = mutableMapOf()

fun addFont(fontDataJson: String, fontAtlas: Bitmap) {
val fontJson =
moshiFontAdapter.fromJson(fontDataJson) ?: throw IllegalArgumentException("Invalid json format for font data!")
open class FontLoader(context: Context, private val dpFactor: Float = context.resources.displayMetrics.density) : FontLoaderInterface() {

/**
* Create a FontLoader that loads all fonts from the provided asset folder (e.g. "fonts/"). For each available font, there is
* expected to be a .json File with the glyph descriptions and a .png with the font texture.
*/
constructor(context: Context, fontAssetFolder: String, dpFactor: Float = context.resources.displayMetrics.density)
: this(context, dpFactor) {
val assetFiles = context.assets.list(fontAssetFolder)?.mapNotNull { it.split('.').firstOrNull() }?.toSet()?.toList() ?: return
assetFiles.forEach { fontPath ->
val jsonString =
context.resources.assets.open("$fontAssetFolder$fontPath.json").bufferedReader().use { it.readText() }
val fontAtlas = BitmapFactory.decodeStream(context.resources.assets.open("$fontAssetFolder$fontPath.png"))
addFont(jsonString, fontAtlas)
}
}

constructor(context: Context, fonts: List<FontDescription>, dpFactor: Float = context.resources.displayMetrics.density)
: this(context, dpFactor) {
fonts.forEach { font -> addFont(font.fontJson, font.fontTexture) }
}

private val moshi = Moshi.Builder().build();
private val moshiFontAdapter = moshi.adapter(FontDataJson::class.java)

private val fontMap: MutableMap<String, FontDataHolder> = mutableMapOf()

fun addFont(fontDataJson: String, fontAtlas: Bitmap) {
val fontJson =
moshiFontAdapter.fromJson(fontDataJson) ?: throw IllegalArgumentException("Invalid json format for font data!")

val imageSize = fontJson.common.scaleW.toDouble()
val size = fontJson.info.size.toDouble()

val fontWrapper = FontWrapper(
fontJson.info.face,
fontJson.common.lineHeight.toDouble() / size,
fontJson.common.base.toDouble() / size,
Vec2D(imageSize, imageSize),
size * dpFactor
)
val fontWrapper = FontWrapper(
fontJson.info.face,
fontJson.common.lineHeight.toDouble() / size,
fontJson.common.base.toDouble() / size,
Vec2D(imageSize, imageSize),
size * dpFactor
)

val glyphs = fontJson.chars.map { glyphEntry ->
val glyphs = fontJson.chars.map { glyphEntry ->
var s0 = glyphEntry.x.toDouble()
var s1 = s0 + glyphEntry.width.toDouble()
var t0 = glyphEntry.y.toDouble()
Expand All @@ -47,60 +68,60 @@ open class FontLoader(context: Context, private val dpFactor: Float) : FontLoade
val advance = Vec2D(glyphEntry.xadvance.toDouble() / size, 0.0)
val bbox = Vec2D(glyphEntry.width.toDouble() / size, glyphEntry.height.toDouble() / size)

FontGlyph(
glyphEntry.char,
advance,
bbox,
bearing,
Quad2dD(
Vec2D(s0, t1),
Vec2D(s1, t1),
Vec2D(s1, t0),
Vec2D(s0, t0)
)
)
}.toCollection(ArrayList())
fontMap[fontJson.info.face] = FontDataHolder(BitmapTextureHolder(fontAtlas), FontData(fontWrapper, glyphs))
}

fun addFont(fontData: FontData, fontAtlas: BitmapTextureHolder) {
fontMap[fontData.info.name] = FontDataHolder(fontAtlas, fontData)
}

override fun loadFont(font: Font): FontLoaderResult {
val fontDataHolder = fontMap[font.name]
return if (fontDataHolder != null) {
FontLoaderResult(fontDataHolder.fontTexture, fontDataHolder.fontData, LoaderStatus.OK)
} else {
Log.e(FontLoader::class.java.canonicalName, "Couldn't load font name: ${font.name}!")
FontLoaderResult(null, null, LoaderStatus.ERROR_OTHER)
}
}

private data class FontDataHolder(val fontTexture: BitmapTextureHolder, val fontData: FontData)

@JsonClass(generateAdapter = true)
data class FontDataJson(
val chars: List<FontGlyphJsonData>,
FontGlyph(
glyphEntry.char,
advance,
bbox,
bearing,
Quad2dD(
Vec2D(s0, t1),
Vec2D(s1, t1),
Vec2D(s1, t0),
Vec2D(s0, t0)
)
)
}.toCollection(ArrayList())
fontMap[fontJson.info.face] = FontDataHolder(BitmapTextureHolder(fontAtlas), FontData(fontWrapper, glyphs))
}

fun addFont(fontData: FontData, fontAtlas: BitmapTextureHolder) {
fontMap[fontData.info.name] = FontDataHolder(fontAtlas, fontData)
}

override fun loadFont(font: Font): FontLoaderResult {
val fontDataHolder = fontMap[font.name]
return if (fontDataHolder != null) {
FontLoaderResult(fontDataHolder.fontTexture, fontDataHolder.fontData, LoaderStatus.OK)
} else {
Log.e(FontLoader::class.java.canonicalName, "Couldn't load font name: ${font.name}!")
FontLoaderResult(null, null, LoaderStatus.ERROR_OTHER)
}
}

private data class FontDataHolder(val fontTexture: BitmapTextureHolder, val fontData: FontData)

@JsonClass(generateAdapter = true)
data class FontDataJson(
val chars: List<FontGlyphJsonData>,
val pages: List<String>, // unused
val info: FontInfoData,
val common: FontCommonData
)

@JsonClass(generateAdapter = true)
data class FontGlyphJsonData(
val id: Int,
val index: Int,
val char: String,
val width: Int,
val height: Int,
val xoffset: Int,
val yoffset: Int,
val xadvance: Int,
val chnl: Int, // unused
val x: Int,
val y: Int
)
val common: FontCommonData,
)

@JsonClass(generateAdapter = true)
data class FontGlyphJsonData(
val id: Int,
val index: Int,
val char: String,
val width: Int,
val height: Int,
val xoffset: Int,
val yoffset: Int,
val xadvance: Int,
val chnl: Int, // unused
val x: Int,
val y: Int,
)

@JsonClass(generateAdapter = true)
data class FontInfoData(
Expand All @@ -114,7 +135,7 @@ open class FontLoader(context: Context, private val dpFactor: Float) : FontLoade
val aa: Int, // unused
val padding: List<Int>,
val spacing: List<Int>,
val outline: Int
val outline: Int,
)

@JsonClass(generateAdapter = true)
Expand All @@ -130,12 +151,14 @@ open class FontLoader(context: Context, private val dpFactor: Float) : FontLoade
val blueChnl: Int, // unused
val greenChnl: Int, // unused
val distanceField: DistanceFieldData? = null, // unused
val kernings: List<Int>? = null // unused
val kernings: List<Int>? = null, // unused
)

@JsonClass(generateAdapter = true)
data class DistanceFieldData(
val fieldType: String,
val distanceRange: Int
val distanceRange: Int,
)

data class FontDescription(val fontJson: String, val fontTexture: Bitmap)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import io.openmobilemaps.mapscore.graphics.GlTextureView
import io.openmobilemaps.mapscore.map.layers.TiledRasterLayer
import io.openmobilemaps.mapscore.map.layers.TiledVectorLayer
import io.openmobilemaps.mapscore.map.scheduling.AndroidSchedulerCallback
import io.openmobilemaps.mapscore.map.util.MapViewInterface
import io.openmobilemaps.mapscore.map.util.SaveFrameCallback
Expand Down Expand Up @@ -183,10 +185,26 @@ open class MapView @JvmOverloads constructor(context: Context, attrs: AttributeS
requireMapInterface().addLayer(layer)
}

fun addLayer(layer: TiledRasterLayer) {
requireMapInterface().addLayer(layer.layerInterface())
}

fun addLayer(layer: TiledVectorLayer) {
requireMapInterface().addLayer(layer.layerInterface())
}

override fun insertLayerAt(layer: LayerInterface, at: Int) {
requireMapInterface().insertLayerAt(layer, at)
}

fun insertLayerAt(layer: TiledRasterLayer, at: Int) {
requireMapInterface().insertLayerAt(layer.layerInterface(), at)
}

fun insertLayerAt(layer: TiledVectorLayer, at: Int) {
requireMapInterface().insertLayerAt(layer.layerInterface(), at)
}

override fun insertLayerAbove(layer: LayerInterface, above: LayerInterface) {
requireMapInterface().insertLayerAbove(layer, above)
}
Expand All @@ -199,6 +217,15 @@ open class MapView @JvmOverloads constructor(context: Context, attrs: AttributeS
requireMapInterface().removeLayer(layer)
}

fun removeLayer(layer: TiledRasterLayer) {
requireMapInterface().removeLayer(layer.layerInterface())
}

fun removeLayer(layer: TiledVectorLayer) {
requireMapInterface().removeLayer(layer.layerInterface())
}


override fun getCamera(): MapCamera2dInterface {
return requireMapInterface().getCamera()
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 852a1d0

Please sign in to comment.