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

geotrellis.process.TileSetRasterLayer.scala Maven / Gradle / Ivy

The newest version!
package geotrellis.process

import geotrellis._
import geotrellis.raster._
import geotrellis.util._
import geotrellis.data.arg.ArgReader
import geotrellis.raster._

import com.typesafe.config.Config
import java.io.File

import spire.syntax.cfor._
import scala.collection.mutable

object TileSetRasterLayerBuilder
extends RasterLayerBuilder {
  def apply(ds:Option[String],jsonPath:String, json:Config):RasterLayer = {
    val tileDir = 
      if(json.hasPath("path")) {
        val f = new File(json.getString("path"))
        if(f.isAbsolute) {
          f
        } else {
          new File(new File(jsonPath).getParent, f.getPath)
        }
      } else {
        // Default to a directory with the same name as the layer name.
        new File(new File(jsonPath).getParent, getName(json))
      }

    if(!tileDir.isDirectory) {
      throw new java.io.IOException(s"[ERROR] Raster in catalog points Tile Directory '${tileDir.getPath}'" +
                                     ", but this is not a valid directory.")
    } else {
      val tileDirPath = tileDir.getPath
      val layoutCols = json.getInt("layout_cols")
      val layoutRows = json.getInt("layout_rows")
      val pixelCols = json.getInt("pixel_cols")
      val pixelRows = json.getInt("pixel_rows")
      val cols = layoutCols * pixelCols
      val rows = layoutRows * pixelRows

      val (cw,ch) = getCellWidthAndHeight(json)

      val rasterExtent = RasterExtent(getExtent(json), cw, ch, cols, rows)
      val layout = TileLayout(layoutCols, layoutRows, pixelCols, pixelRows)

      val info = 
        RasterLayerInfo(
          LayerId(ds,getName(json)),
          getRasterType(json),
          rasterExtent,
          getEpsg(json),
          getXskew(json),
          getYskew(json),
          layout,
          getCacheFlag(json)
        )

      new TileSetRasterLayer(info,tileDirPath,layout)
    }
  }
}

object TileSetRasterLayer {
  def tileName(id:LayerId,col:Int,row:Int) = 
    s"${id}_${col}_${row}"

  def tilePath(path:String, id:LayerId, col:Int, row:Int) =
    Filesystem.join(path, s"${id.name}_${col}_${row}.arg")
}

class TileSetRasterLayer(info:RasterLayerInfo, 
                         val tileDirPath:String,
                         val tileLayout:TileLayout)
