Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions test/cli/test_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ def test_main_with_illegal_size_option(self):
def test_main_with_illegal_region_option(self):
with self.assertRaises(ValueError) as cm:
self.invoke_cli(['gen', '-R', '50,_2,55,21', 'input.nc'])
self.assertEqual("output_region must have the form <lon_min>,<lat_min>,<lon_max>,<lat_max>,"
" where all four numbers must be floating point numbers in degrees", f'{cm.exception}')
self.assertEqual("output_region must have the form "
"<x_min>,<y_min>,<x_max>,<y_max>,"
" where all four numbers must be floating point "
"numbers in the units of the output CRS",
f'{cm.exception}')
with self.assertRaises(ValueError) as cm:
self.invoke_cli(['gen', '-R', '50,20,55', 'input.nc'])
self.assertEqual("output_region must have the form <lon_min>,<lat_min>,<lon_max>,<lat_max>,"
" where all four numbers must be floating point numbers in degrees", f'{cm.exception}')
self.assertEqual("output_region must have the form "
"<x_min>,<y_min>,<x_max>,<y_max>,"
" where all four numbers must be floating point "
"numbers in the units of the output CRS",
f'{cm.exception}')

def test_main_with_illegal_variable_option(self):
with self.assertRaises(ValueError) as cm:
Expand Down
12 changes: 8 additions & 4 deletions test/core/gen/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,19 @@ def test_output_region_option(self):
config_obj = dict(output_region='50,_2,55,21')
with self.assertRaises(ValueError) as cm:
get_config_dict(**config_obj)
self.assertEqual("output_region must have the form <lon_min>,<lat_min>,<lon_max>,<lat_max>,"
" where all four numbers must be floating point numbers in degrees",
self.assertEqual("output_region must have the form "
"<x_min>,<y_min>,<x_max>,<y_max>,"
" where all four numbers must be floating point "
"numbers in the units of the output CRS",
f'{cm.exception}')

config_obj = dict(output_region='50, 20, 55')
with self.assertRaises(ValueError) as cm:
get_config_dict(**config_obj)
self.assertEqual("output_region must have the form <lon_min>,<lat_min>,<lon_max>,<lat_max>,"
" where all four numbers must be floating point numbers in degrees",
self.assertEqual("output_region must have the form "
"<x_min>,<y_min>,<x_max>,<y_max>,"
" where all four numbers must be floating point "
"numbers in the units of the output CRS",
f'{cm.exception}')

def test_output_variables_option(self):
Expand Down
48 changes: 1 addition & 47 deletions test/core/gen/test_iproc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,10 @@
import pandas as pd
import xarray as xr

from xcube.core.gen.iproc import DefaultInputProcessor, ReprojectionInfo
from xcube.core.gen.iproc import DefaultInputProcessor
from xcube.core.timecoord import to_time_in_days_since_1970


class ReprojectionInfoTest(unittest.TestCase):
def test_success(self):
# noinspection PyTypeChecker
ri = ReprojectionInfo(xy_names=['x', 'y'])
self.assertEqual(('x', 'y'), ri.xy_names)
self.assertEqual(None, ri.xy_tp_names)
self.assertEqual(None, ri.xy_crs)
self.assertEqual(None, ri.xy_gcp_step)
self.assertEqual(None, ri.xy_tp_gcp_step)

# noinspection PyTypeChecker
ri = ReprojectionInfo(xy_names=['x', 'y'], xy_gcp_step=[2, 1])
self.assertEqual(('x', 'y'), ri.xy_names)
self.assertEqual(None, ri.xy_tp_names)
self.assertEqual(None, ri.xy_crs)
self.assertEqual((2, 1), ri.xy_gcp_step)
self.assertEqual(None, ri.xy_tp_gcp_step)

# noinspection PyTypeChecker
ri = ReprojectionInfo(xy_gcp_step=4, xy_tp_gcp_step=2)
self.assertEqual(None, ri.xy_names)
self.assertEqual(None, ri.xy_tp_names)
self.assertEqual(None, ri.xy_crs)
self.assertEqual((4, 4), ri.xy_gcp_step)
self.assertEqual((2, 2), ri.xy_tp_gcp_step)

def test_fail(self):
with self.assertRaises(ValueError):
# noinspection PyTypeChecker
ReprojectionInfo(xy_names=['x', ''])
with self.assertRaises(ValueError):
# noinspection PyTypeChecker
ReprojectionInfo(xy_names=[0, 'y'])
with self.assertRaises(ValueError):
# noinspection PyTypeChecker
ReprojectionInfo(xy_gcp_step=[7, 'y'])
with self.assertRaises(ValueError):
# noinspection PyTypeChecker
ReprojectionInfo(xy_gcp_step=[7, 0])


class DefaultInputProcessorTest(unittest.TestCase):

def setUp(self):
Expand All @@ -62,11 +21,6 @@ def test_props(self):
processor = DefaultInputProcessor(input_reader="zarr")
self.assertEqual('zarr', processor.input_reader)

def test_reprojection_info(self):
# noinspection PyNoneFunctionAssignment
reprojection_info = self.processor.get_reprojection_info(create_default_dataset())
self.assertIsNotNone(reprojection_info)

def test_get_time_range(self):
ds = create_default_dataset(time_mode="time")
t1, t2 = self.processor.get_time_range(ds)
Expand Down
11 changes: 10 additions & 1 deletion xcube/cli/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,15 @@
'xcube gen --info. If omitted, the format will be guessed from the given output path.')
@click.option('--size', '-S', metavar='SIZE',
help='Output size in pixels using format "<width>,<height>".')
@click.option('--crs', metavar='CRS',
help='The target coordinate reference system. Must be given as'
'either (a) a WKT-String decribing a CRS or (b) an EPSG-code'
'(with or without leading "EPSG:". If not provided, the '
'output cube will be resampled to the WGS84 CRS.')
@click.option('--region', '-R', metavar='REGION',
help='Output region using format "<lon-min>,<lat-min>,<lon-max>,<lat-max>"')
help='Output region using format '
'"<X-min>,<Y-min>,<X-max>,<y-max>". Output region '
'must be given in coordinates of the output crs.')
@click.option('--variables', '--vars', metavar='VARIABLES',
help='Variables to be included in output. '
'Comma-separated list of names which may contain wildcard characters "*" and "?".')
Expand Down Expand Up @@ -81,6 +88,7 @@ def gen(input: Sequence[str],
output: str,
format: str,
size: str,
crs: str,
region: str,
variables: str,
resampling: str,
Expand Down Expand Up @@ -115,6 +123,7 @@ def gen(input: Sequence[str],
output_path=output,
output_writer_name=format,
output_size=size,
output_crs=crs,
output_region=region,
output_variables=variables,
output_resampling=resampling,
Expand Down
10 changes: 8 additions & 2 deletions xcube/core/gen/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def get_config_dict(config_files: Sequence[str] = None,
output_path: str = None,
output_writer_name: str = None,
output_size: str = None,
output_crs: str = None,
output_region: str = None,
output_variables: str = None,
output_resampling: str = None,
Expand Down Expand Up @@ -68,6 +69,9 @@ def get_config_dict(config_files: Sequence[str] = None,
if output_writer_name is not None and 'output_writer_name' not in config:
config['output_writer_name'] = output_writer_name

if output_crs is not None and 'output_crs' not in config:
config['output_crs'] = output_crs

if output_resampling is not None and 'output_resampling' not in config:
config['output_resampling'] = output_resampling

Expand All @@ -87,8 +91,10 @@ def get_config_dict(config_files: Sequence[str] = None,
except ValueError:
output_region = None
if output_region is None or len(output_region) != 4:
raise ValueError(f'output_region must have the form <lon_min>,<lat_min>,<lon_max>,<lat_max>,'
f' where all four numbers must be floating point numbers in degrees')
raise ValueError(f'output_region must have the form '
f'<x_min>,<y_min>,<x_max>,<y_max>, '
f'where all four numbers must be floating point '
f'numbers in the units of the output CRS')
config['output_region'] = output_region

if output_variables is not None:
Expand Down
29 changes: 21 additions & 8 deletions xcube/core/gen/gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io
import os
import pstats
import pyproj
import time
import traceback
import warnings
Expand All @@ -36,7 +37,7 @@
from xcube.core.gen.defaults import DEFAULT_OUTPUT_PATH, DEFAULT_OUTPUT_RESAMPLING, DEFAULT_OUTPUT_SIZE
from xcube.core.gen.iproc import InputProcessor, find_input_processor_class
from xcube.core.gridmapping import GridMapping
from xcube.core.gridmapping import CRS_WGS84
from xcube.core.gridmapping import DEFAULT_CRS
from xcube.core.optimize import optimize_dataset
from xcube.core.select import select_spatial_subset, select_variables_subset
from xcube.core.timecoord import add_time_coords, from_time_in_days_since_1970
Expand All @@ -50,6 +51,7 @@ def gen_cube(input_paths: Sequence[str] = None,
input_processor_params: Dict = None,
input_reader_name: str = None,
input_reader_params: Dict[str, Any] = None,
output_crs: str = DEFAULT_CRS,
output_region: Tuple[float, float, float, float] = None,
output_size: Tuple[int, int] = DEFAULT_OUTPUT_SIZE,
output_resampling: str = DEFAULT_OUTPUT_RESAMPLING,
Expand All @@ -70,12 +72,18 @@ def gen_cube(input_paths: Sequence[str] = None,
:param no_sort_mode:
:param input_paths: The input paths.
:param input_processor_name: Name of a registered input processor
(xcube.core.gen.inputprocessor.InputProcessor) to be used to transform the inputs.
:param input_processor_params: Parameters to be passed to the input processor.
:param input_reader_name: Name of a registered input reader (xcube.core.util.dsio.DatasetIO).
(xcube.core.gen.inputprocessor.InputProcessor) to be used to transform
the inputs.
:param input_processor_params: Parameters to be passed to the
input processor.
:param input_reader_name: Name of a registered input reader
(xcube.core.util.dsio.DatasetIO).
:param input_reader_params: Parameters passed to the input reader.
:param output_region: Output region as tuple of floats: (lon_min, lat_min, lon_max, lat_max).
:param output_size: The spatial dimensions of the output as tuple of ints: (width, height).
:param output_crs: Output crs, given as either WLT string or EPSG code
:param output_region: Output region as tuple of floats:
(x_min, y_min, x_max, y_max).
:param output_size: The spatial dimensions of the output as tuple of ints:
(width, height).
:param output_resampling: The resampling method for the output.
:param output_path: The output directory.
:param output_writer_name: Name of an output writer
Expand All @@ -85,7 +93,8 @@ def gen_cube(input_paths: Sequence[str] = None,
:param output_variables: Output variables.
:param processed_variables: Processed variables computed on-the-fly.
:param profile_mode: Whether profiling should be enabled.
:param append_mode: Deprecated. The function will always either insert, replace, or append new time slices.
:param append_mode: Deprecated. The function will always either insert,
replace, or append new time slices.
:param dry_run: Doesn't write any data. For testing.
:param monitor: A progress monitor.
:return: True for success.
Expand Down Expand Up @@ -161,6 +170,7 @@ def monitor(*args):
effective_output_writer_params,
input_file,
output_size,
output_crs,
output_region,
output_resampling,
output_path,
Expand All @@ -187,6 +197,7 @@ def _process_input(input_processor: InputProcessor,
output_writer_params: Dict[str, Any],
input_file: str,
output_size: Tuple[int, int],
output_crs: str,
output_region: Tuple[float, float, float, float],
output_resampling: str,
output_path: str,
Expand Down Expand Up @@ -219,6 +230,8 @@ def _process_input(input_processor: InputProcessor,
time_index, update_mode = find_time_slice(output_path,
from_time_in_days_since_1970((time_range[0] + time_range[1]) / 2))

crs = pyproj.crs.CRS.from_string(output_crs)

width, height = output_size
x_min, y_min, x_max, y_max = output_region
xy_res = max((x_max - x_min) / width, (y_max - y_min) / height)
Expand All @@ -227,7 +240,7 @@ def _process_input(input_processor: InputProcessor,
output_geom = GridMapping.regular(size=output_size,
xy_min=(x_min, y_min),
xy_res=xy_res,
crs=CRS_WGS84,
crs=crs,
tile_size=tile_size)

steps = []
Expand Down
Loading