@@ -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,41 @@ 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 , format : str = _DEFAULT_RASTER_FORMAT , options : Optional [dict ] = None
1818
+ ) -> "DataCube" :
1814
1819
formats = set (self ._connection .list_output_formats ().keys ())
1820
+ # TODO: map format to correct casing too?
1815
1821
if format .lower () not in {f .lower () for f in formats }:
1816
1822
raise ValueError ("Invalid format {f!r}. Should be one of {s}" .format (f = format , s = formats ))
1817
1823
return self .process (
1818
1824
process_id = "save_result" ,
1819
1825
arguments = {
1820
1826
"data" : THIS ,
1821
1827
"format" : format ,
1828
+ # TODO: leave out options if unset?
1822
1829
"options" : options or {}
1823
1830
}
1824
1831
)
1825
1832
1826
- def download (
1827
- self , outputfile : Union [str , pathlib .Path , None ] = None , format : Optional [str ] = None ,
1828
- options : Optional [dict ] = None
1829
- ):
1833
+ def _ensure_save_result (
1834
+ self , format : Optional [str ] = None , options : Optional [dict ] = None
1835
+ ) -> "DataCube" :
1830
1836
"""
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 .
1837
+ Make sure there is a (final) `save_result` node in the process graph .
1838
+ If there is already one: check if it is consistent with the given format/options (if any)
1839
+ and add a new one otherwise .
1834
1840
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.
1841
+ :param format: (optional) desired `save_result` file format
1842
+ :param options: (optional) desired `save_result` file format parameters
1843
+ :return:
1839
1844
"""
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
1845
+ # TODO: move to generic data cube parent class (not only for raster cubes, but also vector cubes)
1846
+ result_node = self .result_node ()
1847
+ if result_node .process_id == "save_result" :
1848
+ # There is already a `save_result` node:
1849
+ # check if it is consistent with given format/options (if any)
1850
+ args = result_node .arguments
1843
1851
if format is not None and format .lower () != args ["format" ].lower ():
1844
1852
raise ValueError (
1845
1853
f"Existing `save_result` node with different format { args ['format' ]!r} != { format !r} "
@@ -1851,10 +1859,28 @@ def download(
1851
1859
cube = self
1852
1860
else :
1853
1861
# 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 )
1862
+ cube = self .save_result (
1863
+ format = format or self ._DEFAULT_RASTER_FORMAT , options = options
1864
+ )
1865
+ return cube
1857
1866
1867
+ def download (
1868
+ self , outputfile : Union [str , pathlib .Path , None ] = None , format : Optional [str ] = None ,
1869
+ options : Optional [dict ] = None
1870
+ ):
1871
+ """
1872
+ Download image collection, e.g. as GeoTIFF.
1873
+ If outputfile is provided, the result is stored on disk locally, otherwise, a bytes object is returned.
1874
+ The bytes object can be passed on to a suitable decoder for decoding.
1875
+
1876
+ :param outputfile: Optional, an output file if the result needs to be stored on disk.
1877
+ :param format: Optional, an output format supported by the backend.
1878
+ :param options: Optional, file format options
1879
+ :return: None if the result is stored to disk, or a bytes object returned by the backend.
1880
+ """
1881
+ if format is None and outputfile is not None :
1882
+ format = guess_format (outputfile )
1883
+ cube = self ._ensure_save_result (format = format , options = options )
1858
1884
return self ._connection .download (cube .flat_graph (), outputfile )
1859
1885
1860
1886
def validate (self ) -> List [dict ]:
@@ -1869,27 +1895,36 @@ def tiled_viewing_service(self, type: str, **kwargs) -> Service:
1869
1895
return self ._connection .create_service (self .flat_graph (), type = type , ** kwargs )
1870
1896
1871
1897
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 :
1898
+ self ,
1899
+ outputfile : Optional [Union [str , pathlib .Path ]] = None ,
1900
+ out_format : Optional [str ] = None ,
1901
+ * ,
1902
+ print : typing .Callable [[str ], None ] = print ,
1903
+ max_poll_interval : float = 60 ,
1904
+ connection_retry_interval : float = 30 ,
1905
+ job_options : Optional [dict ] = None ,
1906
+ # TODO: avoid `format_options` as keyword arguments
1907
+ ** format_options ,
1908
+ ) -> BatchJob :
1876
1909
"""
1877
1910
Evaluate the process graph by creating a batch job, and retrieving the results when it is finished.
1878
1911
This method is mostly recommended if the batch job is expected to run in a reasonable amount of time.
1879
1912
1880
1913
For very long-running jobs, you probably do not want to keep the client running.
1881
1914
1882
- :param job_options:
1883
1915
: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
-
1916
+ :param out_format: (optional) File format to use for the job result.
1917
+ :param job_options:
1918
+ :param format_options: output file format parameters.
1887
1919
"""
1888
1920
if "format" in format_options and not out_format :
1889
1921
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 )
1922
+ if not out_format and outputfile :
1923
+ out_format = guess_format (outputfile )
1924
+
1925
+ job = self .create_job (
1926
+ format = out_format , job_options = job_options , format_options = format_options
1927
+ )
1893
1928
return job .run_synchronous (
1894
1929
outputfile = outputfile ,
1895
1930
print = print , max_poll_interval = max_poll_interval , connection_retry_interval = connection_retry_interval
@@ -1904,6 +1939,7 @@ def create_job(
1904
1939
plan : Optional [str ] = None ,
1905
1940
budget : Optional [float ] = None ,
1906
1941
job_options : Optional [dict ] = None ,
1942
+ # TODO: avoid `format_options` as keyword arguments
1907
1943
** format_options ,
1908
1944
) -> BatchJob :
1909
1945
"""
@@ -1914,22 +1950,19 @@ def create_job(
1914
1950
it still needs to be started and tracked explicitly.
1915
1951
Use :py:meth:`execute_batch` instead to have the openEO Python client take care of that job management.
1916
1952
1917
- :param out_format: String Format of the job result .
1953
+ :param out_format: output file format .
1918
1954
:param title: job title
1919
1955
:param description: job description
1920
1956
:param plan: billing plan
1921
1957
: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
1958
+ :param job_options: custom job options.
1959
+ :param format_options: output file format parameters.
1924
1960
:return: Created job.
1925
1961
"""
1926
1962
# TODO: add option to also automatically start the job?
1927
1963
# TODO: avoid using all kwargs as format_options
1928
1964
# 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 )
1965
+ cube = self ._ensure_save_result (format = out_format , options = format_options )
1933
1966
return self ._connection .create_job (
1934
1967
process_graph = cube .flat_graph (),
1935
1968
title = title ,
0 commit comments