Loading cardinal-android/app/detekt.yml +4 −0 Original line number Diff line number Diff line Loading @@ -5,4 +5,8 @@ naming: complexity: LongParameterList: active: true ignoreDefaultParameters: true CognitiveComplexMethod: active: true excludes: [ '**/cardinal/bottomsheet/**' ] cardinal-android/app/src/main/java/earth/maps/cardinal/tileserver/LocalMapServer.kt +210 −213 Original line number Diff line number Diff line Loading @@ -84,13 +84,54 @@ class LocalMapServer( // Find an available port val availablePort = findAvailablePort() server = embeddedServer(CIO, port = availablePort, host = "127.0.0.1") { server = createEmbeddedServer(availablePort) server?.start(wait = false) port = availablePort Log.d(TAG, "Tile server started on port: $port") } fun stop() { server?.stop(1000, 5000) server = null port = -1 // Close the databases. terrainDatabase?.close() terrainDatabase = null landcoverDatabase?.close() landcoverDatabase = null basemapDatabase?.close() basemapDatabase = null offlineAreasDatabase?.close() offlineAreasDatabase = null Log.d(TAG, "Tile server stopped") } fun getPort(): Int { return port } private fun createEmbeddedServer(port: Int) = embeddedServer(CIO, port = port, host = "127.0.0.1") { routing { get("/") { handleRoot() handleStyleLight() handleStyleDark() handleRoute() handleTerrainTile() handleLandcoverTile() handleOpenMapTiles() } } private fun io.ktor.server.routing.Routing.handleRoot() = get("/") { call.respondText("Tile Server is running!") } get("/style_light.json") { private fun io.ktor.server.routing.Routing.handleStyleLight() = get("/style_light.json") { try { val styleJson = readAssetFile("style_light.json") val modifiedStyleJson = styleJson.replace("{port}", port.toString()) Loading @@ -106,7 +147,7 @@ class LocalMapServer( } } get("/style_dark.json") { private fun io.ktor.server.routing.Routing.handleStyleDark() = get("/style_dark.json") { try { val styleJson = readAssetFile("style_dark.json") val modifiedStyleJson = styleJson.replace("{port}", port.toString()) Loading @@ -122,8 +163,7 @@ class LocalMapServer( } } // Valhalla-compatible routing endpoint post("/route") { private fun io.ktor.server.routing.Routing.handleRoute() = post("/route") { try { val requestBody = call.receiveText() Log.d(TAG, "Received routing request: $requestBody") Loading @@ -145,7 +185,7 @@ class LocalMapServer( } } // Serve tiles from terrain.mbtiles. private fun io.ktor.server.routing.Routing.handleTerrainTile() = get("/terrain/{z}/{x}/{y}.png") { val terrainDatabase = terrainDatabase ?: return@get Loading Loading @@ -176,8 +216,7 @@ class LocalMapServer( } } // Serve tiles from landcover.mbtiles. private fun io.ktor.server.routing.Routing.handleLandcoverTile() = get("/landcover/{z}/{x}/{y}.pbf") { val landcoverDatabase = landcoverDatabase ?: return@get Loading Loading @@ -209,7 +248,7 @@ class LocalMapServer( } } // Serve tiles from basemap.mbtiles. private fun io.ktor.server.routing.Routing.handleOpenMapTiles() = get("/openmaptiles/{z}/{x}/{y}.pbf") { val z = call.parameters["z"]?.toIntOrNull() val x = call.parameters["x"]?.toLongOrNull() Loading @@ -223,33 +262,7 @@ class LocalMapServer( return@get } Log.d(TAG, "Requesting tile: /openmaptiles/$z/$x/$y.pbf") // MBTiles uses TMS (Tile Map Service) coordinate system where Y=0 is at the bottom // Most map libraries use XYZ coordinate system where Y=0 is at the top // Convert Y coordinate from XYZ to TMS: TMS_Y = 2^zoom - 1 - XYZ_Y val tmsY = (2.0.pow(z.toDouble()) - 1 - y).toLong() var isGzipped = true val basemapDatabase = basemapDatabase // First try to get tile from built-in database var tileData = if (basemapDatabase != null) { getTileData(basemapDatabase, z, x, tmsY) } else { Log.w(TAG, "Basemap database is null") null } // If not found, try offline databases if (tileData == null) { Log.d(TAG, "Tile not found in basemap database, checking offline databases") tileData = getTileDataFromOfflineDatabases(z, x, y) isGzipped = false } else { Log.d(TAG, "Tile found in basemap database") } val (tileData, isGzipped) = findTileData(z, x, y) if (tileData != null) { Log.d( Loading @@ -262,30 +275,6 @@ class LocalMapServer( } call.respondBytes(tileData, contentType = ContentType.Application.ProtoBuf) } else { // Check if we should fetch from the internet (not in offline mode) val isOfflineMode = isOfflineMode() if (!isOfflineMode) { Log.d( TAG, "Tile not found in local caches, attempting to fetch from internet" ) tileData = CoroutineScope(Dispatchers.IO).async { fetchTileFromInternet( z, x, y ) }.await() if (tileData != null) { Log.d( TAG, "Successfully fetched tile from internet: /openmaptiles/$z/$x/$y.pbf, size: ${tileData.size} bytes" ) call.respondBytes( tileData, contentType = ContentType.Application.ProtoBuf ) return@get } } Log.d(TAG, "Tile not found: /openmaptiles/$z/$x/$y.pbf") // If we respond with NotFound here (as would make sense) it will cause maplibre to cache the // fact that this tile doesn't exist in this source, which we don't want because it will never Loading @@ -297,36 +286,6 @@ class LocalMapServer( ) } } } } server?.start(wait = false) port = availablePort Log.d(TAG, "Tile server started on port: $port") } fun stop() { server?.stop(1000, 5000) server = null port = -1 // Close the databases. terrainDatabase?.close() terrainDatabase = null landcoverDatabase?.close() landcoverDatabase = null basemapDatabase?.close() basemapDatabase = null offlineAreasDatabase?.close() offlineAreasDatabase = null Log.d(TAG, "Tile server stopped") } fun getPort(): Int { return port } private fun readAssetFile(fileName: String): String { return context.assets.open(fileName).use { inputStream -> Loading Loading @@ -552,6 +511,44 @@ class LocalMapServer( return null } /** * Find tile data for given coordinates from available sources */ private suspend fun findTileData(z: Int, x: Long, y: Long): Pair<ByteArray?, Boolean> { val tmsY = (2.0.pow(z.toDouble()) - 1 - y).toLong() // First try to get tile from built-in database val tileData = basemapDatabase?.let { getTileData(it, z, x, tmsY) } if (tileData != null) { Log.d(TAG, "Tile found in basemap database") return Pair(tileData, true) // gzipped } // If not found, try offline databases Log.d(TAG, "Tile not found in basemap database, checking offline databases") val offlineTileData = getTileDataFromOfflineDatabases(z, x, y) if (offlineTileData != null) { return Pair(offlineTileData, false) // not gzipped } // Check if we should fetch from the internet (not in offline mode) if (!isOfflineMode()) { Log.d(TAG, "Tile not found in local caches, attempting to fetch from internet") val internetTileData = CoroutineScope(Dispatchers.IO).async { fetchTileFromInternet(z, x, y) }.await() if (internetTileData != null) { Log.d( TAG, "Successfully fetched tile from internet: size: ${internetTileData.size} bytes" ) return Pair(internetTileData, false) // not gzipped } } return Pair(null, false) // not found } /** * Check if the app is in offline mode */ Loading cardinal-android/app/src/main/java/earth/maps/cardinal/tileserver/TileDownloadManager.kt +627 −431 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
cardinal-android/app/detekt.yml +4 −0 Original line number Diff line number Diff line Loading @@ -5,4 +5,8 @@ naming: complexity: LongParameterList: active: true ignoreDefaultParameters: true CognitiveComplexMethod: active: true excludes: [ '**/cardinal/bottomsheet/**' ]
cardinal-android/app/src/main/java/earth/maps/cardinal/tileserver/LocalMapServer.kt +210 −213 Original line number Diff line number Diff line Loading @@ -84,13 +84,54 @@ class LocalMapServer( // Find an available port val availablePort = findAvailablePort() server = embeddedServer(CIO, port = availablePort, host = "127.0.0.1") { server = createEmbeddedServer(availablePort) server?.start(wait = false) port = availablePort Log.d(TAG, "Tile server started on port: $port") } fun stop() { server?.stop(1000, 5000) server = null port = -1 // Close the databases. terrainDatabase?.close() terrainDatabase = null landcoverDatabase?.close() landcoverDatabase = null basemapDatabase?.close() basemapDatabase = null offlineAreasDatabase?.close() offlineAreasDatabase = null Log.d(TAG, "Tile server stopped") } fun getPort(): Int { return port } private fun createEmbeddedServer(port: Int) = embeddedServer(CIO, port = port, host = "127.0.0.1") { routing { get("/") { handleRoot() handleStyleLight() handleStyleDark() handleRoute() handleTerrainTile() handleLandcoverTile() handleOpenMapTiles() } } private fun io.ktor.server.routing.Routing.handleRoot() = get("/") { call.respondText("Tile Server is running!") } get("/style_light.json") { private fun io.ktor.server.routing.Routing.handleStyleLight() = get("/style_light.json") { try { val styleJson = readAssetFile("style_light.json") val modifiedStyleJson = styleJson.replace("{port}", port.toString()) Loading @@ -106,7 +147,7 @@ class LocalMapServer( } } get("/style_dark.json") { private fun io.ktor.server.routing.Routing.handleStyleDark() = get("/style_dark.json") { try { val styleJson = readAssetFile("style_dark.json") val modifiedStyleJson = styleJson.replace("{port}", port.toString()) Loading @@ -122,8 +163,7 @@ class LocalMapServer( } } // Valhalla-compatible routing endpoint post("/route") { private fun io.ktor.server.routing.Routing.handleRoute() = post("/route") { try { val requestBody = call.receiveText() Log.d(TAG, "Received routing request: $requestBody") Loading @@ -145,7 +185,7 @@ class LocalMapServer( } } // Serve tiles from terrain.mbtiles. private fun io.ktor.server.routing.Routing.handleTerrainTile() = get("/terrain/{z}/{x}/{y}.png") { val terrainDatabase = terrainDatabase ?: return@get Loading Loading @@ -176,8 +216,7 @@ class LocalMapServer( } } // Serve tiles from landcover.mbtiles. private fun io.ktor.server.routing.Routing.handleLandcoverTile() = get("/landcover/{z}/{x}/{y}.pbf") { val landcoverDatabase = landcoverDatabase ?: return@get Loading Loading @@ -209,7 +248,7 @@ class LocalMapServer( } } // Serve tiles from basemap.mbtiles. private fun io.ktor.server.routing.Routing.handleOpenMapTiles() = get("/openmaptiles/{z}/{x}/{y}.pbf") { val z = call.parameters["z"]?.toIntOrNull() val x = call.parameters["x"]?.toLongOrNull() Loading @@ -223,33 +262,7 @@ class LocalMapServer( return@get } Log.d(TAG, "Requesting tile: /openmaptiles/$z/$x/$y.pbf") // MBTiles uses TMS (Tile Map Service) coordinate system where Y=0 is at the bottom // Most map libraries use XYZ coordinate system where Y=0 is at the top // Convert Y coordinate from XYZ to TMS: TMS_Y = 2^zoom - 1 - XYZ_Y val tmsY = (2.0.pow(z.toDouble()) - 1 - y).toLong() var isGzipped = true val basemapDatabase = basemapDatabase // First try to get tile from built-in database var tileData = if (basemapDatabase != null) { getTileData(basemapDatabase, z, x, tmsY) } else { Log.w(TAG, "Basemap database is null") null } // If not found, try offline databases if (tileData == null) { Log.d(TAG, "Tile not found in basemap database, checking offline databases") tileData = getTileDataFromOfflineDatabases(z, x, y) isGzipped = false } else { Log.d(TAG, "Tile found in basemap database") } val (tileData, isGzipped) = findTileData(z, x, y) if (tileData != null) { Log.d( Loading @@ -262,30 +275,6 @@ class LocalMapServer( } call.respondBytes(tileData, contentType = ContentType.Application.ProtoBuf) } else { // Check if we should fetch from the internet (not in offline mode) val isOfflineMode = isOfflineMode() if (!isOfflineMode) { Log.d( TAG, "Tile not found in local caches, attempting to fetch from internet" ) tileData = CoroutineScope(Dispatchers.IO).async { fetchTileFromInternet( z, x, y ) }.await() if (tileData != null) { Log.d( TAG, "Successfully fetched tile from internet: /openmaptiles/$z/$x/$y.pbf, size: ${tileData.size} bytes" ) call.respondBytes( tileData, contentType = ContentType.Application.ProtoBuf ) return@get } } Log.d(TAG, "Tile not found: /openmaptiles/$z/$x/$y.pbf") // If we respond with NotFound here (as would make sense) it will cause maplibre to cache the // fact that this tile doesn't exist in this source, which we don't want because it will never Loading @@ -297,36 +286,6 @@ class LocalMapServer( ) } } } } server?.start(wait = false) port = availablePort Log.d(TAG, "Tile server started on port: $port") } fun stop() { server?.stop(1000, 5000) server = null port = -1 // Close the databases. terrainDatabase?.close() terrainDatabase = null landcoverDatabase?.close() landcoverDatabase = null basemapDatabase?.close() basemapDatabase = null offlineAreasDatabase?.close() offlineAreasDatabase = null Log.d(TAG, "Tile server stopped") } fun getPort(): Int { return port } private fun readAssetFile(fileName: String): String { return context.assets.open(fileName).use { inputStream -> Loading Loading @@ -552,6 +511,44 @@ class LocalMapServer( return null } /** * Find tile data for given coordinates from available sources */ private suspend fun findTileData(z: Int, x: Long, y: Long): Pair<ByteArray?, Boolean> { val tmsY = (2.0.pow(z.toDouble()) - 1 - y).toLong() // First try to get tile from built-in database val tileData = basemapDatabase?.let { getTileData(it, z, x, tmsY) } if (tileData != null) { Log.d(TAG, "Tile found in basemap database") return Pair(tileData, true) // gzipped } // If not found, try offline databases Log.d(TAG, "Tile not found in basemap database, checking offline databases") val offlineTileData = getTileDataFromOfflineDatabases(z, x, y) if (offlineTileData != null) { return Pair(offlineTileData, false) // not gzipped } // Check if we should fetch from the internet (not in offline mode) if (!isOfflineMode()) { Log.d(TAG, "Tile not found in local caches, attempting to fetch from internet") val internetTileData = CoroutineScope(Dispatchers.IO).async { fetchTileFromInternet(z, x, y) }.await() if (internetTileData != null) { Log.d( TAG, "Successfully fetched tile from internet: size: ${internetTileData.size} bytes" ) return Pair(internetTileData, false) // not gzipped } } return Pair(null, false) // not found } /** * Check if the app is in offline mode */ Loading
cardinal-android/app/src/main/java/earth/maps/cardinal/tileserver/TileDownloadManager.kt +627 −431 File changed.Preview size limit exceeded, changes collapsed. Show changes