There are three limitations on fileDimensions
. It need to be a multiple of shardSize
, it cannot be larger than 131072
, and the size of the file before compression cannot exceed 17179869183
bytes. To come up with a way to find the maximum valid fileDimensions
is a tricky little problem.
My take on this require you to specify the number of bits the pixels in each layer have. I couldn't come up with a way to automatically figure this value out based on an image. It would be great if someone else have an idea how to do that.
var image = ee.Image(0).float()
var region = ee.Geometry(Map.getBounds(true))
var scale = 30
var shardSize = 256 // Will not really change your tile size
var size = imageSize({
image: image,
region: ee.Geometry(Map.getBounds(true)),
scale: scale,
bits: 32,
shardSize: shardSize
})
print(size)
// Export require client-side objects, so we evaluate to
// get the fileDimensions as a client-side object.
size.evaluate(function (size) {
try {
Export.image.toDrive({
image: image,
scale: scale,
maxPixels: 1e13,
region: region,
shardSize: shardSize,
fileDimensions: size.fileDimensions
})
} catch(e) {
print('ERROR: ', e)
}
})
/**
* Calculates information about the image size.
*
* Arguments:
*
* params - an object with:
*
* image - the ee.Image to get the size for
* region - the ee.Geometry to include
* scale - resolution in meters per pixel
* bits - the number of bits for each band in the image
* float (32-bit), double (64-bit), int (16-bit)
* int8, uint8, int16, uint16, int32, uint32
* are 8-, 16-, and 32-bit, as their name hints
* shardSize - size in pixels of the shards Google split your processing work into
*
* Returns an ee.Dictionary with:
*
* fileDimensions - largest possible fileDimensions when exporting
* fileSize - the uncompressed file size of each tile
* imageDimensions - total image dimensions
* imageSize - the uncompressed file size of the whole image
*/
function imageSize(params) {
var image = params.image
var region = params.region
var scale = params.scale || 30
var bits = params.bits || 64
var shardSize = params.shardSize || 256
var numberOfBands = image.bandNames().size()
var bytesPerPixel = numberOfBands.multiply(bits).divide(8)
var MAX_BYTES = 17179869183
var MAX_SIDE = 131072
var maxMultipleSide = prevMultiple(MAX_SIDE, shardSize)
var maxPixels = ee.Number(MAX_BYTES).divide(bytesPerPixel).floor()
var coordinates = ee.List(region.bounds().coordinates().get(0))
var sw = ee.Geometry.Point(coordinates.get(0))
var se = ee.Geometry.Point(coordinates.get(1))
var ne = ee.Geometry.Point(coordinates.get(2))
var totalWidth = sw.distance(se).divide(scale).int()
var totalHeight = se.distance(ne).divide(scale).int()
var clampedWidth = clampUp(totalWidth)
var clampedHeight = clampUp(totalHeight)
var dimensionsOptions = ee.List([
[clampedWidth, clampDown(maxPixels.divide(clampedWidth))],
[clampDown(maxPixels.divide(clampedHeight)), clampedHeight]
])
var optionSizes = dimensionsOptions.map(function (dimensions) {
return ee.List(dimensions).reduce(ee.Reducer.product())
})
var dimensions = ee.List(dimensionsOptions.sort(optionSizes).get(1))
return ee.Dictionary({
imageDimensions: ee.List([totalWidth, totalHeight]),
imageSize: bytesPerPixel.multiply(totalWidth).multiply(totalHeight),
fileDimensions: dimensions,
fileSize: bytesPerPixel.multiply(dimensions.reduce(ee.Reducer.product()))
})
function clampUp(side) {
return nextMultiple(side).min(maxMultipleSide)
}
function clampDown(side) {
return prevMultiple(side).min(maxMultipleSide)
}
function nextMultiple(side) {
return ee.Number(side)
.divide(shardSize).ceil()
.multiply(shardSize)
}
function prevMultiple(side) {
return ee.Number(side)
.divide(shardSize).floor()
.multiply(shardSize)
}
}
https://code.earthengine.google.com/eea7d691959a7c25a9206a107cea638d