Esempio n. 1
0
def test_geobox_simple():
    from affine import Affine
    t = geometry.GeoBox(4000, 4000,
                        Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0),
                        epsg4326)

    expect_lon = np.asarray([
        151.000125, 151.000375, 151.000625, 151.000875, 151.001125, 151.001375,
        151.001625, 151.001875, 151.002125, 151.002375
    ])

    expect_lat = np.asarray([
        -29.000125, -29.000375, -29.000625, -29.000875, -29.001125, -29.001375,
        -29.001625, -29.001875, -29.002125, -29.002375
    ])
    expect_resolution = np.asarray([-0.00025, 0.00025])

    assert t.coordinates['latitude'].values.shape == (4000, )
    assert t.coordinates['longitude'].values.shape == (4000, )

    np.testing.assert_almost_equal(t.resolution, expect_resolution)
    np.testing.assert_almost_equal(t.coords['latitude'].values[:10],
                                   expect_lat)
    np.testing.assert_almost_equal(t.coords['longitude'].values[:10],
                                   expect_lon)

    assert (t == "some random thing") is False

    # ensure GeoBox accepts string CRS
    assert isinstance(
        geometry.GeoBox(4000, 4000,
                        Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0),
                        'epsg:4326').crs, CRS)
Esempio n. 2
0
def test_geobox_simple():
    t = geometry.GeoBox(4000, 4000,
                        Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0),
                        epsg4326)

    expect_lon = np.asarray([151.000125, 151.000375, 151.000625, 151.000875, 151.001125,
                             151.001375, 151.001625, 151.001875, 151.002125, 151.002375])

    expect_lat = np.asarray([-29.000125, -29.000375, -29.000625, -29.000875, -29.001125,
                             -29.001375, -29.001625, -29.001875, -29.002125, -29.002375])
    expect_resolution = np.asarray([-0.00025, 0.00025])

    assert t.coordinates['latitude'].values.shape == (4000,)
    assert t.coordinates['longitude'].values.shape == (4000,)

    np.testing.assert_almost_equal(t.resolution, expect_resolution)
    np.testing.assert_almost_equal(t.coords['latitude'].values[:10], expect_lat)
    np.testing.assert_almost_equal(t.coords['longitude'].values[:10], expect_lon)

    assert (t == "some random thing") is False

    # ensure GeoBox accepts string CRS
    assert isinstance(geometry.GeoBox(4000, 4000,
                                      Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0),
                                      'epsg:4326').crs, CRS)

    # Check GeoBox class is hashable
    t_copy = GeoBox(t.width, t.height, t.transform, t.crs)
    t_other = GeoBox(t.width+1, t.height, t.transform, t.crs)
    assert t_copy is not t
    assert t == t_copy
    assert len(set([t, t, t_copy])) == 1
    assert len(set([t, t_copy, t_other])) == 2
Esempio n. 3
0
def test_geobox():
    points_list = [
        [(148.2697, -35.20111), (149.31254, -35.20111), (149.31254, -36.331431), (148.2697, -36.331431)],
        [(148.2697, 35.20111), (149.31254, 35.20111), (149.31254, 36.331431), (148.2697, 36.331431)],
        [(-148.2697, 35.20111), (-149.31254, 35.20111), (-149.31254, 36.331431), (-148.2697, 36.331431)],
        [(-148.2697, -35.20111), (-149.31254, -35.20111), (-149.31254, -36.331431), (-148.2697, -36.331431),
         (148.2697, -35.20111)],
    ]
    for points in points_list:
        polygon = geometry.polygon(points, crs=epsg3577)
        resolution = (-25, 25)
        geobox = geometry.GeoBox.from_geopolygon(polygon, resolution)

        assert abs(resolution[0]) > abs(geobox.extent.boundingbox.left - polygon.boundingbox.left)
        assert abs(resolution[0]) > abs(geobox.extent.boundingbox.right - polygon.boundingbox.right)
        assert abs(resolution[1]) > abs(geobox.extent.boundingbox.top - polygon.boundingbox.top)
        assert abs(resolution[1]) > abs(geobox.extent.boundingbox.bottom - polygon.boundingbox.bottom)

    A = mkA(0, scale=(10, -10),
            translation=(-48800, -2983006))

    w, h = 512, 256
    gbox = geometry.GeoBox(w, h, A, epsg3577)

    assert gbox.shape == (h, w)
    assert gbox.transform == A
    assert gbox.extent.crs == gbox.crs
    assert gbox.geographic_extent.crs == epsg4326
    assert gbox.extent.boundingbox.height == h*10.0
    assert gbox.extent.boundingbox.width == w*10.0
    assert isinstance(str(gbox), str)
    assert 'EPSG:3577' in repr(gbox)

    assert geometry.GeoBox(1, 1, mkA(0), epsg4326).geographic_extent.crs == epsg4326

    g2 = gbox[:-10, :-20]
    assert g2.shape == (gbox.height - 10, gbox.width - 20)

    # step of 1 is ok
    g2 = gbox[::1, ::1]
    assert g2.shape == gbox.shape

    assert gbox[0].shape == (1, gbox.width)
    assert gbox[:3].shape == (3, gbox.width)

    with pytest.raises(NotImplementedError):
        gbox[::2, :]

    # too many slices
    with pytest.raises(ValueError):
        gbox[:1, :1, :]

    assert gbox.buffered(10, 0).shape == (gbox.height + 2*1, gbox.width)
    assert gbox.buffered(30, 20).shape == (gbox.height + 2*3, gbox.width + 2*2)

    assert (gbox | gbox) == gbox
    assert (gbox & gbox) == gbox
def simple_geobox():
    from datacube_ows.wms_utils import _get_geobox
    from affine import Affine
    from datacube.utils import geometry

    aff = Affine.translation(145.0, -35.0) * Affine.scale(1.0 / 256, 2.0 / 256)
    return geometry.GeoBox(256, 256, aff, 'EPSG:4326')
def _xarray_geobox(obj):
    transform, sdims = _xarray_affine_impl(obj)
    if sdims is None:
        return None

    crs = None
    try:
        crs = _get_crs_from_coord(obj)
    except ValueError:
        pass

    if crs is None:
        crs = _get_crs_from_attrs(obj, sdims)

    if crs is None:
        return None

    try:
        crs = _norm_crs(crs)
    except (ValueError, geometry.CRSError):
        warnings.warn(f"Encountered malformed CRS: {crs}")
        return None

    h, w = (obj.coords[dim].size for dim in sdims)

    return geometry.GeoBox(w, h, transform, crs)
Esempio n. 6
0
def test_write_dataset_to_netcdf(tmpnetcdf_filename):
    affine = Affine.scale(0.1, 0.1) * Affine.translation(20, 30)
    geobox = geometry.GeoBox(100, 100, affine, geometry.CRS(GEO_PROJ))
    dataset = xarray.Dataset(attrs={'extent': geobox.extent, 'crs': geobox.crs})
    for name, coord in geobox.coordinates.items():
        dataset[name] = (name, coord.values, {'units': coord.units, 'crs': geobox.crs})

    dataset['B10'] = (geobox.dimensions,
                      np.arange(10000, dtype='int16').reshape(geobox.shape),
                      {'nodata': 0, 'units': '1', 'crs': geobox.crs})

    write_dataset_to_netcdf(dataset, tmpnetcdf_filename, global_attributes={'foo': 'bar'},
                            variable_params={'B10': {'attrs': {'abc': 'xyz'}}})

    with netCDF4.Dataset(tmpnetcdf_filename) as nco:
        nco.set_auto_mask(False)
        assert 'B10' in nco.variables
        var = nco.variables['B10']
        assert (var[:] == dataset['B10'].values).all()

        assert 'foo' in nco.ncattrs()
        assert nco.getncattr('foo') == 'bar'

        assert 'abc' in var.ncattrs()
        assert var.getncattr('abc') == 'xyz'
Esempio n. 7
0
def test_geobox_simple():
    from affine import Affine
    t = geometry.GeoBox(4000, 4000,
                        Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0),
                        geometry.CRS('EPSG:4326'))

    expect_lon = np.asarray([
        151.000125, 151.000375, 151.000625, 151.000875, 151.001125, 151.001375,
        151.001625, 151.001875, 151.002125, 151.002375
    ])

    expect_lat = np.asarray([
        -29.000125, -29.000375, -29.000625, -29.000875, -29.001125, -29.001375,
        -29.001625, -29.001875, -29.002125, -29.002375
    ])
    expect_resolution = np.asarray([-0.00025, 0.00025])

    assert t.coordinates['latitude'].values.shape == (4000, )
    assert t.coordinates['longitude'].values.shape == (4000, )

    np.testing.assert_almost_equal(t.resolution, expect_resolution)
    np.testing.assert_almost_equal(t.coords['latitude'].values[:10],
                                   expect_lat)
    np.testing.assert_almost_equal(t.coords['longitude'].values[:10],
                                   expect_lon)
Esempio n. 8
0
def odc_style_xr_dataset():
    """An xarray.Dataset with ODC style coordinates and CRS, and no time dimension.

    Contains an EPSG:4326, single variable 'B10' of 100x100 int16 pixels."""
    affine = Affine.scale(0.1, 0.1) * Affine.translation(20, 30)
    geobox = geometry.GeoBox(100, 100, affine, geometry.CRS(GEO_PROJ))
    dataset = xarray.Dataset(attrs={
        'extent': geobox.extent,
        'crs': geobox.crs
    })
    for name, coord in geobox.coordinates.items():
        dataset[name] = (name, coord.values, {
            'units': coord.units,
            'crs': geobox.crs
        })
    dataset['B10'] = (geobox.dimensions,
                      np.arange(10000, dtype='int16').reshape(geobox.shape), {
                          'nodata': 0,
                          'units': '1',
                          'crs': geobox.crs
                      })

    # To include a time dimension:
    # dataset['B10'] = (('time', 'latitude', 'longitude'), np.arange(10000, dtype='int16').reshape((1, 100, 100)),
    #  {'nodata': 0, 'units': '1', 'crs': geobox.crs})

    return dataset
Esempio n. 9
0
def check_data_with_api(index, time_slices):
    """Chek retrieved data for specific values.

    We scale down by 100 and check for predefined values in the
    corners.
    """
    from datacube import Datacube
    dc = Datacube(index=index)

    # Make the retrieved data 100 less granular
    shape_x = int(GEOTIFF['shape']['x'] / 100.0)
    shape_y = int(GEOTIFF['shape']['y'] / 100.0)
    pixel_x = int(GEOTIFF['pixel_size']['x'] * 100)
    pixel_y = int(GEOTIFF['pixel_size']['y'] * 100)

    input_type_name = 'ls5_nbar_albers'
    input_type = dc.index.products.get_by_name(input_type_name)
    geobox = geometry.GeoBox(
        shape_x + 1, shape_y + 1,
        Affine(pixel_x, 0.0, GEOTIFF['ul']['x'], 0.0, pixel_y,
               GEOTIFF['ul']['y']), geometry.CRS(GEOTIFF['crs']))
    observations = dc.find_datasets(product='ls5_nbar_albers',
                                    geopolygon=geobox.extent)
    group_by = query_group_by('time')
    sources = dc.group_datasets(observations, group_by)
    data = dc.load_data(sources, geobox, input_type.measurements.values())
    assert hashlib.md5(
        data.green.data).hexdigest() == '7f5ace486e88d33edf3512e8de6b6996'
    assert hashlib.md5(
        data.blue.data).hexdigest() == 'b58204f1e10dd678b292df188c242c7e'
    for time_slice in range(time_slices):
        assert data.blue.values[time_slice][-1, -1] == -999
Esempio n. 10
0
def load_with_dc(dc,
                 product_def,
                 product_id,
                 measurement,
                 time=None,
                 datasets=None,
                 dask_chunks=None):
    """Load data with dc, with settable params.

    If `datasets` is specified, the no `product` is used in the load command.
    `dask_chunks` get passed as-is.
    """
    params = SimpleNamespace(
        measurements=[measurement],
        like=geometry.GeoBox(
            *product_id.size,
            product_id.affine,
            GEDI_PRODUCT.crs,
        ),
        dask_chunks=dask_chunks,
    )
    if time:
        params.time = time
    if product_def.wavelengths:
        params.z = product_def.wavelengths

    if datasets:
        params.datasets = datasets
    else:
        params.product = product_def.name.format(measurement=measurement)
    _LOG.info(f"DC Loading {params}")
    data = dc.load(**params.__dict__)
    _LOG.info(f"DC Loaded\n{data}\n{'-'*80}")
    return data
def _xarray_geobox(obj):
    crs = _norm_crs(_get_crs(obj))
    if crs is None:
        return None

    dims = crs.dimensions
    return geometry.GeoBox(obj[dims[1]].size, obj[dims[0]].size, obj.affine,
                           crs)
Esempio n. 12
0
def _get_geobox(args, crs):
    width = int(args['width'])
    height = int(args['height'])
    minx, miny, maxx, maxy = map(float, args['bbox'].split(','))

    # miny-maxy for negative scale factor and maxy in the translation, includes inversion of Y axis.
    affine = Affine.translation(minx, maxy) * Affine.scale((maxx - minx) / width, (miny - maxy) / height)
    return geometry.GeoBox(width, height, affine, crs)
Esempio n. 13
0
def odc_style_xr_dataset():
    """An xarray.Dataset with ODC style coordinates and CRS, and no time dimension.

    Contains an EPSG:4326, single variable 'B10' of 100x100 int16 pixels."""
    affine = Affine.scale(0.1, 0.1) * Affine.translation(20, 30)
    geobox = geometry.GeoBox(100, 100, affine, geometry.CRS(GEO_PROJ))

    return Datacube.create_storage({}, geobox, [Measurement(name='B10', dtype='int16', nodata=0, units='1')])
Esempio n. 14
0
def create_geobox(
        crs,
        minx, miny,
        maxx, maxy,
        width, height,
):
    affine = Affine.translation(minx, maxy) * Affine.scale((maxx - minx) / width, (miny - maxy) / height)
    return geometry.GeoBox(width, height, affine, crs)
Esempio n. 15
0
def _get_geobox(args):
    width = int(args['width'])
    height = int(args['height'])
    minx, miny, maxx, maxy = map(float, args['bbox'].split(','))
    crs = geometry.CRS(args['srs'])

    affine = Affine.translation(minx, miny) * Affine.scale(
        (maxx - minx) / width, (maxy - miny) / height)
    return geometry.GeoBox(width, height, affine, crs)
Esempio n. 16
0
    def tile_geobox(self, tile_index: Tuple[int, int]) -> geometry.GeoBox:
        """
        Tile geobox.

        :param (int,int) tile_index:
        """
        res_y, res_x = self.resolution
        y, x = self.tile_coords(tile_index)
        h, w = self.tile_resolution
        geobox = geometry.GeoBox(crs=self.crs, affine=Affine(res_x, 0.0, x, 0.0, res_y, y), width=w, height=h)
        return geobox
Esempio n. 17
0
def _get_geobox(args, crs):
    width = int(args['width'])
    height = int(args['height'])
    if service_cfg["published_CRSs"][crs.crs_str].get("vertical_coord_first"):
        miny, minx, maxy, maxx = map(float, args['bbox'].split(','))
    else:
        minx, miny, maxx, maxy = map(float, args['bbox'].split(','))

    # miny-maxy for negative scale factor and maxy in the translation, includes inversion of Y axis.
    affine = Affine.translation(minx, maxy) * Affine.scale(
        (maxx - minx) / width, (miny - maxy) / height)
    return geometry.GeoBox(width, height, affine, crs)
Esempio n. 18
0
def check_open_with_api(driver_manager, time_slices):
    from datacube import Datacube
    dc = Datacube(driver_manager=driver_manager)

    input_type_name = 'ls5_nbar_albers'
    input_type = dc.index.products.get_by_name(input_type_name)
    geobox = geometry.GeoBox(200, 200, Affine(25, 0.0, 638000, 0.0, -25, 6276000), geometry.CRS('EPSG:28355'))
    observations = dc.find_datasets(product='ls5_nbar_albers', geopolygon=geobox.extent)
    group_by = query_group_by('time')
    sources = dc.group_datasets(observations, group_by)
    data = dc.load_data(sources, geobox, input_type.measurements.values(), driver_manager=driver_manager)
    assert data.blue.shape == (time_slices, 200, 200)
Esempio n. 19
0
def check_open_with_api(index):
    from datacube import Datacube
    dc = Datacube(index=index)

    input_type_name = 'ls5_nbar_albers'
    input_type = dc.index.products.get_by_name(input_type_name)

    geobox = geometry.GeoBox(200, 200,
                             Affine(25, 0.0, 1500000, 0.0, -25, -3900000),
                             geometry.CRS('EPSG:3577'))
    observations = dc.find_datasets(product='ls5_nbar_albers',
                                    geopolygon=geobox.extent)
    group_by = query_group_by('time')
    sources = dc.group_datasets(observations, group_by)
    data = dc.load_data(sources, geobox, input_type.measurements.values())
    assert data.blue.shape == (1, 200, 200)
Esempio n. 20
0
def test_pix_transform():
    pt = tuple([
        int(x / 10) * 10
        for x in geometry.point(145, -35, epsg4326).to_crs(epsg3577).coords[0]
    ])

    A = mkA(scale=(20, -20), translation=pt)

    src = geometry.GeoBox(1024, 512, A, epsg3577)
    dst = geometry.GeoBox.from_geopolygon(src.geographic_extent,
                                          (0.0001, -0.0001))

    tr = native_pix_transform(src, dst)

    pts_src = [(0, 0), (10, 20), (300, 200)]
    pts_dst = tr(pts_src)
    pts_src_ = tr.back(pts_dst)

    np.testing.assert_almost_equal(pts_src, pts_src_)
    assert tr.linear is None

    # check identity transform
    tr = native_pix_transform(src, src)

    pts_src = [(0, 0), (10, 20), (300, 200)]
    pts_dst = tr(pts_src)
    pts_src_ = tr.back(pts_dst)

    np.testing.assert_almost_equal(pts_src, pts_src_)
    np.testing.assert_almost_equal(pts_src, pts_dst)
    assert tr.linear is not None
    assert tr.back.linear is not None
    assert tr.back.back is tr

    # check scale only change
    tr = native_pix_transform(src, scaled_down_geobox(src, 2))
    pts_dst = tr(pts_src)
    pts_src_ = tr.back(pts_dst)

    assert tr.linear is not None
    assert tr.back.linear is not None
    assert tr.back.back is tr

    np.testing.assert_almost_equal(pts_dst,
                                   [(x / 2, y / 2) for (x, y) in pts_src])

    np.testing.assert_almost_equal(pts_src, pts_src_)
Esempio n. 21
0
def check_open_with_api(index, time_slices):
    with rasterio.Env():
        from datacube import Datacube
        dc = Datacube(index=index)

        input_type_name = 'ls5_nbar_albers'
        input_type = dc.index.products.get_by_name(input_type_name)
        geobox = geometry.GeoBox(200, 200, Affine(25, 0.0, 638000, 0.0, -25, 6276000), geometry.CRS('EPSG:28355'))
        observations = dc.find_datasets(product='ls5_nbar_albers', geopolygon=geobox.extent)
        group_by = query_group_by('time')
        sources = dc.group_datasets(observations, group_by)
        data = dc.load_data(sources, geobox, input_type.measurements.values())
        assert data.blue.shape == (time_slices, 200, 200)

        chunk_profile = {'time': 1, 'x': 100, 'y': 100}
        lazy_data = dc.load_data(sources, geobox, input_type.measurements.values(), dask_chunks=chunk_profile)
        assert lazy_data.blue.shape == (time_slices, 200, 200)
        assert (lazy_data.blue.load() == data.blue).all()
Esempio n. 22
0
def _get_geobox(args, src_crs, dst_crs=None):
    width = int(args['width'])
    height = int(args['height'])
    minx, miny, maxx, maxy = _get_geobox_xy(args, src_crs)

    if minx == maxx or miny == maxy:
        raise WMSException("Bounding box must enclose a non-zero area")
    if dst_crs is not None:
        minx, miny, maxx, maxy = _bounding_pts(
            minx, miny,
            maxx, maxy,
            width, height,
            src_crs, dst_crs=dst_crs
        )

    out_crs = src_crs if dst_crs is None else dst_crs
    affine = Affine.translation(minx, maxy) * Affine.scale((maxx - minx) / width, (miny - maxy) / height)
    return geometry.GeoBox(width, height, affine, out_crs)
Esempio n. 23
0
def test_geobox_simple():
    from affine import Affine
    t = geometry.GeoBox(4000, 4000,
                        Affine(0.00025, 0.0, 151.0, 0.0, -0.00025, -29.0),
                        geometry.CRS('EPSG:4326'))

    expect_lon = np.asarray([151.000125,  151.000375,  151.000625,  151.000875,  151.001125,
                             151.001375,  151.001625,  151.001875,  151.002125,  151.002375])

    expect_lat = np.asarray([-29.000125, -29.000375, -29.000625, -29.000875, -29.001125,
                             -29.001375, -29.001625, -29.001875, -29.002125, -29.002375])
    expect_resolution = np.asarray([-0.00025, 0.00025])

    assert (np.abs(np.r_[t.resolution] - expect_resolution) < 1e-6).all()
    assert t.coordinates['latitude'].values.shape == (4000,)
    assert t.coordinates['longitude'].values.shape == (4000,)
    assert (np.abs(t.coords['latitude'].values[:10] - expect_lat) < 1e-6).all()
    assert (np.abs(t.coords['longitude'].values[:10] - expect_lon) < 1e-6).all()
Esempio n. 24
0
def _get_geobox(args, src_crs, dst_crs=None):
    width = int(args['width'])
    height = int(args['height'])
    minx, miny, maxx, maxy = _get_geobox_xy(args, src_crs)

    if dst_crs is not None:
        minx, miny, maxx, maxy = _bounding_pts(minx,
                                               miny,
                                               maxx,
                                               maxy,
                                               width,
                                               height,
                                               src_crs,
                                               dst_crs=dst_crs)

    out_crs = src_crs if dst_crs is None else dst_crs
    affine = Affine.translation(minx, maxy) * Affine.scale(
        (maxx - minx) / width, (miny - maxy) / height)
    return geometry.GeoBox(width, height, affine, out_crs)
def check_data_with_api(index, time_slices):
    """Chek retrieved data for specific values.

    We scale down by 100 and check for predefined values in the
    corners.
    """
    from datacube import Datacube
    dc = Datacube(index=index)

    # TODO: this test needs to change, it tests that results are exactly the
    #       same as some time before, but with the current zoom out factor it's
    #       hard to verify that results are as expected even with human
    #       judgement. What it should test is that reading native from the
    #       ingested product gives exactly the same results as reading into the
    #       same GeoBox from the original product. Separate to that there
    #       should be a read test that confirms that what you read from native
    #       product while changing projection is of expected value

    # Make the retrieved data lower res
    ss = 100
    shape_x = int(GEOTIFF['shape']['x'] / ss)
    shape_y = int(GEOTIFF['shape']['y'] / ss)
    pixel_x = int(GEOTIFF['pixel_size']['x'] * ss)
    pixel_y = int(GEOTIFF['pixel_size']['y'] * ss)

    input_type_name = 'ls5_nbar_albers'
    input_type = dc.index.products.get_by_name(input_type_name)
    geobox = geometry.GeoBox(
        shape_x + 2, shape_y + 2,
        Affine(pixel_x, 0.0, GEOTIFF['ul']['x'], 0.0, pixel_y,
               GEOTIFF['ul']['y']), geometry.CRS(GEOTIFF['crs']))
    observations = dc.find_datasets(product='ls5_nbar_albers',
                                    geopolygon=geobox.extent)
    group_by = query_group_by('time')
    sources = dc.group_datasets(observations, group_by)
    data = dc.load_data(sources, geobox, input_type.measurements.values())
    assert hashlib.md5(
        data.green.data).hexdigest() == '0f64647bad54db4389fb065b2128025e'
    assert hashlib.md5(
        data.blue.data).hexdigest() == '41a7b50dfe5c4c1a1befbc378225beeb'
    for time_slice in range(time_slices):
        assert data.blue.values[time_slice][-1, -1] == -999
Esempio n. 26
0
def geobox_from_rio(xds):
    """This function retrieves the geobox using rioxarray extension.

    Parameters
    ----------
    xds: :obj:`xarray.DataArray` or :obj:`xarray.Dataset`
        The xarray dataset to get the geobox from.

    Returns
    -------
    :obj:`datacube.utils.geometry.GeoBox`

    """
    width, height = xds.rio.shape
    try:
        transform = xds.rio.transform()
    except AttributeError:
        transform = xds[xds.rio.vars[0]].rio.transform()
    return geometry.GeoBox(
        width=width,
        height=height,
        affine=transform,
        crs=geometry.CRS(crs_to_wkt(xds.rio.crs)),
    )
Esempio n. 27
0
def _xarray_geobox(obj):
    crs = None
    try:
        crs = _get_crs_from_coord(obj)
    except ValueError:
        pass

    if crs is None:
        try:
            crs = _get_crs_from_attrs(obj)
        except ValueError:
            pass

    if crs is None:
        return None

    try:
        crs = _norm_crs(crs)
    except ValueError:
        return None

    dims = crs.dimensions
    return geometry.GeoBox(obj[dims[1]].size, obj[dims[0]].size, obj.affine,
                           crs)
Esempio n. 28
0
    def __init__(self, args):
        self.args = args
        cfg = get_config()

        # Argument: Coverage (required)
        if "coverage" not in args:
            raise WCS1Exception("No coverage specified",
                                WCS1Exception.MISSING_PARAMETER_VALUE,
                                locator="COVERAGE parameter",
                                valid_keys=list(cfg.product_index))
        self.product_name = args["coverage"]
        self.product = cfg.product_index.get(self.product_name)
        if not self.product or not self.product.wcs:
            raise WCS1Exception("Invalid coverage: %s" % self.product_name,
                                WCS1Exception.COVERAGE_NOT_DEFINED,
                                locator="COVERAGE parameter",
                                valid_keys=list(cfg.product_index))

        # Argument: FORMAT (required)
        if "format" not in args:
            raise WCS1Exception("No FORMAT parameter supplied",
                                WCS1Exception.MISSING_PARAMETER_VALUE,
                                locator="FORMAT parameter",
                                valid_keys=cfg.wcs_formats_by_name)
        if args["format"] not in cfg.wcs_formats_by_name:
            raise WCS1Exception("Unsupported format: %s" % args["format"],
                                WCS1Exception.INVALID_PARAMETER_VALUE,
                                locator="FORMAT parameter",
                                valid_keys=cfg.wcs_formats_by_name)
        self.format = cfg.wcs_formats_by_name[args["format"]]

        # Argument: (request) CRS (required)
        if "crs" not in args:
            raise WCS1Exception("No request CRS specified",
                                WCS1Exception.MISSING_PARAMETER_VALUE,
                                locator="CRS parameter",
                                valid_keys=list(cfg.published_CRSs))
        self.request_crsid = args["crs"]
        if self.request_crsid not in cfg.published_CRSs:
            raise WCS1Exception("%s is not a supported CRS" %
                                self.request_crsid,
                                WCS1Exception.INVALID_PARAMETER_VALUE,
                                locator="CRS parameter",
                                valid_keys=list(cfg.published_CRSs))
        self.request_crs = cfg.crs(self.request_crsid)

        # Argument: response_crs (optional)
        if "response_crs" in args:
            self.response_crsid = args["response_crs"]
            if self.response_crsid not in cfg.published_CRSs:
                raise WCS1Exception("%s is not a supported CRS" %
                                    self.response_crsid,
                                    WCS1Exception.INVALID_PARAMETER_VALUE,
                                    locator="RESPONSE_CRS parameter",
                                    valid_keys=list(cfg.published_CRSs))
            self.response_crs = geometry.CRS(self.response_crsid)
            self.response_crs = cfg.crs(self.response_crsid)
        else:
            self.response_crsid = self.request_crsid
            self.response_crs = self.request_crs

        # Arguments: One of BBOX or TIME is required
        #if "bbox" not in args and "time" not in args:
        #    raise WCS1Exception("At least one of BBOX or TIME parameters must be supplied",
        #                        WCS1Exception.MISSING_PARAMETER_VALUE,
        #                        locator="BBOX or TIME parameter"
        #                        )

        # Argument: BBOX (technically not required if TIME supplied, but
        #       it's not clear to me what that would mean.)
        # For WCS 1.0.0 all bboxes will be specified as minx, miny, maxx, maxy
        if "bbox" not in args:
            raise WCS1Exception("No BBOX parameter supplied",
                                WCS1Exception.MISSING_PARAMETER_VALUE,
                                locator="BBOX or TIME parameter")
        try:
            self.minx, self.miny, self.maxx, self.maxy = map(
                float, args['bbox'].split(','))
        except:
            raise WCS1Exception("Invalid BBOX parameter",
                                WCS1Exception.INVALID_PARAMETER_VALUE,
                                locator="BBOX parameter")

        # Argument: TIME
        # if self.product.wcs_sole_time:
        #    self.times = [parse(self.product.wcs_sole_time).date()]
        if "time" not in args:
            #      CEOS treats no supplied time argument as all time.
            # I'm really not sure what the right thing to do is, but QGIS wants us to do SOMETHING - treat it as "now"
            self.times = [self.product.ranges["times"][-1]]
        else:
            # TODO: the min/max/res format option?
            # It's a bit underspeced. I'm not sure what the "res" would look like.
            times = args["time"].split(",")
            self.times = []
            for t in times:
                if t == "now":
                    continue
                try:
                    time = parse(t).date()
                    if time not in self.product.ranges["time_set"]:
                        raise WCS1Exception(
                            "Time value '%s' not a valid date for coverage %s"
                            % (t, self.product_name),
                            WCS1Exception.INVALID_PARAMETER_VALUE,
                            locator="TIME parameter",
                            valid_keys=[
                                d.strftime('%Y-%m-%d')
                                for d in self.product.ranges["time_set"]
                            ])
                    self.times.append(time)
                except ValueError:
                    raise WCS1Exception(
                        "Time value '%s' not a valid ISO-8601 date" % t,
                        WCS1Exception.INVALID_PARAMETER_VALUE,
                        locator="TIME parameter",
                        valid_keys=[
                            d.strftime('%Y-%m-%d')
                            for d in self.product.ranges["time_set"]
                        ])
            self.times.sort()

            if len(self.times) == 0:
                raise WCS1Exception(
                    "No valid ISO-8601 dates",
                    WCS1Exception.INVALID_PARAMETER_VALUE,
                    locator="TIME parameter",
                    valid_keys=[
                        d.strftime('%Y-%m-%d')
                        for d in self.product.ranges["time_set"]
                    ])
            elif len(self.times) > 1 and not self.format["multi-time"]:
                raise WCS1Exception(
                    "Cannot select more than one time slice with the %s format"
                    % self.format["name"],
                    WCS1Exception.INVALID_PARAMETER_VALUE,
                    locator="TIME and FORMAT parameters")

        # Range constraint parameter: MEASUREMENTS
        # No default is set in the DescribeCoverage, so it is required
        # But QGIS wants us to work without one, so take default from config
        if "measurements" in args:
            bands = args["measurements"]
            self.bands = []
            for b in bands.split(","):
                if not b:
                    continue
                try:
                    self.bands.append(self.product.band_idx.band(b))
                except ConfigException:
                    raise WCS1Exception(
                        f"Invalid measurement: {b}",
                        WCS1Exception.INVALID_PARAMETER_VALUE,
                        locator="MEASUREMENTS parameter",
                        valid_keys=self.product.band_idx.band_labels())
            if not bands:
                raise WCS1Exception(
                    "No measurements supplied",
                    WCS1Exception.INVALID_PARAMETER_VALUE,
                    locator="MEASUREMENTS parameter",
                    valid_keys=self.product.band_idx.band_labels())
        elif "styles" in args and args["styles"]:
            # Use style bands.
            # Non-standard protocol extension.
            #
            # As we have correlated WCS and WMS service implementations,
            # we can accept a style from WMS, and return the bands used for it.
            styles = args["styles"].split(",")
            if len(styles) != 1:
                raise WCS1Exception("Multiple style parameters not supported")
            style = self.product.style_index.get(styles[0])
            if style:
                self.bands = style.needed_bands
            else:
                self.bands = self.product.wcs_default_bands
        else:
            self.bands = self.product.wcs_default_bands

        # Argument: EXCEPTIONS (optional - defaults to XML)
        if "exceptions" in args and args[
                "exceptions"] != "application/vnd.ogc.se_xml":
            raise WCS1Exception(
                f"Unsupported exception format: {args['exceptions']}",
                WCS1Exception.INVALID_PARAMETER_VALUE,
                locator="EXCEPTIONS parameter")

        # Argument: INTERPOLATION (optional only nearest-neighbour currently supported.)
        #      If 'none' is supported in future, validation of width/height/res will need to change.
        if "interpolation" in args and args[
                "interpolation"] != "nearest neighbor":
            raise WCS1Exception(
                f'Unsupported interpolation method: {args["interpolation"]}',
                WCS1Exception.INVALID_PARAMETER_VALUE,
                locator="INTERPOLATION parameter")

        if "width" in args:
            if "resx" in args or "resy" in args:
                raise WCS1Exception(
                    "Specify WIDTH/HEIGHT parameters OR RESX/RESY parameters - not both",
                    WCS1Exception.MISSING_PARAMETER_VALUE,
                    locator="RESX/RESY/WIDTH/HEIGHT parameters")
            if "height" not in args:
                raise WCS1Exception(
                    "WIDTH parameter supplied without HEIGHT parameter",
                    WCS1Exception.MISSING_PARAMETER_VALUE,
                    locator="WIDTH/HEIGHT parameters")
            try:
                self.height = int(args["height"])
                if self.height < 1:
                    raise ValueError()
            except ValueError:
                raise WCS1Exception(
                    "HEIGHT parameter must be a positive integer",
                    WCS1Exception.INVALID_PARAMETER_VALUE,
                    locator="HEIGHT parameter")
            try:
                self.width = int(args["width"])
                if self.width < 1:
                    raise ValueError()
            except ValueError:
                raise WCS1Exception(
                    "WIDTH parameter must be a positive integer",
                    WCS1Exception.INVALID_PARAMETER_VALUE,
                    locator="WIDTH parameter")
            self.resx = (self.maxx - self.minx) / self.width
            self.resy = (self.maxy - self.miny) / self.height
        elif "resx" in args:
            if "height" in args:
                raise WCS1Exception(
                    "Specify WIDTH/HEIGHT parameters OR RESX/RESY parameters - not both",
                    WCS1Exception.MISSING_PARAMETER_VALUE,
                    locator="RESX/RESY/WIDTH/HEIGHT parameters")
            if "resy" not in args:
                raise WCS1Exception(
                    "RESX parameter supplied without RESY parameter",
                    WCS1Exception.MISSING_PARAMETER_VALUE,
                    locator="RESX/RESY parameters")
            try:
                self.resx = float(args["resx"])
                if self.resx <= 0.0:
                    raise ValueError(0)
            except ValueError:
                raise WCS1Exception("RESX parameter must be a positive number",
                                    WCS1Exception.INVALID_PARAMETER_VALUE,
                                    locator="RESX parameter")
            try:
                self.resy = float(args["resy"])
                if self.resy <= 0.0:
                    raise ValueError(0)
            except ValueError:
                raise WCS1Exception("RESY parameter must be a positive number",
                                    WCS1Exception.INVALID_PARAMETER_VALUE,
                                    locator="RESY parameter")
            self.width = (self.maxx - self.minx) / self.resx
            self.height = (self.maxy - self.miny) / self.resy
            self.width = int(self.width + 0.5)
            self.height = int(self.height + 0.5)
        elif "height" in args:
            raise WCS1Exception(
                "HEIGHT parameter supplied without WIDTH parameter",
                WCS1Exception.MISSING_PARAMETER_VALUE,
                locator="WIDTH/HEIGHT parameters")
        elif "resy" in args:
            raise WCS1Exception(
                "RESY parameter supplied without RESX parameter",
                WCS1Exception.MISSING_PARAMETER_VALUE,
                locator="RESX/RESY parameters")
        else:
            raise WCS1Exception(
                "You must specify either the WIDTH/HEIGHT parameters or RESX/RESY",
                WCS1Exception.MISSING_PARAMETER_VALUE,
                locator="RESX/RESY/WIDTH/HEIGHT parameters")

        self.extent = geometry.polygon([(self.minx, self.miny),
                                        (self.minx, self.maxy),
                                        (self.maxx, self.maxy),
                                        (self.maxx, self.miny),
                                        (self.minx, self.miny)],
                                       self.request_crs)

        xscale = (self.maxx - self.minx) / self.width
        yscale = (self.miny - self.maxy) / self.height
        trans_aff = Affine.translation(self.minx, self.maxy)
        scale_aff = Affine.scale(xscale, yscale)
        self.affine = trans_aff * scale_aff
        self.geobox = geometry.GeoBox(self.width, self.height, self.affine,
                                      self.request_crs)
Esempio n. 29
0
def _xarray_geobox(obj):
    dims = obj.crs.dimensions
    return geometry.GeoBox(obj[dims[1]].size, obj[dims[0]].size, obj.affine, obj.crs)
def sample_geometry():
    gb = geometry.GeoBox(40, 40,
                         Affine(2500, 0.0, 1200000.0, 0.0, -2500, -4300000.0),
                         geometry.CRS('EPSG:3577'))
    json = gb.extent.json
    return json