From 9bc4e88109f4a168c8519f194a261f6cf526f05d Mon Sep 17 00:00:00 2001 From: Eugene Maksymenko Date: Fri, 13 Jan 2023 04:05:40 +0200 Subject: [PATCH] Throw exceptions outside cache configuration to have more control. --- .../earth/worldwind/layer/TiledImageLayer.kt | 16 +++---- .../coverage/TiledElevationCoverage.kt | 46 +++++++++---------- .../layer/AbstractTiledImageLayer.kt | 6 +++ .../worldwind/ogc/gpkg/AbstractGeoPackage.kt | 11 +++++ .../earth/worldwind/layer/TiledImageLayer.kt | 4 +- 5 files changed, 50 insertions(+), 33 deletions(-) diff --git a/worldwind/src/androidMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt b/worldwind/src/androidMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt index dcc6dfad1..064836712 100644 --- a/worldwind/src/androidMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt +++ b/worldwind/src/androidMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt @@ -9,6 +9,7 @@ import earth.worldwind.render.RenderResourceCache import earth.worldwind.util.Logger.WARN import earth.worldwind.util.Logger.logMessage import kotlinx.coroutines.* +import kotlin.jvm.Throws actual abstract class TiledImageLayer actual constructor(name: String): AbstractTiledImageLayer(name) { /** @@ -21,12 +22,15 @@ actual abstract class TiledImageLayer actual constructor(name: String): Abstract * @param quality Tile image compression quality * * @return Cache configured successfully + * @throws IllegalArgumentException In case of incompatible level set configured in cache content. + * @throws IllegalStateException In case of new content creation required on read-only database. */ @Suppress("DEPRECATION") @JvmOverloads + @Throws(IllegalArgumentException::class, IllegalStateException::class) suspend fun configureCache( pathName: String, tableName: String, readOnly: Boolean = false, format: CompressFormat = CompressFormat.PNG, quality: Int = 100 - ) = try { + ) { val isWebp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { format == CompressFormat.WEBP_LOSSLESS || format == CompressFormat.WEBP_LOSSY } else { @@ -37,13 +41,6 @@ actual abstract class TiledImageLayer actual constructor(name: String): Abstract it.format = format it.quality = quality } - true - } catch (e: IllegalArgumentException) { - logMessage(WARN, "TiledImageLayer", "configureCache", e.message!!) - false - } catch (e: IllegalStateException) { - logMessage(WARN, "TiledImageLayer", "configureCache", e.message!!) - false } /** @@ -62,9 +59,10 @@ actual abstract class TiledImageLayer actual constructor(name: String): Abstract * @return the coroutine Job executing the retrieval or `null` if the specified sector does * not intersect the layer bounding sector. * - * @throws IllegalStateException if tiled surface image is not initialized or cache not configured. + * @throws IllegalStateException if tiled surface image is not initialized or cache is not configured. */ @OptIn(DelicateCoroutinesApi::class) + @Throws(IllegalStateException::class) fun makeLocal( sector: Sector, resolution: Angle, cache: RenderResourceCache, scope: CoroutineScope = GlobalScope, onProgress: ((Int, Int) -> Unit)? = null diff --git a/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/globe/elevation/coverage/TiledElevationCoverage.kt b/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/globe/elevation/coverage/TiledElevationCoverage.kt index e26960efe..e2fe7a289 100644 --- a/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/globe/elevation/coverage/TiledElevationCoverage.kt +++ b/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/globe/elevation/coverage/TiledElevationCoverage.kt @@ -21,6 +21,7 @@ import io.ktor.client.network.sockets.* import kotlinx.coroutines.* import java.io.FileNotFoundException import java.net.SocketTimeoutException +import kotlin.jvm.Throws actual open class TiledElevationCoverage actual constructor( tileMatrixSet: TileMatrixSet, tileFactory: ElevationTileFactory, @@ -53,30 +54,25 @@ actual open class TiledElevationCoverage actual constructor( * @param isFloat If true, then cache will be stored in Float32 format, else Int16. * * @return Cache configured successfully + * @throws IllegalArgumentException In case of incompatible matrix set configured in cache content. + * @throws IllegalStateException In case of new content creation required on read-only database. */ - suspend fun configureCache(pathName: String, tableName: String, readOnly: Boolean = false, isFloat: Boolean = false): Boolean { - return try { - val geoPackage = GeoPackage(pathName, readOnly) - val content = geoPackage.content.firstOrNull { it.tableName == tableName }?.also { - // Check if current layer fits cache content - val matrixSet = geoPackage.buildTileMatrixSet(it) - require(matrixSet.sector == tileMatrixSet.sector) { "Invalid sector" } - require(matrixSet.entries.size == tileMatrixSet.entries.size) { "Invalid number of matrices" } - requireNotNull(geoPackage.griddedCoverages.firstOrNull { gc -> - gc.tileMatrixSetName == tableName && gc.datatype == if (isFloat) "float" else "integer" - }) { "Invalid data type" } - } ?: geoPackage.setupGriddedCoverageContent(tableName, displayName ?: tableName, tileMatrixSet, isFloat) + @JvmOverloads + @Throws(IllegalArgumentException::class, IllegalStateException::class) + suspend fun configureCache(pathName: String, tableName: String, readOnly: Boolean = false, isFloat: Boolean = false) { + val geoPackage = GeoPackage(pathName, readOnly) + val content = geoPackage.content.firstOrNull { it.tableName == tableName }?.also { + // Check if current layer fits cache content + val matrixSet = geoPackage.buildTileMatrixSet(it) + require(matrixSet.sector == tileMatrixSet.sector) { "Invalid sector" } + require(matrixSet.entries.size == tileMatrixSet.entries.size) { "Invalid number of matrices" } + requireNotNull(geoPackage.griddedCoverages.firstOrNull { gc -> + gc.tileMatrixSetName == tableName && gc.datatype == if (isFloat) "float" else "integer" + }) { "Invalid data type" } + } ?: geoPackage.setupGriddedCoverageContent(tableName, displayName ?: tableName, tileMatrixSet, isFloat) - cacheContent = content - cacheTileFactory = GpkgElevationTileFactory(content, isFloat) - true - } catch (e: IllegalArgumentException) { - logMessage(WARN, "TiledImageLayer", "configureCache", e.message!!) - false - } catch (e: IllegalStateException) { - logMessage(WARN, "TiledImageLayer", "configureCache", e.message!!) - false - } + cacheContent = content + cacheTileFactory = GpkgElevationTileFactory(content, isFloat) } /** @@ -89,7 +85,10 @@ actual open class TiledElevationCoverage actual constructor( /** * Delete all tiles from current cache storage + * + * @throws IllegalStateException In case of read-only database. */ + @Throws(IllegalStateException::class) suspend fun clearCache() = cacheContent?.run { container.deleteContent(tableName) }.also { disableCache() } /** @@ -107,9 +106,10 @@ actual open class TiledElevationCoverage actual constructor( * @return the coroutine Job executing the retrieval or `null` if the specified sector does * not intersect the elevation model bounding sector. * - * @throws IllegalStateException if cache not configured. + * @throws IllegalStateException if cache is not configured. */ @OptIn(DelicateCoroutinesApi::class) + @Throws(IllegalStateException::class) fun makeLocal( sector: Sector, resolution: Angle, scope: CoroutineScope = GlobalScope, onProgress: ((Int, Int) -> Unit)? = null ): Job? { diff --git a/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/layer/AbstractTiledImageLayer.kt b/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/layer/AbstractTiledImageLayer.kt index 0ae8fcf1f..152b506bc 100644 --- a/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/layer/AbstractTiledImageLayer.kt +++ b/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/layer/AbstractTiledImageLayer.kt @@ -9,6 +9,7 @@ import earth.worldwind.render.image.ImageSource import earth.worldwind.render.image.ImageTile import earth.worldwind.shape.TiledSurfaceImage import kotlinx.coroutines.* +import kotlin.jvm.Throws actual abstract class AbstractTiledImageLayer actual constructor(name: String): RenderableLayer(name) { actual var tiledSurfaceImage: TiledSurfaceImage? = null @@ -43,9 +44,13 @@ actual abstract class AbstractTiledImageLayer actual constructor(name: String): /** * Delete all tiles from current cache storage + * + * @throws IllegalStateException In case of read-only database. */ + @Throws(IllegalStateException::class) suspend fun clearCache() = cacheContent?.run { container.deleteContent(tableName) }.also { disableCache() } + @Throws(IllegalArgumentException::class, IllegalStateException::class) protected open suspend fun getOrSetupTilesContent(pathName: String, tableName: String, readOnly: Boolean, isWebp: Boolean): GpkgContent { val tiledSurfaceImage = tiledSurfaceImage ?: error("Surface image not defined") val levelSet = tiledSurfaceImage.levelSet @@ -66,6 +71,7 @@ actual abstract class AbstractTiledImageLayer actual constructor(name: String): } ?: geoPackage.setupTilesContent(tableName, displayName ?: tableName, levelSet, isWebp) } + @Throws(IllegalStateException::class) protected open fun launchBulkRetrieval( scope: CoroutineScope, sector: Sector, resolution: Angle, onProgress: ((Int, Int) -> Unit)?, retrieveTile: suspend (imageSource: ImageSource, cacheSource: ImageSource, options: ImageOptions?) -> Unit diff --git a/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/ogc/gpkg/AbstractGeoPackage.kt b/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/ogc/gpkg/AbstractGeoPackage.kt index c35036a14..5933d89c1 100644 --- a/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/ogc/gpkg/AbstractGeoPackage.kt +++ b/worldwind/src/jvmCommonMain/kotlin/earth/worldwind/ogc/gpkg/AbstractGeoPackage.kt @@ -8,6 +8,7 @@ import earth.worldwind.layer.mercator.MercatorSector import earth.worldwind.util.LevelSet import earth.worldwind.util.LevelSetConfig import kotlinx.coroutines.runBlocking +import kotlin.jvm.Throws // TODO verify its a GeoPackage container abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { @@ -47,6 +48,7 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { suspend fun readTileUserData(tiles: GpkgContent, zoomLevel: Int, tileColumn: Int, tileRow: Int) = readTileUserData(tiles.tableName, zoomLevel, tileColumn, tileRow) + @Throws(IllegalStateException::class) suspend fun writeTileUserData(tiles: GpkgContent, zoomLevel: Int, tileColumn: Int, tileRow: Int, tileData: ByteArray) { if (isReadOnly) error("Tile cannot be saved. GeoPackage is read-only!") val tileUserData = readTileUserData(tiles.tableName, zoomLevel, tileColumn, tileRow)?.also { it.tileData = tileData } @@ -54,6 +56,7 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { writeTileUserData(tiles.tableName, tileUserData) } + @Throws(IllegalStateException::class) suspend fun writeGriddedTile( tiles: GpkgContent, zoomLevel: Int, tileColumn: Int, tileRow: Int, scale: Double = 1.0, offset: Double = 0.0, min: Double? = null, max: Double? = null, mean: Double? = null, stdDev: Double? = null @@ -72,6 +75,7 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { } } + @Throws(IllegalArgumentException::class) fun buildLevelSetConfig(content: GpkgContent): LevelSetConfig { require(content.dataType.equals("tiles", true)) { "Unsupported GeoPackage content data_type: " + content.dataType @@ -106,6 +110,7 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { } // TODO What if data already exists? + @Throws(IllegalStateException::class) suspend fun setupTilesContent( tableName: String, identifier: String, levelSet: LevelSet, isWebp: Boolean = false ): GpkgContent { @@ -138,6 +143,7 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { return content } + @Throws(IllegalStateException::class) suspend fun setupTileMatrices(tableName: String, levelSet: LevelSet) { if (isReadOnly) error("Content $tableName cannot be updated. GeoPackage is read-only!") for (i in 0 until levelSet.numLevels) levelSet.level(i)?.run { @@ -156,6 +162,7 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { } } + @Throws(IllegalArgumentException::class) fun buildTileMatrixSet(content: GpkgContent): TileMatrixSet { require(content.dataType.equals("2d-gridded-coverage", true)) { "Unsupported GeoPackage content data_type: " + content.dataType @@ -180,6 +187,7 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { } // TODO What if data already exists? + @Throws(IllegalStateException::class) suspend fun setupGriddedCoverageContent(tableName: String, identifier: String, tileMatrixSet: TileMatrixSet, isFloat: Boolean = false): GpkgContent { if (isReadOnly) error("Content $tableName cannot be created. GeoPackage is read-only!") createRequiredTables() @@ -232,7 +240,10 @@ abstract class AbstractGeoPackage(pathName: String, val isReadOnly: Boolean) { /** * Delete specified content table and its related metadata + * + * @throws IllegalStateException In case of new content creation required on read-only database. */ + @Throws(IllegalStateException::class) suspend fun deleteContent(tableName: String) { if (isReadOnly) error("Content $tableName cannot be deleted. GeoPackage is read-only!") diff --git a/worldwind/src/jvmMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt b/worldwind/src/jvmMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt index 32f49ef8b..4501d845c 100644 --- a/worldwind/src/jvmMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt +++ b/worldwind/src/jvmMain/kotlin/earth/worldwind/layer/TiledImageLayer.kt @@ -4,6 +4,7 @@ import earth.worldwind.geom.Angle import earth.worldwind.geom.Sector import earth.worldwind.render.RenderResourceCache import kotlinx.coroutines.* +import kotlin.jvm.Throws actual abstract class TiledImageLayer actual constructor(name: String): AbstractTiledImageLayer(name) { /** @@ -22,9 +23,10 @@ actual abstract class TiledImageLayer actual constructor(name: String): Abstract * @return the coroutine Job executing the retrieval or `null` if the specified sector does * not intersect the layer bounding sector. * - * @throws IllegalStateException if tiled surface image is not initialized or cache not configured. + * @throws IllegalStateException if tiled surface image is not initialized or cache is not configured. */ @OptIn(DelicateCoroutinesApi::class) + @Throws(IllegalStateException::class) fun makeLocal( sector: Sector, resolution: Angle, cache: RenderResourceCache, scope: CoroutineScope = GlobalScope, onProgress: ((Int, Int) -> Unit)? = null