All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmCommonMain.earth.worldwind.ogc.GpkgElevationDataFactory.kt Maven / Gradle / Ivy

package earth.worldwind.ogc

import earth.worldwind.globe.elevation.ElevationDecoder
import earth.worldwind.globe.elevation.ElevationSource
import earth.worldwind.ogc.gpkg.GeoPackage
import earth.worldwind.ogc.gpkg.GpkgContent
import earth.worldwind.util.ResourcePostprocessor
import java.nio.Buffer
import java.nio.FloatBuffer
import java.nio.ShortBuffer
import kotlin.math.roundToInt

open class GpkgElevationDataFactory(
    protected val geoPackage: GeoPackage,
    protected val content: GpkgContent,
    protected val zoomLevel: Int,
    protected val tileColumn: Int,
    protected val tileRow: Int,
    protected val isFloat: Boolean
): ElevationSource.ElevationDataFactory, ResourcePostprocessor {
    protected val elevationDecoder = ElevationDecoder()

    override suspend fun fetchElevationData(): Buffer? {
        // Attempt to read the GeoPackage tile user data and gridded tile metadata
        val tileUserData = geoPackage.readTileUserData(content, zoomLevel, tileColumn, tileRow) ?: return null
        val griddedTile = geoPackage.readGriddedTile(content, tileUserData) ?: return null
        val griddedCoverage = geoPackage.getGriddedCoverage(content) ?: return null

        // Decode the tile user data either as TIFF32 or PNG16
        return if (isFloat) elevationDecoder.decodeTiff(tileUserData.tileData)
        else elevationDecoder.decodePng(
            tileUserData.tileData, griddedTile.scale, griddedTile.offset,
            griddedCoverage.scale, griddedCoverage.offset, griddedCoverage.dataNull
        )
    }

    override suspend fun  process(resource: Resource): Resource {
        // Attempt to write tile user data only if container is not read-only
        if (resource is Buffer && !geoPackage.isReadOnly) encodeToImage(resource)?.let {
            geoPackage.writeTileUserData(content, zoomLevel, tileColumn, tileRow, it)
            // TODO Calculate and save gridded tile meta data, such as min and max altitude...
            geoPackage.writeGriddedTile(content, zoomLevel, tileColumn, tileRow)
        }
        return resource
    }

    protected open fun encodeToImage(resource: Buffer): ByteArray? {
        val matrix = content.tileMatrices?.firstOrNull { it.zoomLevel == zoomLevel } ?: return null
        val tileWidth = matrix.tileWidth
        val tileHeight = matrix.tileHeight
        return when (resource) {
            is FloatBuffer -> if (isFloat) {
                elevationDecoder.encodeTiff(resource, tileWidth, tileHeight)
            } else {
                elevationDecoder.encodePng(ShortBuffer.wrap(
                    ShortArray(resource.remaining()) {
                        val value = resource.get()
                        // Consider converting null value from float to short
                        if (value == Float.MAX_VALUE) Short.MIN_VALUE else value.roundToInt().toShort()
                    }.also { resource.clear() }
                ), tileWidth, tileHeight)
            }

            is ShortBuffer -> if (isFloat) {
                elevationDecoder.encodeTiff(FloatBuffer.wrap(
                    FloatArray(resource.remaining()) { resource.get().toFloat() }.also { resource.clear() }
                ), tileWidth, tileHeight)
            } else {
                elevationDecoder.encodePng(resource, tileWidth, tileHeight)
            }

            else -> null // Do not save tile with incorrect datatype
        }
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is GpkgElevationDataFactory) return false
        if (content.tableName != other.content.tableName) return false
        if (zoomLevel != other.zoomLevel) return false
        if (tileColumn != other.tileColumn) return false
        if (tileRow != other.tileRow) return false
        return true
    }

    override fun hashCode(): Int {
        var result = content.tableName.hashCode()
        result = 31 * result + zoomLevel
        result = 31 * result + tileColumn
        result = 31 * result + tileRow
        return result
    }

    override fun toString() = "GpkgElevationDataFactory(tableName=${content.tableName}, zoomLevel=$zoomLevel, tileColumn=$tileColumn, tileRow=$tileRow)"
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy