commonMain.earth.worldwind.util.LevelSet.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwind-jvm Show documentation
Show all versions of worldwind-jvm Show documentation
The WorldWind Kotlin SDK (WWK) includes the library, examples and tutorials for building multiplatform 3D virtual globe applications for Android, Web and Java.
The newest version!
package earth.worldwind.util
import earth.worldwind.geom.Angle
import earth.worldwind.geom.Location
import earth.worldwind.geom.Sector
import earth.worldwind.util.Logger.ERROR
import earth.worldwind.util.Logger.logMessage
import kotlin.math.ln
import kotlin.math.roundToInt
/**
* Multi-resolution, hierarchical collection of tiles organized into levels of increasing resolution. Applications
* typically do not interact with this class.
*/
open class LevelSet {
/**
* The sector spanned by this level set.
*/
val sector: Sector
/**
* Tile origin for this level set
*/
val tileOrigin: Sector
/**
* The geographic width and height of tiles in the first level (the lowest resolution) of this level set.
*/
val firstLevelDelta: Location
/**
* The width in pixels of images associated with tiles in this level set, or the number of sample points in the
* longitudinal direction of elevation tiles associated with this level set.
*/
val tileWidth: Int
/**
* The height in pixels of images associated with tiles in this level set, or the number of sample points in the
* latitudinal direction of elevation tiles associated with this level set.
*/
val tileHeight: Int
/**
* Determines how many levels to skip from retrieving tile data during tile pyramid subdivision.
*/
val levelOffset: Int
/**
* The hierarchical levels, sorted from lowest to highest resolution.
*/
protected val levels: Array
/**
* Returns the number of levels in this level set.
*/
val numLevels get() = levels.size
/**
* Returns the first level (the lowest resolution) of this level set.
*/
val firstLevel get() = levels.first()
/**
* Returns the last level (the highest resolution) of this level set.
*/
val lastLevel get() = levels.last()
/**
* Constructs an empty level set with no levels. The methods `level`, `levelForResolution`,
* `firstLevel` and `lastLevel` always return null.
*/
constructor() {
sector = Sector()
tileOrigin = Sector()
firstLevelDelta = Location()
tileWidth = 0
tileHeight = 0
levelOffset = 0
levels = emptyArray()
}
/**
* Constructs a level set with specified parameters.
*
* @param sector the sector spanned by this level set
* @param tileOrigin the origin for this level set
* @param firstLevelDelta the geographic width and height of tiles in the first level (the lowest resolution)
* of the level set
* @param numLevels the number of levels in the level set
* @param tileWidth the height in pixels of images associated with tiles in this level set, or the number of
* sample points in the longitudinal direction of elevation tiles associate with this leve set
* @param tileHeight the height in pixels of images associated with tiles in this level set, or the number of
* sample points in the latitudinal direction of elevation tiles associate with this level set
* @param levelOffset determines how many levels to skip from retrieving texture during tile pyramid subdivision
*
* @throws IllegalArgumentException If any dimension is zero
*/
constructor(
sector: Sector, tileOrigin: Sector, firstLevelDelta: Location, numLevels: Int, tileWidth: Int, tileHeight: Int, levelOffset: Int = 0
) {
require(firstLevelDelta.latitude.inDegrees > 0.0 && firstLevelDelta.longitude.inDegrees > 0.0) {
logMessage(ERROR, "LevelSet", "constructor", "invalidTileDelta")
}
require(numLevels >= 0) {
logMessage(ERROR, "LevelSet", "constructor", "invalidNumLevels")
}
require(tileWidth >= 1 && tileHeight >= 1) {
logMessage(ERROR, "LevelSet", "constructor", "invalidWidthOrHeight")
}
this.sector = sector
this.tileOrigin = tileOrigin
this.firstLevelDelta = firstLevelDelta
this.tileWidth = tileWidth
this.tileHeight = tileHeight
this.levelOffset = levelOffset
this.levels = Array(numLevels) {
val divisor = 1 shl it
Level(this, it, Location(firstLevelDelta.latitude / divisor, firstLevelDelta.longitude / divisor))
}
}
/**
* Constructs a level set with parameters from a specified level set.
*
* @param levelSet source level set
*/
constructor(levelSet: LevelSet): this(
Sector(levelSet.sector),
Sector(levelSet.tileOrigin),
Location(levelSet.firstLevelDelta),
levelSet.numLevels,
levelSet.tileWidth,
levelSet.tileHeight,
levelSet.levelOffset
)
/**
* Constructs a level set with parameters from a specified configuration. The configuration's sector must be
* non-null, its first level delta must be positive, its number of levels must be 1 or more, and its tile width and
* tile height must be 1 or greater.
*
* @param config the configuration for this level set
*/
constructor(config: LevelSetConfig): this(
config.sector,
config.tileOrigin,
config.firstLevelDelta,
config.numLevels,
config.tileWidth,
config.tileHeight,
config.levelOffset
)
/**
* Returns the [Level] for a specified level number.
*
* @param levelNumber the number of the desired level
*
* @return the requested level, or null if the level does not exist
*/
fun level(levelNumber: Int) = if (levelNumber in levels.indices) levels[levelNumber] else null
/**
* Returns the level that most closely approximates the specified resolution.
*
* @param resolution the desired resolution in angular value of latitude per pixel.
*
* @return the level for the specified resolution, or null if this level set is empty
*
* @throws IllegalArgumentException If the resolution is not positive
* @throws IllegalStateException If this level set is empty
*/
fun levelForResolution(resolution: Angle): Level {
require(resolution.inDegrees > 0.0) {
logMessage(ERROR, "LevelSetConfig", "levelForResolution", "invalidResolution")
}
if (levels.isEmpty()) error("This level set is empty")
val firstLevelDegreesPerPixel = firstLevelDelta.latitude.inDegrees / tileHeight
val level = ln(firstLevelDegreesPerPixel / resolution.inDegrees) / ln(2.0) // fractional level address
val levelNumber = level.roundToInt() // nearest neighbor level
return when {
levelNumber < 0 -> levels[0] // unable to match the resolution; return the first level
levelNumber < levels.size -> levels[levelNumber] // nearest neighbor level is in this level set
else -> levels[levels.size - 1] // unable to match the resolution; return the last level
}
}
/**
* Determine the min relevant level for the sector of this level set
*
* @param maxLevel Maximum available level
* @return Minimum relevant level number
*/
fun minRelevantLevel(maxLevel: Level = lastLevel) = levels[sector.minLevelNumber(maxLevel.levelNumber)]
/**
* Calculates approximate count of tiles in specified sector within specified resolution range
*
* @param sector co calculate tiles amount
* @param minLevel minimal required level
* @param maxLevel maximal required level
* @return tiles count
*/
fun tileCount(sector: Sector, minLevel: Level, maxLevel: Level): Long {
var tileCount = 0L
var level = minLevel
do {
tileCount += level.tilesInSector(sector)
level = level.nextLevel ?: break
} while (level.levelNumber <= maxLevel.levelNumber)
return tileCount
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy