batch.py 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780
  1. #!/usr/bin/env python
  2. """An interface to the Earth Engine batch processing system.
  3. Use the static methods on the Export class to create export tasks, call start()
  4. on them to launch them, then poll status() to find out when they are finished.
  5. The public function styling uses camelCase to match the JavaScript names.
  6. """
  7. # pylint: disable=g-bad-name
  8. # pylint: disable=g-bad-import-order
  9. import json
  10. import re
  11. from . import _cloud_api_utils
  12. from . import data
  13. from . import ee_exception
  14. from . import geometry
  15. class Task(object):
  16. """A batch task that can be run on the EE batch processing system."""
  17. def __init__(self, task_id, task_type, state, config=None, name=None):
  18. """Creates a Task with the given ID and configuration.
  19. The constructor is not for public use. Instances can be obtained by:
  20. - Calling the static method Task.list().
  21. - Calling any of the methods on the Export static class.
  22. - Unpickling a previously pickled Task object.
  23. If you're looking for a task's status but don't need a full task object,
  24. ee.data.getTaskStatus() may be appropriate.
  25. Args:
  26. task_id: The task ID, originally obtained through ee.data.newTaskId().
  27. May be None if the ID is not yet known.
  28. task_type: The type of the task; one of the values in Task.Type.
  29. state: The state of the task; one of the values entries in Task.State.
  30. config: The task configuration dictionary. Only necessary if start()
  31. will be called. Fields shared by all tasks are:
  32. - description: The name of the task, a freeform string.
  33. - sourceUrl: An optional URL for the script that generated the task.
  34. Specific task types have other custom config fields.
  35. name: The name of the operation. Only relevant when using the cloud api.
  36. """
  37. self.id = self._request_id = task_id
  38. self.config = config and config.copy()
  39. self.workload_tag = data.getWorkloadTag()
  40. self.task_type = task_type
  41. self.state = state
  42. self.name = name
  43. class Type(object):
  44. EXPORT_IMAGE = 'EXPORT_IMAGE'
  45. EXPORT_MAP = 'EXPORT_TILES'
  46. EXPORT_TABLE = 'EXPORT_FEATURES'
  47. EXPORT_VIDEO = 'EXPORT_VIDEO'
  48. class State(object):
  49. UNSUBMITTED = 'UNSUBMITTED'
  50. READY = 'READY'
  51. RUNNING = 'RUNNING'
  52. COMPLETED = 'COMPLETED'
  53. FAILED = 'FAILED'
  54. CANCEL_REQUESTED = 'CANCEL_REQUESTED'
  55. CANCELLED = 'CANCELLED'
  56. # Export destinations.
  57. class ExportDestination(object):
  58. DRIVE = 'DRIVE'
  59. GCS = 'GOOGLE_CLOUD_STORAGE'
  60. ASSET = 'ASSET'
  61. FEATURE_VIEW = 'FEATURE_VIEW'
  62. def start(self):
  63. """Starts the task. No-op for started tasks."""
  64. if not self.config:
  65. raise ee_exception.EEException(
  66. 'Task config must be specified for tasks to be started.')
  67. if not self._request_id:
  68. self._request_id = data.newTaskId()[0]
  69. # Supply the workload tag, even if empty, to prevent it from being reset
  70. # later.
  71. if 'workloadTag' not in self.config:
  72. self.config['workloadTag'] = self.workload_tag
  73. if self.task_type == Task.Type.EXPORT_IMAGE:
  74. result = data.exportImage(self._request_id, self.config)
  75. elif self.task_type == Task.Type.EXPORT_MAP:
  76. result = data.exportMap(self._request_id, self.config)
  77. elif self.task_type == Task.Type.EXPORT_TABLE:
  78. result = data.exportTable(self._request_id, self.config)
  79. elif self.task_type == Task.Type.EXPORT_VIDEO:
  80. result = data.exportVideo(self._request_id, self.config)
  81. else:
  82. raise ee_exception.EEException(
  83. 'Unknown Task type "{}"'.format(self.task_type))
  84. if not self.id:
  85. self.id = _cloud_api_utils.convert_operation_name_to_task_id(
  86. result['name'])
  87. self.name = result['name']
  88. def status(self):
  89. """Fetches the current status of the task.
  90. Returns:
  91. A dictionary describing the current status of the task as it appears on
  92. the EE server. Includes the following fields:
  93. - state: One of the values in Task.State.
  94. - creation_timestamp_ms: The Unix timestamp of when the task was created.
  95. - update_timestamp_ms: The Unix timestamp of when the task last changed.
  96. - output_url: URL of the output. Appears only if state is COMPLETED.
  97. - error_message: Failure reason. Appears only if state is FAILED.
  98. May also include other fields.
  99. """
  100. if self.id:
  101. result = data.getTaskStatus(self.id)[0]
  102. if result['state'] == 'UNKNOWN':
  103. result['state'] = Task.State.UNSUBMITTED
  104. else:
  105. result = {'state': Task.State.UNSUBMITTED}
  106. return result
  107. def active(self):
  108. """Returns whether the task is still running."""
  109. return self.status()['state'] in (Task.State.READY,
  110. Task.State.RUNNING,
  111. Task.State.CANCEL_REQUESTED)
  112. def cancel(self):
  113. """Cancels the task."""
  114. data.cancelTask(self.id)
  115. @staticmethod
  116. def list():
  117. """Returns the tasks submitted to EE by the current user.
  118. These include all currently running tasks as well as recently canceled or
  119. failed tasks.
  120. Returns:
  121. A list of Tasks.
  122. """
  123. statuses = data.getTaskList()
  124. tasks = []
  125. for status in statuses:
  126. tasks.append(Task(status['id'],
  127. status.get('task_type'),
  128. status.get('state'),
  129. {'description': status.get('description')},
  130. status.get('name')))
  131. return tasks
  132. def __repr__(self):
  133. """Returns a string representation of the task."""
  134. if self.config and self.id:
  135. return '<Task %s %s: %s (%s)>' % (self.id, self.task_type,
  136. self.config['description'], self.state)
  137. elif self.config:
  138. return '<Task %s: %s (%s)>' % (self.task_type, self.config['description'],
  139. self.state)
  140. else:
  141. return '<Task "%s">' % self.id
  142. class Export(object):
  143. """A class with static methods to start export tasks."""
  144. def __init__(self):
  145. """Forbids class instantiation."""
  146. raise AssertionError('This class cannot be instantiated.')
  147. class image(object):
  148. """A static class with methods to start image export tasks."""
  149. def __init__(self):
  150. """Forbids class instantiation."""
  151. raise AssertionError('This class cannot be instantiated.')
  152. def __new__(cls, image, description='myExportImageTask', config=None):
  153. """Creates a task to export an EE Image to Google Drive or Cloud Storage.
  154. Args:
  155. image: The image to be exported.
  156. description: Human-readable name of the task.
  157. config: A dictionary that will be copied and used as parameters
  158. for the task:
  159. - region: The lon,lat coordinates for a LinearRing or Polygon
  160. specifying the region to export. Can be specified as a nested
  161. lists of numbers or a serialized string. Defaults to the image's
  162. region.
  163. - scale: The resolution in meters per pixel.
  164. Defaults to the native resolution of the image assset unless
  165. a crs_transform is specified.
  166. - maxPixels: The maximum allowed number of pixels in the exported
  167. image. The task will fail if the exported region covers
  168. more pixels in the specified projection. Defaults to 100,000,000.
  169. - crs: The coordinate reference system of the exported image's
  170. projection. Defaults to the image's default projection.
  171. - crs_transform: A comma-separated string of 6 numbers describing
  172. the affine transform of the coordinate reference system of the
  173. exported image's projection, in the order: xScale, xShearing,
  174. xTranslation, yShearing, yScale and yTranslation. Defaults to
  175. the image's native CRS transform.
  176. - dimensions: The dimensions of the exported image. Takes either a
  177. single positive integer as the maximum dimension or
  178. "WIDTHxHEIGHT" where WIDTH and HEIGHT are each positive integers.
  179. - skipEmptyTiles: If true, skip writing empty (i.e. fully-masked)
  180. image tiles. Defaults to false.
  181. If exporting to Google Drive (default):
  182. - driveFolder: The Google Drive Folder that the export will reside
  183. in. Note: (a) if the folder name exists at any level, the output
  184. is written to it, (b) if duplicate folder names exist, output is
  185. written to the most recently modified folder, (c) if the folder
  186. name does not exist, a new folder will be created at the root,
  187. and (d) folder names with separators (e.g. 'path/to/file') are
  188. interpreted as literal strings, not system paths. Defaults to
  189. Drive root.
  190. - driveFileNamePrefix: The Google Drive filename for the export.
  191. Defaults to the name of the task.
  192. If exporting to Google Cloud Storage:
  193. - outputBucket: The name of a Cloud Storage bucket for the export.
  194. - outputPrefix: Cloud Storage object name prefix for the export.
  195. Returns:
  196. An unstarted Task that exports the image.
  197. """
  198. config = (config or {}).copy()
  199. if 'driveFileNamePrefix' not in config and 'outputBucket' not in config:
  200. config['driveFileNamePrefix'] = description
  201. if 'driveFileNamePrefix' in config:
  202. return Export.image.toDrive(image, description, **config)
  203. else:
  204. return Export.image.toCloudStorage(image, description, **config)
  205. # Disable argument usage check; arguments are accessed using locals().
  206. # pylint: disable=unused-argument
  207. @staticmethod
  208. def toAsset(
  209. image,
  210. description='myExportImageTask',
  211. assetId=None,
  212. pyramidingPolicy=None,
  213. dimensions=None,
  214. region=None,
  215. scale=None,
  216. crs=None,
  217. crsTransform=None,
  218. maxPixels=None,
  219. **kwargs):
  220. """Creates a task to export an EE Image to an EE Asset.
  221. Args:
  222. image: The image to be exported.
  223. description: Human-readable name of the task.
  224. assetId: The destination asset ID.
  225. pyramidingPolicy: The pyramiding policy to apply to each band in the
  226. image, a dictionary keyed by band name. Values must be
  227. one of: "mean", "sample", "min", "max", or "mode".
  228. Defaults to "mean". A special key, ".default", may be used to
  229. change the default for all bands.
  230. dimensions: The dimensions of the exported image. Takes either a
  231. single positive integer as the maximum dimension or "WIDTHxHEIGHT"
  232. where WIDTH and HEIGHT are each positive integers.
  233. region: The lon,lat coordinates for a LinearRing or Polygon
  234. specifying the region to export. Can be specified as a nested
  235. lists of numbers or a serialized string. Defaults to the image's
  236. region.
  237. scale: The resolution in meters per pixel. Defaults to the
  238. native resolution of the image assset unless a crsTransform
  239. is specified.
  240. crs: The coordinate reference system of the exported image's
  241. projection. Defaults to the image's default projection.
  242. crsTransform: A comma-separated string of 6 numbers describing
  243. the affine transform of the coordinate reference system of the
  244. exported image's projection, in the order: xScale, xShearing,
  245. xTranslation, yShearing, yScale and yTranslation. Defaults to
  246. the image's native CRS transform.
  247. maxPixels: The maximum allowed number of pixels in the exported
  248. image. The task will fail if the exported region covers more
  249. pixels in the specified projection. Defaults to 100,000,000.
  250. **kwargs: Holds other keyword arguments that may have been deprecated
  251. such as 'crs_transform'.
  252. Returns:
  253. An unstarted Task that exports the image as an Earth Engine asset.
  254. """
  255. config = _capture_parameters(locals(), ['image'])
  256. config = _prepare_image_export_config(image, config,
  257. Task.ExportDestination.ASSET)
  258. return _create_export_task(config, Task.Type.EXPORT_IMAGE)
  259. # Disable argument usage check; arguments are accessed using locals().
  260. # pylint: disable=unused-argument
  261. @staticmethod
  262. def toCloudStorage(image,
  263. description='myExportImageTask',
  264. bucket=None,
  265. fileNamePrefix=None,
  266. dimensions=None,
  267. region=None,
  268. scale=None,
  269. crs=None,
  270. crsTransform=None,
  271. maxPixels=None,
  272. shardSize=None,
  273. fileDimensions=None,
  274. skipEmptyTiles=None,
  275. fileFormat=None,
  276. formatOptions=None,
  277. **kwargs):
  278. """Creates a task to export an EE Image to Google Cloud Storage.
  279. Args:
  280. image: The image to be exported.
  281. description: Human-readable name of the task.
  282. bucket: The name of a Cloud Storage bucket for the export.
  283. fileNamePrefix: Cloud Storage object name prefix for the export.
  284. Defaults to the name of the task.
  285. dimensions: The dimensions of the exported image. Takes either a
  286. single positive integer as the maximum dimension or "WIDTHxHEIGHT"
  287. where WIDTH and HEIGHT are each positive integers.
  288. region: The lon,lat coordinates for a LinearRing or Polygon
  289. specifying the region to export. Can be specified as a nested
  290. lists of numbers or a serialized string. Defaults to the image's
  291. region.
  292. scale: The resolution in meters per pixel. Defaults to the
  293. native resolution of the image assset unless a crsTransform
  294. is specified.
  295. crs: The coordinate reference system of the exported image's
  296. projection. Defaults to the image's default projection.
  297. crsTransform: A comma-separated string of 6 numbers describing
  298. the affine transform of the coordinate reference system of the
  299. exported image's projection, in the order: xScale, xShearing,
  300. xTranslation, yShearing, yScale and yTranslation. Defaults to
  301. the image's native CRS transform.
  302. maxPixels: The maximum allowed number of pixels in the exported
  303. image. The task will fail if the exported region covers more
  304. pixels in the specified projection. Defaults to 100,000,000.
  305. shardSize: Size in pixels of the tiles in which this image will be
  306. computed. Defaults to 256.
  307. fileDimensions: The dimensions in pixels of each image file, if the
  308. image is too large to fit in a single file. May specify a
  309. single number to indicate a square shape, or a tuple of two
  310. dimensions to indicate (width,height). Note that the image will
  311. still be clipped to the overall image dimensions. Must be a
  312. multiple of shardSize.
  313. skipEmptyTiles: If true, skip writing empty (i.e. fully-masked)
  314. image tiles. Defaults to false.
  315. fileFormat: The string file format to which the image is exported.
  316. Currently only 'GeoTIFF' and 'TFRecord' are supported, defaults to
  317. 'GeoTIFF'.
  318. formatOptions: A dictionary of string keys to format specific options.
  319. **kwargs: Holds other keyword arguments that may have been deprecated
  320. such as 'crs_transform'.
  321. Returns:
  322. An unstarted Task that exports the image to Google Cloud Storage.
  323. """
  324. config = _capture_parameters(locals(), ['image'])
  325. config = _prepare_image_export_config(image, config,
  326. Task.ExportDestination.GCS)
  327. return _create_export_task(config, Task.Type.EXPORT_IMAGE)
  328. @staticmethod
  329. def toDrive(image,
  330. description='myExportImageTask',
  331. folder=None,
  332. fileNamePrefix=None,
  333. dimensions=None,
  334. region=None,
  335. scale=None,
  336. crs=None,
  337. crsTransform=None,
  338. maxPixels=None,
  339. shardSize=None,
  340. fileDimensions=None,
  341. skipEmptyTiles=None,
  342. fileFormat=None,
  343. formatOptions=None,
  344. **kwargs):
  345. """Creates a task to export an EE Image to Drive.
  346. Args:
  347. image: The image to be exported.
  348. description: Human-readable name of the task.
  349. folder: The name of a unique folder in your Drive account to
  350. export into. Defaults to the root of the drive.
  351. fileNamePrefix: The Google Drive filename for the export.
  352. Defaults to the name of the task.
  353. dimensions: The dimensions of the exported image. Takes either a
  354. single positive integer as the maximum dimension or "WIDTHxHEIGHT"
  355. where WIDTH and HEIGHT are each positive integers.
  356. region: The lon,lat coordinates for a LinearRing or Polygon
  357. specifying the region to export. Can be specified as a nested
  358. lists of numbers or a serialized string. Defaults to the image's
  359. region.
  360. scale: The resolution in meters per pixel. Defaults to the
  361. native resolution of the image assset unless a crsTransform
  362. is specified.
  363. crs: The coordinate reference system of the exported image's
  364. projection. Defaults to the image's default projection.
  365. crsTransform: A comma-separated string of 6 numbers describing
  366. the affine transform of the coordinate reference system of the
  367. exported image's projection, in the order: xScale, xShearing,
  368. xTranslation, yShearing, yScale and yTranslation. Defaults to
  369. the image's native CRS transform.
  370. maxPixels: The maximum allowed number of pixels in the exported
  371. image. The task will fail if the exported region covers more
  372. pixels in the specified projection. Defaults to 100,000,000.
  373. shardSize: Size in pixels of the tiles in which this image will be
  374. computed. Defaults to 256.
  375. fileDimensions: The dimensions in pixels of each image file, if the
  376. image is too large to fit in a single file. May specify a
  377. single number to indicate a square shape, or a tuple of two
  378. dimensions to indicate (width,height). Note that the image will
  379. still be clipped to the overall image dimensions. Must be a
  380. multiple of shardSize.
  381. skipEmptyTiles: If true, skip writing empty (i.e. fully-masked)
  382. image tiles. Defaults to false.
  383. fileFormat: The string file format to which the image is exported.
  384. Currently only 'GeoTIFF' and 'TFRecord' are supported, defaults to
  385. 'GeoTIFF'.
  386. formatOptions: A dictionary of string keys to format specific options.
  387. **kwargs: Holds other keyword arguments that may have been deprecated
  388. such as 'crs_transform', 'driveFolder', and 'driveFileNamePrefix'.
  389. Returns:
  390. An unstarted Task that exports the image to Drive.
  391. """
  392. config = _capture_parameters(locals(), ['image'])
  393. config = _prepare_image_export_config(image, config,
  394. Task.ExportDestination.DRIVE)
  395. return _create_export_task(config, Task.Type.EXPORT_IMAGE)
  396. # pylint: enable=unused-argument
  397. class map(object):
  398. """A class with a static method to start map export tasks."""
  399. def __init__(self):
  400. """Forbids class instantiation."""
  401. raise AssertionError('This class cannot be instantiated.')
  402. # Disable argument usage check; arguments are accessed using locals().
  403. # pylint: disable=unused-argument
  404. @staticmethod
  405. def toCloudStorage(image, description='myExportMapTask', bucket=None,
  406. fileFormat=None, path=None, writePublicTiles=None,
  407. maxZoom=None, scale=None, minZoom=None,
  408. region=None, skipEmptyTiles=None, mapsApiKey=None,
  409. **kwargs):
  410. """Creates a task to export an Image as a pyramid of map tiles.
  411. Exports a rectangular pyramid of map tiles for use with web map
  412. viewers. The map tiles will be accompanied by a reference
  413. index.html file that displays them using the Google Maps API,
  414. and an earth.html file for opening the map on Google Earth.
  415. Args:
  416. image: The image to export as tiles.
  417. description: Human-readable name of the task.
  418. bucket: The destination bucket to write to.
  419. fileFormat: The map tiles' file format, one of 'auto', 'png',
  420. or 'jpeg'. Defaults to 'auto', which means that opaque tiles
  421. will be encoded as 'jpg' and tiles with transparency will be
  422. encoded as 'png'.
  423. path: The string used as the output's path. A trailing '/'
  424. is optional. Defaults to the task's description.
  425. writePublicTiles: Whether to write public tiles instead of using the
  426. bucket's default object ACL. Defaults to True and requires the
  427. invoker to be an OWNER of bucket.
  428. maxZoom: The maximum zoom level of the map tiles to export.
  429. scale: The max image resolution in meters per pixel, as an alternative
  430. to 'maxZoom'. The scale will be converted to the most appropriate
  431. maximum zoom level at the equator.
  432. minZoom: The optional minimum zoom level of the map tiles to export.
  433. region: The lon,lat coordinates for a LinearRing or Polygon
  434. specifying the region to export. Can be specified as a nested
  435. lists of numbers or a serialized string. Map tiles will be
  436. produced in the rectangular region containing this geometry.
  437. Defaults to the image's region.
  438. skipEmptyTiles: If true, skip writing empty (i.e. fully-transparent)
  439. map tiles. Defaults to false.
  440. mapsApiKey: Used in index.html to initialize the Google Maps API. This
  441. removes the "development purposes only" message from the map.
  442. **kwargs: Holds other keyword arguments that may have been deprecated
  443. such as 'crs_transform'.
  444. Returns:
  445. An unstarted Task that exports the image to Google Cloud Storage.
  446. """
  447. config = _capture_parameters(locals(), ['image'])
  448. config = _prepare_map_export_config(image, config)
  449. return _create_export_task(config, Task.Type.EXPORT_MAP)
  450. # pylint: enable=unused-argument
  451. class table(object):
  452. """A class with static methods to start table export tasks."""
  453. def __init__(self):
  454. """Forbids class instantiation."""
  455. raise AssertionError('This class cannot be instantiated.')
  456. def __new__(cls, collection, description='myExportTableTask', config=None):
  457. """Export an EE FeatureCollection as a table.
  458. The exported table will reside in Google Drive, Cloud Storage, or as a
  459. FeatureView.
  460. Args:
  461. collection: The feature collection to be exported.
  462. description: Human-readable name of the task.
  463. config: A dictionary that will be copied and used as parameters
  464. for the task:
  465. - fileFormat: The output format: "CSV" (default), "GeoJSON", "KML",
  466. "KMZ", or "SHP".
  467. If exporting to Google Drive (default):
  468. - driveFolder: The Google Drive Folder that the export will reside
  469. in. Note: (a) if the folder name exists at any level, the output
  470. is written to it, (b) if duplicate folder names exist, output is
  471. written to the most recently modified folder, (c) if the folder
  472. name does not exist, a new folder will be created at the root,
  473. and (d) folder names with separators (e.g. 'path/to/file') are
  474. interpreted as literal strings, not system paths. Defaults to
  475. Drive root.
  476. - driveFileNamePrefix: The Google Drive filename for the export.
  477. Defaults to the name of the task.
  478. If exporting to Google Cloud Storage:
  479. - outputBucket: The name of a Cloud Storage bucket for the export.
  480. - outputPrefix: Cloud Storage object name prefix for the export.
  481. If exporting to FeatureView:
  482. - thinning_options: Options that control the density at which
  483. features are displayed per tile.
  484. - ranking_options: Options for assigning z-order ranks and thinning
  485. ranks to features.
  486. Returns:
  487. An unstarted Task that exports the table.
  488. """
  489. config = (config or {}).copy()
  490. if 'driveFileNamePrefix' not in config and 'outputBucket' not in config:
  491. config['driveFileNamePrefix'] = description
  492. if 'driveFileNamePrefix' in config:
  493. return Export.table.toDrive(collection, description, **config)
  494. else:
  495. return Export.table.toCloudStorage(collection, description, **config)
  496. # Disable argument usage check; arguments are accessed using locals().
  497. # pylint: disable=unused-argument
  498. @staticmethod
  499. def toCloudStorage(collection,
  500. description='myExportTableTask',
  501. bucket=None,
  502. fileNamePrefix=None,
  503. fileFormat=None,
  504. selectors=None,
  505. maxVertices=None,
  506. **kwargs):
  507. """Creates a task to export a FeatureCollection to Google Cloud Storage.
  508. Args:
  509. collection: The feature collection to be exported.
  510. description: Human-readable name of the task.
  511. bucket: The name of a Cloud Storage bucket for the export.
  512. fileNamePrefix: Cloud Storage object name prefix for the export.
  513. Defaults to the name of the task.
  514. fileFormat: The output format: "CSV" (default), "GeoJSON", "KML", "KMZ",
  515. "SHP", or "TFRecord".
  516. selectors: The list of properties to include in the output, as a list
  517. of strings or a comma-separated string. By default, all properties
  518. are included.
  519. maxVertices:
  520. Max number of uncut vertices per geometry; geometries with more
  521. vertices will be cut into pieces smaller than this size.
  522. **kwargs: Holds other keyword arguments that may have been deprecated
  523. such as 'outputBucket'.
  524. Returns:
  525. An unstarted Task that exports the table to Google Cloud Storage.
  526. """
  527. config = _capture_parameters(locals(), ['collection'])
  528. config = _prepare_table_export_config(collection, config,
  529. Task.ExportDestination.GCS)
  530. return _create_export_task(config, Task.Type.EXPORT_TABLE)
  531. @staticmethod
  532. def toDrive(collection,
  533. description='myExportTableTask',
  534. folder=None,
  535. fileNamePrefix=None,
  536. fileFormat=None,
  537. selectors=None,
  538. maxVertices=None,
  539. **kwargs):
  540. """Creates a task to export a FeatureCollection to Drive.
  541. Args:
  542. collection: The feature collection to be exported.
  543. description: Human-readable name of the task.
  544. folder: The name of a unique folder in your Drive account to
  545. export into. Defaults to the root of the drive.
  546. fileNamePrefix: The Google Drive filename for the export.
  547. Defaults to the name of the task.
  548. fileFormat: The output format: "CSV" (default), "GeoJSON", "KML",
  549. "KMZ", "SHP", or "TFRecord".
  550. selectors: The list of properties to include in the output, as a list
  551. of strings or a comma-separated string. By default, all properties
  552. are included.
  553. maxVertices:
  554. Max number of uncut vertices per geometry; geometries with more
  555. vertices will be cut into pieces smaller than this size.
  556. **kwargs: Holds other keyword arguments that may have been deprecated
  557. such as 'driveFolder' and 'driveFileNamePrefix'.
  558. Returns:
  559. An unstarted Task that exports the table.
  560. """
  561. config = _capture_parameters(locals(), ['collection'])
  562. config = _prepare_table_export_config(collection, config,
  563. Task.ExportDestination.DRIVE)
  564. return _create_export_task(config, Task.Type.EXPORT_TABLE)
  565. @staticmethod
  566. def toAsset(
  567. collection,
  568. description='myExportTableTask',
  569. assetId=None,
  570. maxVertices=None,
  571. **kwargs):
  572. """Creates a task to export a FeatureCollection to an EE table asset.
  573. Args:
  574. collection: The feature collection to be exported.
  575. description: Human-readable name of the task.
  576. assetId: The destination asset ID.
  577. maxVertices:
  578. Max number of uncut vertices per geometry; geometries with more
  579. vertices will be cut into pieces smaller than this size.
  580. **kwargs: Holds other keyword arguments that may have been deprecated.
  581. Returns:
  582. An unstarted Task that exports the table.
  583. """
  584. config = {
  585. 'description': description,
  586. 'assetId': assetId,
  587. 'maxVertices': maxVertices,
  588. }
  589. config = {k: v for k, v, in config.items() if v is not None}
  590. config = _prepare_table_export_config(collection, config,
  591. Task.ExportDestination.ASSET)
  592. return _create_export_task(config, Task.Type.EXPORT_TABLE)
  593. @staticmethod
  594. def toFeatureView(
  595. collection,
  596. description='myExportTableTask',
  597. assetId=None,
  598. ingestionTimeParameters=None,
  599. **kwargs):
  600. """Creates a task to export a FeatureCollection to a FeatureView.
  601. Args:
  602. collection: The feature collection to be exported.
  603. description: Human-readable name of the task.
  604. assetId: The destination asset ID.
  605. ingestionTimeParameters: The FeatureView ingestion time parameters.
  606. **kwargs: Holds other keyword arguments that may have been deprecated.
  607. Returns:
  608. An unstarted Task that exports the table.
  609. """
  610. config = {
  611. 'description': description,
  612. 'assetId': assetId,
  613. 'ingestionTimeParameters': ingestionTimeParameters,
  614. }
  615. config = {k: v for k, v, in config.items() if v is not None}
  616. config = _prepare_table_export_config(collection, config,
  617. Task.ExportDestination.FEATURE_VIEW)
  618. return _create_export_task(config, Task.Type.EXPORT_TABLE)
  619. class video(object):
  620. """A class with static methods to start video export task."""
  621. def __init__(self):
  622. """Forbids class instantiation."""
  623. raise AssertionError('This class cannot be instantiated.')
  624. def __new__(cls, collection, description='myExportVideoTask', config=None):
  625. """Exports an EE ImageCollection as a video.
  626. The exported video will reside in Google Drive or Cloud Storage.
  627. Args:
  628. collection: The image collection to be exported. The collection must
  629. only contain RGB images.
  630. description: Human-readable name of the task.
  631. config: A dictionary of configuration parameters for the task:
  632. - region: The lon,lat coordinates for a LinearRing or Polygon
  633. specifying the region to export. Can be specified as a nested
  634. lists of numbers or a serialized string. Defaults to the first
  635. image's region.
  636. - scale: The resolution in meters per pixel.
  637. - crs: The coordinate reference system of the exported video's
  638. projection. Defaults to SR-ORG:6627.
  639. - crs_transform: A comma-separated string of 6 numbers describing
  640. the affine transform of the coordinate reference system of the
  641. exported video's projection, in the order: xScale, xShearing,
  642. xTranslation, yShearing, yScale and yTranslation. Defaults to
  643. the image collection's native CRS transform.
  644. - dimensions: The dimensions of the exported video. Takes either a
  645. single positive integer as the maximum dimension or "WIDTHxHEIGHT"
  646. where WIDTH and HEIGHT are each positive integers.
  647. - framesPerSecond: A number between .1 and 120 describing the
  648. framerate of the exported video.
  649. - maxPixels: The maximum number of pixels per frame.
  650. Defaults to 1e8 pixels per frame. By setting this explicitly,
  651. you may raise or lower the limit.
  652. - maxFrames: The maximum number of frames.
  653. Defaults to 1000 frames. By setting this explicitly, you may
  654. raise or lower the limit.
  655. If exporting to Google Drive (default):
  656. - driveFolder: The Google Drive Folder that the export will reside
  657. in. Note: (a) if the folder name exists at any level, the output
  658. is written to it, (b) if duplicate folder names exist, output is
  659. written to the most recently modified folder, (c) if the folder
  660. name does not exist, a new folder will be created at the root,
  661. and (d) folder names with separators (e.g. 'path/to/file') are
  662. interpreted as literal strings, not system paths. Defaults to
  663. Drive root.
  664. - driveFileNamePrefix: The Google Drive filename for the export.
  665. Defaults to the name of the task.
  666. If exporting to Google Cloud Storage:
  667. - outputBucket: The name of a Cloud Storage bucket for the export.
  668. - outputPrefix: Cloud Storage object name prefix for the export.
  669. Returns:
  670. An unstarted Task that exports the video.
  671. """
  672. config = (config or {}).copy()
  673. if 'driveFileNamePrefix' not in config and 'outputBucket' not in config:
  674. config['driveFileNamePrefix'] = description
  675. if 'driveFileNamePrefix' in config:
  676. return Export.video.toDrive(collection, description, **config)
  677. else:
  678. return Export.video.toCloudStorage(collection, description, **config)
  679. # Disable argument usage check; arguments are accessed using locals().
  680. # pylint: disable=unused-argument
  681. @staticmethod
  682. def toCloudStorage(collection, description='myExportVideoTask',
  683. bucket=None, fileNamePrefix=None, framesPerSecond=None,
  684. dimensions=None, region=None, scale=None, crs=None,
  685. crsTransform=None, maxPixels=None,
  686. maxFrames=None, **kwargs):
  687. """Creates a task to export an ImageCollection video to Cloud Storage.
  688. Args:
  689. collection: The image collection to be exported. The collection must
  690. only contain RGB images.
  691. description: Human-readable name of the task.
  692. bucket: The name of a Cloud Storage bucket for the export.
  693. fileNamePrefix: Cloud Storage object name prefix for the export.
  694. Defaults to the task's description.
  695. framesPerSecond: A number between .1 and 120 describing the
  696. framerate of the exported video.
  697. dimensions: The dimensions of the exported video. Takes either a
  698. single positive integer as the maximum dimension or "WIDTHxHEIGHT"
  699. where WIDTH and HEIGHT are each positive integers.
  700. region: The lon,lat coordinates for a LinearRing or Polygon
  701. specifying the region to export. Can be specified as a nested
  702. lists of numbers or a serialized string. Defaults to the first
  703. image's region.
  704. scale: The resolution in meters per pixel.
  705. crs: The coordinate reference system of the exported video's
  706. projection. Defaults to SR-ORG:6627.
  707. crsTransform: A comma-separated string of 6 numbers describing
  708. the affine transform of the coordinate reference system of the
  709. exported video's projection, in the order: xScale, xShearing,
  710. xTranslation, yShearing, yScale and yTranslation. Defaults to
  711. the image collection's native CRS transform.
  712. maxPixels: The maximum number of pixels per frame.
  713. Defaults to 1e8 pixels per frame. By setting this explicitly,
  714. you may raise or lower the limit.
  715. maxFrames: The maximum number of frames to export.
  716. Defaults to 1000 frames. By setting this explicitly, you may
  717. raise or lower the limit.
  718. **kwargs: Holds other keyword arguments that may have been deprecated
  719. such as 'crs_transform'.
  720. Returns:
  721. An unstarted Task that exports the image collection
  722. to Google Cloud Storage.
  723. """
  724. config = _capture_parameters(locals(), ['collection'])
  725. config = _prepare_video_export_config(collection, config,
  726. Task.ExportDestination.GCS)
  727. return _create_export_task(config, Task.Type.EXPORT_VIDEO)
  728. @staticmethod
  729. def toDrive(collection, description='myExportVideoTask',
  730. folder=None, fileNamePrefix=None, framesPerSecond=None,
  731. dimensions=None, region=None, scale=None, crs=None,
  732. crsTransform=None, maxPixels=None, maxFrames=None, **kwargs):
  733. """Creates a task to export an ImageCollection as a video to Drive.
  734. Args:
  735. collection: The image collection to be exported. The collection must
  736. only contain RGB images.
  737. description: Human-readable name of the task.
  738. folder: The name of a unique folder in your Drive account to
  739. export into. Defaults to the root of the drive.
  740. fileNamePrefix: The Google Drive filename for the export.
  741. Defaults to the name of the task.
  742. framesPerSecond: A number between .1 and 120 describing the
  743. framerate of the exported video.
  744. dimensions: The dimensions of the exported video. Takes either a
  745. single positive integer as the maximum dimension or "WIDTHxHEIGHT"
  746. where WIDTH and HEIGHT are each positive integers.
  747. region: The lon,lat coordinates for a LinearRing or Polygon
  748. specifying the region to export. Can be specified as a nested
  749. lists of numbers or a serialized string. Defaults to the first
  750. image's region.
  751. scale: The resolution in meters per pixel.
  752. crs: The coordinate reference system of the exported video's
  753. projection. Defaults to SR-ORG:6627.
  754. crsTransform: A comma-separated string of 6 numbers describing
  755. the affine transform of the coordinate reference system of the
  756. exported video's projection, in the order: xScale, xShearing,
  757. xTranslation, yShearing, yScale and yTranslation. Defaults to
  758. the image collection's native CRS transform.
  759. maxPixels: The maximum number of pixels per frame.
  760. Defaults to 1e8 pixels per frame. By setting this explicitly,
  761. you may raise or lower the limit.
  762. maxFrames: The maximum number of frames to export.
  763. Defaults to 1000 frames. By setting this explicitly, you may
  764. raise or lower the limit.
  765. **kwargs: Holds other keyword arguments that may have been deprecated
  766. such as 'crs_transform'.
  767. Returns:
  768. An unstarted Task that exports the image collection to Drive.
  769. """
  770. config = _capture_parameters(locals(), ['collection'])
  771. config = _prepare_video_export_config(collection, config,
  772. Task.ExportDestination.DRIVE)
  773. return _create_export_task(config, Task.Type.EXPORT_VIDEO)
  774. def _CheckConfigDisallowedPrefixes(config, prefix):
  775. for key in config:
  776. if key.startswith(prefix):
  777. raise ee_exception.EEException(
  778. 'Export config parameter prefix "{}" disallowed, found "{}"'.
  779. format(prefix, key))
  780. # Mapping from file formats to prefixes attached to format specific config.
  781. FORMAT_PREFIX_MAP = {'GEOTIFF': 'tiff', 'TFRECORD': 'tfrecord'}
  782. # Configuration field specifying file format for image exports.
  783. IMAGE_FORMAT_FIELD = 'fileFormat'
  784. # Image format-specific options dictionary config field.
  785. IMAGE_FORMAT_OPTIONS_FIELD = 'formatOptions'
  786. # Format-specific options permitted in formatOptions config parameter.
  787. ALLOWED_FORMAT_OPTIONS = {
  788. 'tiffCloudOptimized', 'tiffFileDimensions', 'tfrecordPatchDimensions',
  789. 'tfrecordKernelSize', 'tfrecordCompressed', 'tfrecordMaxFileSize',
  790. 'tfrecordDefaultValue', 'tfrecordTensorDepths', 'tfrecordSequenceData',
  791. 'tfrecordCollapseBands', 'tfrecordMaskedThreshold'
  792. }
  793. def _ConvertConfigParams(config):
  794. """Converts numeric sequences into comma-separated string representations."""
  795. updatedConfig = {}
  796. # Non-Cloud API expects that pyramiding policy is a JSON string.
  797. if 'pyramidingPolicy' in config:
  798. updatedConfig['pyramidingPolicy'] = json.dumps(config['pyramidingPolicy'])
  799. for k, v in config.items():
  800. if v and isinstance(v, (list, tuple)):
  801. # Leave nested lists/tuples alone. We're only interested in converting
  802. # lists of strings or numbers.
  803. if not isinstance(v[0], (list, tuple)):
  804. updatedConfig[k] = ','.join(str(e) for e in v)
  805. return updatedConfig
  806. # TODO(user): This method and its uses are very hack-y, and once we're using One
  807. # Platform API we should stop sending arbitrary parameters from "options".
  808. def ConvertFormatSpecificParams(configDict):
  809. """Mutates configDict into server params by extracting format options.
  810. For example:
  811. {'fileFormat': 'GeoTIFF', 'formatOptions': {'cloudOptimized': true}}
  812. becomes:
  813. {'fileFormat': 'GeoTIFF', 'tiffCloudOptimized': true}
  814. Also performs checks to make sure any specified options are valid and/or
  815. won't collide with top level arguments when converted to server-friendly
  816. parameters.
  817. Args:
  818. configDict: A task config dict
  819. Raises:
  820. EEException: We were unable to create format specific parameters for the
  821. server.
  822. """
  823. formatString = 'GeoTIFF'
  824. if IMAGE_FORMAT_FIELD in configDict:
  825. formatString = configDict[IMAGE_FORMAT_FIELD]
  826. formatString = formatString.upper()
  827. if formatString not in FORMAT_PREFIX_MAP:
  828. raise ee_exception.EEException(
  829. 'Invalid file format. Currently only "GeoTIFF" and "TFRecord" is '
  830. 'supported.')
  831. if IMAGE_FORMAT_OPTIONS_FIELD in configDict:
  832. options = configDict.pop(IMAGE_FORMAT_OPTIONS_FIELD)
  833. if set(options) & set(configDict):
  834. raise ee_exception.EEException(
  835. 'Parameter specified at least twice: once in config, '
  836. 'and once in format options.')
  837. prefix = FORMAT_PREFIX_MAP[formatString]
  838. _CheckConfigDisallowedPrefixes(configDict, prefix)
  839. prefixedOptions = {}
  840. for key, value in options.items():
  841. prefixedKey = prefix + key[:1].upper() + key[1:]
  842. if prefixedKey not in ALLOWED_FORMAT_OPTIONS:
  843. raise ee_exception.EEException(
  844. '"{}" is not a valid option for "{}".'.format(
  845. key, formatString))
  846. prefixedOptions[prefixedKey] = value
  847. prefixedOptions.update(_ConvertConfigParams(prefixedOptions))
  848. configDict.update(prefixedOptions)
  849. def _prepare_image_export_config(image, config, export_destination):
  850. """Performs all preparation steps for an image export.
  851. Args:
  852. image: The Image to be exported.
  853. config: All the user-specified export parameters. May be modified.
  854. export_destination: One of the Task.ExportDestination values.
  855. Returns:
  856. A config dict containing all information required for the export.
  857. """
  858. # Supply some defaults.
  859. if (export_destination != Task.ExportDestination.ASSET and
  860. 'fileFormat' not in config):
  861. config['fileFormat'] = 'GeoTIFF'
  862. _canonicalize_parameters(config, export_destination)
  863. image, config = image.prepare_for_export(config)
  864. # Build an ExportImageRequest. Delete values from "config" as we go so we
  865. # can check at the end for any leftovers. Any computed objects will be
  866. # serialised in data.py before the request is sent.
  867. request = {}
  868. request['expression'] = image
  869. if 'description' in config:
  870. request['description'] = config.pop('description')
  871. if export_destination == Task.ExportDestination.ASSET:
  872. asset_export_options = {}
  873. asset_export_options[
  874. 'earthEngineDestination'] = _build_earth_engine_destination(config)
  875. # This can only be set by internal users.
  876. if 'tileSize' in config and 'shardSize' in config:
  877. raise ee_exception.EEException(
  878. 'Both "shardSize" and "tileSize" cannot be set.')
  879. if 'tileSize' in config:
  880. asset_export_options['tileSize'] = {
  881. 'value': int(config.pop('tileSize'))}
  882. # "shardSize" is the old name for "tileSize".
  883. if 'shardSize' in config:
  884. asset_export_options['tileSize'] = {'value': int(config.pop('shardSize'))}
  885. if 'pyramidingPolicy' in config:
  886. pyramiding_policy = config.pop('pyramidingPolicy')
  887. if '.default' in pyramiding_policy:
  888. asset_export_options[
  889. 'pyramidingPolicy'] = pyramiding_policy.pop('.default').upper()
  890. if pyramiding_policy:
  891. asset_export_options['pyramidingPolicyOverrides'] = {
  892. band: policy.upper() for band, policy in pyramiding_policy.items()
  893. }
  894. request['assetExportOptions'] = asset_export_options
  895. else:
  896. request['fileExportOptions'] = _build_image_file_export_options(
  897. config, export_destination)
  898. if 'maxPixels' in config:
  899. # This field is an Int64Value, so it needs an inner "value" field, and
  900. # the value itself is a string, not an integer, in the JSON encoding.
  901. request['maxPixels'] = {'value': str(int(config.pop('maxPixels')))}
  902. # This can only be set by internal users.
  903. if 'maxWorkers' in config:
  904. request['maxWorkers'] = {'value': int(config.pop('maxWorkers'))}
  905. # Of the remaining fields in ExportImageRequest:
  906. # - All the values that would go into the PixelGrid should have been folded
  907. # into the image's Expression.
  908. # - The request ID will be populated when the Task is created.
  909. # We've been deleting config parameters as we handle them. Anything left
  910. # over is a problem.
  911. if config:
  912. raise ee_exception.EEException(
  913. 'Unknown configuration options: {}.'.format(config))
  914. return request
  915. def _prepare_map_export_config(image, config):
  916. """Performs all preparation steps for a map export.
  917. Args:
  918. image: The Image to be exported.
  919. config: All the user-specified export parameters. May be modified.
  920. Returns:
  921. A config dict containing all information required for the export.
  922. """
  923. _canonicalize_parameters(config, Task.ExportDestination.GCS)
  924. # We have to protect the "scale" parameter as prepare_for_export will try
  925. # to interpret it inappropriately.
  926. scale = config.pop('scale', None)
  927. image, config = image.prepare_for_export(config)
  928. if scale is not None:
  929. config['scale'] = scale
  930. if 'fileFormat' not in config:
  931. config['fileFormat'] = 'auto'
  932. if 'writePublicTiles' not in config:
  933. config['writePublicTiles'] = True
  934. request = {}
  935. request['expression'] = image
  936. if 'description' in config:
  937. request['description'] = config.pop('description')
  938. request['tileOptions'] = _build_tile_options(config)
  939. request['tileExportOptions'] = _build_image_file_export_options(
  940. config, Task.ExportDestination.GCS)
  941. # This can only be set by internal users.
  942. if 'maxWorkers' in config:
  943. request['maxWorkers'] = {'value': int(config.pop('maxWorkers'))}
  944. if config:
  945. raise ee_exception.EEException(
  946. 'Unknown configuration options: {}.'.format(config))
  947. return request
  948. def _prepare_table_export_config(collection, config, export_destination):
  949. """Performs all preparation steps for a table export.
  950. Args:
  951. collection: The FeatureCollection to be exported.
  952. config: All the user-specified export parameters. May be modified.
  953. export_destination: One of the Task.ExportDestination values.
  954. Returns:
  955. A config dict containing all information required for the export.
  956. """
  957. if (export_destination != Task.ExportDestination.ASSET and
  958. export_destination != Task.ExportDestination.FEATURE_VIEW):
  959. if 'fileFormat' not in config:
  960. config['fileFormat'] = 'CSV'
  961. _canonicalize_parameters(config, export_destination)
  962. # Build an ExportMapRequest. Delete values from "config" as we go so we
  963. # can check at the end for any leftovers. Any computed objects will be
  964. # serialised in data.py before the request is sent.
  965. request = {}
  966. request['expression'] = collection
  967. if 'description' in config:
  968. request['description'] = config.pop('description')
  969. if export_destination == Task.ExportDestination.ASSET:
  970. request['assetExportOptions'] = {
  971. 'earthEngineDestination': _build_earth_engine_destination(config)
  972. }
  973. elif export_destination == Task.ExportDestination.FEATURE_VIEW:
  974. request['featureViewExportOptions'] = {
  975. 'featureViewDestination':
  976. _build_feature_view_destination(config),
  977. 'ingestionTimeParameters':
  978. build_ingestion_time_parameters(
  979. config.pop('ingestionTimeParameters', None))
  980. }
  981. else:
  982. request['fileExportOptions'] = _build_table_file_export_options(
  983. config, export_destination)
  984. if 'selectors' in config:
  985. # Strings have been turned into lists but we still might be holding a
  986. # tuple or other non-list iterable.
  987. request['selectors'] = list(config.pop('selectors'))
  988. if 'maxVertices' in config:
  989. request['maxVertices'] = {'value': int(config.pop('maxVertices'))}
  990. # This can only be set by internal users.
  991. if 'maxWorkers' in config:
  992. request['maxWorkers'] = {'value': int(config.pop('maxWorkers'))}
  993. if config:
  994. raise ee_exception.EEException(
  995. 'Unknown configuration options: {}.'.format(config))
  996. return request
  997. def _prepare_video_export_config(collection, config, export_destination):
  998. """Performs all preparation steps for a video export.
  999. Args:
  1000. collection: The ImageCollection to be exported as a video.
  1001. config: All the user-specified export parameters. May be modified.
  1002. export_destination: One of the Task.ExportDestination values.
  1003. Returns:
  1004. A config dict containing all information required for the export.
  1005. """
  1006. _canonicalize_parameters(config, export_destination)
  1007. if 'crs' not in config:
  1008. config['crs'] = 'SR-ORG:6627'
  1009. collection, config = collection.prepare_for_export(config)
  1010. request = {}
  1011. request['expression'] = collection
  1012. if 'description' in config:
  1013. request['description'] = config.pop('description')
  1014. video_options = _build_video_options(config)
  1015. if video_options:
  1016. request['videoOptions'] = video_options
  1017. request['fileExportOptions'] = _build_video_file_export_options(
  1018. config, export_destination)
  1019. # This can only be set by internal users.
  1020. if 'maxWorkers' in config:
  1021. request['maxWorkers'] = {'value': int(config.pop('maxWorkers'))}
  1022. if config:
  1023. raise ee_exception.EEException(
  1024. 'Unknown configuration options: {}.'.format(config))
  1025. return request
  1026. def _build_image_file_export_options(config, export_destination):
  1027. """Builds an ImageFileExportOptions from values in a config dict.
  1028. Args:
  1029. config: All the user-specified export parameters. Will be modified in-place
  1030. by removing parameters used in the ImageFileExportOptions.
  1031. export_destination: One of the Task.ExportDestination values.
  1032. Returns:
  1033. An ImageFileExportOptions containing information extracted from config.
  1034. """
  1035. file_export_options = {}
  1036. file_format = _cloud_api_utils.convert_to_image_file_format(
  1037. config.pop('fileFormat'))
  1038. file_export_options['fileFormat'] = file_format
  1039. if export_destination == Task.ExportDestination.DRIVE:
  1040. file_export_options['driveDestination'] = _build_drive_destination(
  1041. config)
  1042. elif export_destination == Task.ExportDestination.GCS:
  1043. file_export_options[
  1044. 'cloudStorageDestination'] = _build_cloud_storage_destination(
  1045. config)
  1046. else:
  1047. raise ee_exception.EEException(
  1048. '"{}" is not a valid export destination'.format(export_destination))
  1049. file_format_options = config.pop(IMAGE_FORMAT_OPTIONS_FIELD, {})
  1050. if file_format == 'GEO_TIFF':
  1051. geo_tiff_options = {}
  1052. if file_format_options.pop('cloudOptimized', False):
  1053. geo_tiff_options['cloudOptimized'] = True
  1054. file_dimensions = file_format_options.pop('fileDimensions', None)
  1055. if 'fileDimensions' in config:
  1056. if file_dimensions is not None:
  1057. raise ee_exception.EEException('File dimensions specified twice.')
  1058. file_dimensions = config.pop('fileDimensions')
  1059. if file_dimensions is not None:
  1060. if isinstance(file_dimensions, int):
  1061. file_dimensions = (file_dimensions, file_dimensions)
  1062. geo_tiff_options['tileDimensions'] = {
  1063. 'width': file_dimensions[0],
  1064. 'height': file_dimensions[1]
  1065. }
  1066. if config.pop('skipEmptyTiles', False):
  1067. geo_tiff_options['skipEmptyFiles'] = True
  1068. if config.get('shardSize', None):
  1069. geo_tiff_options['tileSize'] = {'value': config.pop('shardSize')}
  1070. if geo_tiff_options:
  1071. file_export_options['geoTiffOptions'] = geo_tiff_options
  1072. elif file_format == 'TF_RECORD_IMAGE':
  1073. tf_record_options = {}
  1074. if 'patchDimensions' in file_format_options:
  1075. tile_dimensions = file_format_options.pop('patchDimensions')
  1076. tf_record_options['tileDimensions'] = (
  1077. _cloud_api_utils.convert_to_grid_dimensions(tile_dimensions))
  1078. if 'kernelSize' in file_format_options:
  1079. margin_dimensions = file_format_options.pop('kernelSize')
  1080. tf_record_options['marginDimensions'] = (
  1081. _cloud_api_utils.convert_to_grid_dimensions(margin_dimensions))
  1082. if file_format_options.pop('compressed', False):
  1083. tf_record_options['compress'] = True
  1084. if 'maxFileSize' in file_format_options:
  1085. # This field is an Int64Value, so it needs an inner "value" field, and
  1086. # the value itself is a string, not an integer, in the JSON encoding.
  1087. tf_record_options['maxSizeBytes'] = {
  1088. 'value': str(int(file_format_options.pop('maxFileSize')))
  1089. }
  1090. if 'defaultValue' in file_format_options:
  1091. tf_record_options['defaultValue'] = file_format_options.pop(
  1092. 'defaultValue')
  1093. if 'tensorDepths' in file_format_options:
  1094. tensor_depths = file_format_options.pop('tensorDepths')
  1095. if not isinstance(tensor_depths, dict):
  1096. # This used to be a list of integers.
  1097. raise ee_exception.EEException(
  1098. 'tensorDepths must be a map from band name to the depth '
  1099. 'of the tensor for that band')
  1100. tf_record_options['tensorDepths'] = tensor_depths
  1101. if file_format_options.pop('sequenceData', False):
  1102. tf_record_options['sequenceData'] = True
  1103. if file_format_options.pop('collapseBands', False):
  1104. tf_record_options['collapseBands'] = True
  1105. if 'maskedThreshold' in file_format_options:
  1106. tf_record_options['maxMaskedRatio'] = {
  1107. 'value': file_format_options.pop('maskedThreshold')
  1108. }
  1109. if tf_record_options:
  1110. file_export_options['tfRecordOptions'] = tf_record_options
  1111. if file_format_options:
  1112. raise ee_exception.EEException(
  1113. 'Unknown file format options: {}.'.format(file_format_options))
  1114. return file_export_options
  1115. def _build_table_file_export_options(config, export_destination):
  1116. """Builds a TableFileExportOptions from values in a config dict.
  1117. Args:
  1118. config: All the user-specified export parameters. Will be modified in-place
  1119. by removing parameters used in the TableFileExportOptions.
  1120. export_destination: One of the Task.ExportDestination values.
  1121. Returns:
  1122. A TableFileExportOptions containing information extracted from config.
  1123. """
  1124. file_export_options = {}
  1125. file_format = _cloud_api_utils.convert_to_table_file_format(
  1126. config.pop('fileFormat'))
  1127. file_export_options['fileFormat'] = file_format
  1128. if export_destination == Task.ExportDestination.DRIVE:
  1129. file_export_options['driveDestination'] = _build_drive_destination(
  1130. config)
  1131. elif export_destination == Task.ExportDestination.GCS:
  1132. file_export_options[
  1133. 'cloudStorageDestination'] = _build_cloud_storage_destination(
  1134. config)
  1135. else:
  1136. raise ee_exception.EEException(
  1137. '"{}" is not a valid export destination'.format(export_destination))
  1138. return file_export_options
  1139. def _build_video_options(config):
  1140. """Builds a VideoOptions from values in a config dict.
  1141. Args:
  1142. config: All the user-specified export parameters. Will be modified in-place
  1143. by removing parameters used in the VideoOptions.
  1144. Returns:
  1145. A VideoOptions containing information extracted from config.
  1146. """
  1147. video_options = {}
  1148. if 'framesPerSecond' in config:
  1149. video_options['framesPerSecond'] = config.pop('framesPerSecond')
  1150. if 'maxFrames' in config:
  1151. video_options['maxFrames'] = config.pop('maxFrames')
  1152. if 'maxPixels' in config:
  1153. video_options['maxPixelsPerFrame'] = {
  1154. 'value': str(int(config.pop('maxPixels')))}
  1155. return video_options
  1156. def _build_video_file_export_options(config, export_destination):
  1157. """Builds a VideoFileExportOptions from values in a config dict.
  1158. Args:
  1159. config: All the user-specified export parameters. Will be modified in-place
  1160. by removing parameters used in the VideoFileExportOptions.
  1161. export_destination: One of the Task.ExportDestination values.
  1162. Returns:
  1163. A VideoFileExportOptions containing information extracted from config.
  1164. """
  1165. file_export_options = {}
  1166. # There's only one file format currently.
  1167. file_export_options['fileFormat'] = 'MP4'
  1168. if export_destination == Task.ExportDestination.DRIVE:
  1169. file_export_options['driveDestination'] = _build_drive_destination(
  1170. config)
  1171. elif export_destination == Task.ExportDestination.GCS:
  1172. file_export_options[
  1173. 'cloudStorageDestination'] = _build_cloud_storage_destination(
  1174. config)
  1175. else:
  1176. raise ee_exception.EEException(
  1177. '"{}" is not a valid export destination'.format(export_destination))
  1178. return file_export_options
  1179. def _prepare_classifier_export_config(classifier, config, export_destination):
  1180. """Performs all preparation steps for a classifier export.
  1181. Args:
  1182. classifier: The Classifier to be exported.
  1183. config: All the user-specified export parameters. May be modified.
  1184. export_destination: One of the Task.ExportDestination values.
  1185. Returns:
  1186. A config dict containing all information required for the export.
  1187. """
  1188. request = {}
  1189. request['expression'] = classifier
  1190. if 'description' in config:
  1191. request['description'] = config.pop('description')
  1192. if export_destination == Task.ExportDestination.ASSET:
  1193. request['assetExportOptions'] = {
  1194. 'earthEngineDestination': _build_earth_engine_destination(config)
  1195. }
  1196. else:
  1197. raise ValueError('Only the "ASSET" destination type is supported.')
  1198. return request
  1199. def _build_drive_destination(config):
  1200. """Builds a DriveDestination from values in a config dict.
  1201. Args:
  1202. config: All the user-specified export parameters. Will be modified in-place
  1203. by removing parameters used in the DriveDestination.
  1204. Returns:
  1205. A DriveDestination containing information extracted from config.
  1206. """
  1207. drive_destination = {}
  1208. if 'driveFolder' in config:
  1209. drive_destination['folder'] = config.pop('driveFolder')
  1210. if 'driveFileNamePrefix' in config:
  1211. drive_destination['filenamePrefix'] = config.pop(
  1212. 'driveFileNamePrefix')
  1213. return drive_destination
  1214. def _build_cloud_storage_destination(config):
  1215. """Builds a CloudStorageDestination from values in a config dict.
  1216. Args:
  1217. config: All the user-specified export parameters. Will be modified in-place
  1218. by removing parameters used in the CloudStorageDestination.
  1219. Returns:
  1220. A CloudStorageDestination containing information extracted from
  1221. config.
  1222. """
  1223. cloud_storage_export_destination = {'bucket': config.pop('outputBucket')}
  1224. if 'outputPrefix' in config:
  1225. cloud_storage_export_destination['filenamePrefix'] = config.pop(
  1226. 'outputPrefix')
  1227. if config.pop('writePublicTiles', False):
  1228. cloud_storage_export_destination['permissions'] = 'PUBLIC'
  1229. return cloud_storage_export_destination
  1230. def _build_tile_options(config):
  1231. """Builds a TileOptions from values in a config dict.
  1232. Args:
  1233. config: All the user-specified export parameters. Will be modified in-place
  1234. by removing parameters used in the TileOptions.
  1235. Returns:
  1236. A TileOptions containing information extracted from config.
  1237. """
  1238. tile_options = {}
  1239. if 'maxZoom' in config:
  1240. if 'scale' in config:
  1241. raise ee_exception.EEException('Both maxZoom and scale are specified.')
  1242. tile_options['endZoom'] = config.pop('maxZoom')
  1243. if 'endZoom' in config:
  1244. if 'scale' in config:
  1245. raise ee_exception.EEException('Both endZoom and scale are specified.')
  1246. tile_options['endZoom'] = config.pop('endZoom')
  1247. if 'scale' in config:
  1248. tile_options['scale'] = config.pop('scale')
  1249. if 'minZoom' in config:
  1250. tile_options['startZoom'] = config.pop('minZoom')
  1251. if 'startZoom' in config:
  1252. tile_options['startZoom'] = config.pop('startZoom')
  1253. if config.pop('skipEmptyTiles', False):
  1254. tile_options['skipEmpty'] = True
  1255. if 'skipEmpty' in config:
  1256. tile_options['skipEmpty'] = config.pop('skipEmpty')
  1257. if 'mapsApiKey' in config:
  1258. tile_options['mapsApiKey'] = config.pop('mapsApiKey')
  1259. return tile_options
  1260. def _build_earth_engine_destination(config):
  1261. """Builds an EarthEngineDestination from values in a config dict.
  1262. Args:
  1263. config: All the user-specified export parameters. Will be modified in-place
  1264. by removing parameters used in the EarthEngineDestination.
  1265. Returns:
  1266. An EarthEngineDestination containing information extracted from
  1267. config.
  1268. """
  1269. return {
  1270. 'name':
  1271. _cloud_api_utils.convert_asset_id_to_asset_name(
  1272. config.pop('assetId')),
  1273. }
  1274. def _build_feature_view_destination(config):
  1275. """Builds a FeatureViewDestination from values in a config dict.
  1276. Args:
  1277. config: All the user-specified export parameters. Will be modified in-place
  1278. by removing parameters used in the FeatureViewDestination.
  1279. Returns:
  1280. A FeatureViewDestination containing information extracted from
  1281. config.
  1282. """
  1283. feature_view_destination = {
  1284. 'name':
  1285. _cloud_api_utils.convert_asset_id_to_asset_name(
  1286. config.pop('assetId')),
  1287. }
  1288. return feature_view_destination
  1289. def _get_rank_by_one_thing_rule(rule_str):
  1290. """Returns a RankByOneThingRule dict created from the rank-by-one-thing rule.
  1291. Args:
  1292. rule_str: The rule string. The string is expected in the format:
  1293. `rule_type rule_direction`. Valid rule types include `.minZoomLevel`,
  1294. `.geometryType`, or an arbitrary string to represent attribute rules.
  1295. Valid rule directions include `ASC` or `DESC`.
  1296. Returns:
  1297. A RankByOneThingRule object containing information extracted from rule_str.
  1298. """
  1299. matches = re.findall(r'^([\S]+.*)\s+(ASC|DESC)$', rule_str.strip())
  1300. if not matches:
  1301. raise ee_exception.EEException(
  1302. ('Ranking rule format is invalid. Each rule should be defined by a '
  1303. 'rule type and a direction (ASC or DESC), separated by a space. '
  1304. 'Valid rule types are: .geometryType, .minZoomLevel, or a feature '
  1305. 'property name.'))
  1306. output = {}
  1307. rule_type, rule_dir = matches[0]
  1308. if rule_type == '.geometryType':
  1309. output['rankByGeometryTypeRule'] = {}
  1310. elif rule_type == '.minZoomLevel':
  1311. output['rankByMinZoomLevelRule'] = {}
  1312. else:
  1313. output['rankByAttributeRule'] = {'attributeName': rule_type}
  1314. if rule_dir.upper() == 'ASC':
  1315. output['direction'] = 'ASCENDING'
  1316. elif rule_dir.upper() == 'DESC':
  1317. output['direction'] = 'DESCENDING'
  1318. return output
  1319. def _get_ranking_rule(rules):
  1320. """Returns a RankingRule dict created from the rank-by-one-thing rules.
  1321. Args:
  1322. rules: A string representing comma separated rank-by-one-thing rules, or a
  1323. list of rank-by-one-thing rule strings.
  1324. Returns:
  1325. A RankingRule object containing information extracted from rules.
  1326. """
  1327. if not rules:
  1328. return None
  1329. rules_arr = rules
  1330. if isinstance(rules, str):
  1331. rules_arr = rules.split(',')
  1332. if isinstance(rules_arr, list):
  1333. rank_by_one_thing_rules = list(
  1334. map(_get_rank_by_one_thing_rule, rules_arr))
  1335. return {'rankByOneThingRule': rank_by_one_thing_rules}
  1336. raise ee_exception.EEException(
  1337. ('Unable to build ranking rule from rules. Rules should '
  1338. 'either be a comma-separated string or list of strings.'))
  1339. def _build_thinning_options(config):
  1340. """Returns a ThinningOptions dict created from the config.
  1341. Args:
  1342. config: The user-specified ingestion parameters. Will be modified in-place
  1343. by removing parameters used in the ThinningOptions.
  1344. Returns:
  1345. A ThinningOptions object containing information extracted from config.
  1346. """
  1347. if not config:
  1348. return None
  1349. output = {}
  1350. for key in ['maxFeaturesPerTile', 'thinningStrategy']:
  1351. if key in config:
  1352. output[key] = config.pop(key)
  1353. return output
  1354. def _build_ranking_options(config):
  1355. """Returns a RankingOptions dict created from the config.
  1356. Args:
  1357. config: The user-specified export parameters. Will be modified in-place
  1358. by removing parameters used in the RankingOptions.
  1359. Returns:
  1360. A RankingOptions object containing information extracted from config.
  1361. """
  1362. if not config:
  1363. return None
  1364. output = {}
  1365. thinning_ranking = config.pop('thinningRanking', None)
  1366. thinning_ranking_rule = _get_ranking_rule(thinning_ranking)
  1367. if thinning_ranking_rule:
  1368. output['thinningRankingRule'] = thinning_ranking_rule
  1369. z_order_ranking = config.pop('zOrderRanking', None)
  1370. z_order_ranking_rule = _get_ranking_rule(z_order_ranking)
  1371. if z_order_ranking_rule:
  1372. output['zOrderRankingRule'] = z_order_ranking_rule
  1373. return output
  1374. def build_ingestion_time_parameters(input_params):
  1375. """Builds a FeatureViewIngestionTimeParameters from values in a params dict.
  1376. Args:
  1377. input_params: All the user-specified ingestions time parameters. Will be
  1378. modified in-place to remove fields in FeatureViewIngestionTimeParameters.
  1379. Returns:
  1380. A FeatureViewIngestionTimeParameters containing information extracted from
  1381. config.
  1382. """
  1383. output_params = {}
  1384. thinning_options = _build_thinning_options(input_params)
  1385. if thinning_options:
  1386. output_params['thinningOptions'] = thinning_options
  1387. ranking_options = _build_ranking_options(input_params)
  1388. if ranking_options:
  1389. output_params['rankingOptions'] = ranking_options
  1390. if input_params:
  1391. raise ee_exception.EEException(
  1392. 'The following keys are unrecognized in the ingestion parameters: %s' %
  1393. list(input_params.keys()))
  1394. return output_params
  1395. def _create_export_task(config, task_type):
  1396. """Creates an export task.
  1397. Args:
  1398. config: Custom config fields for the task.
  1399. task_type: The type of the task to create. One of Task.Type.
  1400. Returns:
  1401. An unstarted export Task.
  1402. """
  1403. return Task(None, task_type, Task.State.UNSUBMITTED, config)
  1404. def _capture_parameters(all_locals, parameters_to_exclude):
  1405. """Creates a parameter dict by copying all non-None locals.
  1406. This is generally invoked as the first part of call processing, via
  1407. something like
  1408. _capture_parameters(locals(), ['image'])
  1409. so that all call parameters can be pulled into a single dict.
  1410. Args:
  1411. all_locals: The dict of local variables.
  1412. parameters_to_exclude: An iterable giving names of parameters that should
  1413. be excluded from the result.
  1414. Returns:
  1415. A dict containing all the non-None values in all_locals, except for
  1416. those listed in parameters_to_exclude.
  1417. """
  1418. result = {k: v for k, v in all_locals.items() if v is not None}
  1419. for parameter_to_exclude in parameters_to_exclude:
  1420. if parameter_to_exclude in result:
  1421. del result[parameter_to_exclude]
  1422. return result
  1423. def _canonicalize_parameters(config, destination):
  1424. """Canonicalizes a set of parameter names.
  1425. For legacy and other reasons, there are multiple ways to specify some export
  1426. parameters. This function applies canonicalization rules to simplify further
  1427. processing. It validates that the canonicalization introduces no collisions.
  1428. Note that config is changed in place and not returned.
  1429. The parameter name mappings performed here are:
  1430. crsTransform -> crs_transform
  1431. bucket -> outputBucket
  1432. fileNamePrefix, path -> outputPrefix or driveFileNamePrefix depending
  1433. on whether the destination is GCS or Drive. "description" is used as
  1434. the default value of outputPrefix or driveFileNamePrefix if nothing else
  1435. provided a value.
  1436. folder -> driveFolder
  1437. If "fileFormat" is "GeoTIFF": tiffX -> formatOptions { x }
  1438. If "fileFormat" is "TfRecord": tfrecordX -> formatOptions { x }
  1439. The "region" parameter's value, if present, is processed with
  1440. _canonicalize_region.
  1441. The "selectors" parameter's value, if present and a string, is converted
  1442. to a list by splitting at commas.
  1443. Args:
  1444. config: The configuration dictionary to be converted.
  1445. destination: The type of destination targeted by the export.
  1446. """
  1447. if 'kwargs' in config:
  1448. config.update(config['kwargs'])
  1449. del config['kwargs']
  1450. collision_error = 'Both {} and {} are specified.'
  1451. def canonicalize_name(a, b):
  1452. """Renames config[a] to config[b]."""
  1453. if a in config:
  1454. if b in config:
  1455. raise ee_exception.EEException(collision_error.format(a, b))
  1456. config[b] = config.pop(a)
  1457. canonicalize_name('crsTransform', 'crs_transform')
  1458. if 'region' in config:
  1459. config['region'] = _canonicalize_region(config['region'])
  1460. if 'selectors' in config and isinstance(config['selectors'], str):
  1461. config['selectors'] = config['selectors'].split(',')
  1462. if destination == Task.ExportDestination.GCS:
  1463. canonicalize_name('bucket', 'outputBucket')
  1464. canonicalize_name('fileNamePrefix', 'outputPrefix')
  1465. # Only used with Export.map
  1466. canonicalize_name('path', 'outputPrefix')
  1467. if 'outputPrefix' not in config and 'description' in config:
  1468. config['outputPrefix'] = config['description']
  1469. elif destination == Task.ExportDestination.DRIVE:
  1470. canonicalize_name('folder', 'driveFolder')
  1471. canonicalize_name('fileNamePrefix', 'driveFileNamePrefix')
  1472. if 'driveFileNamePrefix' not in config and 'description' in config:
  1473. config['driveFileNamePrefix'] = config['description']
  1474. elif (destination != Task.ExportDestination.ASSET and
  1475. destination != Task.ExportDestination.FEATURE_VIEW):
  1476. raise ee_exception.EEException('Unknown export destination.')
  1477. if (IMAGE_FORMAT_FIELD in config and
  1478. config[IMAGE_FORMAT_FIELD].upper() in FORMAT_PREFIX_MAP):
  1479. prefix = FORMAT_PREFIX_MAP[config[IMAGE_FORMAT_FIELD].upper()]
  1480. format_options = config.get(IMAGE_FORMAT_OPTIONS_FIELD, {})
  1481. keys_to_delete = []
  1482. for key, value in config.items():
  1483. if key.startswith(prefix):
  1484. # Transform like "tiffSomeKey" -> "someKey"
  1485. remapped_key = key[len(prefix):]
  1486. remapped_key = remapped_key[:1].lower() + remapped_key[1:]
  1487. if remapped_key in format_options:
  1488. raise ee_exception.EEException(
  1489. collision_error.format(key, remapped_key))
  1490. format_options[remapped_key] = value
  1491. keys_to_delete.append(key)
  1492. if format_options:
  1493. config[IMAGE_FORMAT_OPTIONS_FIELD] = format_options
  1494. for key in keys_to_delete:
  1495. del config[key]
  1496. def _canonicalize_region(region):
  1497. """Converts a region parameter to a form appropriate for export."""
  1498. region_error = ee_exception.EEException(
  1499. 'Invalid format for "region" property. '
  1500. 'See Export.image() documentation for more details.')
  1501. # pylint: disable=bare-except
  1502. # The Cloud API can accept arbitrary Geometries, even computed ones.
  1503. # Thus, this function tries to turn its parameter into a Geometry.
  1504. if isinstance(region, geometry.Geometry):
  1505. return region
  1506. if isinstance(region, str):
  1507. try:
  1508. region = json.loads(region)
  1509. except:
  1510. raise region_error
  1511. # It's probably a list of coordinates - attempt to parse as a LineString or
  1512. # Polygon.
  1513. try:
  1514. region = geometry.Geometry.LineString(region)
  1515. except:
  1516. try:
  1517. region = geometry.Geometry.Polygon(region)
  1518. except:
  1519. raise region_error
  1520. return region