Skip to content

Commit

Permalink
Clone WebImageLayer and WebElevationCoverage attributes.
Browse files Browse the repository at this point in the history
  • Loading branch information
ComBatVision committed Jun 15, 2024
1 parent a7d9ec7 commit ca992db
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 97 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ repositories {
}
dependencies {
implementation 'earth.worldwind:worldwind:1.5.11'
implementation 'earth.worldwind:worldwind:1.5.12'
}
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ buildscript {

allprojects {
group = "earth.worldwind"
version = "1.5.11"
version = "1.5.12"

extra.apply {
set("minSdk", 21)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ import earth.worldwind.globe.elevation.ElevationSourceFactory
expect open class TiledElevationCoverage(
tileMatrixSet: TileMatrixSet, elevationSourceFactory: ElevationSourceFactory
): AbstractTiledElevationCoverage {
constructor()
open fun clone(): TiledElevationCoverage
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import earth.worldwind.util.LevelSet
open class MercatorTiledImageLayer(
name: String? = null, tiledSurfaceImage: MercatorTiledSurfaceImage? = null
): TiledImageLayer(name, tiledSurfaceImage) {
override fun clone() = MercatorTiledImageLayer(displayName, tiledSurfaceImage?.clone() as MercatorTiledSurfaceImage)
override fun clone() = MercatorTiledImageLayer(displayName, tiledSurfaceImage?.clone() as? MercatorTiledSurfaceImage)

companion object {
fun buildTiledSurfaceImage(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package earth.worldwind.layer.mercator

import earth.worldwind.layer.WebImageLayer

class WebMercatorImageLayer(
override val serviceAddress: String,
override val imageFormat: String = "image/png",
override val isTransparent: Boolean = false,
name: String? = null,
tiledSurfaceImage: MercatorTiledSurfaceImage? = null
) : MercatorTiledImageLayer(name, tiledSurfaceImage), WebImageLayer {
override val serviceType = SERVICE_TYPE

override fun clone() = WebMercatorImageLayer(
serviceAddress, imageFormat, isTransparent, displayName, tiledSurfaceImage?.clone() as? MercatorTiledSurfaceImage
)

companion object {
const val SERVICE_TYPE = "XYZ"
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package earth.worldwind.layer.mercator

import earth.worldwind.layer.WebImageLayer
import earth.worldwind.layer.mercator.MercatorTiledImageLayer.Companion.buildTiledSurfaceImage
import earth.worldwind.render.image.ImageSource
import earth.worldwind.util.locale.language
import kotlin.random.Random

object WebMercatorLayerFactory {
const val SERVICE_TYPE = "XYZ"
private const val OPEN_BRACKET = '{'
private const val CLOSED_BRACKET = '}'
private const val I_EXPRESSION = "{i}"
Expand All @@ -20,7 +18,7 @@ object WebMercatorLayerFactory {
fun createLayer(
urlTemplate: String, imageFormat: String = "image/png", transparent: Boolean = false,
name: String? = null, numLevels: Int = 22, tileSize: Int = 256, levelOffset: Int = 1
): MercatorTiledImageLayer {
): WebMercatorImageLayer {
val urlParts = parseUrl(urlTemplate)
val randomValue = urlParts.find { it.startsWith(RAND_PREFIX) }
val randomValues = randomValue?.removeSurrounding(RAND_PREFIX, "}")?.split(",")
Expand All @@ -46,12 +44,7 @@ object WebMercatorLayerFactory {
}
}
val tiledSurfaceImage = buildTiledSurfaceImage(tileFactory, numLevels, tileSize, transparent, levelOffset)
return object : MercatorTiledImageLayer(name, tiledSurfaceImage), WebImageLayer {
override val serviceType = SERVICE_TYPE
override val serviceAddress = urlTemplate
override val imageFormat = imageFormat
override val isTransparent = transparent
}
return WebMercatorImageLayer(urlTemplate, imageFormat, transparent, name, tiledSurfaceImage)
}

private fun parseUrl(serverUrl: String): List<String> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,36 @@ package earth.worldwind.ogc
import earth.worldwind.geom.Angle
import earth.worldwind.geom.Sector
import earth.worldwind.geom.TileMatrixSet
import earth.worldwind.geom.TileMatrixSet.Companion.fromTilePyramid
import earth.worldwind.globe.elevation.ElevationSourceFactory
import earth.worldwind.globe.elevation.coverage.TiledElevationCoverage
import earth.worldwind.globe.elevation.coverage.WebElevationCoverage

/**
* Generates elevations from OGC Web Coverage Service (WCS) version 1.0.0.
* <br></br>
* Wcs100ElevationCoverage requires the WCS service address, coverage name, and coverage bounding sector. Get Coverage
* requests generated for retrieving data use the WCS version 1.0.0 protocol and are limited to the EPSG:4326 coordinate
* system. Wcs100ElevationCoverage does not perform version negotiation and assumes the service supports the format and
* coordinate system parameters detailed here.
*/
class Wcs100ElevationCoverage(
class Wcs100ElevationCoverage private constructor(
override val serviceAddress: String, override val coverageName: String, override val outputFormat: String,
sector: Sector, resolution: Angle
): TiledElevationCoverage(
TileMatrixSet.fromTilePyramid(sector, if (sector.isFullSphere) 2 else 1, 1, 256, 256, resolution),
Wcs100ElevationSourceFactory(serviceAddress, coverageName, outputFormat)
), WebElevationCoverage {
tileMatrixSet: TileMatrixSet, elevationSourceFactory: ElevationSourceFactory
): TiledElevationCoverage(tileMatrixSet, elevationSourceFactory), WebElevationCoverage {
override val serviceType = SERVICE_TYPE

constructor(
serviceAddress: String, coverageName: String, outputFormat: String, sector: Sector, resolution: Angle
): this(
serviceAddress, coverageName, outputFormat,
fromTilePyramid(sector, if (sector.isFullSphere) 2 else 1, 1, 256, 256, resolution),
Wcs100ElevationSourceFactory(serviceAddress, coverageName, outputFormat)
)

override fun clone() = Wcs100ElevationCoverage(
serviceAddress, coverageName, outputFormat, tileMatrixSet, elevationSourceFactory
)

companion object {
const val SERVICE_TYPE = "WCS 1.0.0"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import earth.worldwind.WorldWind
import earth.worldwind.geom.Angle
import earth.worldwind.geom.Sector
import earth.worldwind.geom.Sector.Companion.fromDegrees
import earth.worldwind.geom.TileMatrix
import earth.worldwind.geom.TileMatrixSet
import earth.worldwind.geom.TileMatrixSet.Companion.fromTilePyramid
import earth.worldwind.globe.elevation.ElevationSource
import earth.worldwind.globe.elevation.ElevationSourceFactory
import earth.worldwind.globe.elevation.coverage.TiledElevationCoverage
import earth.worldwind.globe.elevation.coverage.WebElevationCoverage
import earth.worldwind.ogc.gml.GmlRectifiedGrid
Expand All @@ -29,22 +33,24 @@ import nl.adaptivity.xmlutil.serialization.XML

/**
* Generates elevations from OGC Web Coverage Service (WCS) version 2.0.1.
* <br></br>
* Wcs201ElevationCoverage requires the WCS service address, coverage name, and coverage bounding sector. Get Coverage
* requests generated for retrieving data use the WCS version 2.0.1 protocol and are limited to the "image/tiff" format
* and the EPSG:4326 coordinate system. Wcs201ElevationCoverage does not perform version negotiation and assumes the
* service supports the format and coordinate system parameters detailed here. The subset CRS is configured as EPSG:4326
* and the axis labels are set as "Lat" and "Long". The scaling axis labels are set as:
* <br></br>
* http://www.opengis.net/def/axis/OGC/1/i and http://www.opengis.net/def/axis/OGC/1/j
*/
open class Wcs201ElevationCoverage: TiledElevationCoverage, WebElevationCoverage {
open class Wcs201ElevationCoverage private constructor(
final override val serviceAddress: String,
final override val coverageName: String,
final override val outputFormat: String,
tileMatrixSet: TileMatrixSet,
elevationSourceFactory: ElevationSourceFactory,
serviceMetadata: String? = null
): TiledElevationCoverage(tileMatrixSet, elevationSourceFactory), WebElevationCoverage {
final override val serviceType = SERVICE_TYPE
final override val serviceAddress: String
final override val coverageName: String
final override var serviceMetadata: String? = null
final override var serviceMetadata = serviceMetadata
private set
final override val outputFormat: String
protected val xml = XML(serializersModule) { defaultPolicy { ignoreUnknownChildren() } }

/**
Expand All @@ -59,14 +65,11 @@ open class Wcs201ElevationCoverage: TiledElevationCoverage, WebElevationCoverage
*
* @throws IllegalArgumentException If any argument is null or if the number of levels is less than 0
*/
constructor(serviceAddress: String, coverageName: String, outputFormat: String, sector: Sector, resolution: Angle): super(
TileMatrixSet.fromTilePyramid(sector, if (sector.isFullSphere) 2 else 1, 1, 256, 256, resolution),
constructor(serviceAddress: String, coverageName: String, outputFormat: String, sector: Sector, resolution: Angle): this(
serviceAddress, coverageName, outputFormat,
fromTilePyramid(sector, if (sector.isFullSphere) 2 else 1, 1, 256, 256, resolution),
Wcs201ElevationSourceFactory(serviceAddress, coverageName, outputFormat)
) {
this.serviceAddress = serviceAddress
this.coverageName = coverageName
this.outputFormat = outputFormat
}
)

/**
* Attempts to construct a Web Coverage Service (WCS) elevation coverage with the provided service address and
Expand All @@ -79,11 +82,9 @@ open class Wcs201ElevationCoverage: TiledElevationCoverage, WebElevationCoverage
* @param outputFormat the WCS source data format
* @param serviceMetadata optional WCS coverage description XML string to avoid online coverages request
*/
constructor(serviceAddress: String, coverageName: String, outputFormat: String, serviceMetadata: String? = null) {
this.serviceAddress = serviceAddress
this.coverageName = coverageName
this.outputFormat = outputFormat
this.serviceMetadata = serviceMetadata
constructor(serviceAddress: String, coverageName: String, outputFormat: String, serviceMetadata: String? = null): this(
serviceAddress, coverageName, outputFormat, TileMatrixSet(), dummyElevationSource(), serviceMetadata
) {
mainScope.launch {
try {
// Fetch the DescribeCoverage document and determine the bounding box and number of levels
Expand Down Expand Up @@ -114,6 +115,13 @@ open class Wcs201ElevationCoverage: TiledElevationCoverage, WebElevationCoverage
}
}

override fun clone() = Wcs201ElevationCoverage(
serviceAddress, coverageName, outputFormat, tileMatrixSet, elevationSourceFactory, serviceMetadata
).also {
it.displayName = displayName
it.sector.copy(sector)
}

protected open fun tileMatrixSetFromCoverageDescription(coverageDescription: Wcs201CoverageDescription): TileMatrixSet {
val srsName = coverageDescription.boundedBy.envelope.srsName
require(srsName != null && srsName.contains("4326")) {
Expand Down Expand Up @@ -183,5 +191,15 @@ open class Wcs201ElevationCoverage: TiledElevationCoverage, WebElevationCoverage

companion object {
const val SERVICE_TYPE = "WCS 2.0.1"

/**
* This is a dummy workaround for asynchronously defined ElevationSourceFactory
*/
private fun dummyElevationSource() = object : ElevationSourceFactory {
override val contentType = "Dummy"

override fun createElevationSource(tileMatrix: TileMatrix, row: Int, column: Int) =
ElevationSource.fromUnrecognized(Any())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,34 @@ import earth.worldwind.globe.elevation.coverage.WebElevationCoverage

/**
* Generates elevations from OGC Web Map Service (WMS) version 1.3.0.
*
* @param serviceAddress OGC Web Map Service (WMS) server address
* @param coverageName comma-separated coverage names
* @param outputFormat required image format
* @param sector bounding sector
* @param resolution the target resolution in angular value of latitude per texel
*/
open class WmsElevationCoverage(
open class WmsElevationCoverage private constructor(
override val serviceAddress: String, override val coverageName: String, override val outputFormat: String,
sector: Sector, resolution: Angle
): TiledElevationCoverage(
buildTileMatrixSet(sector, resolution), buildElevationSourceFactory(serviceAddress, coverageName, outputFormat)
), WebElevationCoverage {
tileMatrixSet: TileMatrixSet, elevationSourceFactory: ElevationSourceFactory
): TiledElevationCoverage(tileMatrixSet, elevationSourceFactory), WebElevationCoverage {
override val serviceType = SERVICE_TYPE

/**
* @param serviceAddress OGC Web Map Service (WMS) server address
* @param coverageName comma-separated coverage names
* @param outputFormat required image format
* @param sector bounding sector
* @param resolution the target resolution in angular value of latitude per texel
*/
constructor(
serviceAddress: String, coverageName: String, outputFormat: String, sector: Sector, resolution: Angle
) : this(
serviceAddress, coverageName, outputFormat,
buildTileMatrixSet(sector, resolution), buildElevationSourceFactory(serviceAddress, coverageName, outputFormat)
)

override fun clone() = WmsElevationCoverage(
serviceAddress, coverageName, outputFormat, tileMatrixSet, elevationSourceFactory
).also {
it.displayName = displayName
it.sector.copy(sector)
}

companion object {
const val SERVICE_TYPE = "WMS"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package earth.worldwind.ogc

import earth.worldwind.layer.TiledImageLayer
import earth.worldwind.layer.WebImageLayer
import earth.worldwind.shape.TiledSurfaceImage

class WmsImageLayer(
override val serviceAddress: String,
override val serviceMetadata: String,
override val layerName: String,
name: String? = null,
tiledSurfaceImage: TiledSurfaceImage? = null
) : TiledImageLayer(name, tiledSurfaceImage), WebImageLayer {
override val serviceType = SERVICE_TYPE
override val imageFormat get() = (tiledSurfaceImage?.tileFactory as? WmsTileFactory)?.imageFormat ?: "image/png"
override val isTransparent get() = (tiledSurfaceImage?.tileFactory as? WmsTileFactory)?.isTransparent ?: true

override fun clone() = WmsImageLayer(
serviceAddress, serviceMetadata, layerName, displayName, tiledSurfaceImage?.clone()
)

companion object {
const val SERVICE_TYPE = "WMS"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.eygraber.uri.Uri
import earth.worldwind.geom.Ellipsoid
import earth.worldwind.geom.Sector
import earth.worldwind.layer.TiledImageLayer
import earth.worldwind.layer.WebImageLayer
import earth.worldwind.ogc.wms.WmsCapabilities
import earth.worldwind.ogc.wms.WmsLayer
import earth.worldwind.shape.TiledSurfaceImage
Expand All @@ -24,8 +23,6 @@ import kotlinx.serialization.decodeFromString
import nl.adaptivity.xmlutil.serialization.XML

object WmsLayerFactory {

const val SERVICE_TYPE = "WMS"
private const val DEFAULT_WMS_NUM_LEVELS = 20
private val compatibleImageFormats = listOf("image/png", "image/jpg", "image/jpeg", "image/gif", "image/bmp")
private val xml = XML { defaultPolicy { ignoreUnknownChildren() } }
Expand Down Expand Up @@ -71,16 +68,10 @@ object WmsLayerFactory {

private fun createWmsImageLayer(
serviceAddress: String, serviceMetadata: String, wmsLayers: List<WmsLayer>, name: String?
): TiledImageLayer = object : TiledImageLayer(
) = WmsImageLayer(
serviceAddress, serviceMetadata, wmsLayers.mapNotNull { lc -> lc.name }.joinToString(","),
name ?: wmsLayers.joinToString(",") { lc -> lc.title }, createWmsSurfaceImage(wmsLayers)
), WebImageLayer {
override val serviceType = SERVICE_TYPE
override val serviceAddress = serviceAddress
override val serviceMetadata = serviceMetadata
override val layerName = wmsLayers.mapNotNull { lc -> lc.name }.joinToString(",")
override val imageFormat get() = (tiledSurfaceImage?.tileFactory as? WmsTileFactory)?.imageFormat ?: "image/png"
override val isTransparent get() = (tiledSurfaceImage?.tileFactory as? WmsTileFactory)?.isTransparent ?: true
}
)

private fun createWmsSurfaceImage(wmsLayers: List<WmsLayer>): TiledSurfaceImage {
// Check if the server supports multiple layer request
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package earth.worldwind.ogc

import earth.worldwind.layer.TiledImageLayer
import earth.worldwind.layer.WebImageLayer
import earth.worldwind.shape.TiledSurfaceImage

class WmtsImageLayer(
override val serviceAddress: String,
override val serviceMetadata: String,
override val layerName: String,
name: String? = null,
tiledSurfaceImage: TiledSurfaceImage? = null
) : TiledImageLayer(name, tiledSurfaceImage), WebImageLayer {
override val serviceType = SERVICE_TYPE
override val imageFormat get() = (tiledSurfaceImage?.tileFactory as? WmtsTileFactory)?.imageFormat ?: "image/png"
override val isTransparent = true // WMTS has no transparency data available

override fun clone() = WmtsImageLayer(
serviceAddress, serviceMetadata, layerName, displayName, tiledSurfaceImage?.clone()
)

companion object {
const val SERVICE_TYPE = "WMTS"
}
}
Loading

0 comments on commit ca992db

Please sign in to comment.