extends RasterLayer(info) {
  def getRaster(targetExtent:Option[RasterExtent]) = {
    targetExtent match {
      case Some(re) =>
        // If a specific raster extent is asked for,
        // load an ArrayRasterData for the extent.

        // Create destination raster data
        val data = RasterData.emptyByType(info.rasterType,re.cols,re.rows)

        // Collect data from intersecting tiles
        val targetExtent = re.extent
        val resLayout = tileLayout.getResolutionLayout(info.rasterExtent)
        val loader = getTileLoader()
        cfor(0)(_ < tileLayout.tileCols, _ + 1) { tcol => 
          cfor(0)(_ < tileLayout.tileRows, _ + 1) { trow =>
            val sourceRasterExtent = resLayout.getRasterExtent(tcol,trow)
            val sourceExtent = resLayout.getExtent(tcol,trow)
            sourceExtent.intersect(targetExtent) match {
              case Some(ext) =>
                val cols = math.ceil((ext.xmax - ext.xmin) / re.cellwidth).toInt
                val rows = math.ceil((ext.ymax - ext.ymin) / re.cellheight).toInt
                val tileRe = RasterExtent(ext,re.cellwidth,re.cellheight,cols,rows)

                // Read section of the tile
                val rasterPart = loader.getTile(tcol,trow,Some(tileRe))

                // Copy over the values to the correct place in the raster data
                cfor(0)(_ < cols, _ + 1) { partCol =>
                  cfor(0)(_ < rows, _ + 1) { partRow =>
                    val dataCol = re.mapXToGrid(tileRe.gridColToMap(partCol))
                    val dataRow = re.mapYToGrid(tileRe.gridRowToMap(partRow))
                    if(!(dataCol < 0 || dataCol >= re.cols ||
                         dataRow < 0 || dataRow >= re.rows)) {
                      if(info.rasterType.isDouble) {
                        data.setDouble(dataCol, dataRow, rasterPart.getDouble(partCol, partRow))
                      } else {
                        data.set(dataCol, dataRow, rasterPart.get(partCol, partRow))
                      }
                    }
                  }
                }

              case None => // pass
            }
          }
        }
        Raster(data, re)
      case None => 
        val loader = getTileLoader()
        val tiles = mutable.ListBuffer[Raster]()
        cfor(0)(_ < tileLayout.tileCols, _ + 1) { col =>
          cfor(0)(_ < tileLayout.tileRows, _ + 1) { row =>
            tiles += loader.getTile(col,row,None)
          }
        }
        TileRaster(tiles.toSeq, info.rasterExtent, tileLayout).toArrayRaster
    }
  }

  override
  def getRaster(extent:Extent):Raster = 
    CroppedRaster(getRaster(None),extent)

  def getTile(col:Int, row:Int, targetExtent:Option[RasterExtent]) = 
    getTileLoader().getTile(col,row,targetExtent)

  def getTileLoader() =
    if(isCached)
      new CacheTileLoader(info,tileLayout,getCache)
    else 
      new DiskTileLoader(info,tileLayout,tileDirPath)

  def cache(c:Cache[String]) = {
    for(col <- 0 until tileLayout.tileCols) {
      for(row <- 0 until tileLayout.tileRows) {
        val path = TileSetRasterLayer.tilePath(tileDirPath, info.id, col, row)
        c.insert(TileSetRasterLayer.tileName(info.id,col,row), Filesystem.slurp(path))
      }
    }
  }
}

abstract class TileLoader(tileSetInfo:RasterLayerInfo,
                          tileLayout:TileLayout) extends Serializable {
  val resLayout = tileLayout.getResolutionLayout(tileSetInfo.rasterExtent)

  val rasterExtent = tileSetInfo.rasterExtent

  def getTile(col:Int,row:Int,targetExtent:Option[RasterExtent]):Raster = {
    val re = resLayout.getRasterExtent(col,row)
    if(col < 0 || row < 0 ||
       tileLayout.tileCols <= col || tileLayout.tileRows <= row) {
      val tre = 
        targetExtent match {
          case Some(x) => x
          case None => re
        }

      Raster(IntConstant(NODATA, tre.cols, tre.rows),  rasterExtent)
    } else {
      loadRaster(col,row,re,targetExtent)
    }
  }

  protected def loadRaster(col:Int,row:Int,re:RasterExtent,tre:Option[RasterExtent]):Raster
}

class DiskTileLoader(tileSetInfo:RasterLayerInfo,
                     tileLayout:TileLayout,
                     tileDirPath:String)
extends TileLoader(tileSetInfo,tileLayout) {
  def loadRaster(col:Int,row:Int,re:RasterExtent,targetExtent:Option[RasterExtent]) = {
    val path = TileSetRasterLayer.tilePath(tileDirPath, tileSetInfo.id, col, row)
    targetExtent match {
      case Some(tre) => 
        ArgReader.read(path,tileSetInfo.rasterType,re,tre)
      case None => 
        ArgReader.read(path,tileSetInfo.rasterType,re)
    }

  }
}

class CacheTileLoader(info:RasterLayerInfo,
                      tileLayout:TileLayout,
                      c:Cache[String])
extends TileLoader(info,tileLayout) {
  def loadRaster(col:Int,row:Int,re:RasterExtent,targetExtent:Option[RasterExtent]) = {
    c.lookup[Array[Byte]](TileSetRasterLayer.tileName(info.id,col,row)) match {
      case Some(bytes) =>
        targetExtent match {
          case Some(tre) => 
            val data = ArgReader.warpBytes(bytes,info.rasterType,re,tre)
            Raster(data,tre)
          case None => 
            val data = RasterData.fromArrayByte(bytes,info.rasterType,re.cols,re.rows)
            Raster(data,re)
        }
      case None =>
        sys.error("Cache problem: Tile thinks it's cached but it is in fact not cached.")
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy