locationtech / geotrellis

GeoTrellis is a geographic data processing engine for high performance applications.
http://geotrellis.io
Other
1.33k stars 360 forks source link

FileCOGLayerWriter can't write WGS84 FloatingLayoutScheme #2609

Open Frophie opened 6 years ago

Frophie commented 6 years ago

Tried the COG example with a data in WGS84 crs.

Tile Layout of layer and ZoomedLayoutScheme do not match. GridExtent(Extent(89.99986111111112, 49.93347222222266, 91.06652777777774, 51.00013888888889),2.777777777777657E-4,2.7777777777766394E-4) != GridExtent(Extent(-180.0, -89.99999, 179.99999, 89.99999),1.4062499609375,0.703124921875)
java.lang.RuntimeException: Tile Layout of layer and ZoomedLayoutScheme do not match. GridExtent(Extent(89.99986111111112, 49.93347222222266, 91.06652777777774, 51.00013888888889),2.777777777777657E-4,2.7777777777766394E-4) != GridExtent(Extent(-180.0, -89.99999, 179.99999, 89.99999),1.4062499609375,0.703124921875)
    at scala.sys.package$.error(package.scala:27)
    at geotrellis.spark.io.cog.COGLayer$.fromLayerRDD(COGLayer.scala:74)
    at geotrellis.spark.io.cog.COGLayerWriter$class.write(COGLayerWriter.scala:72)
    at geotrellis.spark.io.file.cog.FileCOGLayerWriter.write(FileCOGLayerWriter.scala:20)
    at geotrellis.spark.io.cog.COGLayerWriter$class.write(COGLayerWriter.scala:57)
    at geotrellis.spark.io.file.cog.FileCOGLayerWriter.write(FileCOGLayerWriter.scala:20)

Maybe it doesn't support this situation? then the example code need a adjustment.

kuroismith commented 6 years ago

same issue, any solutions?

pomadchin commented 6 years ago

Hey @Frophie and @kuroismith. It looks like you were using not a ZoomedLayoutScheme for your dataset(s), but used LocalLayoutScheme or FloatingLayoutScheme. At this point we support only ZoomedLayoutScheme usage. This problem sounds more like a limitation of the initial COGs support, and would be resolved in the future.

aklink commented 5 years ago

@pomadchin I don't have a final solution, but as first try I have "hacked" into geotrellis.spark.io.cog.generateGeoTiffRDD in https://github.com/locationtech/geotrellis/blob/master/spark/src/main/scala/geotrellis/spark/io/cog/COGLayer.scala

Since the "hacked" function header is not compatible to original one, it breaks the Geotrellis workflow, therefore must be called manually to use it, e.g.

val singlebandrdd = rdd.asInstanceOf[TileLayerRDD[SpatialKey]]
val geotiff = generateGeoTiffRDDFloatingLayoutScheme(singlebandrdd, 4, singlebandrdd.metadata, NoCompression)  // Generate GeoTiff without reprojection (FloatingLayoutScheme, not ZoomedLayoutScheme)
val singlebandgeotiff : RDD[(SpatialKey,Array[Byte])] = singlebandGeoTiffToByte(geotiff.asInstanceOf[RDD[(SpatialKey,SinglebandGeoTiff)]])
SaveToS3(singlebandgeotiff, keyToURI, { p => p }, s3Client) 
private def generateGeoTiffRDDFloatingLayoutScheme(
                                  rdd: RDD[(SpatialKey, Tile)],
                                  //zoomRange: ZoomRange,
                                  zoomFactor: Int = 4,
                                  //layoutScheme: FloatingLayoutScheme,
                                  metadata: TileLayerMetadata[SpatialKey],
                                  //cellType: CellType,
                                  compression: Compression//,
                                  //resampleMethod: ResampleMethod
                                ): RDD[(SpatialKey, GeoTiff[Tile])] = {
    val kwFomat = KryoWrapper(implicitly[JsonFormat[SpatialKey]])
    val crs = metadata.crs//layoutScheme.crs
    val cellType = metadata.cellType

    //val zoomFactor = 4
    val zoomProduct : Int = (2**zoomFactor).toInt
    val minZoomLayout = LayoutDefinition(GridExtent(metadata.layout.extent, CellSize(metadata.cellSize.width, metadata.cellSize.height)), metadata.layout.tileCols * zoomProduct)//layoutScheme.levelForZoom(zoomRange.minZoom).layout
    val maxZoomLayout = metadata.layout//layoutScheme.levelForZoom(zoomRange.maxZoom).layout

    val options: GeoTiffOptions =
      GeoTiffOptions(
        storageMethod = Tiled(maxZoomLayout.tileCols, maxZoomLayout.tileRows),
        compression = compression
      )

    rdd.
      mapPartitions { partition =>
        partition.map { case (key, tile) =>
          val extent: Extent = key.extent(maxZoomLayout)//key.getComponent[SpatialKey].extent(maxZoomLayout)
          val minZoomSpatialKey = minZoomLayout.mapTransform(extent.center)

          (minZoomSpatialKey, (key, tile))//(key.setComponent(minZoomSpatialKey), (key, tile))
        }
      }.
      groupByKey(new HashPartitioner(rdd.partitions.length)).
      mapPartitions { partition =>
        val keyFormat = kwFomat.value
        partition.map { case (key, tiles) =>
          val cogExtent = key.extent(minZoomLayout)// key.getComponent[SpatialKey].extent(minZoomLayout)
          val centerToCenter: Extent = {
            val h = maxZoomLayout.cellheight / 2
            val w = maxZoomLayout.cellwidth / 2
            Extent(
              xmin = cogExtent.xmin + w,
              ymin = cogExtent.ymin + h,
              xmax = cogExtent.xmax - w,
              ymax = cogExtent.ymax - h)
          }
          val cogTileBounds: GridBounds = maxZoomLayout.mapTransform.extentToBounds(centerToCenter)
          val cogLayout: TileLayout = maxZoomLayout.layoutForBounds(cogTileBounds).tileLayout

          val segments = tiles.map { case (key, value) =>
            val SpatialKey(col, row) = key  //key.getComponent[SpatialKey]
            (SpatialKey(col - cogTileBounds.colMin, row - cogTileBounds.rowMin), value)
          }

          val cogTile = GeoTiffBuilder[Tile].makeTile(
            segments.iterator,
            cogLayout,
            cellType,
            Tiled(cogLayout.tileCols, cogLayout.tileRows),
            compression)

          val cogTiff = GeoTiffBuilder[Tile].makeGeoTiff(
            cogTile, cogExtent, crs,
            Tags(Map("GT_KEY" -> keyFormat.write(key).prettyPrint), Nil),
            options
          )//.withOverviews(resampleMethod)

          (key, cogTiff)
        }
      }
  }