@@ -58,6 +58,9 @@ class DataCube(_ProcessGraphAbstraction):
58
58
and this process graph can be "grown" to a desired workflow by calling the appropriate methods.
59
59
"""
60
60
61
+ # TODO: set this based on back-end or user preference?
62
+ _DEFAULT_RASTER_FORMAT = "GTiff"
63
+
61
64
def __init__ (self , graph : PGNode , connection : 'openeo.Connection' , metadata : CollectionMetadata = None ):
62
65
super ().__init__ (pgnode = graph , connection = connection )
63
66
self .metadata = CollectionMetadata .get_or_create (metadata )
@@ -1810,36 +1813,45 @@ def atmospheric_correction(
1810
1813
})
1811
1814
1812
1815
@openeo_process
1813
- def save_result (self , format : str = "GTiff" , options : dict = None ) -> 'DataCube' :
1816
+ def save_result (
1817
+ self ,
1818
+ format : str = _DEFAULT_RASTER_FORMAT ,
1819
+ options : Optional [dict ] = None ,
1820
+ ) -> "DataCube" :
1814
1821
formats = set (self ._connection .list_output_formats ().keys ())
1822
+ # TODO: map format to correct casing too?
1815
1823
if format .lower () not in {f .lower () for f in formats }:
1816
1824
raise ValueError ("Invalid format {f!r}. Should be one of {s}" .format (f = format , s = formats ))
1817
1825
return self .process (
1818
1826
process_id = "save_result" ,
1819
1827
arguments = {
1820
1828
"data" : THIS ,
1821
1829
"format" : format ,
1830
+ # TODO: leave out options if unset?
1822
1831
"options" : options or {}
1823
1832
}
1824
1833
)
1825
1834
1826
- def download (
1827
- self , outputfile : Union [str , pathlib .Path , None ] = None , format : Optional [str ] = None ,
1828
- options : Optional [dict ] = None
1829
- ):
1835
+ def _ensure_save_result (
1836
+ self ,
1837
+ format : Optional [str ] = None ,
1838
+ options : Optional [dict ] = None ,
1839
+ ) -> "DataCube" :
1830
1840
"""
1831
- Download image collection, e.g. as GeoTIFF .
1832
- If outputfile is provided, the result is stored on disk locally, otherwise, a bytes object is returned.
1833
- The bytes object can be passed on to a suitable decoder for decoding .
1841
+ Make sure there is a (final) `save_result` node in the process graph .
1842
+ If there is already one: check if it is consistent with the given format/options (if any)
1843
+ and add a new one otherwise .
1834
1844
1835
- :param outputfile: Optional, an output file if the result needs to be stored on disk.
1836
- :param format: Optional, an output format supported by the backend.
1837
- :param options: Optional, file format options
1838
- :return: None if the result is stored to disk, or a bytes object returned by the backend.
1845
+ :param format: (optional) desired `save_result` file format
1846
+ :param options: (optional) desired `save_result` file format parameters
1847
+ :return:
1839
1848
"""
1840
- if self .result_node ().process_id == "save_result" :
1841
- # There is already a `save_result` node: check if it is consistent with given format/options
1842
- args = self .result_node ().arguments
1849
+ # TODO: move to generic data cube parent class (not only for raster cubes, but also vector cubes)
1850
+ result_node = self .result_node ()
1851
+ if result_node .process_id == "save_result" :
1852
+ # There is already a `save_result` node:
1853
+ # check if it is consistent with given format/options (if any)
1854
+ args = result_node .arguments
1843
1855
if format is not None and format .lower () != args ["format" ].lower ():
1844
1856
raise ValueError (
1845
1857
f"Existing `save_result` node with different format { args ['format' ]!r} != { format !r} "
@@ -1851,10 +1863,30 @@ def download(
1851
1863
cube = self
1852
1864
else :
1853
1865
# No `save_result` node yet: automatically add it.
1854
- if not format :
1855
- format = guess_format (outputfile ) if outputfile else "GTiff"
1856
- cube = self .save_result (format = format , options = options )
1866
+ cube = self .save_result (
1867
+ format = format or self ._DEFAULT_RASTER_FORMAT , options = options
1868
+ )
1869
+ return cube
1870
+
1871
+ def download (
1872
+ self ,
1873
+ outputfile : Optional [Union [str , pathlib .Path ]] = None ,
1874
+ format : Optional [str ] = None ,
1875
+ options : Optional [dict ] = None ,
1876
+ ) -> Union [None , bytes ]:
1877
+ """
1878
+ Download the raster data cube, e.g. as GeoTIFF.
1879
+ If outputfile is provided, the result is stored on disk locally, otherwise, a bytes object is returned.
1880
+ The bytes object can be passed on to a suitable decoder for decoding.
1857
1881
1882
+ :param outputfile: Optional, an output file if the result needs to be stored on disk.
1883
+ :param format: Optional, an output format supported by the backend.
1884
+ :param options: Optional, file format options
1885
+ :return: None if the result is stored to disk, or a bytes object returned by the backend.
1886
+ """
1887
+ if format is None and outputfile is not None :
1888
+ format = guess_format (outputfile )
1889
+ cube = self ._ensure_save_result (format = format , options = options )
1858
1890
return self ._connection .download (cube .flat_graph (), outputfile )
1859
1891
1860
1892
def validate (self ) -> List [dict ]:
@@ -1869,27 +1901,35 @@ def tiled_viewing_service(self, type: str, **kwargs) -> Service:
1869
1901
return self ._connection .create_service (self .flat_graph (), type = type , ** kwargs )
1870
1902
1871
1903
def execute_batch (
1872
- self ,
1873
- outputfile : Union [str , pathlib .Path ] = None , out_format : str = None ,
1874
- print = print , max_poll_interval = 60 , connection_retry_interval = 30 ,
1875
- job_options = None , ** format_options ) -> BatchJob :
1904
+ self ,
1905
+ outputfile : Optional [Union [str , pathlib .Path ]] = None ,
1906
+ out_format : Optional [str ] = None ,
1907
+ * ,
1908
+ print : typing .Callable [[str ], None ] = print ,
1909
+ max_poll_interval : float = 60 ,
1910
+ connection_retry_interval : float = 30 ,
1911
+ job_options : Optional [dict ] = None ,
1912
+ # TODO: avoid `format_options` as keyword arguments
1913
+ ** format_options ,
1914
+ ) -> BatchJob :
1876
1915
"""
1877
1916
Evaluate the process graph by creating a batch job, and retrieving the results when it is finished.
1878
1917
This method is mostly recommended if the batch job is expected to run in a reasonable amount of time.
1879
1918
1880
1919
For very long-running jobs, you probably do not want to keep the client running.
1881
1920
1882
- :param job_options:
1883
1921
:param outputfile: The path of a file to which a result can be written
1884
- :param out_format: (optional) Format of the job result.
1885
- :param format_options: String Parameters for the job result format
1886
-
1922
+ :param out_format: (optional) File format to use for the job result.
1923
+ :param job_options:
1887
1924
"""
1888
1925
if "format" in format_options and not out_format :
1889
1926
out_format = format_options ["format" ] # align with 'download' call arg name
1890
- if not out_format :
1891
- out_format = guess_format (outputfile ) if outputfile else "GTiff"
1892
- job = self .create_job (out_format , job_options = job_options , ** format_options )
1927
+ if not out_format and outputfile :
1928
+ out_format = guess_format (outputfile )
1929
+
1930
+ job = self .create_job (
1931
+ out_format = out_format , job_options = job_options , ** format_options
1932
+ )
1893
1933
return job .run_synchronous (
1894
1934
outputfile = outputfile ,
1895
1935
print = print , max_poll_interval = max_poll_interval , connection_retry_interval = connection_retry_interval
@@ -1904,6 +1944,7 @@ def create_job(
1904
1944
plan : Optional [str ] = None ,
1905
1945
budget : Optional [float ] = None ,
1906
1946
job_options : Optional [dict ] = None ,
1947
+ # TODO: avoid `format_options` as keyword arguments
1907
1948
** format_options ,
1908
1949
) -> BatchJob :
1909
1950
"""
@@ -1914,22 +1955,18 @@ def create_job(
1914
1955
it still needs to be started and tracked explicitly.
1915
1956
Use :py:meth:`execute_batch` instead to have the openEO Python client take care of that job management.
1916
1957
1917
- :param out_format: String Format of the job result .
1958
+ :param out_format: output file format .
1918
1959
:param title: job title
1919
1960
:param description: job description
1920
1961
:param plan: billing plan
1921
1962
:param budget: maximum cost the request is allowed to produce
1922
- :param job_options: A dictionary containing (custom) job options
1923
- :param format_options: String Parameters for the job result format
1963
+ :param job_options: custom job options.
1924
1964
:return: Created job.
1925
1965
"""
1926
1966
# TODO: add option to also automatically start the job?
1927
1967
# TODO: avoid using all kwargs as format_options
1928
1968
# TODO: centralize `create_job` for `DataCube`, `VectorCube`, `MlModel`, ...
1929
- cube = self
1930
- if out_format :
1931
- # add `save_result` node
1932
- cube = cube .save_result (format = out_format , options = format_options )
1969
+ cube = self ._ensure_save_result (format = out_format , options = format_options )
1933
1970
return self ._connection .create_job (
1934
1971
process_graph = cube .flat_graph (),
1935
1972
title = title ,
0 commit comments