From 787ab1aafbccabdc588c673fdba4fa3e016e30b7 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Feb 2023 08:08:02 -0800 Subject: [PATCH 001/184] Add Mask subclass --- geoutils/__init__.py | 2 +- geoutils/georaster/__init__.py | 2 +- geoutils/georaster/raster.py | 141 ++++++++++++++++++++++++++++++++- geoutils/geovector.py | 21 +++-- 4 files changed, 154 insertions(+), 12 deletions(-) diff --git a/geoutils/__init__.py b/geoutils/__init__.py index 16d131353..c2bda48cd 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -4,7 +4,7 @@ from geoutils import spatial_tools # noqa from geoutils import examples, georaster, geovector, projtools, satimg # noqa -from geoutils.georaster import Raster # noqa +from geoutils.georaster import Raster, Mask # noqa from geoutils.geovector import Vector # noqa from geoutils.satimg import SatelliteImage # noqa diff --git a/geoutils/georaster/__init__.py b/geoutils/georaster/__init__.py index 6a82886ef..71375543e 100644 --- a/geoutils/georaster/__init__.py +++ b/geoutils/georaster/__init__.py @@ -1,3 +1,3 @@ -from geoutils.georaster.raster import Raster, RasterType # noqa isort:skip +from geoutils.georaster.raster import Raster, RasterType, Mask # noqa isort:skip __all__ = ["RasterType", "Raster"] diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 896ff8568..a8bf14a0c 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -519,7 +519,12 @@ def from_array( if isinstance(crs, int): crs = CRS.from_epsg(crs) - return cls({"data": data, "transform": transform, "crs": crs, "nodata": nodata}) + # If the data was transformed into boolean, re-initialize as a Mask subclass + if data.dtype == bool: + return Mask({"data": data, "transform": transform, "crs": crs, "nodata": nodata}) + # Otherwise, keep as a given RasterType subclass + else: + return cls({"data": data, "transform": transform, "crs": crs, "nodata": nodata}) def __repr__(self) -> str: """Convert object to formal string representation.""" @@ -533,9 +538,16 @@ def __str__(self) -> str: """Provide string of information about Raster.""" return self.info() - def __getitem__(self, value: Raster | Vector | list[float] | tuple[float, ...]) -> Raster: + def __getitem__(self, value: Raster | Vector | list[float] | tuple[float, ...]) -> np.ndarray | Raster: """Subset the Raster object: calls the crop method with default parameters""" - return self.crop(cropGeom=value, inplace=False) + + # If input is Mask with the same shape and georeferencing, index in 1D + if isinstance(value, Mask) and self.equal_georeferenced_grid(value): + return self.data[value.data] + + # Otherwise, subset with crop + else: + return self.crop(cropGeom=value, inplace=False) def __eq__(self, other: object) -> bool: """Check if a Raster masked array's data (including masked values), mask, fill_value and dtype are equal, @@ -2567,6 +2579,128 @@ def proximity( return self.copy(new_array=proximity) +# Subclass Mask for manipulating boolean Rasters +class Mask(Raster): + + def __init__(self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict, **kwargs): + + # If a Mask is passed, simply point back to Mask + if isinstance(filename_or_dataset, Mask): + for key in filename_or_dataset.__dict__: + setattr(self, key, filename_or_dataset.__dict__[key]) + return + # Else rely on parent Raster class options (including raised errors) + else: + super().__init__(filename_or_dataset, **kwargs) + + # If nbands larger than one, use only first band and raise a warning + if self.nbands > 1: + warnings.warn(category=UserWarning, + message="Multi-band raster provided to create a Mask, only the first band will be used.") + self._data = np.reshape(self.data[0, :], (1, self.shape[0], self.shape[1])) + + # Convert masked array to boolean + self._data = self.data.astype(bool) + + # Fix nband to one + self._nbands = 1 + + # Fix nodata to None + self._nodata = None + + # Define in dtypes + self._dtypes = (bool,) + + def from_array( + cls: type[RasterType], + data: np.ndarray | np.ma.masked_array, + transform: tuple[float, ...] | Affine, + crs: CRS | int | None, + nodata: int | float | list[int] | list[float] | None = None, + ) -> Mask: + + return Mask(Raster.from_array(data=data, transform=transform, crs=crs, nodata=nodata)) + + + def reproject( + self: Mask, + **kwargs + ) -> Mask: + + # Depending on resampling, adjust to rasterio supported types + if kwargs["resampling"] in [Resampling.nearest, "nearest"]: + self.data = self.data.astype("uint8") + else: + self.data = self.data.astype("float32") + + # Call Raster.reproject() + output = super().reproject(**kwargs) # type: ignore + + # Transform back to a boolean array + output.data = output.data.astype(bool) + + return output + + def crop( + self: RasterType, + **kwargs + ) -> None: + + # If there is resampling involved during cropping, encapsulate type as in reproject() + if kwargs["match_extent"]: + self.data = self.data.astype("float32") + if kwargs["in_place"]: + super().crop(**kwargs) + self.data = self.data.astype(bool) + else: + output = super().crop(**kwargs) + output.data = output.data.astype(bool) + return output.data + # Otherwise, run a classic crop + else: + super().crop(**kwargs) + + def polygonize( + self, in_value: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = 1 + ) -> Vector: + + if in_value not in [0, 1]: + warnings.warn("In-value converted to boolean for polygonizing mask.") + in_value = in_value.astype(bool).astype(int) + + self.data = self.data.astype('uint8') + return super().polygonize(in_value=in_value) + + # Logical operations between mask objects: scale to the entire mask + def __and__(self, other): + + return self.from_array(data=np.logical_and(self.data, other.data), + transform=self.transform, + crs=self.crs, + nodata=self.nodata) + + def __or__(self, other): + + return self.from_array(data=np.logical_or(self.data, other.data), + transform=self.transform, + crs=self.crs, + nodata=self.nodata) + + def __xor__(self, other): + + return self.from_array(data=np.logical_xor(self.data, other.data), + transform=self.transform, + crs=self.crs, + nodata=self.nodata) + + def __invert__(self): + + return self.from_array(data=np.logical_not(self.data), + transform=self.transform, + crs=self.crs, + nodata=self.nodata) + + # ----------------------------------------- # Additional stand-alone utility functions # ----------------------------------------- @@ -2641,3 +2775,4 @@ def proximity_from_vector_or_raster( raise ValueError('The type of proximity must be one of "in", "out" or "both".') return proximity + diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 70573ff31..92a850031 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -154,10 +154,9 @@ def create_mask( yres: float | None = None, bounds: tuple[float, float, float, float] | None = None, buffer: int | float | np.number = 0, - ) -> np.ndarray: + ) -> gu.Mask: """ - Rasterize the vector features into a boolean raster which has the extent/dimensions of \ - the provided raster file. + Rasterize the vector features into a boolean mask matching the georeferencing of a raster. Alternatively, user can specify a grid to rasterize on using xres, yres, bounds and crs. Only xres is mandatory, by default yres=xres and bounds/crs are set to self's. @@ -172,7 +171,7 @@ def create_mask( :param buffer: Size of buffer to be added around the features, in the raster's projection units. If a negative value is set, will erode the features. - :returns: array containing the mask + :returns: A Mask object contain a boolean array """ # If input rst is string, open as Raster if isinstance(rst, str): @@ -250,7 +249,7 @@ def create_mask( if rst is not None: mask = mask.reshape((rst.count, rst.height, rst.width)) # type: ignore - return mask + return gu.Mask.from_array(data=mask, transform=transform, crs=crs, nodata=None) def rasterize( self, @@ -261,7 +260,7 @@ def rasterize( bounds: tuple[float, float, float, float] | None = None, in_value: int | float | abc.Iterable[int | float] | None = None, out_value: int | float = 0, - ) -> np.ndarray: + ) -> gu.Raster | gu.Mask: """ Return an array with input geometries burned in. @@ -359,7 +358,15 @@ def rasterize( else: raise ValueError("in_value must be a single number or an iterable with same length as self.ds.geometry") - return mask + # We return a mask if there is a single value to burn and this value is ojne + if isinstance(in_value, Number) and in_value == 1: + output = gu.Mask.from_array(data=mask, transform=transform, crs=crs, nodata=None) + + # Otherwise we return a Raster if there are several values to burn + else: + output = gu.Raster.from_array(data=mask, transform=transform, crs=crs, nodata=None) + + return output def query(self: VectorType, expression: str, inplace: bool = False) -> VectorType: """ From 83c0f99f861144027f89138103564739ae28d460 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Feb 2023 13:01:12 -0800 Subject: [PATCH 002/184] Add arithmetic tests and move __eq__ to raster_equal() --- geoutils/georaster/raster.py | 5 +- geoutils/geovector.py | 2 +- tests/test_georaster.py | 430 +++++++++++++++++++++++++---------- tests/test_satimg.py | 3 +- 4 files changed, 319 insertions(+), 121 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index a8bf14a0c..250347d5e 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -549,7 +549,7 @@ def __getitem__(self, value: Raster | Vector | list[float] | tuple[float, ...]) else: return self.crop(cropGeom=value, inplace=False) - def __eq__(self, other: object) -> bool: + def raster_equal(self, other: object) -> bool: """Check if a Raster masked array's data (including masked values), mask, fill_value and dtype are equal, as well as the Raster's nodata, and georeferencing.""" @@ -567,9 +567,6 @@ def __eq__(self, other: object) -> bool: ] ) - def __ne__(self, other: object) -> bool: - return not self.__eq__(other) - def _overloading_check( self: RasterType, other: RasterType | np.ndarray | Number ) -> tuple[np.ma.masked_array, np.ma.masked_array | Number, float | int | list[int] | list[float] | None]: diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 92a850031..d6fe17f39 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -358,7 +358,7 @@ def rasterize( else: raise ValueError("in_value must be a single number or an iterable with same length as self.ds.geometry") - # We return a mask if there is a single value to burn and this value is ojne + # We return a mask if there is a single value to burn and this value is 1 if isinstance(in_value, Number) and in_value == 1: output = gu.Mask.from_array(data=mask, transform=transform, crs=crs, nodata=None) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index f5275d792..b092eb0de 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -101,7 +101,7 @@ def test_init(self, example: str) -> None: r4 = gr.Raster(memfile) assert isinstance(r4, gr.Raster) - assert r0 == r1 == r2 == r3 == r4 + assert all([r0.raster_equal(r1), r0.raster_equal(r1), r0.raster_equal(r2), r0.raster_equal(r3), r0.raster_equal(r4)]) # The data will not be copied, immutable objects will r0.data[0, 0, 0] += 5 @@ -428,7 +428,7 @@ def test_get_nanarray(self, example: str) -> None: # Check that modifying the NaN array does not back-propagate to the original array (np.ma.filled returns a view # when there is no invalid data, but in this case get_nanarray should copy the data). rst_arr += 5 - assert rst == rst_copy + assert rst.raster_equal(rst_copy) # -- Then, we test with a mask returned -- rst_arr, mask = rst.get_nanarray(return_mask=True) @@ -438,7 +438,7 @@ def test_get_nanarray(self, example: str) -> None: # Also check for back-propagation here with the mask and array rst_arr += 5 mask = ~mask - assert rst == rst_copy + assert rst.raster_equal(rst_copy) def test_downsampling(self) -> None: """ @@ -600,7 +600,7 @@ def test_copy(self, example: str) -> None: # First, we pass the new array as the masked array, mask and data of the new Raster object should be identical r2 = r.copy(new_array=r.data) - assert r == r2 + assert r.raster_equal(r2) # When passing the new array as a NaN ndarray, only the valid data is equal, because masked data is NaN in one # case, and -9999 in the other @@ -611,7 +611,7 @@ def test_copy(self, example: str) -> None: # be perfectly equal if r2.nodata is not None: r2.data.data[np.isnan(r2.data.data)] = r2.nodata - assert r == r2 + assert r.raster_equal(r2) # -- Fifth test: check that the new_array argument works when providing a new dtype ## if "int" in r.dtypes[0]: @@ -701,11 +701,11 @@ def test_crop_and_getitem(self, data: list[str]) -> None: # Test with same bounds -> should be the same # cropGeom2 = [cropGeom[0], cropGeom[1], cropGeom[2], cropGeom[3]] r_cropped = r.crop(cropGeom2, inplace=False) - assert r_cropped == r + assert r_cropped.raster_equal(r) # Test with bracket call r_cropped_getitem = r[cropGeom2] - assert r_cropped_getitem == r_cropped + assert r_cropped_getitem.raster_equal(r_cropped) # - Test cropping each side by a random integer of pixels - # rand_int = np.random.randint(1, min(r.shape) - 1) @@ -752,7 +752,7 @@ def test_crop_and_getitem(self, data: list[str]) -> None: # -- Test with CropGeom being a Raster -- # r_cropped2 = r.crop(r_cropped, inplace=False) - assert r_cropped2 == r_cropped + assert r_cropped2.raster_equal(r_cropped) # Check that bound reprojection is done automatically if the CRS differ with warnings.catch_warnings(): @@ -764,16 +764,16 @@ def test_crop_and_getitem(self, data: list[str]) -> None: # Original CRS bounds can be deformed during transformation, but result should be equivalent to this r_cropped4 = r.crop(cropGeom=r_cropped_reproj.get_bounds_projected(out_crs=r.crs), inplace=False) - assert r_cropped3 == r_cropped4 + assert r_cropped3.raster_equal(r_cropped4) # Check with bracket call r_cropped5 = r[r_cropped_reproj] - assert r_cropped4 == r_cropped5 + assert r_cropped4.raster_equal(r_cropped5) # -- Test with inplace=True (Default) -- # r_copy = r.copy() r_copy.crop(r_cropped) - assert r_copy == r_cropped + assert r_copy.raster_equal(r_cropped) # - Test cropping each side with a non integer pixel, mode='match_pixel' - # rand_float = np.random.randint(1, min(r.shape) - 1) + 0.25 @@ -836,7 +836,7 @@ def test_crop_and_getitem(self, data: list[str]) -> None: "ignore", category=UserWarning, message="For reprojection, dst_nodata must be set.*" ) r_cropped2 = r.crop(r_cropped, inplace=False, mode="match_extent") - assert r_cropped2 == r_cropped + assert r_cropped2.raster_equal(r_cropped) # -- Test with CropGeom being a Vector -- # outlines = gu.Vector(outlines_path) @@ -1069,7 +1069,7 @@ def test_reproject(self, example: str) -> None: r3 = r.reproject(dst_crs=out_crs, dst_nodata=0) r = gr.Raster(example, load_data=False) r4 = r.reproject(dst_crs=out_crs, dst_nodata=0) - assert r3 == r4 + assert r3.raster_equal(r4) # Test that reproject does not fail with resolution as np.integer or np.float types, single value or tuple astype_funcs = [int, np.int32, float, np.float64] @@ -1534,7 +1534,7 @@ def test_nodata_setter(self, example: str) -> None: r.set_nodata(_default_nodata(r.dtypes[0])) r_copy.nodata = _default_nodata(r.dtypes[0]) - assert r == r_copy + assert r.raster_equal(r_copy) def test_default_nodata(self) -> None: """ @@ -1667,7 +1667,7 @@ def test_save(self, example: str) -> None: temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) img.save(temp_file.name) saved = gr.Raster(temp_file.name) - assert img == saved + assert img.raster_equal(saved) # Try to save with a pathlib path (create a new temp file for Windows) temp_file_1 = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) @@ -1680,7 +1680,7 @@ def test_save(self, example: str) -> None: temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) img.save(temp_file.name, co_opts=co_opts, metadata=metadata) saved = gr.Raster(temp_file.name) - assert img == saved + assert img.raster_equal(saved) assert saved.tags["Type"] == "test" # Test that nodata value is enforced when masking - since value 0 is not used, data should be unchanged @@ -1764,7 +1764,7 @@ def test_from_array(self) -> None: # -> most tests already performed in test_copy, no need for more img = gr.Raster(self.landsat_b4_path) out_img = gr.Raster.from_array(img.data, img.transform, img.crs, nodata=img.nodata) - assert out_img == img + assert out_img.raster_equal(img) # Test that changes to data are taken into account bias = 5 @@ -1840,15 +1840,15 @@ def test_split_bands(self) -> None: blue2, green2 = img.split_bands(copy=False, subset=[2, 1]) # Check that the subset functionality works as expected. - assert red == red2 - assert green == green2 - assert blue == blue2 + assert red.raster_equal(red2) + assert green.raster_equal(green2) + assert blue.raster_equal(blue2) # Check that the red channel and the rgb data shares memory assert np.shares_memory(red.data, img.data) # Check that the red band data is not equal to the full RGB data. - assert red != img + assert not red.raster_equal(img) # Test that the red band corresponds to the first band of the img assert np.array_equal( @@ -1894,7 +1894,7 @@ def test_resampling_str(self) -> None: # Resample the rasters using a new resampling method and see that the string and enum gives the same result. img3a = img1.reproject(img2, resampling="q1") img3b = img1.reproject(img2, resampling=rio.enums.Resampling.q1) - assert img3a == img3b + assert img3a.raster_equal(img3b) @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_polygonize(self, example: str) -> None: @@ -2073,37 +2073,130 @@ def test_to_points(self) -> None: assert points_frame.crs == img2.crs -@pytest.mark.parametrize("dtype", ["float32", "uint8", "int32"]) # type: ignore -def test_numpy_functions(dtype: str) -> None: - """Test how rasters can be used as/with numpy arrays.""" - warnings.simplefilter("error") +class TestMask: + # Real data + landsat_b4_path = examples.get_path("everest_landsat_b4") + landsat_b4_crop_path = examples.get_path("everest_landsat_b4_cropped") + landsat_rgb_path = examples.get_path("everest_landsat_rgb") + everest_outlines_path = examples.get_path("everest_rgi_outlines") + aster_dem_path = examples.get_path("exploradores_aster_dem") + aster_outlines_path = examples.get_path("exploradores_rgi_outlines") - # Create an array of unique values starting at 0 and ending at 24 - array = np.arange(25, dtype=dtype).reshape((1, 5, 5)) - # Create an associated dummy transform - transform = rio.transform.from_origin(0, 5, 1, 1) + # Synthetic data + width = height = 5 + transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) + np.random.seed(42) + arr = np.random.randint(low=0, high=2, size=(1, width, height), dtype=bool) + arr_mask = np.random.randint(0, 2, size=(1, width, height), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) - # Create a raster from the array - raster = gu.Raster.from_array(array, transform=transform, crs=4326) + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) + def test_init(self, example: str): + """Test that Mask subclass intialization function as intended.""" - # Test some ufuncs - assert np.median(raster) == 12.0 - assert np.mean(raster) == 12.0 + # A warning should be raised when the raster is a multi-band + if "rgb" not in os.path.basename(example): + mask = gu.Mask(example) + else: + with pytest.warns(UserWarning, match="Multi-band raster provided to create a Mask, " + "only the first band will be used."): + mask = gu.Mask(example) + + # Check the masked array type + assert mask.data.dtype == "bool" + # Check output is the correct instance + assert isinstance(mask, gu.Mask) + # Check the dtypes metadata + assert mask.dtypes[0] == "bool" + # Check the nodata + assert mask.nodata is None + # Check the nbands metadata + assert mask.nbands == 1 + + # Check that a mask object is sent back from its own init + mask2 = gu.Mask(mask) + assert mask.raster_equal(mask2) + + def test_from_array(self): + """Test that Raster.__init__ casts to Mask with dict input of from_array() and a boolean data array.""" + + mask_rst = gu.Raster.from_array(data=self.mask_ma, transform=self.transform, crs=None, nodata=None) + + assert isinstance(mask_rst, gu.Mask) + assert mask_rst.transform == self.transform + assert mask_rst.crs is None + assert mask_rst.nodata is None + + # List all logical operators which will cast Rasters into Masks + ops_logical = [ + "__lt__", + "__le__", + "__eq__", + "__ne__", + "__gt__", + "__ge__", + ] - # Check that rasters don't become arrays when using simple arithmetic. - assert isinstance(raster + 1, gr.Raster) + @pytest.mark.parametrize("op", ops_logical) + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) + def test_logical_casting_real(self, example: str, op: str): + """ + Test that logical operations cast Raster object to Mask on real data + (synthetic done in TestArithmetic). + """ - # Test the data setter method by creating a new array - raster.data = array + 2 + rst = gu.Raster(example) - # Check that the median updated accordingly. - assert np.median(raster) == 14.0 + # Logical operations should cast to a Mask object, preserving the mask + mask = getattr(rst, op)(1) + assert isinstance(mask, gu.Mask) + assert np.array_equal(mask.data.data, getattr(rst.data.data, op)(1)) + assert np.array_equal(mask.data.mask, rst.data.mask) - # Test - raster += array + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) + def test_implicit_logical_casting_real(self, example: str): + """ + Test that implicit logical operations on real data + (synthetic done in TestArithmetic). + """ - assert isinstance(raster, gr.Raster) - assert np.median(raster) == 26.0 + rst = gu.Raster(example) + + # Equality + mask = rst == 1 + assert isinstance(mask, gu.Mask) + assert np.array_equal(mask.data.data, rst.data.data == 1) + assert np.array_equal(mask.data.mask, rst.data.mask) + + # Non-equality + mask = rst != 1 + assert isinstance(mask, gu.Mask) + assert np.array_equal(mask.data.data, rst.data.data != 1) + assert np.array_equal(mask.data.mask, rst.data.mask) + + # Lower than + mask = rst < 1 + assert isinstance(mask, gu.Mask) + assert np.array_equal(mask.data.data, rst.data.data < 1) + assert np.array_equal(mask.data.mask, rst.data.mask) + + # Lower equal + mask = rst <= 1 + assert isinstance(mask, gu.Mask) + assert np.array_equal(mask.data.data, rst.data.data <= 1) + assert np.array_equal(mask.data.mask, rst.data.mask) + + # Greater than + mask = rst > 1 + assert isinstance(mask, gu.Mask) + assert np.array_equal(mask.data.data, rst.data.data > 1) + assert np.array_equal(mask.data.mask, rst.data.mask) + + # Greater equal + mask = rst >= 1 + assert isinstance(mask, gu.Mask) + assert np.array_equal(mask.data.data, rst.data.data >= 1) + assert np.array_equal(mask.data.mask, rst.data.mask) class TestArithmetic: @@ -2163,47 +2256,47 @@ class TestArithmetic: np.random.randint(1, 255, (height, width)).astype("float32"), transform=transform, crs=None ) - def test_equal(self) -> None: + def test_raster_equal(self) -> None: """ - Test that __eq__ and __ne__ work as expected + Test that raster_equal() works as expected. """ r1 = self.r1 r2 = r1.copy() - assert r1 == r2 + assert r1.raster_equal(r2) # Change data r2.data += 1 - assert r1 != r2 + assert r1.raster_equal(r2) # Change mask (False by default) r2 = r1.copy() r2.data[0, 0] = np.ma.masked - assert r1 != r2 + assert r1.raster_equal(r2) # Change fill_value (999999 by default) r2 = r1.copy() r2.data.fill_value = 0 - assert r1 != r2 + assert r1.raster_equal(r2) # Change dtype r2 = r1.copy() r2 = r2.astype("float32") - assert r1 != r2 + assert r1.raster_equal(r2) # Change transform r2 = r1.copy() r2.transform = rio.transform.from_bounds(0, 0, 1, 1, self.width + 1, self.height) - assert r1 != r2 + assert r1.raster_equal(r2) # Change CRS r2 = r1.copy() r2.crs = rio.crs.CRS.from_epsg(4326) - assert r1 != r2 + assert r1.raster_equal(r2) # Change nodata r2 = r1.copy() r2.set_nodata(34) - assert r1 != r2 + assert r1.raster_equal(r2) def test_equal_georeferenced_grid(self) -> None: """ @@ -2311,8 +2404,8 @@ def test_ops_2args_expl(self, op: str) -> None: r2_copy = r2.copy() r3 = getattr(r1, op)(r2) assert isinstance(r3, gr.Raster) - assert r1 == r1_copy - assert r2 == r2_copy + assert r1.raster_equal(r1_copy) + assert r2.raster_equal(r2_copy) # Test with different dtypes r1 = self.r1_f32 @@ -2397,37 +2490,37 @@ def test_reflectivity(self, ops: list[str]) -> None: # Test with uint8 rasters r3 = getattr(self.r1, op1)(self.r2) r4 = getattr(self.r1, op2)(self.r2) - assert r3 == r4 + assert r3.raster_equal(r4) # Test with different dtypes r3 = getattr(self.r1_f32, op1)(self.r2) r4 = getattr(self.r1_f32, op2)(self.r2) - assert r3 == r4 + assert r3.raster_equal(r4) # Test with nodata set r3 = getattr(self.r1_nodata, op1)(self.r2) r4 = getattr(self.r1_nodata, op2)(self.r2) - assert r3 == r4 + assert r3.raster_equal(r4) # Test with zeros values (e.g. division) r3 = getattr(self.r1, op1)(self.r2_zero) r4 = getattr(self.r1, op2)(self.r2_zero) - assert r3 == r4 + assert r3.raster_equal(r4) # Test with a numpy array r3 = getattr(self.r1, op1)(array) r4 = getattr(self.r1, op2)(array) - assert r3 == r4 + assert r3.raster_equal(r4) # Test with an integer r3 = getattr(self.r1, op1)(intval) r4 = getattr(self.r1, op2)(intval) - assert r3 == r4 + assert r3.raster_equal(r4) # Test with a float value r3 = getattr(self.r1, op1)(floatval) r4 = getattr(self.r1, op2)(floatval) - assert r3 == r4 + assert r3.raster_equal(r4) @classmethod def from_array( @@ -2446,7 +2539,7 @@ def from_array( def test_ops_2args_implicit(self) -> None: """ - Test certain arithmetic overloading when called with symbols (+, -, *, /, //, %) + Test certain arithmetic overloading when called with symbols (+, -, *, /, //, %). """ warnings.filterwarnings("ignore", message="invalid value encountered") @@ -2459,65 +2552,140 @@ def test_ops_2args_implicit(self) -> None: floatval = 3.14 # Addition - assert r1 + r2 == self.from_array(r1.data + r2.data, rst_ref=r1) - assert r1_f32 + r2 == self.from_array(r1_f32.data + r2.data, rst_ref=r1) - assert array_3d + r2 == self.from_array(array_3d + r2.data, rst_ref=r2) - assert r2 + array_3d == self.from_array(r2.data + array_3d, rst_ref=r2) - assert array_2d + r2 == self.from_array(array_2d[np.newaxis, :, :] + r2.data, rst_ref=r2) - assert r2 + array_2d == self.from_array(r2.data + array_2d[np.newaxis, :, :], rst_ref=r2) - assert r1 + floatval == self.from_array(r1.data + floatval, rst_ref=r1) - assert floatval + r1 == self.from_array(floatval + r1.data, rst_ref=r1) - assert r1 + r2 == r2 + r1 + assert (r1 + r2).raster_equal(self.from_array(r1.data + r2.data, rst_ref=r1)) + assert (r1_f32 + r2).raster_equal(self.from_array(r1_f32.data + r2.data, rst_ref=r1)) + assert (array_3d + r2).raster_equal(self.from_array(array_3d + r2.data, rst_ref=r2)) + assert (r2 + array_3d).raster_equal(self.from_array(r2.data + array_3d, rst_ref=r2)) + assert (array_2d + r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] + r2.data, rst_ref=r2)) + assert (r2 + array_2d).raster_equal(self.from_array(r2.data + array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 + floatval).raster_equal(self.from_array(r1.data + floatval, rst_ref=r1)) + assert (floatval + r1).raster_equal(self.from_array(floatval + r1.data, rst_ref=r1)) + assert (r1 + r2).raster_equal(r2 + r1) # Multiplication - assert r1 * r2 == self.from_array(r1.data * r2.data, rst_ref=r1) - assert r1_f32 * r2 == self.from_array(r1_f32.data * r2.data, rst_ref=r1) - assert array_3d * r2 == self.from_array(array_3d * r2.data, rst_ref=r2) - assert r2 * array_3d == self.from_array(r2.data * array_3d, rst_ref=r2) - assert array_2d * r2 == self.from_array(array_2d[np.newaxis, :, :] * r2.data, rst_ref=r2) - assert r2 * array_2d == self.from_array(r2.data * array_2d[np.newaxis, :, :], rst_ref=r2) - assert r1 * floatval == self.from_array(r1.data * floatval, rst_ref=r1) - assert floatval * r1 == self.from_array(floatval * r1.data, rst_ref=r1) - assert r1 * r2 == r2 * r1 + assert (r1 * r2).raster_equal(self.from_array(r1.data * r2.data, rst_ref=r1)) + assert (r1_f32 * r2).raster_equal(self.from_array(r1_f32.data * r2.data, rst_ref=r1)) + assert (array_3d * r2).raster_equal(self.from_array(array_3d * r2.data, rst_ref=r2)) + assert (r2 * array_3d).raster_equal(self.from_array(r2.data * array_3d, rst_ref=r2)) + assert (array_2d * r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] * r2.data, rst_ref=r2)) + assert (r2 * array_2d).raster_equal(self.from_array(r2.data * array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 * floatval).raster_equal(self.from_array(r1.data * floatval, rst_ref=r1)) + assert (floatval * r1).raster_equal(self.from_array(floatval * r1.data, rst_ref=r1)) + assert (r1 * r2).raster_equal(r2 * r1) # Subtraction - assert r1 - r2 == self.from_array(r1.data - r2.data, rst_ref=r1) - assert r1_f32 - r2 == self.from_array(r1_f32.data - r2.data, rst_ref=r1) - assert array_3d - r2 == self.from_array(array_3d - r2.data, rst_ref=r2) - assert r2 - array_3d == self.from_array(r2.data - array_3d, rst_ref=r2) - assert array_2d - r2 == self.from_array(array_2d[np.newaxis, :, :] - r2.data, rst_ref=r2) - assert r2 - array_2d == self.from_array(r2.data - array_2d[np.newaxis, :, :], rst_ref=r2) - assert r1 - floatval == self.from_array(r1.data - floatval, rst_ref=r1) - assert floatval - r1 == self.from_array(floatval - r1.data, rst_ref=r1) + assert (r1 - r2).raster_equal(self.from_array(r1.data - r2.data, rst_ref=r1)) + assert (r1_f32 - r2).raster_equal(self.from_array(r1_f32.data - r2.data, rst_ref=r1)) + assert (array_3d - r2).raster_equal(self.from_array(array_3d - r2.data, rst_ref=r2)) + assert (r2 - array_3d).raster_equal(self.from_array(r2.data - array_3d, rst_ref=r2)) + assert (array_2d - r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] - r2.data, rst_ref=r2)) + assert (r2 - array_2d).raster_equal(self.from_array(r2.data - array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 - floatval).raster_equal(self.from_array(r1.data - floatval, rst_ref=r1)) + assert (floatval - r1).raster_equal(self.from_array(floatval - r1.data, rst_ref=r1)) # True division - assert r1 / r2 == self.from_array(r1.data / r2.data, rst_ref=r1) - assert r1_f32 / r2 == self.from_array(r1_f32.data / r2.data, rst_ref=r1) - assert array_3d / r2 == self.from_array(array_3d / r2.data, rst_ref=r2) - assert r2 / array_3d == self.from_array(r2.data / array_3d, rst_ref=r2) - assert array_2d / r2 == self.from_array(array_2d[np.newaxis, :, :] / r2.data, rst_ref=r1) - assert r2 / array_2d == self.from_array(r2.data / array_2d[np.newaxis, :, :], rst_ref=r2) - assert r1 / floatval == self.from_array(r1.data / floatval, rst_ref=r1) - assert floatval / r1 == self.from_array(floatval / r1.data, rst_ref=r1) + assert (r1 / r2).raster_equal(self.from_array(r1.data / r2.data, rst_ref=r1)) + assert (r1_f32 / r2).raster_equal(self.from_array(r1_f32.data / r2.data, rst_ref=r1)) + assert (array_3d / r2).raster_equal(self.from_array(array_3d / r2.data, rst_ref=r2)) + assert (r2 / array_3d).raster_equal(self.from_array(r2.data / array_3d, rst_ref=r2)) + assert (array_2d / r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] / r2.data, rst_ref=r1)) + assert (r2 / array_2d).raster_equal(self.from_array(r2.data / array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 / floatval).raster_equal(self.from_array(r1.data / floatval, rst_ref=r1)) + assert (floatval / r1).raster_equal(self.from_array(floatval / r1.data, rst_ref=r1)) # Floor division - assert r1 // r2 == self.from_array(r1.data // r2.data, rst_ref=r1) - assert r1_f32 // r2 == self.from_array(r1_f32.data // r2.data, rst_ref=r1) - assert array_3d // r2 == self.from_array(array_3d // r2.data, rst_ref=r1) - assert r2 // array_3d == self.from_array(r2.data // array_3d, rst_ref=r1) - assert array_2d // r2 == self.from_array(array_2d[np.newaxis, :, :] // r2.data, rst_ref=r1) - assert r2 // array_2d == self.from_array(r2.data // array_2d[np.newaxis, :, :], rst_ref=r1) - assert r1 // floatval == self.from_array(r1.data // floatval, rst_ref=r1) - assert floatval // r1 == self.from_array(floatval // r1.data, rst_ref=r1) + assert (r1 // r2).raster_equal(self.from_array(r1.data // r2.data, rst_ref=r1)) + assert (r1_f32 // r2).raster_equal(self.from_array(r1_f32.data // r2.data, rst_ref=r1)) + assert (array_3d // r2).raster_equal(self.from_array(array_3d // r2.data, rst_ref=r1)) + assert (r2 // array_3d).raster_equal(self.from_array(r2.data // array_3d, rst_ref=r1)) + assert (array_2d // r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] // r2.data, rst_ref=r1)) + assert (r2 // array_2d).raster_equal(self.from_array(r2.data // array_2d[np.newaxis, :, :], rst_ref=r1)) + assert (r1 // floatval).raster_equal(self.from_array(r1.data // floatval, rst_ref=r1)) + assert (floatval // r1).raster_equal(self.from_array(floatval // r1.data, rst_ref=r1)) # Modulo - assert r1 % r2 == self.from_array(r1.data % r2.data, rst_ref=r1) - assert r1_f32 % r2 == self.from_array(r1_f32.data % r2.data, rst_ref=r1) - assert array_3d % r2 == self.from_array(array_3d % r2.data, rst_ref=r1) - assert r2 % array_3d == self.from_array(r2.data % array_3d, rst_ref=r1) - assert array_2d % r2 == self.from_array(array_2d[np.newaxis, :, :] % r2.data, rst_ref=r1) - assert r2 % array_2d == self.from_array(r2.data % array_2d[np.newaxis, :, :], rst_ref=r1) - assert r1 % floatval == self.from_array(r1.data % floatval, rst_ref=r1) + assert (r1 % r2).raster_equal(self.from_array(r1.data % r2.data, rst_ref=r1)) + assert (r1_f32 % r2).raster_equal(self.from_array(r1_f32.data % r2.data, rst_ref=r1)) + assert (array_3d % r2).raster_equal(self.from_array(array_3d % r2.data, rst_ref=r1)) + assert (r2 % array_3d).raster_equal(self.from_array(r2.data % array_3d, rst_ref=r1)) + assert (array_2d % r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] % r2.data, rst_ref=r1)) + assert (r2 % array_2d).raster_equal(self.from_array(r2.data % array_2d[np.newaxis, :, :], rst_ref=r1)) + assert (r1 % floatval).raster_equal(self.from_array(r1.data % floatval, rst_ref=r1)) + + def test_ops_logical_implicit(self) -> None: + """ + Test logical arithmetic overloading when called with symbols (==, !=, <, <=, >, >=). + """ + warnings.filterwarnings("ignore", message="invalid value encountered") + + # Test various inputs: Raster with different dtypes, np.ndarray with 2D or 3D shape, single number + r1 = self.r1 + r1_f32 = self.r1_f32 + r2 = self.r2 + array_3d = np.random.randint(1, 255, (1, self.height, self.width)).astype("uint8") + array_2d = np.random.randint(1, 255, (self.height, self.width)).astype("uint8") + floatval = 3.14 + + # Equality + assert (r1 == r2).raster_equal(self.from_array(r1.data == r2.data, rst_ref=r1)) + assert (r1_f32 == r2).raster_equal(self.from_array(r1_f32.data == r2.data, rst_ref=r1)) + assert (array_3d == r2).raster_equal(self.from_array(array_3d == r2.data, rst_ref=r2)) + assert (r2 == array_3d).raster_equal(self.from_array(r2.data == array_3d, rst_ref=r2)) + assert (array_2d == r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] == r2.data, rst_ref=r2)) + assert (r2 == array_2d).raster_equal(self.from_array(r2.data == array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 == floatval).raster_equal(self.from_array(r1.data == floatval, rst_ref=r1)) + assert (floatval == r1).raster_equal(self.from_array(floatval == r1.data, rst_ref=r1)) + assert (r1 == r2).raster_equal(r2 == r1) + + # Non-equality + assert (r1 != r2).raster_equal(self.from_array(r1.data != r2.data, rst_ref=r1)) + assert (r1_f32 != r2).raster_equal(self.from_array(r1_f32.data != r2.data, rst_ref=r1)) + assert (array_3d != r2).raster_equal(self.from_array(array_3d != r2.data, rst_ref=r2)) + assert (r2 != array_3d).raster_equal(self.from_array(r2.data != array_3d, rst_ref=r2)) + assert (array_2d != r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] != r2.data, rst_ref=r2)) + assert (r2 != array_2d).raster_equal(self.from_array(r2.data != array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 != floatval).raster_equal(self.from_array(r1.data != floatval, rst_ref=r1)) + assert (floatval != r1).raster_equal(self.from_array(floatval != r1.data, rst_ref=r1)) + assert (r1 != r2).raster_equal(r2 != r1) + + # Lower than + assert (r1 < r2).raster_equal(self.from_array(r1.data < r2.data, rst_ref=r1)) + assert (r1_f32 < r2).raster_equal(self.from_array(r1_f32.data < r2.data, rst_ref=r1)) + assert (array_3d < r2).raster_equal(self.from_array(array_3d < r2.data, rst_ref=r2)) + assert (r2 < array_3d).raster_equal(self.from_array(r2.data < array_3d, rst_ref=r2)) + assert (array_2d < r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] < r2.data, rst_ref=r2)) + assert (r2 < array_2d).raster_equal(self.from_array(r2.data < array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 < floatval).raster_equal(self.from_array(r1.data < floatval, rst_ref=r1)) + assert (floatval < r1).raster_equal(self.from_array(floatval < r1.data, rst_ref=r1)) + + # Lower equal + assert (r1 <= r2).raster_equal(self.from_array(r1.data <= r2.data, rst_ref=r1)) + assert (r1_f32 <= r2).raster_equal(self.from_array(r1_f32.data <= r2.data, rst_ref=r1)) + assert (array_3d <= r2).raster_equal(self.from_array(array_3d <= r2.data, rst_ref=r2)) + assert (r2 <= array_3d).raster_equal(self.from_array(r2.data <= array_3d, rst_ref=r2)) + assert (array_2d <= r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] <= r2.data, rst_ref=r1)) + assert (r2 <= array_2d).raster_equal(self.from_array(r2.data <= array_2d[np.newaxis, :, :], rst_ref=r2)) + assert (r1 <= floatval).raster_equal(self.from_array(r1.data <= floatval, rst_ref=r1)) + assert (floatval <= r1).raster_equal(self.from_array(floatval <= r1.data, rst_ref=r1)) + + # Greater than + assert (r1 > r2).raster_equal(self.from_array(r1.data > r2.data, rst_ref=r1)) + assert (r1_f32 > r2).raster_equal(self.from_array(r1_f32.data > r2.data, rst_ref=r1)) + assert (array_3d > r2).raster_equal(self.from_array(array_3d > r2.data, rst_ref=r1)) + assert (r2 > array_3d).raster_equal(self.from_array(r2.data > array_3d, rst_ref=r1)) + assert (array_2d > r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] > r2.data, rst_ref=r1)) + assert (r2 > array_2d).raster_equal(self.from_array(r2.data > array_2d[np.newaxis, :, :], rst_ref=r1)) + assert (r1 > floatval).raster_equal(self.from_array(r1.data > floatval, rst_ref=r1)) + assert (floatval > r1).raster_equal(self.from_array(floatval > r1.data, rst_ref=r1)) + + # Greater equal + assert (r1 >= r2).raster_equal(self.from_array(r1.data >= r2.data, rst_ref=r1)) + assert (r1_f32 >= r2).raster_equal(self.from_array(r1_f32.data >= r2.data, rst_ref=r1)) + assert (array_3d >= r2).raster_equal(self.from_array(array_3d >= r2.data, rst_ref=r1)) + assert (r2 >= array_3d).raster_equal(self.from_array(r2.data >= array_3d, rst_ref=r1)) + assert (array_2d >= r2).raster_equal(self.from_array(array_2d[np.newaxis, :, :] >= r2.data, rst_ref=r1)) + assert (r2 >= array_2d).raster_equal(self.from_array(r2.data >= array_2d[np.newaxis, :, :], rst_ref=r1)) + assert (r1 >= floatval).raster_equal(self.from_array(r1.data >= floatval, rst_ref=r1)) @pytest.mark.parametrize("op", ops_2args) # type: ignore def test_raise_errors(self, op: str) -> None: @@ -2549,6 +2717,38 @@ def test_power(self, power: float | int) -> None: assert self.r1**power == self.from_array(self.r1.data**power, rst_ref=self.r1) assert self.r1_f32**power == self.from_array(self.r1_f32.data**power, rst_ref=self.r1_f32) + @pytest.mark.parametrize("dtype", ["float32", "uint8", "int32"]) # type: ignore + def test_numpy_functions(self, dtype: str) -> None: + """Test how rasters can be used as/with numpy arrays.""" + warnings.simplefilter("error") + + # Create an array of unique values starting at 0 and ending at 24 + array = np.arange(25, dtype=dtype).reshape((1, 5, 5)) + # Create an associated dummy transform + transform = rio.transform.from_origin(0, 5, 1, 1) + + # Create a raster from the array + raster = gu.Raster.from_array(array, transform=transform, crs=4326) + + # Test some ufuncs + assert np.median(raster) == 12.0 + assert np.mean(raster) == 12.0 + + # Check that rasters don't become arrays when using simple arithmetic. + assert isinstance(raster + 1, gr.Raster) + + # Test the data setter method by creating a new array + raster.data = array + 2 + + # Check that the median updated accordingly. + assert np.median(raster) == 14.0 + + # Test + raster += array + + assert isinstance(raster, gr.Raster) + assert np.median(raster) == 26.0 + class TestArrayInterface: """Test that the array interface of Raster works as expected for ufuncs and array functions""" @@ -2826,4 +3026,4 @@ def test_array_functions(self, arrfunc_str: str, dtype: str, nodata_init: None | if isinstance(output_rst, np.ndarray): assert np.ma.allequal(output_rst, output_ma) else: - assert output_rst == output_ma + assert output_rst.raster_equal(output_ma) diff --git a/tests/test_satimg.py b/tests/test_satimg.py index b83cc4bec..7151174ab 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -42,7 +42,8 @@ def test_init(self, example: str) -> None: img3 = si.SatelliteImage(r) assert isinstance(img3, si.SatelliteImage) - assert img == img2 == img3 + assert img.raster_equal(img2) + assert img.raster_equal(img3) @pytest.mark.parametrize("example", [landsat_b4, aster_dem]) # type: ignore def test_silent(self, example: str) -> None: From 8967f2b6ccaab8efafd3adcab74320bff869db68 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Feb 2023 17:09:39 -0800 Subject: [PATCH 003/184] Incremental commit on tests --- geoutils/georaster/raster.py | 92 ++++++++++++++++++++++++++++++------ geoutils/geovector.py | 2 +- tests/test_georaster.py | 77 +++++++++++++++++++++++++----- tests/test_geovector.py | 16 +++---- 4 files changed, 151 insertions(+), 36 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 250347d5e..75286084a 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -86,6 +86,9 @@ "quantile", ] + ["sort", "count_nonzero", "unique"] + + ["all", "any", "isfinite", "isinf", "isnan"] + + ["logical_and", "logical_or", "logical_xor", "logical_not", "all_close", "isclose", "array_equal", "array_equiv", + "greater", "greater_equal", "less", "less_equal", "equal", "not_equal"] ) @@ -482,7 +485,7 @@ def from_array( transform: tuple[float, ...] | Affine, crs: CRS | int | None, nodata: int | float | list[int] | list[float] | None = None, - ) -> RasterType: + ) -> RasterType | Mask: """Create a Raster from a numpy array and some geo-referencing information. :param data: data array @@ -782,6 +785,78 @@ def __pow__(self: RasterType, power: int | float) -> RasterType: out_rst = self.from_array(out_data, self.transform, self.crs, nodata=nodata) return out_rst + def __eq__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + """ + Element-wise equality cast into a Mask subclass. + If other is a Raster, it must have the same data.shape, transform and crs as self. + If other is a np.ndarray, it must have the same shape. + Otherwise, other must be a single number. + """ + self_data, other_data, _ = self._overloading_check(other) + out_data = self_data == other_data + out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) + return out_mask + + def __ne__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + """ + Element-wise equality cast into a Mask subclass. + If other is a Raster, it must have the same data.shape, transform and crs as self. + If other is a np.ndarray, it must have the same shape. + Otherwise, other must be a single number. + """ + self_data, other_data, _ = self._overloading_check(other) + out_data = self_data != other_data + out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) + return out_mask + + def __lt__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + """ + Element-wise equality cast into a Mask subclass. + If other is a Raster, it must have the same data.shape, transform and crs as self. + If other is a np.ndarray, it must have the same shape. + Otherwise, other must be a single number. + """ + self_data, other_data, _ = self._overloading_check(other) + out_data = self_data < other_data + out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) + return out_mask + + def __le__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + """ + Element-wise equality cast into a Mask subclass. + If other is a Raster, it must have the same data.shape, transform and crs as self. + If other is a np.ndarray, it must have the same shape. + Otherwise, other must be a single number. + """ + self_data, other_data, _ = self._overloading_check(other) + out_data = self_data <= other_data + out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) + return out_mask + + def __gt__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + """ + Element-wise equality cast into a Mask subclass. + If other is a Raster, it must have the same data.shape, transform and crs as self. + If other is a np.ndarray, it must have the same shape. + Otherwise, other must be a single number. + """ + self_data, other_data, _ = self._overloading_check(other) + out_data = self_data > other_data + out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) + return out_mask + + def __ge__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + """ + Element-wise equality cast into a Mask subclass. + If other is a Raster, it must have the same data.shape, transform and crs as self. + If other is a np.ndarray, it must have the same shape. + Otherwise, other must be a single number. + """ + self_data, other_data, _ = self._overloading_check(other) + out_data = self_data >= other_data + out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) + return out_mask + @overload def astype(self, dtype: DTypeLike, inplace: Literal[False] = False) -> Raster: ... @@ -2608,17 +2683,6 @@ def __init__(self, filename_or_dataset: str | RasterType | rio.io.DatasetReader # Define in dtypes self._dtypes = (bool,) - def from_array( - cls: type[RasterType], - data: np.ndarray | np.ma.masked_array, - transform: tuple[float, ...] | Affine, - crs: CRS | int | None, - nodata: int | float | list[int] | list[float] | None = None, - ) -> Mask: - - return Mask(Raster.from_array(data=data, transform=transform, crs=crs, nodata=nodata)) - - def reproject( self: Mask, **kwargs @@ -2732,7 +2796,7 @@ def proximity_from_vector_or_raster( # We create a geodataframe with the geometry type boundary_shp = gpd.GeoDataFrame(geometry=vector.ds.__getattr__(geometry_type), crs=vector.crs) # We mask the pixels that make up the geometry type - mask_boundary = Vector(boundary_shp).create_mask(raster).squeeze() + mask_boundary = Vector(boundary_shp).create_mask(raster).get_nanarray() else: # We mask target pixels @@ -2763,7 +2827,7 @@ def proximity_from_vector_or_raster( if in_or_out == "both": pass elif in_or_out in ["in", "out"]: - mask_polygon = Vector(vector.ds).create_mask(raster).squeeze() + mask_polygon = Vector(vector.ds).create_mask(raster).get_nanarray() if in_or_out == "in": proximity[~mask_polygon] = 0 else: diff --git a/geoutils/geovector.py b/geoutils/geovector.py index d6fe17f39..26d8f6370 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -249,7 +249,7 @@ def create_mask( if rst is not None: mask = mask.reshape((rst.count, rst.height, rst.width)) # type: ignore - return gu.Mask.from_array(data=mask, transform=transform, crs=crs, nodata=None) + return gu.Raster.from_array(data=mask, transform=transform, crs=crs, nodata=None) def rasterize( self, diff --git a/tests/test_georaster.py b/tests/test_georaster.py index b092eb0de..aabfc65f1 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2138,7 +2138,7 @@ def test_from_array(self): ] @pytest.mark.parametrize("op", ops_logical) - @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) def test_logical_casting_real(self, example: str, op: str): """ Test that logical operations cast Raster object to Mask on real data @@ -2153,7 +2153,7 @@ def test_logical_casting_real(self, example: str, op: str): assert np.array_equal(mask.data.data, getattr(rst.data.data, op)(1)) assert np.array_equal(mask.data.mask, rst.data.mask) - @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) def test_implicit_logical_casting_real(self, example: str): """ Test that implicit logical operations on real data @@ -2266,37 +2266,37 @@ def test_raster_equal(self) -> None: # Change data r2.data += 1 - assert r1.raster_equal(r2) + assert not r1.raster_equal(r2) # Change mask (False by default) r2 = r1.copy() r2.data[0, 0] = np.ma.masked - assert r1.raster_equal(r2) + assert not r1.raster_equal(r2) # Change fill_value (999999 by default) r2 = r1.copy() r2.data.fill_value = 0 - assert r1.raster_equal(r2) + assert not r1.raster_equal(r2) # Change dtype r2 = r1.copy() r2 = r2.astype("float32") - assert r1.raster_equal(r2) + assert not r1.raster_equal(r2) # Change transform r2 = r1.copy() r2.transform = rio.transform.from_bounds(0, 0, 1, 1, self.width + 1, self.height) - assert r1.raster_equal(r2) + assert not r1.raster_equal(r2) # Change CRS r2 = r1.copy() r2.crs = rio.crs.CRS.from_epsg(4326) - assert r1.raster_equal(r2) + assert not r1.raster_equal(r2) # Change nodata r2 = r1.copy() r2.set_nodata(34) - assert r1.raster_equal(r2) + assert not r1.raster_equal(r2) def test_equal_georeferenced_grid(self) -> None: """ @@ -2809,6 +2809,12 @@ class TestArrayInterface: # The full list exists in Raster handled_functions = gu.georaster.raster._HANDLED_FUNCTIONS + # Separate between two lists (single input and double input) for testing + handled_functions_2in = ["logical_and", "logical_or", "logical_xor", "logical_not", "all_close", "isclose", "array_equal", "array_equiv", + "greater", "greater_equal", "less", "less_equal", "equal", "not_equal"] + + handled_functions_1in = [hf for hf in handled_functions if hf not in handled_functions_2in] + # Details below: # NaN functions: [f for f in np.lib.nanfunctions.__all__] # nanstatfuncs = ['nansum', 'nanmax', 'nanmin', 'nanargmax', 'nanargmin', 'nanmean', 'nanmedian', 'nanpercentile', @@ -2975,13 +2981,13 @@ def test_array_ufunc_2nin_1nout( with pytest.raises(TypeError): ufunc(rst1, rst2) - @pytest.mark.parametrize("arrfunc_str", handled_functions) # type: ignore + @pytest.mark.parametrize("arrfunc_str", handled_functions_1in) # type: ignore @pytest.mark.parametrize( "dtype", ["uint8", "int8", "uint16", "int16", "uint32", "int32", "float32", "float64", "longdouble"] ) # type: ignore @pytest.mark.parametrize("nodata_init", [None, "type_default"]) # type: ignore - def test_array_functions(self, arrfunc_str: str, dtype: str, nodata_init: None | str) -> None: - """Test that array functions that we support give the same output as they would on the masked array""" + def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: None | str) -> None: + """Test that single-input array functions that we support give the same output as they would on the masked array""" # We set the default nodata if nodata_init == "type_default": @@ -3023,7 +3029,52 @@ def test_array_functions(self, arrfunc_str: str, dtype: str, nodata_init: None | output_rst = arrfunc(rst) output_ma = arrfunc(rst.data) + # This test is for when the NumPy function reduces the dimension of the array but not completely if isinstance(output_rst, np.ndarray): assert np.ma.allequal(output_rst, output_ma) + # This test is for when the NumPy function reduces the dimension to a single number else: - assert output_rst.raster_equal(output_ma) + assert output_rst == output_ma + + @pytest.mark.parametrize("arrfunc_str", handled_functions_2in) # type: ignore + @pytest.mark.parametrize( + "dtype1", ["uint8", "int8", "uint16", "int16", "uint32", "int32", "float32", "float64", "longdouble"] + ) # type: ignore + @pytest.mark.parametrize( + "dtype2", ["uint8", "int8", "uint16", "int16", "uint32", "int32", "float32", "float64", "longdouble"] + ) # type: ignore + @pytest.mark.parametrize("nodata1_init", [None, "type_default"]) # type: ignore + @pytest.mark.parametrize("nodata2_init", [None, "type_default"]) # type: ignore + def test_array_functions_2nin( + self, arrfunc_str: str, nodata1_init: None | str, nodata2_init: str, dtype1: str, dtype2: str + ) -> None: + """Test that double-input array functions that we support give the same output as they would on the masked array""" + + # We set the default nodatas + if nodata1_init == "type_default": + nodata1: int | None = _default_nodata(dtype1) + else: + nodata1 = None + if nodata2_init == "type_default": + nodata2: int | None = _default_nodata(dtype2) + else: + nodata2 = None + + ma1 = np.ma.masked_array(data=self.arr1.astype(dtype1), mask=self.mask1) + ma2 = np.ma.masked_array(data=self.arr2.astype(dtype2), mask=self.mask2) + rst1 = gr.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata1) + rst2 = gr.Raster.from_array(ma2, transform=self.transform, crs=None, nodata=nodata2) + + # Get array func + arrfunc = getattr(np, arrfunc_str) + + # Catch warnings + with warnings.catch_warnings(): + + warnings.filterwarnings("ignore", category=RuntimeWarning) + + output_rst = arrfunc(rst1, rst2) + output_ma = arrfunc(rst1.data, rst2.data) + + assert np.ma.allequal(output_rst, output_ma) + diff --git a/tests/test_geovector.py b/tests/test_geovector.py index ecd0bfb46..51a7f3f2a 100644 --- a/tests/test_geovector.py +++ b/tests/test_geovector.py @@ -207,7 +207,7 @@ def test_create_mask(self) -> None: """ # First with given res and bounds -> Should be a 21 x 21 array with 0 everywhere except center pixel vector = self.vector.copy() - out_mask = vector.create_mask(xres=1, bounds=(0, 0, 21, 21)) + out_mask = vector.create_mask(xres=1, bounds=(0, 0, 21, 21)).get_nanarray() ref_mask = np.zeros((21, 21), dtype="bool") ref_mask[10, 10] = True assert out_mask.shape == (21, 21) @@ -220,22 +220,22 @@ def test_create_mask(self) -> None: # Then with a gu.Raster as reference, single band rst = gu.Raster.from_array(np.zeros((21, 21)), transform=(1.0, 0.0, 0.0, 0.0, -1.0, 21.0), crs="EPSG:4326") - out_mask = vector.create_mask(rst) - assert out_mask.shape == (1, 21, 21) + out_mask = vector.create_mask(rst).get_nanarray() + assert out_mask.shape == (21, 21) # With gu.Raster, 2 bands -> fails... # rst = gu.Raster.from_array(np.zeros((2, 21, 21)), transform=(1., 0., 0., 0., -1., 21.), crs='EPSG:4326') # out_mask = vector.create_mask(rst) # Test that buffer = 0 works - out_mask_buff = vector.create_mask(rst, buffer=0) + out_mask_buff = vector.create_mask(rst, buffer=0).get_nanarray() assert np.all(ref_mask == out_mask_buff) # Test that buffer > 0 works rst = gu.Raster.from_array(np.zeros((21, 21)), transform=(1.0, 0.0, 0.0, 0.0, -1.0, 21.0), crs="EPSG:4326") - out_mask = vector.create_mask(rst) + out_mask = vector.create_mask(rst).get_nanarray() for buffer in np.arange(1, 8): - out_mask_buff = vector.create_mask(rst, buffer=buffer) + out_mask_buff = vector.create_mask(rst, buffer=buffer).get_nanarray() diff = out_mask_buff & ~out_mask assert np.count_nonzero(diff) > 0 # Difference between masks should always be thinner than buffer + 1 @@ -244,9 +244,9 @@ def test_create_mask(self) -> None: # Test that buffer < 0 works vector_5 = self.vector_5 - out_mask = vector_5.create_mask(rst) + out_mask = vector_5.create_mask(rst).get_nanarray() for buffer in np.arange(-1, -3, -1): - out_mask_buff = vector_5.create_mask(rst, buffer=buffer) + out_mask_buff = vector_5.create_mask(rst, buffer=buffer).get_nanarray() diff = ~out_mask_buff & out_mask assert np.count_nonzero(diff) > 0 # Difference between masks should always be thinner than buffer + 1 From a69262cbbaac4e1c7f37ede3b432100a669bc121 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Feb 2023 17:45:33 -0800 Subject: [PATCH 004/184] Finalize tests and linting --- geoutils/__init__.py | 2 +- geoutils/georaster/raster.py | 70 +++++++++++++++++------------------- tests/test_georaster.py | 30 ++++++++-------- 3 files changed, 50 insertions(+), 52 deletions(-) diff --git a/geoutils/__init__.py b/geoutils/__init__.py index c2bda48cd..2838e8e4b 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -4,7 +4,7 @@ from geoutils import spatial_tools # noqa from geoutils import examples, georaster, geovector, projtools, satimg # noqa -from geoutils.georaster import Raster, Mask # noqa +from geoutils.georaster import Mask, Raster # noqa from geoutils.geovector import Vector # noqa from geoutils.satimg import SatelliteImage # noqa diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 75286084a..5d29afb53 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -52,7 +52,7 @@ RasterType = TypeVar("RasterType", bound="Raster") # List of numpy functions that are handled: nan statistics function, normal statistics function and sorting/counting -_HANDLED_FUNCTIONS = ( +_HANDLED_FUNCTIONS_1NIN = ( [ "nansum", "nanmax", @@ -86,10 +86,10 @@ "quantile", ] + ["sort", "count_nonzero", "unique"] - + ["all", "any", "isfinite", "isinf", "isnan"] - + ["logical_and", "logical_or", "logical_xor", "logical_not", "all_close", "isclose", "array_equal", "array_equiv", - "greater", "greater_equal", "less", "less_equal", "equal", "not_equal"] -) + + ["all", "any", "isfinite", "isinf", "isnan", "logical_not"]) + +_HANDLED_FUNCTIONS_2NIN = (["logical_and", "logical_or", "logical_xor", "allclose","isclose","array_equal","array_equiv","greater", + "greater_equal", "less", "less_equal", "equal", "not_equal"]) # Function to set the default nodata values for any given dtype @@ -1357,7 +1357,7 @@ def __array_function__( """ # If function is not implemented - if func.__name__ not in _HANDLED_FUNCTIONS: + if func.__name__ not in _HANDLED_FUNCTIONS_1NIN + _HANDLED_FUNCTIONS_2NIN: return NotImplemented # For subclassing @@ -1381,7 +1381,11 @@ def __array_function__( else: first_arg = args[0].data - return func(first_arg, *args[1:], **kwargs) # type: ignore + if func.__name__ in _HANDLED_FUNCTIONS_1NIN: + return func(first_arg, *args[1:], **kwargs) # type: ignore + else: + second_arg = args[1].data + return func(first_arg, second_arg, *args[2:], **kwargs) # type: ignore # Note the star is needed because of the default argument 'mode' preceding non default arg 'inplace' # Then the final overload must be duplicated @@ -2653,8 +2657,9 @@ def proximity( # Subclass Mask for manipulating boolean Rasters class Mask(Raster): - - def __init__(self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict, **kwargs): + def __init__( + self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict, **kwargs + ): # If a Mask is passed, simply point back to Mask if isinstance(filename_or_dataset, Mask): @@ -2667,8 +2672,10 @@ def __init__(self, filename_or_dataset: str | RasterType | rio.io.DatasetReader # If nbands larger than one, use only first band and raise a warning if self.nbands > 1: - warnings.warn(category=UserWarning, - message="Multi-band raster provided to create a Mask, only the first band will be used.") + warnings.warn( + category=UserWarning, + message="Multi-band raster provided to create a Mask, only the first band will be used.", + ) self._data = np.reshape(self.data[0, :], (1, self.shape[0], self.shape[1])) # Convert masked array to boolean @@ -2683,10 +2690,7 @@ def __init__(self, filename_or_dataset: str | RasterType | rio.io.DatasetReader # Define in dtypes self._dtypes = (bool,) - def reproject( - self: Mask, - **kwargs - ) -> Mask: + def reproject(self: Mask, **kwargs) -> Mask: # Depending on resampling, adjust to rasterio supported types if kwargs["resampling"] in [Resampling.nearest, "nearest"]: @@ -2702,10 +2706,7 @@ def reproject( return output - def crop( - self: RasterType, - **kwargs - ) -> None: + def crop(self: RasterType, **kwargs) -> None: # If there is resampling involved during cropping, encapsulate type as in reproject() if kwargs["match_extent"]: @@ -2729,37 +2730,33 @@ def polygonize( warnings.warn("In-value converted to boolean for polygonizing mask.") in_value = in_value.astype(bool).astype(int) - self.data = self.data.astype('uint8') + self._data = self.data.astype("uint8") return super().polygonize(in_value=in_value) # Logical operations between mask objects: scale to the entire mask def __and__(self, other): - return self.from_array(data=np.logical_and(self.data, other.data), - transform=self.transform, - crs=self.crs, - nodata=self.nodata) + return self.from_array( + data=np.logical_and(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + ) def __or__(self, other): - return self.from_array(data=np.logical_or(self.data, other.data), - transform=self.transform, - crs=self.crs, - nodata=self.nodata) + return self.from_array( + data=np.logical_or(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + ) def __xor__(self, other): - return self.from_array(data=np.logical_xor(self.data, other.data), - transform=self.transform, - crs=self.crs, - nodata=self.nodata) + return self.from_array( + data=np.logical_xor(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + ) def __invert__(self): - return self.from_array(data=np.logical_not(self.data), - transform=self.transform, - crs=self.crs, - nodata=self.nodata) + return self.from_array( + data=np.logical_not(self.data), transform=self.transform, crs=self.crs, nodata=self.nodata + ) # ----------------------------------------- @@ -2836,4 +2833,3 @@ def proximity_from_vector_or_raster( raise ValueError('The type of proximity must be one of "in", "out" or "both".') return proximity - diff --git a/tests/test_georaster.py b/tests/test_georaster.py index aabfc65f1..0d846ab24 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -101,7 +101,9 @@ def test_init(self, example: str) -> None: r4 = gr.Raster(memfile) assert isinstance(r4, gr.Raster) - assert all([r0.raster_equal(r1), r0.raster_equal(r1), r0.raster_equal(r2), r0.raster_equal(r3), r0.raster_equal(r4)]) + assert all( + [r0.raster_equal(r1), r0.raster_equal(r1), r0.raster_equal(r2), r0.raster_equal(r3), r0.raster_equal(r4)] + ) # The data will not be copied, immutable objects will r0.data[0, 0, 0] += 5 @@ -2092,14 +2094,15 @@ class TestMask: @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) def test_init(self, example: str): - """Test that Mask subclass intialization function as intended.""" + """Test that Mask subclass initialization function as intended.""" # A warning should be raised when the raster is a multi-band if "rgb" not in os.path.basename(example): mask = gu.Mask(example) else: - with pytest.warns(UserWarning, match="Multi-band raster provided to create a Mask, " - "only the first band will be used."): + with pytest.warns( + UserWarning, match="Multi-band raster provided to create a Mask, only the first band will be used." + ): mask = gu.Mask(example) # Check the masked array type @@ -2806,14 +2809,10 @@ class TestArrayInterface: # - sorting and counting; # Most other math functions are already universal functions - # The full list exists in Raster - handled_functions = gu.georaster.raster._HANDLED_FUNCTIONS # Separate between two lists (single input and double input) for testing - handled_functions_2in = ["logical_and", "logical_or", "logical_xor", "logical_not", "all_close", "isclose", "array_equal", "array_equiv", - "greater", "greater_equal", "less", "less_equal", "equal", "not_equal"] - - handled_functions_1in = [hf for hf in handled_functions if hf not in handled_functions_2in] + handled_functions_2in = gu.georaster.raster._HANDLED_FUNCTIONS_2NIN + handled_functions_1in = gu.georaster.raster._HANDLED_FUNCTIONS_1NIN # Details below: # NaN functions: [f for f in np.lib.nanfunctions.__all__] @@ -2987,7 +2986,9 @@ def test_array_ufunc_2nin_1nout( ) # type: ignore @pytest.mark.parametrize("nodata_init", [None, "type_default"]) # type: ignore def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: None | str) -> None: - """Test that single-input array functions that we support give the same output as they would on the masked array""" + """ + Test that single-input array functions that we support give the same output as they would on the masked array. + """ # We set the default nodata if nodata_init == "type_default": @@ -3046,9 +3047,11 @@ def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: N @pytest.mark.parametrize("nodata1_init", [None, "type_default"]) # type: ignore @pytest.mark.parametrize("nodata2_init", [None, "type_default"]) # type: ignore def test_array_functions_2nin( - self, arrfunc_str: str, nodata1_init: None | str, nodata2_init: str, dtype1: str, dtype2: str + self, arrfunc_str: str, nodata1_init: None | str, nodata2_init: str, dtype1: str, dtype2: str ) -> None: - """Test that double-input array functions that we support give the same output as they would on the masked array""" + """ + Test that double-input array functions that we support give the same output as they would on the masked array. + """ # We set the default nodatas if nodata1_init == "type_default": @@ -3077,4 +3080,3 @@ def test_array_functions_2nin( output_ma = arrfunc(rst1.data, rst2.data) assert np.ma.allequal(output_rst, output_ma) - From f0036dfc10f72513890c8881ce435fca2140c7c2 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Feb 2023 17:47:37 -0800 Subject: [PATCH 005/184] Linting --- geoutils/georaster/raster.py | 24 +++++++++++++++++++----- tests/test_georaster.py | 1 - 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 5d29afb53..2723022d1 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -86,10 +86,24 @@ "quantile", ] + ["sort", "count_nonzero", "unique"] - + ["all", "any", "isfinite", "isinf", "isnan", "logical_not"]) - -_HANDLED_FUNCTIONS_2NIN = (["logical_and", "logical_or", "logical_xor", "allclose","isclose","array_equal","array_equiv","greater", - "greater_equal", "less", "less_equal", "equal", "not_equal"]) + + ["all", "any", "isfinite", "isinf", "isnan", "logical_not"] +) + +_HANDLED_FUNCTIONS_2NIN = [ + "logical_and", + "logical_or", + "logical_xor", + "allclose", + "isclose", + "array_equal", + "array_equiv", + "greater", + "greater_equal", + "less", + "less_equal", + "equal", + "not_equal", +] # Function to set the default nodata values for any given dtype @@ -1385,7 +1399,7 @@ def __array_function__( return func(first_arg, *args[1:], **kwargs) # type: ignore else: second_arg = args[1].data - return func(first_arg, second_arg, *args[2:], **kwargs) # type: ignore + return func(first_arg, second_arg, *args[2:], **kwargs) # type: ignore # Note the star is needed because of the default argument 'mode' preceding non default arg 'inplace' # Then the final overload must be duplicated diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 0d846ab24..1389f9ee7 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2809,7 +2809,6 @@ class TestArrayInterface: # - sorting and counting; # Most other math functions are already universal functions - # Separate between two lists (single input and double input) for testing handled_functions_2in = gu.georaster.raster._HANDLED_FUNCTIONS_2NIN handled_functions_1in = gu.georaster.raster._HANDLED_FUNCTIONS_1NIN From 94bb65edbdf80563aba6d9ae8460e31a2f115cbd Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Feb 2023 16:50:56 -0800 Subject: [PATCH 006/184] Make mypy happy --- geoutils/georaster/raster.py | 124 +++++++++++++++++++++++++++-------- geoutils/geovector.py | 2 +- tests/test_georaster.py | 16 ++--- 3 files changed, 106 insertions(+), 36 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 53f497db8..83f19bef7 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -520,7 +520,7 @@ def from_array( transform: tuple[float, ...] | Affine, crs: CRS | int | None, nodata: int | float | list[int] | list[float] | None = None, - ) -> RasterType | Mask: + ) -> RasterType: """Create a Raster from a numpy array and some geo-referencing information. :param data: data array @@ -558,8 +558,9 @@ def from_array( crs = CRS.from_epsg(crs) # If the data was transformed into boolean, re-initialize as a Mask subclass + # Typing: we can specify this behaviour in @overload once we add the NumPy plugin of MyPy if data.dtype == bool: - return Mask({"data": data, "transform": transform, "crs": crs, "nodata": nodata}) + return Mask({"data": data, "transform": transform, "crs": crs, "nodata": nodata}) # type: ignore # Otherwise, keep as a given RasterType subclass else: return cls({"data": data, "transform": transform, "crs": crs, "nodata": nodata}) @@ -844,7 +845,7 @@ def __pow__(self: RasterType, power: int | float) -> RasterType: out_rst = self.from_array(out_data, self.transform, self.crs, nodata=nodata) return out_rst - def __eq__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + def __eq__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: # type: ignore """ Element-wise equality cast into a Mask subclass. If other is a Raster, it must have the same data.shape, transform and crs as self. @@ -856,7 +857,7 @@ def __eq__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) return out_mask - def __ne__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + def __ne__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: # type: ignore """ Element-wise equality cast into a Mask subclass. If other is a Raster, it must have the same data.shape, transform and crs as self. @@ -868,7 +869,7 @@ def __ne__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) return out_mask - def __lt__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + def __lt__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ Element-wise equality cast into a Mask subclass. If other is a Raster, it must have the same data.shape, transform and crs as self. @@ -880,7 +881,7 @@ def __lt__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) return out_mask - def __le__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + def __le__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ Element-wise equality cast into a Mask subclass. If other is a Raster, it must have the same data.shape, transform and crs as self. @@ -892,7 +893,7 @@ def __le__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) return out_mask - def __gt__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + def __gt__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ Element-wise equality cast into a Mask subclass. If other is a Raster, it must have the same data.shape, transform and crs as self. @@ -904,7 +905,7 @@ def __gt__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: out_mask = self.from_array(out_data, self.transform, self.crs, nodata=None) return out_mask - def __ge__(self: RasterType, other: RasterType | np.ndarray | Number) -> Mask: + def __ge__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ Element-wise equality cast into a Mask subclass. If other is a Raster, it must have the same data.shape, transform and crs as self. @@ -2790,8 +2791,10 @@ def proximity( # Subclass Mask for manipulating boolean Rasters class Mask(Raster): def __init__( - self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict, **kwargs - ): + self, + filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], + **kwargs: Any, + ) -> None: # If a Mask is passed, simply point back to Mask if isinstance(filename_or_dataset, Mask): @@ -2822,69 +2825,136 @@ def __init__( # Define in dtypes self._dtypes = (bool,) - def reproject(self: Mask, **kwargs) -> Mask: + def reproject( + self: Mask, + dst_ref: RasterType | rio.io.Dataset | str | None = None, + dst_crs: CRS | str | int | None = None, + dst_size: tuple[int, int] | None = None, + dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, + dst_res: float | abc.Iterable[float] | None = None, + dst_nodata: int | float | list[int] | list[float] | None = None, + src_nodata: int | float | list[int] | list[float] | None = None, + dst_dtype: np.dtype | None = None, + resampling: Resampling | str = Resampling.bilinear, + silent: bool = False, + n_threads: int = 0, + memory_limit: int = 64, + ) -> Mask: # Depending on resampling, adjust to rasterio supported types - if kwargs["resampling"] in [Resampling.nearest, "nearest"]: + if resampling in [Resampling.nearest, "nearest"]: self.data = self.data.astype("uint8") else: self.data = self.data.astype("float32") # Call Raster.reproject() - output = super().reproject(**kwargs) # type: ignore + output = super().reproject( + dst_ref=dst_ref, + dst_crs=dst_crs, + dst_size=dst_size, + dst_bounds=dst_bounds, + dst_res=dst_res, + dst_nodata=dst_nodata, + src_nodata=src_nodata, + dst_dtype=dst_dtype, + resampling=resampling, + silent=silent, + n_threads=n_threads, + memory_limit=memory_limit, + ) # Transform back to a boolean array output.data = output.data.astype(bool) return output - def crop(self: RasterType, **kwargs) -> None: + # Note the star is needed because of the default argument 'mode' preceding non default arg 'inplace' + # Then the final overload must be duplicated + @overload + def crop( + self: Mask, + cropGeom: Mask | Vector | list[float] | tuple[float, ...], + mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", + *, + inplace: Literal[True], + ) -> None: + ... + + @overload + def crop( + self: Mask, + cropGeom: Mask | Vector | list[float] | tuple[float, ...], + mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", + *, + inplace: Literal[False], + ) -> Mask: + ... + + @overload + def crop( + self: Mask, + cropGeom: Mask | Vector | list[float] | tuple[float, ...], + mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", + inplace: bool = True, + ) -> Mask | None: + ... + + def crop( + self: Mask, + cropGeom: Mask | Vector | list[float] | tuple[float, ...], + mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", + inplace: bool = True, + ) -> Mask | None: # If there is resampling involved during cropping, encapsulate type as in reproject() - if kwargs["match_extent"]: + if mode == "match_extent": self.data = self.data.astype("float32") - if kwargs["in_place"]: - super().crop(**kwargs) + if inplace: + super().crop(cropGeom=cropGeom, mode=mode, inplace=inplace) self.data = self.data.astype(bool) + return None else: - output = super().crop(**kwargs) + output = super().crop(cropGeom=cropGeom, mode=mode, inplace=inplace) output.data = output.data.astype(bool) - return output.data + return output # Otherwise, run a classic crop else: - super().crop(**kwargs) + if not inplace: + return super().crop(cropGeom=cropGeom, mode=mode, inplace=inplace) + else: + return None def polygonize( self, in_value: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = 1 ) -> Vector: - if in_value not in [0, 1]: - warnings.warn("In-value converted to boolean for polygonizing mask.") - in_value = in_value.astype(bool).astype(int) + if not isinstance(in_value, (int, np.integer, float, np.floating)) or in_value not in [0, 1]: + warnings.warn("In-value converted to 1 for polygonizing boolean mask.") + in_value = 1 self._data = self.data.astype("uint8") return super().polygonize(in_value=in_value) # Logical operations between mask objects: scale to the entire mask - def __and__(self, other): + def __and__(self: Mask, other: Mask) -> Mask: return self.from_array( data=np.logical_and(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata ) - def __or__(self, other): + def __or__(self: Mask, other: Mask) -> Mask: return self.from_array( data=np.logical_or(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata ) - def __xor__(self, other): + def __xor__(self: Mask, other: Mask) -> Mask: return self.from_array( data=np.logical_xor(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata ) - def __invert__(self): + def __invert__(self: Mask) -> Mask: return self.from_array( data=np.logical_not(self.data), transform=self.transform, crs=self.crs, nodata=self.nodata diff --git a/geoutils/geovector.py b/geoutils/geovector.py index b4309969a..ee247aa32 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -423,7 +423,7 @@ def rasterize( raise ValueError("in_value must be a single number or an iterable with same length as self.ds.geometry") # We return a mask if there is a single value to burn and this value is 1 - if isinstance(in_value, Number) and in_value == 1: + if isinstance(in_value, (int, np.integer, float, np.floating)) and in_value == 1: output = gu.Mask.from_array(data=mask, transform=transform, crs=crs, nodata=None) # Otherwise we return a Raster if there are several values to burn diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 048678d3c..e0d09051b 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2288,8 +2288,8 @@ class TestMask: arr_mask = np.random.randint(0, 2, size=(1, width, height), dtype=bool) mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) - @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) - def test_init(self, example: str): + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore + def test_init(self, example: str) -> None: """Test that Mask subclass initialization function as intended.""" # A warning should be raised when the raster is a multi-band @@ -2316,7 +2316,7 @@ def test_init(self, example: str): mask2 = gu.Mask(mask) assert mask.raster_equal(mask2) - def test_from_array(self): + def test_from_array(self) -> None: """Test that Raster.__init__ casts to Mask with dict input of from_array() and a boolean data array.""" mask_rst = gu.Raster.from_array(data=self.mask_ma, transform=self.transform, crs=None, nodata=None) @@ -2336,9 +2336,9 @@ def test_from_array(self): "__ge__", ] - @pytest.mark.parametrize("op", ops_logical) - @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) - def test_logical_casting_real(self, example: str, op: str): + @pytest.mark.parametrize("op", ops_logical) # type: ignore + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore + def test_logical_casting_real(self, example: str, op: str) -> None: """ Test that logical operations cast Raster object to Mask on real data (synthetic done in TestArithmetic). @@ -2352,8 +2352,8 @@ def test_logical_casting_real(self, example: str, op: str): assert np.array_equal(mask.data.data, getattr(rst.data.data, op)(1)) assert np.array_equal(mask.data.mask, rst.data.mask) - @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) - def test_implicit_logical_casting_real(self, example: str): + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore + def test_implicit_logical_casting_real(self, example: str) -> None: """ Test that implicit logical operations on real data (synthetic done in TestArithmetic). From 16795fc7e91e441466b7ad3eb0dddc6e1fcb29df Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Feb 2023 20:44:05 -0800 Subject: [PATCH 007/184] Update nbands into count after merge of related PR --- geoutils/georaster/raster.py | 5 +---- tests/test_georaster.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 83f19bef7..712397cce 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -2806,7 +2806,7 @@ def __init__( super().__init__(filename_or_dataset, **kwargs) # If nbands larger than one, use only first band and raise a warning - if self.nbands > 1: + if self.count > 1: warnings.warn( category=UserWarning, message="Multi-band raster provided to create a Mask, only the first band will be used.", @@ -2816,9 +2816,6 @@ def __init__( # Convert masked array to boolean self._data = self.data.astype(bool) - # Fix nband to one - self._nbands = 1 - # Fix nodata to None self._nodata = None diff --git a/tests/test_georaster.py b/tests/test_georaster.py index e0d09051b..bf1a6d6bb 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2310,7 +2310,7 @@ def test_init(self, example: str) -> None: # Check the nodata assert mask.nodata is None # Check the nbands metadata - assert mask.nbands == 1 + assert mask.count == 1 # Check that a mask object is sent back from its own init mask2 = gu.Mask(mask) From 7122c03536bd7497880891d40a8f7e66621f1c07 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 10 Feb 2023 14:23:21 -0800 Subject: [PATCH 008/184] Move doc to sphinx-book-theme --- dev-environment.yml | 5 +- dev-requirements.txt | 3 +- doc/source/about_geoutils.md | 68 ++++++++++ doc/source/api.md | 64 +++++++++ doc/source/api.rst | 61 --------- doc/source/conf.py | 12 +- doc/source/index.md | 47 +++++++ doc/source/index.rst | 38 ------ doc/source/quick_start.md | 9 ++ doc/source/raster-basics.md | 128 ++++++++++++++++++ doc/source/raster-basics.rst | 119 ---------------- .../{satimg-basics.rst => satimg-basics.md} | 21 +-- doc/source/vector-basics.md | 49 +++++++ doc/source/vector-basics.rst | 45 ------ 14 files changed, 393 insertions(+), 276 deletions(-) create mode 100644 doc/source/about_geoutils.md create mode 100644 doc/source/api.md delete mode 100644 doc/source/api.rst create mode 100644 doc/source/index.md delete mode 100644 doc/source/index.rst create mode 100644 doc/source/quick_start.md create mode 100644 doc/source/raster-basics.md delete mode 100644 doc/source/raster-basics.rst rename doc/source/{satimg-basics.rst => satimg-basics.md} (67%) create mode 100644 doc/source/vector-basics.md delete mode 100644 doc/source/vector-basics.rst diff --git a/dev-environment.yml b/dev-environment.yml index 38a838994..37b0f62bc 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -12,7 +12,7 @@ dependencies: # Development-specific - sphinx - - sphinx_rtd_theme + - myst-parser - sphinx-autodoc-typehints - sphinxcontrib-programoutput - numpydoc @@ -27,3 +27,6 @@ dependencies: - scikit-image - pyyaml - rioxarray + + - pip: + - sphinx-book-theme diff --git a/dev-requirements.txt b/dev-requirements.txt index 15a509ce4..52adaeb11 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -6,11 +6,12 @@ pytest pytest-xdist pytest-lazy-fixture sphinx -sphinx_rtd_theme +sphinx-book-theme pylint sphinxcontrib_programoutput sphinx_autodoc_typehints matplotlib +myst-parser scipy pre-commit typing-extensions diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md new file mode 100644 index 000000000..ddd18ecaa --- /dev/null +++ b/doc/source/about_geoutils.md @@ -0,0 +1,68 @@ +(about-geoutils)= + +# About GeoUtils + + +GeoUtils is a [Python](https://www.python.org/) package for the analysis of georeferenced raster and vectors, with name standing for _geospatial utilities_. +It is designed for all Earth and planetary observation science. + + +```{epigraph} +The core mission of GeoUtils is to be **easy-of-use**, **robust**, **reproducible** and **fully open**. + +Additionally, GeoUtils aims to be **efficient**, **scalable** and **state-of-the-art**. +``` + +```{important} +:class: margin +GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for +**reproducibility**! +We are working on making features fully consistent for the first long-term release ``v0.1`` (likely sometime in 2023). +``` + +In details, those mean: + +- **Ease-of-use:** all basic operations or methods should only require a few lines of code to be performed; + +- **Robustness:** all DEM methods should be tested within our continuous integration test-suite, to enforce that they always perform as expected; + +- **Reproducibility:** all code should be version-controlled and release-based, to ensure consistency of dependent + packages and works; + +- **Open-source:** all code should be accessible and re-usable to anyone in the community, for transparency and open governance. + +```{note} +:class: margin +Additional mission points, in particular **scalability**, are partly developed but not a priority until our first long-term release ``v0.1`` is reached. +``` + +And, additionally: + +- **Efficiency**: all methods should be optimized at the lower-level, to function with the highest performance offered by Python packages; + +- **Scalability**: all methods should support both lazy processing and distributed parallelized processing, to work with high-resolution data on local machines as well as on HPCs; + +- **State-of-the-art**: all methods should be at the cutting edge of remote sensing science, to provide users with the most reliable and up-to-date tools. + + +# The people behind GeoUtils + +```{margin} +2More on our GlacioHack founder at [adehecq.github.io](https://adehecq.github.io/)! +``` + +GeoUtils was created during the [GlacioHack](https://github.com/GlacioHack) hackaton event, that was initiated by +Amaury Dehecq2 and took place online on November 8, 2020. + +```{margin} +3Check-out [glaciology.ch](https://glaciology.ch) on our founding group of VAW glaciology! +``` + +The initial core development of GeoUtils was performed by members of the Glaciology group of the Laboratory of Hydraulics, Hydrology and +Glaciology (VAW) at ETH Zürich3, with contributions by members of the University of Oslo, the University of Washington, and University +Grenobles Alpes. + +We are not software developers but geoscientists, and we try our best to offer tools that can be useful to a larger group, +documented, reliable and maintained. All development and maintenance is made on a voluntary basis and we welcome +any new contributors. See some information on how to contribute in the dedicated page of our +[GitHub repository](https://github.com/GlacioHack/geoutils/blob/main/CONTRIBUTING.md). diff --git a/doc/source/api.md b/doc/source/api.md new file mode 100644 index 000000000..1790a23b4 --- /dev/null +++ b/doc/source/api.md @@ -0,0 +1,64 @@ +(api)= + +# API Reference + +Full information about geoutils' functionality is provided on this page. + +```{eval-rst} +.. currentmodule:: geoutils +``` + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + :template: module.rst + :recursive: + + georaster + georaster.raster + geovector + projtools + satimg + geoviewer +``` + +% automodule geoutils +% +% .. contents:: Contents +% :local: +% +% Raster +% ------ +% .. autoclass:: Raster +% :show-inheritance: +% :special-members: __init__ +% :members: +% +% .. _sphx_glr_backref_geoutils.Raster: +% +% .. minigallery:: geoutils.Raster +% :add-heading: +% +% Vector +% ------ +% .. autoclass:: Vector +% :show-inheritance: +% :special-members: __init__ +% :members: +% +% .. _sphx_glr_backref_geoutils.Vector: +% +% .. minigallery:: geoutils.Vector +% :add-heading: +% +% SatelliteImage +% -------------- +% .. autoclass:: SatelliteImage +% :show-inheritance: +% :special-members: __init__ +% :members: +% +% .. _sphx_glr_backref_geoutils.SatelliteImage: +% +% .. minigallery:: geoutils.SatelliteImage +% :add-heading: diff --git a/doc/source/api.rst b/doc/source/api.rst deleted file mode 100644 index 230189832..000000000 --- a/doc/source/api.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _api: - -API Reference -============= - -Full information about geoutils' functionality is provided on this page. - -.. currentmodule:: geoutils - -.. autosummary:: - :toctree: gen_modules/ - :template: module.rst - :recursive: - - georaster - georaster.raster - geovector - projtools - satimg - geoviewer - -.. automodule geoutils - - .. contents:: Contents - :local: - - Raster - ------ - .. autoclass:: Raster - :show-inheritance: - :special-members: __init__ - :members: - - .. _sphx_glr_backref_geoutils.Raster: - - .. minigallery:: geoutils.Raster - :add-heading: - - Vector - ------ - .. autoclass:: Vector - :show-inheritance: - :special-members: __init__ - :members: - - .. _sphx_glr_backref_geoutils.Vector: - - .. minigallery:: geoutils.Vector - :add-heading: - - SatelliteImage - -------------- - .. autoclass:: SatelliteImage - :show-inheritance: - :special-members: __init__ - :members: - - .. _sphx_glr_backref_geoutils.SatelliteImage: - - .. minigallery:: geoutils.SatelliteImage - :add-heading: diff --git a/doc/source/conf.py b/doc/source/conf.py index db4f6047c..0874ead1c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -35,6 +35,7 @@ "sphinxcontrib.programoutput", "sphinx_gallery.gen_gallery", # Examples gallery "sphinx.ext.intersphinx", + "myst_parser", # Form of Markdown that works with sphinx, used a lot by the Sphinx Book Theme ] intersphinx_mapping = { @@ -87,7 +88,16 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme" +html_theme = "sphinx_book_theme" + +html_theme_options = { + "use_sidenotes": True, + "repository_url": "https://github.com/GlacioHack/xdem", + "use_repository_button": True, +} + +# html_logo = "path/to/myimage.png" +html_title = "GeoUtils" # Add any paths that contain custom static files (such as style sheets) here, diff --git a/doc/source/index.md b/doc/source/index.md new file mode 100644 index 000000000..283de6c33 --- /dev/null +++ b/doc/source/index.md @@ -0,0 +1,47 @@ +% GeoUtils documentation master file, created by +% sphinx-quickstart on Fri Nov 13 17:43:16 2020. +% You can adapt this file completely to your liking, but it should at least +% contain the root 'toctree' directive. + +# Welcome to GeoUtils' documentation! + +GeoUtils aims to make handling of georeferenced data, either in raster or vector format, easier and available to anyone. + +:::{important} +GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are +working on for reproducibility! +We are working on making features fully consistent for the first long-term release `v0.1` (likely sometime in 2023). +::: + +```{toctree} +:caption: Getting started +:maxdepth: 2 + +about_xdem +how_to_install +quick_start +``` + +```{literalinclude} code/index_example.py +``` + +```{eval-rst} +.. program-output:: $PYTHON code/index_example.py + :shell: +``` + +```{toctree} +:maxdepth: 1 + +raster-basics +satimg-basics +vector-basics +auto_examples/index.rst +api +``` + +# Indices and tables + +- {ref}`genindex` +- {ref}`modindex` +- {ref}`search` diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index 41f255c30..000000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. GeoUtils documentation master file, created by - sphinx-quickstart on Fri Nov 13 17:43:16 2020. - You can adapt this file completely to your liking, but it should at least - contain the root 'toctree' directive. - -Welcome to GeoUtils's documentation! -==================================== -GeoUtils aims to make handling of georeferenced data, either in raster or vector format, easier and available to anyone. - -Our functionalities are mostly based on `rasterio `_ and `GeoPandas `_, but here are some of the additional benefits provided by GeoUtils: - -* for raster objects, georeferences and data are stored into a single object of the class ``Raster``, making it easier to modify the data in-place: reprojection, cropping, additions/subtractions are all (or will be...) on-line operations! -* the interactions between raster and vectors, such as rasterizing, clipping or cropping are made easier thanks to the class ``Vector``. - - -.. literalinclude:: code/index_example.py - -.. program-output:: $PYTHON code/index_example.py - :shell: - -.. toctree:: - :maxdepth: 1 - - raster-basics - satimg-basics - vector-basics - auto_examples/index.rst - api - - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md new file mode 100644 index 000000000..bd37ed577 --- /dev/null +++ b/doc/source/quick_start.md @@ -0,0 +1,9 @@ +(quick-start)= + +# Quick start + +Our functionalities are mostly based on [rasterio](https://rasterio.readthedocs.io) and [GeoPandas](https://geopandas.org/), but here are some of the +additional benefits provided by GeoUtils: + +- for raster objects, georeferences and data are stored into a single object of the class `Raster`, making it easier to modify the data in-place: reprojection, cropping, additions/subtractions are all (or will be...) on-line operations! +- the interactions between raster and vectors, such as rasterizing, clipping or cropping are made easier thanks to the class `Vector`. diff --git a/doc/source/raster-basics.md b/doc/source/raster-basics.md new file mode 100644 index 000000000..0af7d1066 --- /dev/null +++ b/doc/source/raster-basics.md @@ -0,0 +1,128 @@ +(raster-basics)= + +# Raster basics + +## Opening a raster file + +```{literalinclude} code/raster-basics_open_file.py +:lines: 2-8 +``` + +## Basic information about a Raster + +To print information directly to your console: + +```python +print(image) +``` + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/raster-basics_open_file.py').read()); print(image)" + :shell: +``` + +If you'd like to retrieve a string of information about the raster to be saved +to a variable, output to a text file etc: + +```{literalinclude} code/raster-basics_open_file.py +:lines: 10 +``` + +With added stats: + +```{literalinclude} code/raster-basics_open_file.py +:lines: 12 +``` + +Then to write a file: + +```{literalinclude} code/raster-basics_open_file.py +:lines: 14-15 +``` + +Or just print nicely to console: + +```python +print(information) +``` + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/raster-basics_open_file.py').read()); print(information)" + :shell: +``` + +## Resampling a Raster to fit another + +Comparing multiple rasters can often be a burden if multiple coordinate systems, bounding boxes, and resolutions are involved. +The {class}`geoutils.Raster` class simplifies this using two methods: `Raster.crop()` and `Raster.reproject()`. + +### Cropping a Raster + +{func}`geoutils.Raster.crop` + +If a large raster should be cropped to a smaller extent without changing the uncropped data, this is possible through the crop function. + +```{literalinclude} code/raster-basics_cropping_and_reprojecting.py +:lines: 4-6 +``` + +```python +print(large_image.shape) + +print(smaller_image.shape) +``` + +prints: + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/raster-basics_cropping_and_reprojecting.py').read());print(large_image_orig.shape);print(smaller_image.shape)" + :shell: +``` + +If we want to crop the larger image to fit the smaller one: + +```{literalinclude} code/raster-basics_cropping_and_reprojecting.py +:lines: 9 +``` + +Now, they have the same shape, and can be compared directly: + +```python +print(large_image.shape) + +print(smaller_image.shape) +``` + +prints: + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/raster-basics_cropping_and_reprojecting.py').read());print(large_image.shape);print(smaller_image.shape)" + :shell: +``` + +### Reprojecting a Raster + +{func}`geoutils.Raster.reproject` + +For rasters with different coordinate systems, resolutions or grids, reprojection is needed to fit one raster to another. +`Raster.reproject()` is apt for these use-cases: + +```{literalinclude} code/raster-basics_cropping_and_reprojecting.py +:lines: 11 +``` + +This call will crop, project and resample the `larger_raster` to fit the `smaller_raster` exactly. +By default, `Raster.resample()` uses nearest neighbour resampling, which is good for fast reprojections, but may induce unintended artefacts when precision is important. +It is therefore recommended to choose the method that fits the purpose best, using the `resampling=` keyword argument: + +1. `resampling="nearest"`: Default. Performant but is not good for changes in resolution and grid. +2. `resampling="bilinear"`: Good when changes in resolution and grid are involved. +3. `resampling="cubic_spline"`: Often considered the best approach. Not as performant as simpler methods. + +All valid resampling methods can be seen in the [Rasterio documentation](https://rasterio.readthedocs.io/en/latest/api/rasterio.enums.html#rasterio.enums.Resampling). + +```{eval-rst} +.. minigallery:: geoutils.Raster + :add-heading: + :heading-level: - +``` diff --git a/doc/source/raster-basics.rst b/doc/source/raster-basics.rst deleted file mode 100644 index 46fa3a552..000000000 --- a/doc/source/raster-basics.rst +++ /dev/null @@ -1,119 +0,0 @@ -.. _raster-basics: - -Raster basics -============= - - -Opening a raster file ---------------------- - -.. literalinclude:: code/raster-basics_open_file.py - :lines: 2-8 - -Basic information about a Raster --------------------------------- - -To print information directly to your console: - -.. code:: python - - print(image) - -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_open_file.py').read()); print(image)" - :shell: - -If you'd like to retrieve a string of information about the raster to be saved -to a variable, output to a text file etc: - - -.. literalinclude:: code/raster-basics_open_file.py - :lines: 10 - -With added stats: - -.. literalinclude:: code/raster-basics_open_file.py - :lines: 12 - -Then to write a file: - -.. literalinclude:: code/raster-basics_open_file.py - :lines: 14-15 - -Or just print nicely to console: - -.. code:: python - - print(information) - -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_open_file.py').read()); print(information)" - :shell: - -Resampling a Raster to fit another ----------------------------------- - -Comparing multiple rasters can often be a burden if multiple coordinate systems, bounding boxes, and resolutions are involved. -The :class:`geoutils.Raster` class simplifies this using two methods: ``Raster.crop()`` and ``Raster.reproject()``. - -Cropping a Raster -***************** -:func:`geoutils.Raster.crop` - -If a large raster should be cropped to a smaller extent without changing the uncropped data, this is possible through the crop function. - -.. literalinclude:: code/raster-basics_cropping_and_reprojecting.py - :lines: 4-6 - -.. code:: python - - print(large_image.shape) - - print(smaller_image.shape) - -prints: - -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_cropping_and_reprojecting.py').read());print(large_image_orig.shape);print(smaller_image.shape)" - :shell: - -If we want to crop the larger image to fit the smaller one: - -.. literalinclude:: code/raster-basics_cropping_and_reprojecting.py - :lines: 9 - -Now, they have the same shape, and can be compared directly: - -.. code:: python - - print(large_image.shape) - - print(smaller_image.shape) - -prints: - -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_cropping_and_reprojecting.py').read());print(large_image.shape);print(smaller_image.shape)" - :shell: - -Reprojecting a Raster -********************* -:func:`geoutils.Raster.reproject` - -For rasters with different coordinate systems, resolutions or grids, reprojection is needed to fit one raster to another. -``Raster.reproject()`` is apt for these use-cases: - -.. literalinclude:: code/raster-basics_cropping_and_reprojecting.py - :lines: 11 - -This call will crop, project and resample the ``larger_raster`` to fit the ``smaller_raster`` exactly. -By default, ``Raster.resample()`` uses nearest neighbour resampling, which is good for fast reprojections, but may induce unintended artefacts when precision is important. -It is therefore recommended to choose the method that fits the purpose best, using the ``resampling=`` keyword argument: - -1) ``resampling="nearest"``: Default. Performant but is not good for changes in resolution and grid. - -2) ``resampling="bilinear"``: Good when changes in resolution and grid are involved. - -3) ``resampling="cubic_spline"``: Often considered the best approach. Not as performant as simpler methods. - -All valid resampling methods can be seen in the `Rasterio documentation `_. - -.. minigallery:: geoutils.Raster - :add-heading: - :heading-level: - diff --git a/doc/source/satimg-basics.rst b/doc/source/satimg-basics.md similarity index 67% rename from doc/source/satimg-basics.rst rename to doc/source/satimg-basics.md index 9fa086654..b02a95213 100644 --- a/doc/source/satimg-basics.rst +++ b/doc/source/satimg-basics.md @@ -1,30 +1,31 @@ -.. _satimg-basics: +(satimg-basics)= -SatImg basics -============= +# SatImg basics -Opening a raster file through SatImg ------------------------------------- +## Opening a raster file through SatImg Example with a Landsat image: -.. literalinclude:: code/satimg-basics_open_file.py - :lines: 2- +```{literalinclude} code/satimg-basics_open_file.py +:lines: 2- +``` - -What the SatImg class does for you ----------------------------------- +## What the SatImg class does for you When reading your file, SatImg will try to load metadata information from the filename. For the above filename, this will be printed in the console: +```{eval-rst} .. program-output:: $PYTHON -c "exec(open('code/satimg-basics_open_file.py').read())" :shell: +``` Currently supporting the nomenclatures used for: Landsat, Sentinel-2, ArcticDEM, REMA, ASTER L1A, ASTER GDEM, NASADEM, TanDEM-X, SRTM and SPOT-5 More to come... +```{eval-rst} .. minigallery:: geoutils.SatelliteImage :add-heading: :heading-level: - +``` diff --git a/doc/source/vector-basics.md b/doc/source/vector-basics.md new file mode 100644 index 000000000..b73d25b8a --- /dev/null +++ b/doc/source/vector-basics.md @@ -0,0 +1,49 @@ +(vector-basics)= + +# Vector basics + +The Vector class builds upon the great functionalities of [GeoPandas](https://geopandas.org/), with the aim to bridge the gap between vector and raster files. +It uses `geopandas.GeoDataFrame` as a base driver, accessible through `Vector.ds`. + +## Opening a Vector file + +```{literalinclude} code/vector-basics_open_file.py +:lines: 2-6 +``` + +Printing the Vector shows the underlying `GeoDataFrame` and some extra statistics: + +```python +print(outlines) +``` + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/vector-basics_open_file.py').read()); print(outlines)" + :shell: + +``` + +Masks can easily be generated for use with Rasters: + +```{literalinclude} code/vector-basics_open_file.py +:lines: 8-13 +``` + +We can prove that glaciers are bright (who could have known!?) by masking the values outside and inside of the glaciers: + +```python +print(f"Inside: {image.data[mask].mean():.1f}, outside: {image.data[~mask].mean():.1f}") +``` + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/vector-basics_open_file.py').read()); print(f'Inside: {image.data[mask].mean():.1f}, outside: {image.data[~mask].mean():.1f}')" + :shell: +``` + +TODO: Add rasterize text. + +```{eval-rst} +.. minigallery:: geoutils.Raster + :add-heading: + :heading-level: - +``` diff --git a/doc/source/vector-basics.rst b/doc/source/vector-basics.rst deleted file mode 100644 index 13fb95c4e..000000000 --- a/doc/source/vector-basics.rst +++ /dev/null @@ -1,45 +0,0 @@ -.. _vector-basics: - -Vector basics -============= - -The Vector class builds upon the great functionalities of `GeoPandas `_, with the aim to bridge the gap between vector and raster files. -It uses ``geopandas.GeoDataFrame`` as a base driver, accessible through ``Vector.ds``. - - -Opening a Vector file ---------------------- - -.. literalinclude:: code/vector-basics_open_file.py - :lines: 2-6 - - -Printing the Vector shows the underlying ``GeoDataFrame`` and some extra statistics: - -.. code:: python - - print(outlines) - -.. program-output:: $PYTHON -c "exec(open('code/vector-basics_open_file.py').read()); print(outlines)" - :shell: - - -Masks can easily be generated for use with Rasters: - -.. literalinclude:: code/vector-basics_open_file.py - :lines: 8-13 - -We can prove that glaciers are bright (who could have known!?) by masking the values outside and inside of the glaciers: - -.. code:: python - - print(f"Inside: {image.data[mask].mean():.1f}, outside: {image.data[~mask].mean():.1f}") - -.. program-output:: $PYTHON -c "exec(open('code/vector-basics_open_file.py').read()); print(f'Inside: {image.data[mask].mean():.1f}, outside: {image.data[~mask].mean():.1f}')" - :shell: - -TODO: Add rasterize text. - -.. minigallery:: geoutils.Raster - :add-heading: - :heading-level: - From f01304dba9ee8a427b27ba27597830fcdecc8c7f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 10 Feb 2023 17:21:35 -0800 Subject: [PATCH 009/184] Restructure --- doc/source/about_geoutils.md | 9 ++-- doc/source/how_to_install.md | 43 ++++++++++++++++ doc/source/index.md | 50 +++++++++++++------ doc/source/quick_start.md | 10 ++++ .../{raster-basics.md => raster_class.md} | 44 +++++++++++++++- doc/source/satimg-basics.md | 31 ------------ .../{vector-basics.md => vector_class.md} | 4 +- 7 files changed, 135 insertions(+), 56 deletions(-) create mode 100644 doc/source/how_to_install.md rename doc/source/{raster-basics.md => raster_class.md} (76%) delete mode 100644 doc/source/satimg-basics.md rename doc/source/{vector-basics.md => vector_class.md} (97%) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index ddd18ecaa..e96e96fef 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -22,14 +22,13 @@ We are working on making features fully consistent for the first long-term relea In details, those mean: -- **Ease-of-use:** all basic operations or methods should only require a few lines of code to be performed; +- **Ease-of-use:** all basic operations or methods only require a few lines of code to be performed; -- **Robustness:** all DEM methods should be tested within our continuous integration test-suite, to enforce that they always perform as expected; +- **Robustness:** all methods are tested within our continuous integration test-suite, to enforce that they always perform as expected; -- **Reproducibility:** all code should be version-controlled and release-based, to ensure consistency of dependent - packages and works; +- **Reproducibility:** all code is version-controlled and release-based, to ensure consistency of dependent packages and works; -- **Open-source:** all code should be accessible and re-usable to anyone in the community, for transparency and open governance. +- **Open-source:** all code is accessible and re-usable to anyone in the community, for transparency and open governance. ```{note} :class: margin diff --git a/doc/source/how_to_install.md b/doc/source/how_to_install.md new file mode 100644 index 000000000..4feb28aab --- /dev/null +++ b/doc/source/how_to_install.md @@ -0,0 +1,43 @@ +(how-to-install)= + +# How to install + +## Installing with conda (recommended) + +```bash +conda install -c conda-forge --strict-channel-priority geoutils +``` + +**Notes** + +- The `--strict-channel-priority` flag seems essential for Windows installs to function correctly, and is recommended for UNIX-based systems as well. + +- Solving dependencies can take a long time with `conda`. To speed up this, consider installing `mamba`: + + ```bash + conda install mamba -n base -c conda-forge + ``` + + Once installed, the same commands can be run by simply replacing `conda` by `mamba`. More details available through the [mamba project](https://github.com/mamba-org/mamba). + +## Installing with pip + +```bash +pip install geoutils +``` + +**NOTE**: Setting up GDAL and PROJ may need some extra steps, depending on your operating system and configuration. + +## Installing for contributors + +```shell +git clone https://github.com/GlacioHack/xdem.git +cd ./xdem +mamba env create -f dev-environment.yml +mamba activate xdem +pip install -e . +``` + +After installing, we recommend to check that everything is working by running the tests: + +`pytest -rA` diff --git a/doc/source/index.md b/doc/source/index.md index 283de6c33..3da0b18b2 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -5,38 +5,56 @@ # Welcome to GeoUtils' documentation! -GeoUtils aims to make handling of georeferenced data, either in raster or vector format, easier and available to anyone. +GeoUtils aims to make the handling and analysis of georeferenced raster and vector data intuitive, robust, and easy-of-use. GeoUtils is built upon `rasterio`, +`geopandas` and `pyproj`, and facilitates geospatial operations between rasters and vectors, and the analysis of rasters by interfacing with `numpy` arrays. + + +```{important} +:class: margin +GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for +**reproducibility**! +We are working on making features fully consistent for the first long-term release ``v0.1`` (likely sometime in 2023). +``` -:::{important} -GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are -working on for reproducibility! -We are working on making features fully consistent for the first long-term release `v0.1` (likely sometime in 2023). -::: ```{toctree} :caption: Getting started :maxdepth: 2 -about_xdem +about_geoutils how_to_install quick_start ``` -```{literalinclude} code/index_example.py +```{toctree} +:caption: Background +:maxdepth: 2 + +motivation +rasterio_geopandas ``` -```{eval-rst} -.. program-output:: $PYTHON code/index_example.py - :shell: + +```{toctree} +:caption: Features +:maxdepth: 2 + +raster_class +vector_class +proj_tools ``` ```{toctree} -:maxdepth: 1 +:caption: Gallery of examples +:maxdepth: 2 + +auto_examples/index.rst +``` -raster-basics -satimg-basics -vector-basics -auto_examples/index.rst +```{toctree} +:caption: API reference +:maxdepth: 2 + api ``` diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index bd37ed577..0e2cd8cb7 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -7,3 +7,13 @@ additional benefits provided by GeoUtils: - for raster objects, georeferences and data are stored into a single object of the class `Raster`, making it easier to modify the data in-place: reprojection, cropping, additions/subtractions are all (or will be...) on-line operations! - the interactions between raster and vectors, such as rasterizing, clipping or cropping are made easier thanks to the class `Vector`. + +# Code example + +```{literalinclude} code/index_example.py +``` + +```{eval-rst} +.. program-output:: $PYTHON code/index_example.py + :shell: +``` \ No newline at end of file diff --git a/doc/source/raster-basics.md b/doc/source/raster_class.md similarity index 76% rename from doc/source/raster-basics.md rename to doc/source/raster_class.md index 0af7d1066..d32e21594 100644 --- a/doc/source/raster-basics.md +++ b/doc/source/raster_class.md @@ -1,6 +1,15 @@ -(raster-basics)= +(raster-class)= + +# Raster object + +A {class}`geoutils.Raster` is a georeferenced raster object read, written or reprojected by `rasterio`. + +It contains: +- an array `.data` as a `np.ma.MaskedArray`, +- a geotransform `.transform` as a `affine.Affine`, +- a coordinate reference system(CRS) `.crs` as a `pyproj.CRS`, +- a nodata value `nodata` as `float` or `int`. -# Raster basics ## Opening a raster file @@ -126,3 +135,34 @@ All valid resampling methods can be seen in the [Rasterio documentation](https:/ :add-heading: :heading-level: - ``` + + +# SatImg basics + +## Opening a raster file through SatImg + +Example with a Landsat image: + +```{literalinclude} code/satimg-basics_open_file.py +:lines: 2- +``` + +## What the SatImg class does for you + +When reading your file, SatImg will try to load metadata information from the filename. +For the above filename, this will be printed in the console: + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/satimg-basics_open_file.py').read())" + :shell: +``` + +Currently supporting the nomenclatures used for: Landsat, Sentinel-2, ArcticDEM, REMA, ASTER L1A, ASTER GDEM, NASADEM, TanDEM-X, SRTM and SPOT-5 + +More to come... + +```{eval-rst} +.. minigallery:: geoutils.SatelliteImage + :add-heading: + :heading-level: - +``` \ No newline at end of file diff --git a/doc/source/satimg-basics.md b/doc/source/satimg-basics.md deleted file mode 100644 index b02a95213..000000000 --- a/doc/source/satimg-basics.md +++ /dev/null @@ -1,31 +0,0 @@ -(satimg-basics)= - -# SatImg basics - -## Opening a raster file through SatImg - -Example with a Landsat image: - -```{literalinclude} code/satimg-basics_open_file.py -:lines: 2- -``` - -## What the SatImg class does for you - -When reading your file, SatImg will try to load metadata information from the filename. -For the above filename, this will be printed in the console: - -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/satimg-basics_open_file.py').read())" - :shell: -``` - -Currently supporting the nomenclatures used for: Landsat, Sentinel-2, ArcticDEM, REMA, ASTER L1A, ASTER GDEM, NASADEM, TanDEM-X, SRTM and SPOT-5 - -More to come... - -```{eval-rst} -.. minigallery:: geoutils.SatelliteImage - :add-heading: - :heading-level: - -``` diff --git a/doc/source/vector-basics.md b/doc/source/vector_class.md similarity index 97% rename from doc/source/vector-basics.md rename to doc/source/vector_class.md index b73d25b8a..ee815632b 100644 --- a/doc/source/vector-basics.md +++ b/doc/source/vector_class.md @@ -1,6 +1,6 @@ -(vector-basics)= +(vector-class)= -# Vector basics +# Vector object The Vector class builds upon the great functionalities of [GeoPandas](https://geopandas.org/), with the aim to bridge the gap between vector and raster files. It uses `geopandas.GeoDataFrame` as a base driver, accessible through `Vector.ds`. From 5aad281670f974de07533138a7bb0f6f287af8b0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sat, 11 Feb 2023 19:49:20 -0800 Subject: [PATCH 010/184] Skeleton --- doc/source/core_concepts.md | 19 +++++++++++++ doc/source/from_gdal.md | 3 ++ doc/source/index.md | 14 +++------ doc/source/mask_class.md | 14 +++++++++ doc/source/motivation_package.md | 3 ++ doc/source/proj_tools.md | 13 +++++++++ doc/source/raster_class.md | 49 +++++++++----------------------- doc/source/satimg_class.md | 35 +++++++++++++++++++++++ doc/source/vector_class.md | 29 +++++++++++++++++-- 9 files changed, 132 insertions(+), 47 deletions(-) create mode 100644 doc/source/core_concepts.md create mode 100644 doc/source/from_gdal.md create mode 100644 doc/source/mask_class.md create mode 100644 doc/source/motivation_package.md create mode 100644 doc/source/proj_tools.md create mode 100644 doc/source/satimg_class.md diff --git a/doc/source/core_concepts.md b/doc/source/core_concepts.md new file mode 100644 index 000000000..7d7c2c5e3 --- /dev/null +++ b/doc/source/core_concepts.md @@ -0,0 +1,19 @@ +(core-concepts)= +# Core concepts + +## Composition from `rasterio` and `geopandas` + + +## Intuitive match-reference operations + + +## Support of pythonic operators + + +## Masked-array `numpy` interface + + +## Implicit lazy loading + + +## Inheritance to `SatelliteImage`, `DEM` and others diff --git a/doc/source/from_gdal.md b/doc/source/from_gdal.md new file mode 100644 index 000000000..dfd007b88 --- /dev/null +++ b/doc/source/from_gdal.md @@ -0,0 +1,3 @@ +(from-gdal)= + +# Moving from GDAL or other Python packages \ No newline at end of file diff --git a/doc/source/index.md b/doc/source/index.md index 3da0b18b2..529728c2b 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -26,26 +26,20 @@ how_to_install quick_start ``` -```{toctree} -:caption: Background -:maxdepth: 2 - -motivation -rasterio_geopandas -``` - - ```{toctree} :caption: Features :maxdepth: 2 +core_concepts raster_class +mask_class +satimg_class vector_class proj_tools ``` ```{toctree} -:caption: Gallery of examples +:caption: Examples :maxdepth: 2 auto_examples/index.rst diff --git a/doc/source/mask_class.md b/doc/source/mask_class.md new file mode 100644 index 000000000..51bf82251 --- /dev/null +++ b/doc/source/mask_class.md @@ -0,0 +1,14 @@ +(mask-class)= + +# The `Mask` object + + +## Object definition + +## Arithmetic + +## Cast from `Raster` + +## Create from `Vector` + +## Indexing \ No newline at end of file diff --git a/doc/source/motivation_package.md b/doc/source/motivation_package.md new file mode 100644 index 000000000..789383847 --- /dev/null +++ b/doc/source/motivation_package.md @@ -0,0 +1,3 @@ +(motivation-package)= + +# Motivation for a geospatial analysis package diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md new file mode 100644 index 000000000..f8e0a5be7 --- /dev/null +++ b/doc/source/proj_tools.md @@ -0,0 +1,13 @@ +(proj-tools)= + +# Projection tools + +Convenient `pyproj` wrappers. + +## Find a local metric projection + +## Merge or align multiple bounds + +## Create a tiling + +## Shape-preserving vector reprojection \ No newline at end of file diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index d32e21594..28e0d73e1 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -1,6 +1,8 @@ (raster-class)= -# Raster object +# The `Raster` object + +## Object definition A {class}`geoutils.Raster` is a georeferenced raster object read, written or reprojected by `rasterio`. @@ -11,14 +13,12 @@ It contains: - a nodata value `nodata` as `float` or `int`. -## Opening a raster file +## Open and save ```{literalinclude} code/raster-basics_open_file.py :lines: 2-8 ``` -## Basic information about a Raster - To print information directly to your console: ```python @@ -60,13 +60,15 @@ print(information) :shell: ``` -## Resampling a Raster to fit another +## Arithmetic + +## Array interface + +## Crop Comparing multiple rasters can often be a burden if multiple coordinate systems, bounding boxes, and resolutions are involved. The {class}`geoutils.Raster` class simplifies this using two methods: `Raster.crop()` and `Raster.reproject()`. -### Cropping a Raster - {func}`geoutils.Raster.crop` If a large raster should be cropped to a smaller extent without changing the uncropped data, this is possible through the crop function. @@ -109,7 +111,7 @@ prints: :shell: ``` -### Reprojecting a Raster +## Reproject {func}`geoutils.Raster.reproject` @@ -136,33 +138,10 @@ All valid resampling methods can be seen in the [Rasterio documentation](https:/ :heading-level: - ``` +## Polygonize -# SatImg basics - -## Opening a raster file through SatImg - -Example with a Landsat image: - -```{literalinclude} code/satimg-basics_open_file.py -:lines: 2- -``` - -## What the SatImg class does for you +## Proximity -When reading your file, SatImg will try to load metadata information from the filename. -For the above filename, this will be printed in the console: +## Interpolate or extract to point -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/satimg-basics_open_file.py').read())" - :shell: -``` - -Currently supporting the nomenclatures used for: Landsat, Sentinel-2, ArcticDEM, REMA, ASTER L1A, ASTER GDEM, NASADEM, TanDEM-X, SRTM and SPOT-5 - -More to come... - -```{eval-rst} -.. minigallery:: geoutils.SatelliteImage - :add-heading: - :heading-level: - -``` \ No newline at end of file +## Export \ No newline at end of file diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md new file mode 100644 index 000000000..100858076 --- /dev/null +++ b/doc/source/satimg_class.md @@ -0,0 +1,35 @@ +(satimg-class)= + +# The `SatelliteImage` object + +## Object definition + +## Datetime parsing + +Example with a Landsat image: + +```{literalinclude} code/satimg-basics_open_file.py +:lines: 2- +``` + +When reading your file, SatImg will try to load metadata information from the filename. +For the above filename, this will be printed in the console: + +```{eval-rst} +.. program-output:: $PYTHON -c "exec(open('code/satimg-basics_open_file.py').read())" + :shell: +``` + +Currently supporting the nomenclatures used for: Landsat, Sentinel-2, ArcticDEM, REMA, ASTER L1A, ASTER GDEM, NASADEM, TanDEM-X, SRTM and SPOT-5 + +More to come... + +```{eval-rst} +.. minigallery:: geoutils.SatelliteImage + :add-heading: + :heading-level: - +``` + +## Tile parsing + +## Instrument metadata parsing diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index ee815632b..ba1938552 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -1,11 +1,13 @@ (vector-class)= -# Vector object +# The `Vector` object + +## Object definition The Vector class builds upon the great functionalities of [GeoPandas](https://geopandas.org/), with the aim to bridge the gap between vector and raster files. It uses `geopandas.GeoDataFrame` as a base driver, accessible through `Vector.ds`. -## Opening a Vector file +## Open and save ```{literalinclude} code/vector-basics_open_file.py :lines: 2-6 @@ -47,3 +49,26 @@ TODO: Add rasterize text. :add-heading: :heading-level: - ``` + + +## Arithmetic + + +## Reproject + + +## Crop + + +## Rasterize + + +## Proximity + + +## Create a `Mask` + + +## Buffering + + From fed7ff2898de5ac5cf16684e140d11869473971b Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 12:31:28 -0800 Subject: [PATCH 011/184] Incremental commit on doc --- doc/source/_static/css/custom.css | 0 doc/source/about_geoutils.md | 39 +++++++++--------------- doc/source/api.md | 49 +++++++++++++++++-------------- doc/source/background.md | 25 ++++++++++++++++ doc/source/conf.py | 6 ++++ doc/source/core_array_funcs.md | 3 ++ doc/source/core_composition.md | 2 ++ doc/source/core_concepts.md | 19 ------------ doc/source/core_index.md | 13 ++++++++ doc/source/core_inheritance.md | 2 ++ doc/source/core_lazy_load.md | 3 ++ doc/source/core_match_ref.md | 2 ++ doc/source/core_py_ops.md | 3 ++ doc/source/index.md | 11 ++++--- doc/source/mask_class.md | 2 +- doc/source/motivation_package.md | 3 -- doc/source/raster_class.md | 12 ++++---- doc/source/rasters_index.md | 11 +++++++ doc/source/satimg_class.md | 2 +- doc/source/vector_class.md | 2 +- doc/source/vectors_index.md | 9 ++++++ 21 files changed, 134 insertions(+), 84 deletions(-) create mode 100644 doc/source/_static/css/custom.css create mode 100644 doc/source/background.md create mode 100644 doc/source/core_array_funcs.md create mode 100644 doc/source/core_composition.md delete mode 100644 doc/source/core_concepts.md create mode 100644 doc/source/core_index.md create mode 100644 doc/source/core_inheritance.md create mode 100644 doc/source/core_lazy_load.md create mode 100644 doc/source/core_match_ref.md create mode 100644 doc/source/core_py_ops.md delete mode 100644 doc/source/motivation_package.md create mode 100644 doc/source/rasters_index.md create mode 100644 doc/source/vectors_index.md diff --git a/doc/source/_static/css/custom.css b/doc/source/_static/css/custom.css new file mode 100644 index 000000000..e69de29bb diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index e96e96fef..505f7b38d 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -2,10 +2,22 @@ # About GeoUtils +## What is GeoUtils? -GeoUtils is a [Python](https://www.python.org/) package for the analysis of georeferenced raster and vectors, with name standing for _geospatial utilities_. -It is designed for all Earth and planetary observation science. +GeoUtils is a [Python](https://www.python.org/) package for the manipulation and analysis of georeferenced data, developed with the objective of +making geospatial analysis intuitive, accessible and robust. It is designed for all Earth and planetary observation science. +GeoUtils is built on top of the geospatial packages [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs. +html) and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) to provide: +- A **common consistent interface** between georeferenced rasters and vectors for robustness of geospatial handling, +- A geospatial framework following the **principal of least knowledge** for ease-of-use and accessibility, + +Additionally + + + + +## Mission ```{epigraph} The core mission of GeoUtils is to be **easy-of-use**, **robust**, **reproducible** and **fully open**. @@ -42,26 +54,3 @@ And, additionally: - **Scalability**: all methods should support both lazy processing and distributed parallelized processing, to work with high-resolution data on local machines as well as on HPCs; - **State-of-the-art**: all methods should be at the cutting edge of remote sensing science, to provide users with the most reliable and up-to-date tools. - - -# The people behind GeoUtils - -```{margin} -2More on our GlacioHack founder at [adehecq.github.io](https://adehecq.github.io/)! -``` - -GeoUtils was created during the [GlacioHack](https://github.com/GlacioHack) hackaton event, that was initiated by -Amaury Dehecq2 and took place online on November 8, 2020. - -```{margin} -3Check-out [glaciology.ch](https://glaciology.ch) on our founding group of VAW glaciology! -``` - -The initial core development of GeoUtils was performed by members of the Glaciology group of the Laboratory of Hydraulics, Hydrology and -Glaciology (VAW) at ETH Zürich3, with contributions by members of the University of Oslo, the University of Washington, and University -Grenobles Alpes. - -We are not software developers but geoscientists, and we try our best to offer tools that can be useful to a larger group, -documented, reliable and maintained. All development and maintenance is made on a voluntary basis and we welcome -any new contributors. See some information on how to contribute in the dedicated page of our -[GitHub repository](https://github.com/GlacioHack/geoutils/blob/main/CONTRIBUTING.md). diff --git a/doc/source/api.md b/doc/source/api.md index 1790a23b4..b5a796592 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -2,42 +2,32 @@ # API Reference -Full information about geoutils' functionality is provided on this page. +Full information about the functionalities of GeoUtils are provided on this page. ```{eval-rst} .. currentmodule:: geoutils ``` +## Classes + ```{eval-rst} -.. autosummary:: - :toctree: gen_modules/ - :template: module.rst - :recursive: +.. autoclass:: Raster + :show-inheritance: + :special-members: __init__ + :members: +.. _sphx_glr_backref_geoutils.Raster: - georaster - georaster.raster - geovector - projtools - satimg - geoviewer +.. minigallery:: geoutils.Raster + :add-heading: ``` -% automodule geoutils -% +automodule geoutils % .. contents:: Contents % :local: % % Raster % ------ -% .. autoclass:: Raster -% :show-inheritance: -% :special-members: __init__ -% :members: -% -% .. _sphx_glr_backref_geoutils.Raster: -% -% .. minigallery:: geoutils.Raster -% :add-heading: + % % Vector % ------ @@ -62,3 +52,18 @@ Full information about geoutils' functionality is provided on this page. % % .. minigallery:: geoutils.SatelliteImage % :add-heading: + + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + :template: module.rst + :recursive: + + georaster + georaster.raster + geovector + projtools + satimg + geoviewer +``` \ No newline at end of file diff --git a/doc/source/background.md b/doc/source/background.md new file mode 100644 index 000000000..7389d7be9 --- /dev/null +++ b/doc/source/background.md @@ -0,0 +1,25 @@ +(background)= + +# Background + +## The people behind GeoUtils + +```{margin} +2More on our GlacioHack founder at [adehecq.github.io](https://adehecq.github.io/)! +``` + +GeoUtils was created during the [GlacioHack](https://github.com/GlacioHack) hackaton event, that was initiated by +Amaury Dehecq2 and took place online on November 8, 2020. + +```{margin} +3Check-out [glaciology.ch](https://glaciology.ch) on our founding group of VAW glaciology! +``` + +The initial core development of GeoUtils was performed by members of the Glaciology group of the Laboratory of Hydraulics, Hydrology and +Glaciology (VAW) at ETH Zürich3, with contributions by members of the University of Oslo, the University of Washington, and University +Grenobles Alpes. + +We are not software developers but geoscientists, and we try our best to offer tools that can be useful to a larger group, +documented, reliable and maintained. All development and maintenance is made on a voluntary basis and we welcome +any new contributors. See some information on how to contribute in the dedicated page of our +[GitHub repository](https://github.com/GlacioHack/geoutils/blob/main/CONTRIBUTING.md). diff --git a/doc/source/conf.py b/doc/source/conf.py index 0874ead1c..545347713 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -42,6 +42,7 @@ "rasterio": ("https://rasterio.readthedocs.io/en/latest", None), "numpy": ("https://numpy.org/doc/stable", None), "matplotlib": ("https://matplotlib.org/stable", None), + "pyproj": ("https://pyproj4.github.io/pyproj/stable", None), } sphinx_gallery_conf = { @@ -99,6 +100,11 @@ # html_logo = "path/to/myimage.png" html_title = "GeoUtils" +html_static_path = ['_static'] + +html_css_files = [ + 'css/custom.css', +] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md new file mode 100644 index 000000000..c89bd9a9c --- /dev/null +++ b/doc/source/core_array_funcs.md @@ -0,0 +1,3 @@ +(core-array-funcs)= + +# Masked-array NumPy interface diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md new file mode 100644 index 000000000..7f1a6f47a --- /dev/null +++ b/doc/source/core_composition.md @@ -0,0 +1,2 @@ +(core-composition)= +# Composition from Rasterio and GeoPandas diff --git a/doc/source/core_concepts.md b/doc/source/core_concepts.md deleted file mode 100644 index 7d7c2c5e3..000000000 --- a/doc/source/core_concepts.md +++ /dev/null @@ -1,19 +0,0 @@ -(core-concepts)= -# Core concepts - -## Composition from `rasterio` and `geopandas` - - -## Intuitive match-reference operations - - -## Support of pythonic operators - - -## Masked-array `numpy` interface - - -## Implicit lazy loading - - -## Inheritance to `SatelliteImage`, `DEM` and others diff --git a/doc/source/core_index.md b/doc/source/core_index.md new file mode 100644 index 000000000..7671cf598 --- /dev/null +++ b/doc/source/core_index.md @@ -0,0 +1,13 @@ +(core-concepts)= +# Fundamentals + +```{toctree} +:maxdepth: 2 + +core_composition +core_match_ref +core_py_ops +core_array_funcs +core_lazy_load +core_inheritance +``` \ No newline at end of file diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md new file mode 100644 index 000000000..73e96d255 --- /dev/null +++ b/doc/source/core_inheritance.md @@ -0,0 +1,2 @@ +(core-inheritance)= +# Inheritance for satellite images, DEMs diff --git a/doc/source/core_lazy_load.md b/doc/source/core_lazy_load.md new file mode 100644 index 000000000..e9e15c4d9 --- /dev/null +++ b/doc/source/core_lazy_load.md @@ -0,0 +1,3 @@ +(core-lazy-load)= + +# Implicit lazy loading diff --git a/doc/source/core_match_ref.md b/doc/source/core_match_ref.md new file mode 100644 index 000000000..fb2504839 --- /dev/null +++ b/doc/source/core_match_ref.md @@ -0,0 +1,2 @@ +(core-match-ref)= +# Intuitive match-reference operations diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md new file mode 100644 index 000000000..a25bb9f47 --- /dev/null +++ b/doc/source/core_py_ops.md @@ -0,0 +1,3 @@ +(core-py-ops)= + +# Support of pythonic operators diff --git a/doc/source/index.md b/doc/source/index.md index 529728c2b..69a9fd927 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -30,11 +30,9 @@ quick_start :caption: Features :maxdepth: 2 -core_concepts -raster_class -mask_class -satimg_class -vector_class +core_index +rasters_index +vectors_index proj_tools ``` @@ -46,10 +44,11 @@ auto_examples/index.rst ``` ```{toctree} -:caption: API reference +:caption: Reference :maxdepth: 2 api +background ``` # Indices and tables diff --git a/doc/source/mask_class.md b/doc/source/mask_class.md index 51bf82251..f25287113 100644 --- a/doc/source/mask_class.md +++ b/doc/source/mask_class.md @@ -1,6 +1,6 @@ (mask-class)= -# The `Mask` object +# The georeferenced mask (`Mask`) ## Object definition diff --git a/doc/source/motivation_package.md b/doc/source/motivation_package.md deleted file mode 100644 index 789383847..000000000 --- a/doc/source/motivation_package.md +++ /dev/null @@ -1,3 +0,0 @@ -(motivation-package)= - -# Motivation for a geospatial analysis package diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 28e0d73e1..dc8c178ec 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -1,16 +1,16 @@ (raster-class)= -# The `Raster` object +# The georeferenced raster (`Raster`) ## Object definition -A {class}`geoutils.Raster` is a georeferenced raster object read, written or reprojected by `rasterio`. +A {class}`~geoutils.georaster.raster.Raster` is a georeferenced raster read, written or reprojected by `rasterio`. It contains: -- an array `.data` as a `np.ma.MaskedArray`, -- a geotransform `.transform` as a `affine.Affine`, -- a coordinate reference system(CRS) `.crs` as a `pyproj.CRS`, -- a nodata value `nodata` as `float` or `int`. +- an array `.data` as a {class}`numpy.ma.MaskedArray`, +- a geotransform `.transform` as a {class}`affine.Affine`, +- a coordinate reference system (CRS) `.crs` as a {class}`pyproj.CRS`, +- a nodata value `.nodata` as ``float`` or ``int``. ## Open and save diff --git a/doc/source/rasters_index.md b/doc/source/rasters_index.md new file mode 100644 index 000000000..916575227 --- /dev/null +++ b/doc/source/rasters_index.md @@ -0,0 +1,11 @@ +(rasters-index)= +# Rasters + + +```{toctree} +:maxdepth: 2 + +raster_class +mask_class +satimg_class +``` \ No newline at end of file diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md index 100858076..6d6910e5e 100644 --- a/doc/source/satimg_class.md +++ b/doc/source/satimg_class.md @@ -1,6 +1,6 @@ (satimg-class)= -# The `SatelliteImage` object +# The satellite image (`SatelliteImage`) ## Object definition diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index ba1938552..313cdcf10 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -1,6 +1,6 @@ (vector-class)= -# The `Vector` object +# The georeferenced vector (`Vector`) ## Object definition diff --git a/doc/source/vectors_index.md b/doc/source/vectors_index.md new file mode 100644 index 000000000..955a100e0 --- /dev/null +++ b/doc/source/vectors_index.md @@ -0,0 +1,9 @@ +(vectors-index)= +# Vectors + + +```{toctree} +:maxdepth: 2 + +vector_class +``` \ No newline at end of file From 7903969575f156ca42b8ec7aaf261629930016c3 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 13:22:18 -0800 Subject: [PATCH 012/184] Incremental commit on doc --- doc/source/about_geoutils.md | 54 +++++++++++++----------------------- doc/source/background.md | 33 ++++++++++++++++++++++ doc/source/index.md | 6 ++-- 3 files changed, 56 insertions(+), 37 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 505f7b38d..7754bf8d6 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -4,53 +4,39 @@ ## What is GeoUtils? -GeoUtils is a [Python](https://www.python.org/) package for the manipulation and analysis of georeferenced data, developed with the objective of +GeoUtils1 is a [Python](https://www.python.org/) package for the manipulation and analysis of georeferenced data, developed with the objective of making geospatial analysis intuitive, accessible and robust. It is designed for all Earth and planetary observation science. -GeoUtils is built on top of the geospatial packages [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs. -html) and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) to provide: -- A **common consistent interface** between georeferenced rasters and vectors for robustness of geospatial handling, -- A geospatial framework following the **principal of least knowledge** for ease-of-use and accessibility, - -Additionally - - - - -## Mission - -```{epigraph} -The core mission of GeoUtils is to be **easy-of-use**, **robust**, **reproducible** and **fully open**. - -Additionally, GeoUtils aims to be **efficient**, **scalable** and **state-of-the-art**. +```{margin} +1With name standing for *Geospatial Utilities*. ``` ```{important} -:class: margin GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for **reproducibility**! We are working on making features fully consistent for the first long-term release ``v0.1`` (likely sometime in 2023). ``` -In details, those mean: +## Why use GeoUtils? -- **Ease-of-use:** all basic operations or methods only require a few lines of code to be performed; +GeoUtils is built on top of the packages [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) +and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenced operations, and relies on [NumPy](https://numpy.org/doc/stable/), +[SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: +- A **common and consistent framework** for rasters and vectors handling and analysis, +- A structure following the **principal of least knowledge**2 to foster accessibility, +- A **pythonic arithmetic** and **NumPy masked-array interfacing** for intuitive use. -- **Robustness:** all methods are tested within our continuous integration test-suite, to enforce that they always perform as expected; - -- **Reproducibility:** all code is version-controlled and release-based, to ensure consistency of dependent packages and works; - -- **Open-source:** all code is accessible and re-usable to anyone in the community, for transparency and open governance. - -```{note} -:class: margin -Additional mission points, in particular **scalability**, are partly developed but not a priority until our first long-term release ``v0.1`` is reached. +```{margin} +2Or the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) for software development. ``` -And, additionally: - -- **Efficiency**: all methods should be optimized at the lower-level, to function with the highest performance offered by Python packages; +In particular, GeoUtils: +- Rarely requires more than **single-line operations** due to its object-based structure, +- Allows for **match-reference geospatial operations** to facilitate geospatial handling, +- Re-implements **several of [GDAL](https://gdal.org/)'s missing features** (Proximity, DEM, Calc, etc), +- Naturally handles **different `dtypes` and `nodata`** values through its NumPy masked-array interface. -- **Scalability**: all methods should support both lazy processing and distributed parallelized processing, to work with high-resolution data on local machines as well as on HPCs; -- **State-of-the-art**: all methods should be at the cutting edge of remote sensing science, to provide users with the most reliable and up-to-date tools. +```{note} +More on these core features of GeoUtils in the {ref}`core-concepts`. +``` \ No newline at end of file diff --git a/doc/source/background.md b/doc/source/background.md index 7389d7be9..602f93238 100644 --- a/doc/source/background.md +++ b/doc/source/background.md @@ -2,6 +2,39 @@ # Background + +## Mission + +```{epigraph} +The core mission of GeoUtils is to be **easy-of-use**, **robust**, **reproducible** and **fully open**. + +Additionally, GeoUtils aims to be **efficient**, **scalable** and **state-of-the-art**. +``` + +In details, those mean: + +- **Ease-of-use:** all basic operations or methods only require a few lines of code to be performed; + +- **Robustness:** all methods are tested within our continuous integration test-suite, to enforce that they always perform as expected; + +- **Reproducibility:** all code is version-controlled and release-based, to ensure consistency of dependent packages and works; + +- **Open-source:** all code is accessible and re-usable to anyone in the community, for transparency and open governance. + +```{note} +:class: margin +Additional mission points, in particular **scalability**, are partly developed but not a priority until our first long-term release ``v0.1`` is reached. +``` + +And, additionally: + +- **Efficiency**: all methods should be optimized at the lower-level, to function with the highest performance offered by Python packages; + +- **Scalability**: all methods should support both lazy processing and distributed parallelized processing, to work with high-resolution data on local machines as well as on HPCs; + +- **State-of-the-art**: all methods should be at the cutting edge of remote sensing science, to provide users with the most reliable and up-to-date tools. + + ## The people behind GeoUtils ```{margin} diff --git a/doc/source/index.md b/doc/source/index.md index 69a9fd927..ac65108ca 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -5,9 +5,9 @@ # Welcome to GeoUtils' documentation! -GeoUtils aims to make the handling and analysis of georeferenced raster and vector data intuitive, robust, and easy-of-use. GeoUtils is built upon `rasterio`, -`geopandas` and `pyproj`, and facilitates geospatial operations between rasters and vectors, and the analysis of rasters by interfacing with `numpy` arrays. - +```{epigraph} +GeoUtils is a **intuitive**, **robust** and **accessible** package to handle and analyze geospatial data. +``` ```{important} :class: margin From fcec6134ff820e32b8078947c1affa733acd2f78 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 13:56:40 -0800 Subject: [PATCH 013/184] Incremental commit on doc --- doc/source/how_to_install.md | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/doc/source/how_to_install.md b/doc/source/how_to_install.md index 4feb28aab..1604040b8 100644 --- a/doc/source/how_to_install.md +++ b/doc/source/how_to_install.md @@ -2,23 +2,19 @@ # How to install -## Installing with conda (recommended) +## Installing with mamba (recommended) ```bash -conda install -c conda-forge --strict-channel-priority geoutils +mamba install -c conda-forge geoutils ``` -**Notes** +```{note} +Solving dependencies can take a long time with `conda`, `mamba` significantly speeds up the process. Install it with: -- The `--strict-channel-priority` flag seems essential for Windows installs to function correctly, and is recommended for UNIX-based systems as well. + conda install mamba -n base -c conda-forge -- Solving dependencies can take a long time with `conda`. To speed up this, consider installing `mamba`: - - ```bash - conda install mamba -n base -c conda-forge - ``` - - Once installed, the same commands can be run by simply replacing `conda` by `mamba`. More details available through the [mamba project](https://github.com/mamba-org/mamba). +Once installed, the same commands can be run by simply replacing `conda` by `mamba`. More details available in the [mamba documentation](https://mamba.readthedocs.io/en/latest/). +``` ## Installing with pip @@ -26,7 +22,9 @@ conda install -c conda-forge --strict-channel-priority geoutils pip install geoutils ``` -**NOTE**: Setting up GDAL and PROJ may need some extra steps, depending on your operating system and configuration. +```{note} +Setting up GDAL and PROJ may need some extra steps, depending on your operating system and configuration. +``` ## Installing for contributors From 3d2175cfc34bc07f531a71f3a5c23d9b39cd0cdc Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 15:56:32 -0800 Subject: [PATCH 014/184] Incremental commit on doc --- doc/source/about_geoutils.md | 2 +- doc/source/how_to_install.md | 4 +--- doc/source/satimg_class.md | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 7754bf8d6..78cbff10f 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -24,7 +24,7 @@ and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenc [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: - A **common and consistent framework** for rasters and vectors handling and analysis, - A structure following the **principal of least knowledge**2 to foster accessibility, -- A **pythonic arithmetic** and **NumPy masked-array interfacing** for intuitive use. +- A **pythonic arithmetic** and **NumPy masked-array interface** for intuitive use. ```{margin} 2Or the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) for software development. diff --git a/doc/source/how_to_install.md b/doc/source/how_to_install.md index 1604040b8..3094a016f 100644 --- a/doc/source/how_to_install.md +++ b/doc/source/how_to_install.md @@ -36,6 +36,4 @@ mamba activate xdem pip install -e . ``` -After installing, we recommend to check that everything is working by running the tests: - -`pytest -rA` +After installing, check that everything is working by running the tests: `pytest -rA`. diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md index 6d6910e5e..0f58d439a 100644 --- a/doc/source/satimg_class.md +++ b/doc/source/satimg_class.md @@ -1,6 +1,6 @@ (satimg-class)= -# The satellite image (`SatelliteImage`) +# The geo-image (`SatelliteImage`) ## Object definition From 4bdea6fbf2b90cf26542654019bef73e021eba1a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 16:11:47 -0800 Subject: [PATCH 015/184] Incremental commit on doc --- doc/source/api.md | 30 +++++++++++++----------------- doc/source/how_to_install.md | 4 ++-- doc/source/raster_class.md | 2 +- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index b5a796592..b814228ce 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -1,8 +1,8 @@ (api)= -# API Reference +# API -Full information about the functionalities of GeoUtils are provided on this page. +Full information about the functionalities of GeoUtils. ```{eval-rst} .. currentmodule:: geoutils @@ -10,20 +10,6 @@ Full information about the functionalities of GeoUtils are provided on this page ## Classes -```{eval-rst} -.. autoclass:: Raster - :show-inheritance: - :special-members: __init__ - :members: -.. _sphx_glr_backref_geoutils.Raster: - -.. minigallery:: geoutils.Raster - :add-heading: -``` - -automodule geoutils -% .. contents:: Contents -% :local: % % Raster % ------ @@ -60,10 +46,20 @@ automodule geoutils :template: module.rst :recursive: - georaster georaster.raster geovector projtools satimg geoviewer +``` + +```{eval-rst} +.. autoclass:: Raster + :show-inheritance: + :special-members: __init__ + :members: +.. _sphx_glr_backref_geoutils.Raster: + +.. minigallery:: geoutils.Raster + :add-heading: ``` \ No newline at end of file diff --git a/doc/source/how_to_install.md b/doc/source/how_to_install.md index 3094a016f..fb1a4520e 100644 --- a/doc/source/how_to_install.md +++ b/doc/source/how_to_install.md @@ -2,7 +2,7 @@ # How to install -## Installing with mamba (recommended) +## Installing with `mamba` (recommended) ```bash mamba install -c conda-forge geoutils @@ -16,7 +16,7 @@ Solving dependencies can take a long time with `conda`, `mamba` significantly sp Once installed, the same commands can be run by simply replacing `conda` by `mamba`. More details available in the [mamba documentation](https://mamba.readthedocs.io/en/latest/). ``` -## Installing with pip +## Installing with `pip` ```bash pip install geoutils diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index dc8c178ec..94a193f95 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -4,7 +4,7 @@ ## Object definition -A {class}`~geoutils.georaster.raster.Raster` is a georeferenced raster read, written or reprojected by `rasterio`. +A {class}`geoutils.Raster` is a georeferenced raster read, written or reprojected by `rasterio`. It contains: - an array `.data` as a {class}`numpy.ma.MaskedArray`, From 87ba89dd2777819e543ed5fe183977ac051982b8 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 19:02:10 -0800 Subject: [PATCH 016/184] Incremental commit on doc --- doc/source/api.md | 335 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 288 insertions(+), 47 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index b814228ce..b9e5c9a3d 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -1,65 +1,306 @@ (api)= +# API reference -# API - -Full information about the functionalities of GeoUtils. +This page provides an auto-generated summary of GeoUtils’s API. +For more details and examples, refer to the relevant chapters in the main part of the +documentation. ```{eval-rst} .. currentmodule:: geoutils ``` -## Classes +**Overview of class inheritance in GeoUtils:** + +```{eval-rst} +.. inheritance-diagram:: geoutils + :top-classes: geoutils.Raster geoutils.Vector +``` + + +## Raster + +```{eval-rst} +.. minigallery:: geoutils.Raster + :add-heading: +``` + +### Opening a file -% -% Raster -% ------ +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ -% -% Vector -% ------ -% .. autoclass:: Vector -% :show-inheritance: -% :special-members: __init__ -% :members: -% -% .. _sphx_glr_backref_geoutils.Vector: -% -% .. minigallery:: geoutils.Vector -% :add-heading: -% -% SatelliteImage -% -------------- -% .. autoclass:: SatelliteImage -% :show-inheritance: -% :special-members: __init__ -% :members: -% -% .. _sphx_glr_backref_geoutils.SatelliteImage: -% -% .. minigallery:: geoutils.SatelliteImage -% :add-heading: + Raster + Raster.info +``` +### Create from an array ```{eval-rst} .. autosummary:: - :toctree: gen_modules/ - :template: module.rst - :recursive: + :toctree: gen_modules/ - georaster.raster - geovector - projtools - satimg - geoviewer + Raster.from_array ``` +### Unique attributes + ```{eval-rst} -.. autoclass:: Raster - :show-inheritance: - :special-members: __init__ - :members: -.. _sphx_glr_backref_geoutils.Raster: +.. autosummary:: + :toctree: gen_modules/ -.. minigallery:: geoutils.Raster - :add-heading: -``` \ No newline at end of file + Raster.data + Raster.crs + Raster.transform + Raster.nodata +``` + +### Derived attributes + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.shape + Raster.height + Raster.width + Raster.count + Raster.count_on_disk + Raster.indexes + Raster.indexes_on_disk + Raster.res + Raster.bounds + Raster.dtypes + Raster.is_loaded + Raster.is_modified + Raster.name + Raster.driver +``` + +### Geospatial handling methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.crop + Raster.reproject + Raster.polygonize + Raster.proximity + Raster.value_at_coords + Raster.interp_points +``` + +### Get or update data methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.copy + Raster.astype + Raster.set_mask + Raster.set_nodata + Raster.get_nanarray +``` + +### I/O methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.load + Raster.save + Raster.to_points + Raster.to_rio_dataset + Raster.to_xarray +``` + +### Logical methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.raster_equal + Raster.equal_georeferenced_grid +``` + +### Coordinate and extent methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.xy2ij + Raster.ij2xy + Raster.coords + Raster.shift + Raster.get_bounds_projected + Raster.intersection + Raster.outside_image +``` + +### Arithmetic with other rasters, arrays or numbers + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.__add__ + Raster.__sub__ + Raster.__neg__ + Raster.__mul__ + Raster.__truediv__ + Raster.__floordiv__ + Raster.__mod__ + Raster.__power__ +``` + +And reverse operations. + +### Logical operator casting to Mask + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.__eq__ + Raster.__neq__ + Raster.__lt__ + Raster.__le__ + Raster.__gt__ + Raster.__ge__ + Raster.__mod__ + Raster.__power__ +``` + +### Array interface with NumPy + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.__array_ufunc__ + Raster.__array_function__ +``` + +## SatelliteImage + +```{eval-rst} +.. minigallery:: geoutils.SatelliteImage + :add-heading: +``` + +### Opening a file + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + SatelliteImage +``` + +### Satellite image metadata + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + SatelliteImage.datetime + SatelliteImage.tile_name + SatelliteImage.satellite + SatelliteImage.sensor + SatelliteImage.product + SatelliteImage.version +``` + +## Mask + +```{eval-rst} +.. minigallery:: geoutils.Mask + :add-heading: +``` + +### Opening a file + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Mask +``` + +### Booleans + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Mask.datetime +``` + +## Vectors + + +### Opening a file + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector + Vector.info +``` + +### Unique attributes + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.ds + Vector.crs + Vector.bounds + Vector.name +``` + +### Geospatial handling methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.crop + Vector.reproject + Vector.rasterize + Vector.proximity +``` + +### Create mask + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.create_mask +``` + +### Geometry manipulation + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.buffer_metric + Vector.buffer_without_overlap +``` + +### Coordinate and extent methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.get_bounds_projected \ No newline at end of file From a77bd908a87bf4e7d8b4647db8f4190f7f7a18bf Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 20:15:54 -0800 Subject: [PATCH 017/184] Incremental commit on doc --- dev-environment.yml | 1 + dev-requirements.txt | 2 +- doc/source/api.md | 15 ++++++++++----- doc/source/conf.py | 2 ++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/dev-environment.yml b/dev-environment.yml index 37b0f62bc..e746f3193 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -11,6 +11,7 @@ dependencies: - tqdm # Development-specific + - autovizwidget - sphinx - myst-parser - sphinx-autodoc-typehints diff --git a/dev-requirements.txt b/dev-requirements.txt index 52adaeb11..e60323281 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -18,4 +18,4 @@ typing-extensions sphinx-gallery scikit-image pyyaml - +autovizwidget diff --git a/doc/source/api.md b/doc/source/api.md index b9e5c9a3d..34ac5de9f 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -12,8 +12,8 @@ documentation. **Overview of class inheritance in GeoUtils:** ```{eval-rst} -.. inheritance-diagram:: geoutils - :top-classes: geoutils.Raster geoutils.Vector +.. inheritance-diagram:: geoutils.georaster.raster + :top-classes: geoutils.georaster.raster.Raster ``` @@ -232,18 +232,23 @@ And reverse operations. Mask ``` -### Booleans +### Overloaded Raster methods ```{eval-rst} .. autosummary:: :toctree: gen_modules/ - Mask.datetime + Mask.crop + Mask.reproject + Mask.polygonize ``` ## Vectors - +```{eval-rst} +.. minigallery:: geoutils.Vector + :add-heading: +``` ### Opening a file ```{eval-rst} diff --git a/doc/source/conf.py b/doc/source/conf.py index 545347713..e4443bde4 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -35,6 +35,8 @@ "sphinxcontrib.programoutput", "sphinx_gallery.gen_gallery", # Examples gallery "sphinx.ext.intersphinx", + "sphinx.ext.graphviz", # To render inheritance diagrams + "sphinx.ext.inheritance_diagram", # For class inheritance diagrams "myst_parser", # Form of Markdown that works with sphinx, used a lot by the Sphinx Book Theme ] From e4c08db542040f120c5229798ec0fb2a111ffcfc Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sun, 12 Feb 2023 22:16:55 -0800 Subject: [PATCH 018/184] Incremental commit on doc --- doc/source/code/index_example.py | 35 ++++++++++++-- doc/source/index.md | 2 +- doc/source/quick_start.md | 79 ++++++++++++++++++++++++++++++-- 3 files changed, 107 insertions(+), 9 deletions(-) diff --git a/doc/source/code/index_example.py b/doc/source/code/index_example.py index 9bf1a34b1..160bfd677 100644 --- a/doc/source/code/index_example.py +++ b/doc/source/code/index_example.py @@ -1,7 +1,36 @@ import geoutils as gu -filename = gu.examples.get_path("everest_landsat_b4") +# Examples files +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") -raster = gu.Raster(filename) +# Open files +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) + +# Crop raster to vector's extent +rast.crop(vect) + +# Proximity to vector on raster's grid +rast_proximity_to_vec = vect.proximity(rast) + +# Add 1 to the raster array +rast += 1 + +# Apply a sine to the raster array +import numpy as np +rast = np.sin(rast) + +# Get mask where proximity to vector fits specific raster values +mask_aoi = rast >= rast_proximity_to_vec + +# Index raster with the mask to extract a 1-D array of values +values_aoi = rast[mask_aoi] + +# Polygonize areas where mask is True +vect_aoi = mask_aoi.polygonize() + +# Read file as a satellite image +print(filename_rast) +satimg = gu.SatelliteImage(filename_rast, silent=False) -print(raster) diff --git a/doc/source/index.md b/doc/source/index.md index ac65108ca..174ddde29 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -6,7 +6,7 @@ # Welcome to GeoUtils' documentation! ```{epigraph} -GeoUtils is a **intuitive**, **robust** and **accessible** package to handle and analyze geospatial data. +GeoUtils is an **intuitive**, **robust** and **accessible** package to handle and analyze geospatial data. ``` ```{important} diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 0e2cd8cb7..61d72c8d9 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -2,15 +2,84 @@ # Quick start -Our functionalities are mostly based on [rasterio](https://rasterio.readthedocs.io) and [GeoPandas](https://geopandas.org/), but here are some of the -additional benefits provided by GeoUtils: +## The core `Raster` and `Vector` classes -- for raster objects, georeferences and data are stored into a single object of the class `Raster`, making it easier to modify the data in-place: reprojection, cropping, additions/subtractions are all (or will be...) on-line operations! -- the interactions between raster and vectors, such as rasterizing, clipping or cropping are made easier thanks to the class `Vector`. +In GeoUtils, geospatial handling is object-based and revolves around {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. +These link to either on-disk or in-memory datasets, opened by instantiating the class. -# Code example +```{literalinclude} code/index_example.py +:lines: 1-9 +:language: python +``` + +## Class attributes and match-reference methods + +Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update with operations. These operations are generally based on class methods, for +example {func}`~geoutils.Raster.crop` or {func}`~geoutils.Vector.proximity`. Most of these functions can be passed solely another {class}`~geoutils.Raster` or +{class}`~geoutils.Vector` as a reference to match, or to utilize during the operation. + +```{literalinclude} code/index_example.py +:lines: 11-15 +:language: python +``` + +```{note} +Right now, the array `.data` of `rast` is still not loaded. Applying {func}`~geoutils.Raster.crop` does not yet require loading, +and the metadata is sufficient to provide a georeferenced grid for {func}`~geoutils.Vector.proximity`. The array data will only be loaded when necessary. +``` + +## Pythonic arithmetic and NumPy interface + +All {class}`~geoutils.Raster` support Python arithmetic (+, -, /, //, *, %) with any other {class}`~geoutils.Raster`, or {class}`~numpy.ndarray` or number +(in case the georeferencing or shape is the same for all objects). + +```{literalinclude} code/index_example.py +:lines: 17-18 +:language: python +``` + +Additionally, the class {class}`~geoutils.Raster` possesses a NumPy masked-array interface that allows to apply to it any [NumPy universal function](https://numpy.org/doc/stable/reference/ufuncs.html) and +most other NumPy array functions, while logically casting `dtypes` and respecting `.nodata` values. + +```{literalinclude} code/index_example.py +:lines: 20-22 +:language: python +``` + +## Casting to `Mask`, indexing and overloading + +All {class}`~geoutils.Raster` also support Python logical operators (==, !=, >=, >, <=, <), or more complex NumPy logical functions. Those operations +automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. + + +```{literalinclude} code/index_example.py +:lines: 24-25 +:language: python +``` + +Masks can then be used for indexing a {class}`~geoutils.Raster`. + +```{literalinclude} code/index_example.py +:lines: 27-28 +:language: python +``` + +Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Polygonizing a `Mask` is straightforward, for instance, +to retrieve a {class}`~geoutils.Vector` of the area-of-interest identified by the mask: + +```{literalinclude} code/index_example.py +:lines: 30-31 +:language: python +``` + +## Loading image metadata using `SatelliteImage` + +The {class}`~geoutils.Raster` class is also subclassed by {class}`~geoutils.SatelliteImage`, which tentatively parses metadata recognized from the filename +or auxiliary files. ```{literalinclude} code/index_example.py +:lines: 33-35 +:language: python ``` ```{eval-rst} From 84f159e9a63599df7f6abb0058e5c9b3aa2c250a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Feb 2023 17:32:25 -0800 Subject: [PATCH 019/184] Quick start finalized! --- dev-environment.yml | 1 + dev-requirements.txt | 1 + doc/source/about_geoutils.md | 6 +- doc/source/api.md | 25 +++-- doc/source/code/index_example.py | 33 +++++-- doc/source/code/index_rast_info.py | 12 +++ doc/source/code/index_vect_info.py | 12 +++ doc/source/conf.py | 18 +++- doc/source/core_index.md | 2 +- doc/source/how_to_install.md | 2 +- doc/source/quick_start.md | 145 +++++++++++++++++++++++------ doc/source/raster_class.md | 15 +++ doc/source/rasters_index.md | 1 + doc/source/vector_class.md | 16 ++++ geoutils/__init__.py | 5 +- geoutils/georaster/__init__.py | 1 + geoutils/{ => georaster}/satimg.py | 0 tests/test_georaster.py | 2 +- tests/test_satimg.py | 53 ++++++----- 19 files changed, 262 insertions(+), 88 deletions(-) create mode 100644 doc/source/code/index_rast_info.py create mode 100644 doc/source/code/index_vect_info.py rename geoutils/{ => georaster}/satimg.py (100%) diff --git a/dev-environment.yml b/dev-environment.yml index e746f3193..d090c4f9f 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -12,6 +12,7 @@ dependencies: # Development-specific - autovizwidget + - graphviz - sphinx - myst-parser - sphinx-autodoc-typehints diff --git a/dev-requirements.txt b/dev-requirements.txt index e60323281..a86331d83 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -19,3 +19,4 @@ sphinx-gallery scikit-image pyyaml autovizwidget +graphviz \ No newline at end of file diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 78cbff10f..3f65159cd 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -24,7 +24,7 @@ and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenc [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: - A **common and consistent framework** for rasters and vectors handling and analysis, - A structure following the **principal of least knowledge**2 to foster accessibility, -- A **pythonic arithmetic** and **NumPy masked-array interface** for intuitive use. +- A **pythonic arithmetic** and **NumPy interfacing** for intuitive use. ```{margin} 2Or the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) for software development. @@ -32,11 +32,11 @@ and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenc In particular, GeoUtils: - Rarely requires more than **single-line operations** due to its object-based structure, -- Allows for **match-reference geospatial operations** to facilitate geospatial handling, +- Allows for **match-reference operations** to facilitate geospatial handling, - Re-implements **several of [GDAL](https://gdal.org/)'s missing features** (Proximity, DEM, Calc, etc), - Naturally handles **different `dtypes` and `nodata`** values through its NumPy masked-array interface. ```{note} -More on these core features of GeoUtils in the {ref}`core-concepts`. +More on these core features of GeoUtils in the {ref}`quick-start`, or {ref}`core-index` for details. ``` \ No newline at end of file diff --git a/doc/source/api.md b/doc/source/api.md index 34ac5de9f..e6da9c7ce 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -9,14 +9,6 @@ documentation. .. currentmodule:: geoutils ``` -**Overview of class inheritance in GeoUtils:** - -```{eval-rst} -.. inheritance-diagram:: geoutils.georaster.raster - :top-classes: geoutils.georaster.raster.Raster -``` - - ## Raster ```{eval-rst} @@ -91,6 +83,15 @@ documentation. Raster.interp_points ``` +### Plotting + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.show +``` + ### Get or update data methods ```{eval-rst} @@ -155,7 +156,7 @@ documentation. Raster.__truediv__ Raster.__floordiv__ Raster.__mod__ - Raster.__power__ + Raster.__pow__ ``` And reverse operations. @@ -167,13 +168,11 @@ And reverse operations. :toctree: gen_modules/ Raster.__eq__ - Raster.__neq__ + Raster.__ne__ Raster.__lt__ Raster.__le__ Raster.__gt__ - Raster.__ge__ - Raster.__mod__ - Raster.__power__ + Raster.__ge__ ``` ### Array interface with NumPy diff --git a/doc/source/code/index_example.py b/doc/source/code/index_example.py index 160bfd677..fe0fd6712 100644 --- a/doc/source/code/index_example.py +++ b/doc/source/code/index_example.py @@ -1,6 +1,6 @@ import geoutils as gu -# Examples files +# Examples files: infrared band of Landsat and glacier outlines filename_rast = gu.examples.get_path("everest_landsat_b4") filename_vect = gu.examples.get_path("everest_rgi_outlines") @@ -8,29 +8,42 @@ rast = gu.Raster(filename_rast) vect = gu.Vector(filename_vect) +# Open file as a satellite image +import os +print(os.path.basename(filename_rast)) +rast = gu.SatelliteImage(filename_rast, silent=False) + # Crop raster to vector's extent rast.crop(vect) -# Proximity to vector on raster's grid +# Compute proximity to vector on raster's grid rast_proximity_to_vec = vect.proximity(rast) # Add 1 to the raster array rast += 1 -# Apply a sine to the raster array +# Apply a normalization to the raster import numpy as np -rast = np.sin(rast) +rast = (rast - np.min(rast)) / (np.max(rast) - np.min(rast)) -# Get mask where proximity to vector fits specific raster values -mask_aoi = rast >= rast_proximity_to_vec +# Get mask of an AOI: infrared index above 0.9, at least 200 m from glaciers +mask_aoi = np.logical_and(rast > 0.7, rast_proximity_to_vec > 200) -# Index raster with the mask to extract a 1-D array of values +# Index raster with mask to extract a 1-D array values_aoi = rast[mask_aoi] # Polygonize areas where mask is True vect_aoi = mask_aoi.polygonize() -# Read file as a satellite image -print(filename_rast) -satimg = gu.SatelliteImage(filename_rast, silent=False) +# Plot result +import matplotlib.pyplot as plt +fig = plt.figure() +ax = plt.gca() +rast.show(ax=ax, cmap='Reds', cb_title='Normalized infrared') +vect_aoi.ds.plot(ax=ax, fc='none', ec='k', lw=0.5) + +# Save final raster and vector to files +# rast.save() +# vect_aoi.save() +#TODO: maybe add glacier outlines also above, once the plotting through Vector is simplified diff --git a/doc/source/code/index_rast_info.py b/doc/source/code/index_rast_info.py new file mode 100644 index 000000000..44914008d --- /dev/null +++ b/doc/source/code/index_rast_info.py @@ -0,0 +1,12 @@ +import geoutils as gu + +# Examples files +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") + +# Open files +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) + +# Print raster metadata info +print(rast.info()) \ No newline at end of file diff --git a/doc/source/code/index_vect_info.py b/doc/source/code/index_vect_info.py new file mode 100644 index 000000000..2fce27336 --- /dev/null +++ b/doc/source/code/index_vect_info.py @@ -0,0 +1,12 @@ +import geoutils as gu + +# Examples files +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") + +# Open files +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) + +# Print vector metadata info +print(vect.info()) \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index e4443bde4..b03fb06bc 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -30,13 +30,12 @@ "sphinx.ext.viewcode", # Create the "[source]" button in the API to show the source code. "matplotlib.sphinxext.plot_directive", # Render matplotlib figures from code. "sphinx.ext.autosummary", # Create API doc summary texts from the docstrings. - "sphinx.ext.inheritance_diagram", # For class inheritance diagrams (see coregistration.rst). + "sphinx.ext.inheritance_diagram", # For class inheritance diagrams. + "sphinx.ext.graphviz", # To render graphviz diagrams. "sphinx_autodoc_typehints", # Include type hints in the API documentation. "sphinxcontrib.programoutput", "sphinx_gallery.gen_gallery", # Examples gallery "sphinx.ext.intersphinx", - "sphinx.ext.graphviz", # To render inheritance diagrams - "sphinx.ext.inheritance_diagram", # For class inheritance diagrams "myst_parser", # Form of Markdown that works with sphinx, used a lot by the Sphinx Book Theme ] @@ -45,6 +44,8 @@ "numpy": ("https://numpy.org/doc/stable", None), "matplotlib": ("https://matplotlib.org/stable", None), "pyproj": ("https://pyproj4.github.io/pyproj/stable", None), + "geopandas": ("https://geopandas.org/en/stable", None), + "xdem": ("https://xdem.readthedocs.io/en/latest", None), } sphinx_gallery_conf = { @@ -66,6 +67,17 @@ "pull": ("https://github.com/GlacioHack/geoutils/pull/%s", "PR"), } +# For matplotlib figures generate with sphinx plot: (suffix, dpi) +plot_formats = [('.png', 400)] + +# To avoid long path names in inheritance diagrams +inheritance_alias = {"geoutils.georaster.raster.Raster": "geoutils.Raster", "geoutils.georaster.raster.Mask": "geoutils.Mask", + "geoutils.georaster.satimg.SatelliteImage": "geoutils.SatelliteImage", "geoutils.geovector.Vector": "geoutils.Vector", + "xdem.dem.DEM": "xdem.DEM"} + +# To avoid fuzzy PNGs +graphviz_output_format = 'svg' + # Add any paths that contain templates here, relative to this directory. templates_path = [os.path.join(os.path.dirname(__file__), "_templates")] diff --git a/doc/source/core_index.md b/doc/source/core_index.md index 7671cf598..774309eba 100644 --- a/doc/source/core_index.md +++ b/doc/source/core_index.md @@ -1,4 +1,4 @@ -(core-concepts)= +(core-index)= # Fundamentals ```{toctree} diff --git a/doc/source/how_to_install.md b/doc/source/how_to_install.md index fb1a4520e..a04d48d4c 100644 --- a/doc/source/how_to_install.md +++ b/doc/source/how_to_install.md @@ -23,7 +23,7 @@ pip install geoutils ``` ```{note} -Setting up GDAL and PROJ may need some extra steps, depending on your operating system and configuration. +Setting up GDAL and PROJ may require some extra steps, depending on your operating system and configuration. ``` ## Installing for contributors diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 61d72c8d9..d19a9b735 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -2,39 +2,115 @@ # Quick start -## The core `Raster` and `Vector` classes +The following shows how to quickly get started with GeoUtils, show-casing examples on different aspects of the package. + +For more details, refer to the {ref}`core-index`, {ref}`rasters-index` or {ref}`vectors-index` pages. + +## The core {class}`~geoutils.Raster` and {class}`~geoutils.Vector` classes In GeoUtils, geospatial handling is object-based and revolves around {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. -These link to either on-disk or in-memory datasets, opened by instantiating the class. +These link to either **on-disk** or **in-memory** datasets, opened by instantiating the class. ```{literalinclude} code/index_example.py :lines: 1-9 :language: python ``` -## Class attributes and match-reference methods +A {class}`~geoutils.Raster` is a composition class with four main attributes: +1. A {class}`~numpy.ma.MaskedArray` as `.data`, +2. A {class}`~pyproj.crs.CRS` as `.crs`, +3. An {class}`~affine.Affine` as `.transform`, +4. And a {class}`float` or {class}`int` as `.nodata`. + +When a file exists on disk, {class}`~geoutils.Raster` is linked to a {class}`rasterio.DatasetReader` object for loading the metadata, and the array at the +appropriate time. + +A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as `.ds`. + +Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update with georeferenced operations on themselves, or one of their subclasses. -Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update with operations. These operations are generally based on class methods, for -example {func}`~geoutils.Raster.crop` or {func}`~geoutils.Vector.proximity`. Most of these functions can be passed solely another {class}`~geoutils.Raster` or -{class}`~geoutils.Vector` as a reference to match, or to utilize during the operation. +## Parsing image metadata with {class}`~geoutils.SatelliteImage` + +In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses +metadata recognized from the filename or auxiliary files. ```{literalinclude} code/index_example.py -:lines: 11-15 +:lines: 11-14 +:language: python +``` + +```{eval-rst} +.. program-output:: $PYTHON code/index_example.py + :shell: +``` + +There are many possible subclass to derive from a {class}`~geoutils.Raster`. Here's an **overview of current {class}`~geoutils.Raster` class inheritance**, which extends into +[xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: + +```{eval-rst} +.. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM + :top-classes: geoutils.georaster.raster.Raster +``` +```{note} +The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) +(and more) to derive topographic attributes (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same +results. +``` + +## Geospatial handling and match-reference + +Geospatial operations are largely based on class methods, for +example {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Most of these methods can be solely passed another {class}`~geoutils.Raster` or +{class}`~geoutils.Vector` as a **reference to match** during the operation. + +```{literalinclude} code/index_example.py +:lines: 16-20 :language: python ``` ```{note} Right now, the array `.data` of `rast` is still not loaded. Applying {func}`~geoutils.Raster.crop` does not yet require loading, -and the metadata is sufficient to provide a georeferenced grid for {func}`~geoutils.Vector.proximity`. The array data will only be loaded when necessary. +and `rast`'s metadata is sufficient to provide a georeferenced grid for {func}`~geoutils.Vector.proximity`. The array will only be loaded when necessary. +``` + +Additionally, in GeoUtils, **geospatial handling methods that apply to the same georeferencing attributes have consistent naming**1 across {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. + +```{margin} +1The names of geospatial handling methods is largely based on [GDAL and OGR](https://gdal.org/)'s, with the notable exception of {func}`~geoutils.Vector.reproject` that better applies to vectors than `warp`. +``` + +A {func}`~geoutils.Raster.reproject` involves a change in `.crs` or `.transform`, while a {func}`~geoutils.Raster.crop` only involves a change in `.bounds`. +Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, and the other way around for {func}`~geoutils.Vector.rasterize`. + +```{list-table} + :widths: 30 30 30 + :header-rows: 1 + + * - **Class** + - {class}`~geoutils.Raster` + - {class}`~geoutils.Vector` + * - Warping/Reprojection + - {func}`~geoutils.Raster.reproject` + - {func}`~geoutils.Vector.reproject` + * - Cropping/Clipping + - {func}`~geoutils.Raster.crop` + - {func}`~geoutils.Vector.crop` + * - Rasterize/Polygonize + - {func}`~geoutils.Raster.polygonize` + - {func}`~geoutils.Vector.rasterize` + * - Proximity + - {func}`~geoutils.Raster.proximity` + - {func}`~geoutils.Vector.proximity` ``` + ## Pythonic arithmetic and NumPy interface -All {class}`~geoutils.Raster` support Python arithmetic (+, -, /, //, *, %) with any other {class}`~geoutils.Raster`, or {class}`~numpy.ndarray` or number -(in case the georeferencing or shape is the same for all objects). +All {class}`~geoutils.Raster` objects support Python arithmetic (+, -, /, //, *, ^, %) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or +number. For other {class}`~geoutils.Raster`, the georeferencing must match, while only the shape for other {class}`~numpy.ndarray`. ```{literalinclude} code/index_example.py -:lines: 17-18 +:lines: 22-23 :language: python ``` @@ -42,47 +118,64 @@ Additionally, the class {class}`~geoutils.Raster` possesses a NumPy masked-array most other NumPy array functions, while logically casting `dtypes` and respecting `.nodata` values. ```{literalinclude} code/index_example.py -:lines: 20-22 +:lines: 25-27 :language: python ``` -## Casting to `Mask`, indexing and overloading +## Casting to {class}`~geoutils.Mask`, indexing and overload -All {class}`~geoutils.Raster` also support Python logical operators (==, !=, >=, >, <=, <), or more complex NumPy logical functions. Those operations +All {class}`~geoutils.Raster` classes also support Python logical operators (==, !=, >=, >, <=, <), or more complex NumPy logical functions. Those operations automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. ```{literalinclude} code/index_example.py -:lines: 24-25 +:lines: 29-30 :language: python ``` Masks can then be used for indexing a {class}`~geoutils.Raster`. ```{literalinclude} code/index_example.py -:lines: 27-28 +:lines: 32-33 :language: python ``` -Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Polygonizing a `Mask` is straightforward, for instance, -to retrieve a {class}`~geoutils.Vector` of the area-of-interest identified by the mask: +Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Using {func}`~geoutils.Raster.polygonize` with a +{class}`~geoutils.Mask` is straightforward, for instance, to retrieve a {class}`~geoutils.Vector` of the masked area-of-interest: ```{literalinclude} code/index_example.py -:lines: 30-31 +:lines: 35-36 :language: python ``` -## Loading image metadata using `SatelliteImage` +## Plotting and saving geospatial data + -The {class}`~geoutils.Raster` class is also subclassed by {class}`~geoutils.SatelliteImage`, which tentatively parses metadata recognized from the filename -or auxiliary files. +Finally, GeoUtils includes basic plotting tools wrapping directly {func}`rasterio.plot.show` and {func}`geopandas.GeoDataFrame.plot` for {class}`~geoutils.Raster` and {class}`~geoutils.Vector`, respectively. +The plotting function was renamed {func}`~geoutils.Raster.show` everywhere, for consistency. + +For saving, {func}`~geoutils.Raster.save` is used. ```{literalinclude} code/index_example.py -:lines: 33-35 +:lines: 38-47 :language: python ``` +```{admonition} Wrap-up +In a few lines, we: + - **easily handled georeferencing** operations on rasters and vectors, + - performed raster calculations **inherently respecting unvalid data**, and + - **naturally derived a vectorized mask** for an area-of-interest. + +Our high infrared absorption indexes at least 200 meters away from glaciers near Everest likely corresponds to **perennial snowfields** (see +figure below). + +For more hands-on examples, explore GeoUtils' gallery of examples. +``` + ```{eval-rst} -.. program-output:: $PYTHON code/index_example.py - :shell: -``` \ No newline at end of file +.. plot:: code/index_example.py + :caption: High infrared outside of glaciers at Mount Everest (perennial snowfields) + :width: 90% +``` + diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 94a193f95..d80855c36 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -19,6 +19,21 @@ It contains: :lines: 2-8 ``` +````{margin} +**Example of raster from {func}`~geoutils.Raster.info`:** + +```{literalinclude} code/index_rast_info.py + :lines: 11-12 + :language: python +``` + +```{eval-rst} +.. program-output:: $PYTHON code/index_rast_info.py + :shell: +``` + +```` + To print information directly to your console: ```python diff --git a/doc/source/rasters_index.md b/doc/source/rasters_index.md index 916575227..48b5555c7 100644 --- a/doc/source/rasters_index.md +++ b/doc/source/rasters_index.md @@ -2,6 +2,7 @@ # Rasters + ```{toctree} :maxdepth: 2 diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index 313cdcf10..97ba027b7 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -25,6 +25,22 @@ print(outlines) ``` +````{margin} +**Example of vector from {func}`~geoutils.Vector.info`:** + +```{literalinclude} code/index_vect_info.py + :lines: 11-12 + :language: python +``` + +```{eval-rst} +.. program-output:: $PYTHON code/index_vect_info.py + :shell: +``` + + +```` + Masks can easily be generated for use with Rasters: ```{literalinclude} code/vector-basics_open_file.py diff --git a/geoutils/__init__.py b/geoutils/__init__.py index 2838e8e4b..897e8c077 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -3,10 +3,9 @@ """ from geoutils import spatial_tools # noqa -from geoutils import examples, georaster, geovector, projtools, satimg # noqa -from geoutils.georaster import Mask, Raster # noqa +from geoutils import examples, georaster, geovector, projtools # noqa +from geoutils.georaster import Mask, Raster, SatelliteImage # noqa from geoutils.geovector import Vector # noqa -from geoutils.satimg import SatelliteImage # noqa try: from geoutils.version import version as __version__ # noqa diff --git a/geoutils/georaster/__init__.py b/geoutils/georaster/__init__.py index 71375543e..87cc9171c 100644 --- a/geoutils/georaster/__init__.py +++ b/geoutils/georaster/__init__.py @@ -1,3 +1,4 @@ from geoutils.georaster.raster import Raster, RasterType, Mask # noqa isort:skip +from geoutils.georaster.satimg import SatelliteImage __all__ = ["RasterType", "Raster"] diff --git a/geoutils/satimg.py b/geoutils/georaster/satimg.py similarity index 100% rename from geoutils/satimg.py rename to geoutils/georaster/satimg.py diff --git a/tests/test_georaster.py b/tests/test_georaster.py index bf1a6d6bb..bbddf1e8e 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2667,7 +2667,7 @@ def test_ops_2args_expl(self, op: str) -> None: # Test with child class r3 = getattr(satimg, op)(intval) - assert isinstance(r3, gu.satimg.SatelliteImage) + assert isinstance(r3, gr.SatelliteImage) reflective_ops = [["__add__", "__radd__"], ["__mul__", "__rmul__"]] diff --git a/tests/test_satimg.py b/tests/test_satimg.py index 7151174ab..b13bb886a 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -11,7 +11,6 @@ import geoutils import geoutils.georaster as gr -import geoutils.satimg as si from geoutils import examples DO_PLOT = False @@ -29,18 +28,18 @@ def test_init(self, example: str) -> None: """ # from filename, checking option - img = si.SatelliteImage(example, read_from_fn=False) - img = si.SatelliteImage(example) - assert isinstance(img, si.SatelliteImage) + img = gr.SatelliteImage(example, read_from_fn=False) + img = gr.SatelliteImage(example) + assert isinstance(img, gr.SatelliteImage) # from SatelliteImage - img2 = si.SatelliteImage(img) - assert isinstance(img2, si.SatelliteImage) + img2 = gr.SatelliteImage(img) + assert isinstance(img2, gr.SatelliteImage) # from Raster r = gr.Raster(example) - img3 = si.SatelliteImage(r) - assert isinstance(img3, si.SatelliteImage) + img3 = gr.SatelliteImage(r) + assert isinstance(img3, gr.SatelliteImage) assert img.raster_equal(img2) assert img.raster_equal(img3) @@ -65,13 +64,13 @@ def __exit__(self, *args) -> None: # type: ignore sys.stdout = self._stdout with Capturing() as output1: - si.SatelliteImage(example, silent=False) + gr.SatelliteImage(example, silent=False) # check the metadata reading outputs to console assert len(output1) > 0 with Capturing() as output2: - si.SatelliteImage(example, silent=True) + gr.SatelliteImage(example, silent=True) # check nothing outputs to console assert len(output2) == 0 @@ -83,22 +82,22 @@ def test_add_sub(self) -> None: # Create fake rasters with random values in 0-255 and dtype uint8 width = height = 5 transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) - satimg1 = si.SatelliteImage.from_array( + satimg1 = gr.SatelliteImage.from_array( np.random.randint(0, 255, (height, width), dtype="uint8"), transform=transform, crs=None ) - satimg2 = si.SatelliteImage.from_array( + satimg2 = gr.SatelliteImage.from_array( np.random.randint(0, 255, (height, width), dtype="uint8"), transform=transform, crs=None ) # Check that output type is same - other tests are in test_georaster.py sat_out = -satimg1 - assert isinstance(sat_out, si.SatelliteImage) + assert isinstance(sat_out, gr.SatelliteImage) sat_out = satimg1 + satimg2 - assert isinstance(sat_out, si.SatelliteImage) + assert isinstance(sat_out, gr.SatelliteImage) sat_out = satimg1 - satimg2 # type: ignore - assert isinstance(sat_out, si.SatelliteImage) + assert isinstance(sat_out, gr.SatelliteImage) @pytest.mark.parametrize("example", [landsat_b4, aster_dem]) # type: ignore def test_copy(self, example: str) -> None: @@ -109,7 +108,7 @@ def test_copy(self, example: str) -> None: - if r is copied, r.data changed, r2.data should be unchanged """ # Open dataset, update data and make a copy - r = si.SatelliteImage(example) + r = gr.SatelliteImage(example) r.data += 5 r2 = r.copy() @@ -117,7 +116,7 @@ def test_copy(self, example: str) -> None: assert r is not r2 # Check the object is a SatelliteImage - assert isinstance(r2, geoutils.satimg.SatelliteImage) + assert isinstance(r2, gr.SatelliteImage) # check all immutable attributes are equal georaster_attrs = [ @@ -136,7 +135,7 @@ def test_copy(self, example: str) -> None: satimg_attrs = ["satellite", "sensor", "product", "version", "tile_name", "datetime"] # using list directly available in Class attrs = georaster_attrs + satimg_attrs - all_attrs = attrs + si.satimg_attrs + all_attrs = attrs + gr.satimg_attrs for attr in all_attrs: assert r.__getattribute__(attr) == r2.__getattribute__(attr) @@ -193,7 +192,7 @@ def test_filename_parsing(self) -> None: ] for names in copied_names: - attrs = si.parse_metadata_from_fn(names) + attrs = geoutils.georaster.satimg.parse_metadata_from_fn(names) i = copied_names.index(names) assert satellites[i] == attrs[0] assert sensors[i] == attrs[1] @@ -208,17 +207,17 @@ def test_sw_tile_naming_parsing(self) -> None: test_latlon = [(14, -65), (-14, 65), (14, -65), (14, -65), (14, -65), (0, 0)] for tile in test_tiles: - assert si.sw_naming_to_latlon(tile)[0] == test_latlon[test_tiles.index(tile)][0] - assert si.sw_naming_to_latlon(tile)[1] == test_latlon[test_tiles.index(tile)][1] + assert geoutils.georaster.satimg.sw_naming_to_latlon(tile)[0] == test_latlon[test_tiles.index(tile)][0] + assert geoutils.georaster.satimg.sw_naming_to_latlon(tile)[1] == test_latlon[test_tiles.index(tile)][1] for latlon in test_latlon: - assert si.latlon_to_sw_naming(latlon) == test_tiles[test_latlon.index(latlon)] + assert geoutils.georaster.satimg.latlon_to_sw_naming(latlon) == test_tiles[test_latlon.index(latlon)] # check possible exceptions, rounded lat/lon belong to their southwest border - assert si.latlon_to_sw_naming((0, 0)) == "N00E000" + assert geoutils.georaster.satimg.latlon_to_sw_naming((0, 0)) == "N00E000" # those are the same point, should give same naming - assert si.latlon_to_sw_naming((-90, 0)) == "S90E000" - assert si.latlon_to_sw_naming((90, 0)) == "S90E000" + assert geoutils.georaster.satimg.latlon_to_sw_naming((-90, 0)) == "S90E000" + assert geoutils.georaster.satimg.latlon_to_sw_naming((90, 0)) == "S90E000" # same here - assert si.latlon_to_sw_naming((0, -180)) == "N00W180" - assert si.latlon_to_sw_naming((0, 180)) == "N00W180" + assert geoutils.georaster.satimg.latlon_to_sw_naming((0, -180)) == "N00W180" + assert geoutils.georaster.satimg.latlon_to_sw_naming((0, 180)) == "N00W180" From 98d23435c81d1a760b73f53e653c9b79949dc045 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Feb 2023 18:42:59 -0800 Subject: [PATCH 020/184] Incremental commit on docs --- doc/source/code/index_example.py | 2 +- doc/source/quick_start.md | 59 ++++++++++++++++---------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/doc/source/code/index_example.py b/doc/source/code/index_example.py index fe0fd6712..dd1f602f2 100644 --- a/doc/source/code/index_example.py +++ b/doc/source/code/index_example.py @@ -26,7 +26,7 @@ import numpy as np rast = (rast - np.min(rast)) / (np.max(rast) - np.min(rast)) -# Get mask of an AOI: infrared index above 0.9, at least 200 m from glaciers +# Get mask of an AOI: infrared index above 0.7, at least 200 m from glaciers mask_aoi = np.logical_and(rast > 0.7, rast_proximity_to_vec > 200) # Index raster with mask to extract a 1-D array diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index d19a9b735..3b3126d7a 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -29,34 +29,6 @@ A {class}`~geoutils.Vector` is a composition class with a single main attribute: Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update with georeferenced operations on themselves, or one of their subclasses. -## Parsing image metadata with {class}`~geoutils.SatelliteImage` - -In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses -metadata recognized from the filename or auxiliary files. - -```{literalinclude} code/index_example.py -:lines: 11-14 -:language: python -``` - -```{eval-rst} -.. program-output:: $PYTHON code/index_example.py - :shell: -``` - -There are many possible subclass to derive from a {class}`~geoutils.Raster`. Here's an **overview of current {class}`~geoutils.Raster` class inheritance**, which extends into -[xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: - -```{eval-rst} -.. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM - :top-classes: geoutils.georaster.raster.Raster -``` -```{note} -The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) -(and more) to derive topographic attributes (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same -results. -``` - ## Geospatial handling and match-reference Geospatial operations are largely based on class methods, for @@ -170,7 +142,9 @@ In a few lines, we: Our high infrared absorption indexes at least 200 meters away from glaciers near Everest likely corresponds to **perennial snowfields** (see figure below). -For more hands-on examples, explore GeoUtils' gallery of examples. +For a **bonus** example on parsing satellite metadata and DEMs, continue below. + +Otherwise, for more **hands-on** examples, explore GeoUtils' gallery of examples! ``` ```{eval-rst} @@ -179,3 +153,30 @@ For more hands-on examples, explore GeoUtils' gallery of examples. :width: 90% ``` +## **Bonus:** Parsing metadata with {class}`~geoutils.SatelliteImage` + +In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses +metadata recognized from the filename or auxiliary files. + +```{literalinclude} code/index_example.py +:lines: 11-14 +:language: python +``` + +```{eval-rst} +.. program-output:: $PYTHON code/index_example.py + :shell: +``` + +There are many possible subclass to derive from a {class}`~geoutils.Raster`. Here's an **overview of current {class}`~geoutils.Raster` class inheritance**, which extends into +[xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: + +```{eval-rst} +.. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM + :top-classes: geoutils.georaster.raster.Raster +``` +```{note} +The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) +(and more) to derive topographic attributes (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same +results. +``` \ No newline at end of file From 9110aadcc7cab45015b9668a540af091f8303776 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 14 Feb 2023 12:30:54 -0800 Subject: [PATCH 021/184] Incremental commit on doc --- doc/source/about_geoutils.md | 32 ++++++++++++++++++++++++++++---- doc/source/background.md | 1 + doc/source/from_gdal.md | 3 --- doc/source/index.md | 2 +- doc/source/quick_start.md | 35 ++++++++++++++++++----------------- 5 files changed, 48 insertions(+), 25 deletions(-) delete mode 100644 doc/source/from_gdal.md diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 3f65159cd..52223ec4c 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -4,8 +4,12 @@ ## What is GeoUtils? -GeoUtils1 is a [Python](https://www.python.org/) package for the manipulation and analysis of georeferenced data, developed with the objective of -making geospatial analysis intuitive, accessible and robust. It is designed for all Earth and planetary observation science. +GeoUtils1 is a **[Python](https://www.python.org/) package for the handling and analysis of georeferenced data**, developed with the objective of +making geospatial analysis accessible, intuitive and robust. + +GeoUtils is designed for all Earth and planetary observation science. It is generally **most useful for remote sensing and Earth's surface applications +relying on moderate- to high-resolution georeferenced data** (typically < 1 km). Applications that, for analysis, require reprojections, re-gridding, point +interpolation, and other types of fine-grid analysis with millions of pixels. ```{margin} 1With name standing for *Geospatial Utilities*. @@ -19,12 +23,12 @@ We are working on making features fully consistent for the first long-term relea ## Why use GeoUtils? -GeoUtils is built on top of the packages [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) +GeoUtils is built on top of [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenced operations, and relies on [NumPy](https://numpy.org/doc/stable/), [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: - A **common and consistent framework** for rasters and vectors handling and analysis, - A structure following the **principal of least knowledge**2 to foster accessibility, -- A **pythonic arithmetic** and **NumPy interfacing** for intuitive use. +- A **pythonic arithmetic** and **NumPy interfacing** for robust numerical computing and intuitive use. ```{margin} 2Or the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) for software development. @@ -32,6 +36,7 @@ and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenc In particular, GeoUtils: - Rarely requires more than **single-line operations** due to its object-based structure, +- Strives to rely on **lazy-operations** under-the-hood to avoid unnecessary data loading, - Allows for **match-reference operations** to facilitate geospatial handling, - Re-implements **several of [GDAL](https://gdal.org/)'s missing features** (Proximity, DEM, Calc, etc), - Naturally handles **different `dtypes` and `nodata`** values through its NumPy masked-array interface. @@ -39,4 +44,23 @@ In particular, GeoUtils: ```{note} More on these core features of GeoUtils in the {ref}`quick-start`, or {ref}`core-index` for details. +``` + +## Why the need for GeoUtils? + +Recent community efforts have improved open-source geospatial analysis in Python, allowing to **move away from the low-level functions and +complexity of [GDAL and OGR](https://gdal.org/)**'s Python bindings for raster and vector handling. Those efforts include in particular [Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html). + +However, these new packages still maintain a relatively low-level API to serve all types of geospatial informatics users, **slowing down end-users focusing +on data analysis**. As a result, interfacing between vector data and raster data is delicate and simple higher-level operation (such as +reprojection to match a reference) are not always computed consistently in the community. + +Additionally, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses mostly on reading, projecting and writing, and thus **requires array extraction +and re-encapsulation** either before, during or after any numerical computation to interface with other georeferencing packages. + +Finally, **many common geospatial analysis tools are generally unavailable** in existing packages (e.g., boolean-masking from vectors, +[proximity](https://gdal.org/programs/gdal_proximity.html) estimation, metric buffering) as they rely on a combination of lower-level operations. + +```{admonition} Conclusion +Having higher-level geospatial tools implemented in a **consistent** manner and tested for **robustness** is essential for the wider geospatial community. ``` \ No newline at end of file diff --git a/doc/source/background.md b/doc/source/background.md index 602f93238..2276e8d97 100644 --- a/doc/source/background.md +++ b/doc/source/background.md @@ -2,6 +2,7 @@ # Background +More information on the people behind GeoUtils, and the package's mission. ## Mission diff --git a/doc/source/from_gdal.md b/doc/source/from_gdal.md deleted file mode 100644 index dfd007b88..000000000 --- a/doc/source/from_gdal.md +++ /dev/null @@ -1,3 +0,0 @@ -(from-gdal)= - -# Moving from GDAL or other Python packages \ No newline at end of file diff --git a/doc/source/index.md b/doc/source/index.md index 174ddde29..68119783c 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -6,7 +6,7 @@ # Welcome to GeoUtils' documentation! ```{epigraph} -GeoUtils is an **intuitive**, **robust** and **accessible** package to handle and analyze geospatial data. +GeoUtils is an **accessible**, **intuitive** and **robust** package to quickly handle and analyze geospatial data. ``` ```{important} diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 3b3126d7a..116833625 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -2,7 +2,7 @@ # Quick start -The following shows how to quickly get started with GeoUtils, show-casing examples on different aspects of the package. +The following presents how to quickly get started with GeoUtils, show-casing examples on different core aspects of the package. For more details, refer to the {ref}`core-index`, {ref}`rasters-index` or {ref}`vectors-index` pages. @@ -31,8 +31,8 @@ Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update wit ## Geospatial handling and match-reference -Geospatial operations are largely based on class methods, for -example {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Most of these methods can be solely passed another {class}`~geoutils.Raster` or +Geospatial operations are largely based on class methods, such +as {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Most of these methods can be solely passed another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a **reference to match** during the operation. ```{literalinclude} code/index_example.py @@ -51,9 +51,6 @@ Additionally, in GeoUtils, **geospatial handling methods that apply to the same 1The names of geospatial handling methods is largely based on [GDAL and OGR](https://gdal.org/)'s, with the notable exception of {func}`~geoutils.Vector.reproject` that better applies to vectors than `warp`. ``` -A {func}`~geoutils.Raster.reproject` involves a change in `.crs` or `.transform`, while a {func}`~geoutils.Raster.crop` only involves a change in `.bounds`. -Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, and the other way around for {func}`~geoutils.Vector.rasterize`. - ```{list-table} :widths: 30 30 30 :header-rows: 1 @@ -75,6 +72,9 @@ Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils - {func}`~geoutils.Vector.proximity` ``` +A {func}`~geoutils.Raster.reproject` involves a change in `.crs` or `.transform`, while a {func}`~geoutils.Raster.crop` only involves a change in `.bounds`. +Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, and the other way around for {func}`~geoutils.Vector.rasterize`. + ## Pythonic arithmetic and NumPy interface @@ -113,7 +113,7 @@ Masks can then be used for indexing a {class}`~geoutils.Raster`. ``` Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Using {func}`~geoutils.Raster.polygonize` with a -{class}`~geoutils.Mask` is straightforward, for instance, to retrieve a {class}`~geoutils.Vector` of the masked area-of-interest: +{class}`~geoutils.Mask` is straightforward, for instance, to retrieve a {class}`~geoutils.Vector` of the area-of-interest: ```{literalinclude} code/index_example.py :lines: 35-36 @@ -133,26 +133,27 @@ For saving, {func}`~geoutils.Raster.save` is used. :language: python ``` +```{eval-rst} +.. plot:: code/index_example.py + :caption: High infrared outside of glaciers at Mount Everest (perennial snowfields) + :width: 90% +``` + ```{admonition} Wrap-up In a few lines, we: - **easily handled georeferencing** operations on rasters and vectors, - - performed raster calculations **inherently respecting unvalid data**, and - - **naturally derived a vectorized mask** for an area-of-interest. + - performed numerical calculations **inherently respecting unvalid data**, + - **naturally casted to a mask** from a logical operation on raster, and + - **intuitively vectorized a mask** by harnessing overloaded subclass methods. -Our high infrared absorption indexes at least 200 meters away from glaciers near Everest likely corresponds to **perennial snowfields** (see -figure below). +Our result: a vector of high infrared absorption indexes at least 200 meters away from glaciers +near Everest, which likely corresponds to **perennial snowfields**. For a **bonus** example on parsing satellite metadata and DEMs, continue below. Otherwise, for more **hands-on** examples, explore GeoUtils' gallery of examples! ``` -```{eval-rst} -.. plot:: code/index_example.py - :caption: High infrared outside of glaciers at Mount Everest (perennial snowfields) - :width: 90% -``` - ## **Bonus:** Parsing metadata with {class}`~geoutils.SatelliteImage` In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses From d9085d4c80d687fca7646a77f98a48f513f731c3 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 14 Feb 2023 12:37:23 -0800 Subject: [PATCH 022/184] Pin dependencies for sphinx --- dev-environment.yml | 1 + dev-requirements.txt | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dev-environment.yml b/dev-environment.yml index d090c4f9f..b2a2bbd27 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -13,6 +13,7 @@ dependencies: # Development-specific - autovizwidget - graphviz + - jinja2<3.1 - sphinx - myst-parser - sphinx-autodoc-typehints diff --git a/dev-requirements.txt b/dev-requirements.txt index a86331d83..cb0896c93 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,4 @@ -rasterio +rasterio >= 1.3 geopandas >= 0.10.0 pyproj flake8 @@ -19,4 +19,5 @@ sphinx-gallery scikit-image pyyaml autovizwidget -graphviz \ No newline at end of file +graphviz +jinja2 < 3.1 From 02bb6f0ce19f328451fcc1d916d232a699e5eaff Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 14 Feb 2023 18:13:11 -0800 Subject: [PATCH 023/184] Incremental commit on doc --- doc/source/core_composition.md | 48 +++++++++++++++ doc/source/core_inheritance.md | 2 +- doc/source/core_match_ref.md | 108 ++++++++++++++++++++++++++++++++- doc/source/quick_start.md | 33 +++++----- doc/source/vector_class.md | 2 + 5 files changed, 175 insertions(+), 18 deletions(-) diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index 7f1a6f47a..f79e15803 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -1,2 +1,50 @@ (core-composition)= + # Composition from Rasterio and GeoPandas + +## A composition framework + +GeoUtil's main classes {class}`~geoutils.Raster` and {class}`~geoutils.Vector` are linked to [Rasterio](https://rasterio.readthedocs.io/en/latest/) and +[GeoPandas](https://geopandas.org/en/stable/docs.html), respectively, through class composition. + +They directly rely on their robust geospatial handling functionalities, as well of that of [PyProj](https://pyproj4.github.io/pyproj/stable/index.html), and +add a layer on top for interfacing between rasters and vectors with higher-level operations, performing easier numerical analysis, and adding more advanced geospatial functionalities. + +## The {class}`~geoutils.Raster` class composition + +The {class}`~geoutils.Raster` is a composition class with **four main attributes**: + +1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, +2. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, +3. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, and +4. a {class}`float` or {class}`int` as {attr}`~geoutils.Raster.nodata`. + +From these **four main attributes**, many other derivatives attributes exist, such as {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.res` to +describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster. +name` or {attr}`~geoutils.Raster.driver`. + +```{important} +The {class}`~geoutils.Raster` is not a composition of either a {class}`rasterio.DatasetReader`, a {class}`rasterio.MemoryFile` or a {class}`rasterio.DatasetWriter`. +It is only linked to those objects to initiate a {class}`~geoutils.Raster` instance which first loads the metadata (notably the three main metadata attributes +{attr}`~geoutils.Raster.crs`, {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.nodata`). +Then, explicity or implicitly, it can also {class}`~geoutils.Raster.load` the array data when an **on-disk** dataset exists, or {class}`~geoutils.Raster.save` the **in-memory** +dataset to a file. +``` + +A {class}`~geoutils.Raster` generally exists only as an **in-memory** dataset, not linked to anything else than a {class}`numpy.ma.MaskedArray` in {attr}`~geoutils.Raster.data` +(for instance, when creating from {func}`~geoutils.Raster.from_array`). If a numerical operation is performed on a {class}`~geoutils.Raster` opened from an +**on-disk** dataset, the new dataset will be modified **in-memory**. + +```{note} +To check if a {class}`~geoutils.Raster` was loaded from the **on-disk** data, use the {attr}`~geoutils.Raster.is_loaded` attribute. + +To check if the {class}`~geoutils.Raster` was modified since loading from the **on-disk** data, use the {attr}`~geoutils.Raster.is_modified` attribute. +``` + +## The {class}`~geoutils.Vector` class composition + +A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`. + +Because lazy loading is a lesser priority with vector data, a {class}`~geoutils.Vector` directly loads its {attr}`~geoutils.Vector.ds`. Besides, many +higher-level geospatial methods are already available in {class}`~geopandas.GeoDataFrame`. We thus only wrap those directly into {class}`~geoutils.Vector`, +in order to easily call them from the vector object, and build additional methods on top. diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 73e96d255..da567ddaf 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -1,2 +1,2 @@ (core-inheritance)= -# Inheritance for satellite images, DEMs +# Inheritance to geo-images and beyond diff --git a/doc/source/core_match_ref.md b/doc/source/core_match_ref.md index fb2504839..f112c0b05 100644 --- a/doc/source/core_match_ref.md +++ b/doc/source/core_match_ref.md @@ -1,2 +1,108 @@ (core-match-ref)= -# Intuitive match-reference operations +# Match-reference functionality + +## What is match-reference ? + +Match-reference means to **match** the georeferencing of a dataset to that of another **reference** dataset. + +## Why the need for match-reference? + +End-users of geospatial tools largely focus on analyzing their data. To this end, they first **need to reconcile data sources**, often provided **in different +projections, resolutions, and bounds**. This is generally performed by matching all dataset to a certain reference. + +Such functionalities are not always available from lower-level packages, in order to keep geoinformatics flexible for more diverse uses. This +**breaks the principle of least knowledge**, however, for which a user should not need to understand more information than just what is required to run an +operation. + +**GeoUtils allows for match-reference for nearly all geospatial handling operations**, with consistent behaviour across functionalities for intuitive use. + +## Matching with a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector` reference + +The rules of using match-reference with a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector` are always the same. + + - If the **reference** passed is a {class}`~geoutils.Vector`, it can enforce a matching of its {attr}`~geoutils.Vector.bounds` and/or of its {attr}`~geoutils.Vector.crs` (its only two + georeferencing characteristics), + - If the **reference** is a {class}`~geoutils.Raster`, it can also enforce a matching of any aspect of its {attr}`~geoutils.Raster.transform` (i.e, its + {attr}`~geoutils.Raster.res`, {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.shape`) and/or of its {attr}`~geoutils.Raster.crs`. + +Which of these attributes are eventually used to enforce the matching **depends entirely on the nature of the operation**, which are listed below. + +## Geospatial handling rules for match-reference + +Core "handling" methods all support match-reference, and **always enforce the same georeferencing attributes** for either {class}`~geoutils.Raster` +or {class}`~geoutils.Vector`: + +```{list-table} + :widths: 30 30 30 + :header-rows: 1 + + * - **Operation** + - Enforced on {class}`~geoutils.Raster` + - Enforced on {class}`~geoutils.Vector` + * - {func}`~geoutils.Raster.warp` + - {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` + - {attr}`~geoutils.Vector.bounds`1 and {attr}`~geoutils.Vector.crs` + * - {func}`~geoutils.Raster.reproject` + - {attr}`~geoutils.Raster.crs` + - {attr}`~geoutils.Vector.crs` + * - {func}`~geoutils.Raster.crop` + - {attr}`~geoutils.Vector.bounds` + - {attr}`~geoutils.Raster.bounds` + * - {func}`~geoutils.Raster.resample` + - {attr}`~geoutils.Raster.res` + - N/A + +``` + +1Because a {class}`~geoutils.Vector` only possesses the {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Raster`'s {attr}`~geoutils.Raster.transform`. + + +## Other operations supporting match-reference arguments + +There are **other geospatial operation that also support match-reference arguments**. Unlike the geospatial handling methods described above, these do not aim +at modifying the georeferencing of a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector`. Instead, they simply require the georeferencing metadata. + +### From vector to raster + +The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geoutils.Vector` to {class}`~geoutils.Raster` accepts a {class}`~geoutils.Raster` to define the +grid and georeferencing. The behaviour is similar for {func}`~geoutils.Vector.create_mask`, that directly relies on {func}`~geoutils.Vector.rasterize` to +rasterize directly into a boolean {class}`~geoutils.Raster.Mask`. + +In addition, the {func}`~geoutils.Vector.proximity` operation to compute proximity distances from the vector also relies on a +{func}`~geoutils.Vector.rasterize`, and therefore also accepts a {class}`~geoutils.Raster` as reference. + +Therefore, the behaviour is consistent for all {class}`~geoutils.Vector` methods that can be passed a {class}`~geoutils.Raster`: + +```{list-table} + :widths: 50 50 + :header-rows: 1 + + * - **Operation on {class}`~geoutils.Vector`** + - **Behaviour** + * - {func}`~geoutils.Vector.rasterize` + - Gridding with {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` + * - {func}`~geoutils.Vector.create_mask` + - Gridding with {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` + * - {func}`~geoutils.Vector.proximity` + - Gridding with {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` +``` + +### And inversely + +However, in the case of {class}`~geoutils.Raster` methods that yield a {class}`~geoutils.Vector` or {class}`~geoutils.Raster`, a reference is rarely needed. +This is because this reference is derived directly from the input {class}`~geoutils.Raster` itself, harnessing the object-based structure of GeoUtils. + +The user can always {func}`~geoutils.Vector.crop` or {func}`~geoutils.Vector.reproject` the output afterwards, if desired. + +```{list-table} + :widths: 50 50 + :header-rows: 1 + + * - **Operation on {class}`~geoutils.Raster`** + - **Behaviour** + * - {func}`~geoutils.Raster.polygonize` + - Using `.self` ({class}`~geoutils.Raster`) as reference for {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` + * - {func}`~geoutils.Raster.proximity` + - Using `.self` ({class}`~geoutils.Raster`) as reference for {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` +``` + diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 116833625..c19ea926d 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -16,36 +16,37 @@ These link to either **on-disk** or **in-memory** datasets, opened by instantiat :language: python ``` -A {class}`~geoutils.Raster` is a composition class with four main attributes: -1. A {class}`~numpy.ma.MaskedArray` as `.data`, -2. A {class}`~pyproj.crs.CRS` as `.crs`, -3. An {class}`~affine.Affine` as `.transform`, -4. And a {class}`float` or {class}`int` as `.nodata`. +A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as `.data`, a {class}`~pyproj.crs.CRS` as `.crs`, +an {class}`~affine.Affine` as `.transform`, and a {class}`float` or {class}`int` as `.nodata`. When a file exists on disk, {class}`~geoutils.Raster` is +linked to a {class}`rasterio.DatasetReader` object for loading the metadata, and the array at the appropriate time. -When a file exists on disk, {class}`~geoutils.Raster` is linked to a {class}`rasterio.DatasetReader` object for loading the metadata, and the array at the -appropriate time. +A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as `.ds`, for which most methods are +wrapped directly into {class}`~geoutils.Vector`. -A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as `.ds`. - -Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update with georeferenced operations on themselves, or one of their subclasses. +All other attributes are derivatives of those main attributes, or of the filename on disk. Attributes of {class}`~geoutils.Raster` and +{class}`~geoutils.Vector` update with geospatial operations on themselves. ## Geospatial handling and match-reference -Geospatial operations are largely based on class methods, such -as {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Most of these methods can be solely passed another {class}`~geoutils.Raster` or -{class}`~geoutils.Vector` as a **reference to match** during the operation. +Geospatial operations are based on class methods, such as {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Nearly all of these methods can be +passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a **reference to match** during the operation. A **reference {class}`~geoutils.Vector`** +enforces a matching of `.bounds` and/or `.crs`, while a **reference {class}`~geoutils.Raster`** can also enforce a matching of `.res`, depending on the nature of the operation. ```{literalinclude} code/index_example.py :lines: 16-20 :language: python ``` +All methods can be also be passed any number of georeferencing arguments such as `.shape` or `.res`, and will logically deduce others from the input, much +as in [GDAL](https://gdal.org/)'s command line. + + ```{note} Right now, the array `.data` of `rast` is still not loaded. Applying {func}`~geoutils.Raster.crop` does not yet require loading, and `rast`'s metadata is sufficient to provide a georeferenced grid for {func}`~geoutils.Vector.proximity`. The array will only be loaded when necessary. ``` -Additionally, in GeoUtils, **geospatial handling methods that apply to the same georeferencing attributes have consistent naming**1 across {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. +Additionally, in GeoUtils, **methods that apply to the same georeferencing attributes have consistent naming**1 across {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. ```{margin} 1The names of geospatial handling methods is largely based on [GDAL and OGR](https://gdal.org/)'s, with the notable exception of {func}`~geoutils.Vector.reproject` that better applies to vectors than `warp`. @@ -144,9 +145,9 @@ In a few lines, we: - **easily handled georeferencing** operations on rasters and vectors, - performed numerical calculations **inherently respecting unvalid data**, - **naturally casted to a mask** from a logical operation on raster, and - - **intuitively vectorized a mask** by harnessing overloaded subclass methods. + - **straightforwardly vectorized a mask** by harnessing overloaded subclass methods. -Our result: a vector of high infrared absorption indexes at least 200 meters away from glaciers +**Our result:** a vector of high infrared absorption indexes at least 200 meters away from glaciers near Everest, which likely corresponds to **perennial snowfields**. For a **bonus** example on parsing satellite metadata and DEMs, continue below. diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index 97ba027b7..389126b6e 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -78,6 +78,8 @@ TODO: Add rasterize text. ## Rasterize +The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geoutils.Vector` to {class}`~geoutils.Raster` inherently requires a +{attr}`~geoutils.Raster.res` or {attr}`~geoutils.Raster.shape` attribute to define the grid. While those can be passed on their own. ## Proximity From a304191449a15685cea7e632a8842fc8e0fd0a6f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 15 Feb 2023 13:22:15 -0800 Subject: [PATCH 024/184] Incremental commit on doc --- dev-environment.yml | 1 + dev-requirements.txt | 1 + doc/jupyter_execute/core_py_ops.ipynb | 295 ++++++++++++++++++++++++++ doc/source/conf.py | 5 +- doc/source/core_array_funcs.md | 10 + doc/source/core_py_ops.md | 90 +++++++- doc/source/quick_start.md | 6 +- geoutils/georaster/raster.py | 40 +++- 8 files changed, 434 insertions(+), 14 deletions(-) create mode 100644 doc/jupyter_execute/core_py_ops.ipynb diff --git a/dev-environment.yml b/dev-environment.yml index b2a2bbd27..a6a27ac0c 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -16,6 +16,7 @@ dependencies: - jinja2<3.1 - sphinx - myst-parser + - myst-nb - sphinx-autodoc-typehints - sphinxcontrib-programoutput - numpydoc diff --git a/dev-requirements.txt b/dev-requirements.txt index cb0896c93..3453961df 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -12,6 +12,7 @@ sphinxcontrib_programoutput sphinx_autodoc_typehints matplotlib myst-parser +myst-nb scipy pre-commit typing-extensions diff --git a/doc/jupyter_execute/core_py_ops.ipynb b/doc/jupyter_execute/core_py_ops.ipynb new file mode 100644 index 000000000..58bb93e09 --- /dev/null +++ b/doc/jupyter_execute/core_py_ops.ipynb @@ -0,0 +1,295 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d1ed8b4e", + "metadata": {}, + "source": [ + "(core-py-ops)=\n", + "# Support of pythonic operators\n", + "\n", + "GeoUtils integrates most of Python's operators for shorter, more intuitive code to consistently perform arithmetic and logical operations.\n", + " \n", + "Pythonic operators work on {class}`~geoutils.Raster` much as they would on {class}`~numpy.ndarray`, with some more details.\n", + "\n", + "## Arithmetic of {class}`~geoutils.Raster` classes\n", + "\n", + "Arithmetic operators (+, -, /, //, *, **, %) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.\n", + "ndarray` or number.\n", + "\n", + "For an operation with another {class}`~geoutils.Raster`, the georeferencing ({attr}`~geoutils.Raster.crs` and {attr}`~geoutils.Raster.transform`) must match. \n", + "For another {class}`~numpy.ndarray`, the {attr}`~geoutils.Raster.shape` must match. The operation always returns a {class}`~geoutils.Raster`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "29de0559", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Raster(\n", + " data=[[[102 -- --]\n", + " [-- 179 61]\n", + " [234 203 --]]]\n", + "transform=| 0.33, 0.00, 0.00|\n", + " | 0.00,-0.33, 1.00|\n", + " | 0.00, 0.00, 1.00|\n", + "crs=None\n", + "nodata=None" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import geoutils as gu\n", + "import rasterio as rio\n", + "import numpy as np\n", + "\n", + "# Create a random 3 x 3 masked array\n", + "np.random.seed(42)\n", + "arr = np.random.randint(0, 255, size=(3, 3), dtype=\"uint8\")\n", + "mask = np.random.randint(0, 2, size=(3, 3), dtype=\"bool\")\n", + "ma = np.ma.masked_array(data=arr, mask=mask)\n", + "\n", + "# Create an example Raster with only a transform\n", + "raster = gu.Raster.from_array(\n", + " data = ma,\n", + " transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3),\n", + " crs=None\n", + " )\n", + "\n", + "raster" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7f363e0c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Raster(\n", + " data=[[[103 -- --]\n", + " [-- 180 62]\n", + " [235 204 --]]]\n", + "transform=| 0.33, 0.00, 0.00|\n", + " | 0.00,-0.33, 1.00|\n", + " | 0.00, 0.00, 1.00|\n", + "crs=None\n", + "nodata=None" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Arithmetic with a number\n", + "raster + 1" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f1afca04", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Raster(\n", + " data=[[[1.0 -- --]\n", + " [-- 1.0 1.0]\n", + " [1.0 1.0 --]]]\n", + "transform=| 0.33, 0.00, 0.00|\n", + " | 0.00,-0.33, 1.00|\n", + " | 0.00, 0.00, 1.00|\n", + "crs=None\n", + "nodata=None" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Arithmetic with an array\n", + "raster / arr\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "31186cf5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Raster(\n", + " data=[[[91.90049506163793 -- --]\n", + " [-- 165.62091183974036 53.189750324093346]\n", + " [218.70294145922165 188.752193151225 --]]]\n", + "transform=| 0.33, 0.00, 0.00|\n", + " | 0.00,-0.33, 1.00|\n", + " | 0.00, 0.00, 1.00|\n", + "crs=None\n", + "nodata=None" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Arithmetic with a raster\n", + "raster - (raster**0.5)" + ] + }, + { + "cell_type": "markdown", + "id": "8a62d81a", + "metadata": {}, + "source": [ + "If an unmasked {class}`~numpy.ndarray` is passed, it will internally be cast into a {class}`~numpy.ma.MaskedArray` to respect the propagation of \n", + "{class}`~geoutils.Raster.nodata` values. Additionally, the {attr}`~geoutils.Raster.dtypes` are also reconciled as they would for {class}`~numpy.ndarray`, \n", + "following [standard NumPy coercion rules](https://numpy.org/doc/stable/reference/generated/numpy.find_common_type.html).\n", + "\n", + "## Logical comparisons cast to {class}`~geoutils.Mask`\n", + "\n", + "Logical comparison operators (==, !=, >=, >, <=, <) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, \n", + "{class}`~numpy.ndarray` or number.\n", + "\n", + "Those operation always return a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster` with a boolean {class}`~numpy.ma.MaskedArray` \n", + "as {class}`~geoutils.Raster.data`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4e8aa41a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Mask(\n", + " data=[[[True -- --]\n", + " [-- True False]\n", + " [True True --]]]\n", + "transform=| 0.33, 0.00, 0.00|\n", + " | 0.00,-0.33, 1.00|\n", + " | 0.00, 0.00, 1.00|\n", + "crs=None" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Logical comparison with a number\n", + "mask = raster > 100\n", + "mask" + ] + }, + { + "cell_type": "markdown", + "id": "2d8d4173", + "metadata": {}, + "source": [ + "```{note}\n", + "A {class}`~geoutils.Mask`'s {attr}`~geoutils.Raster.data` remains a {class}`~numpy.ma.MaskedArray`. Therefore, it still maps unvalid values through its \n", + "{attr}`~numpy.ma.MaskedArray.mask`, but has no associated {attr}`~geoutils.Raster.nodata`.\n", + "```\n", + "\n", + "## Logical bitwise operations on {class}`~geoutils.Mask`\n", + "\n", + "Logical bitwise operators (~, &, |, ^) can be used to combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2217b60f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Mask(\n", + " data=[[[True -- --]\n", + " [-- False False]\n", + " [True False --]]]\n", + "transform=| 0.33, 0.00, 0.00|\n", + " | 0.00,-0.33, 1.00|\n", + " | 0.00, 0.00, 1.00|\n", + "crs=None" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Logical bitwise operation between masks\n", + "mask = (raster > 100) & ((raster % 2) == 0)\n", + "mask" + ] + }, + { + "cell_type": "markdown", + "id": "3ff6fbb0", + "metadata": {}, + "source": [ + "## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask`" + ] + } + ], + "metadata": { + "file_format": "mystnb", + "kernelspec": { + "display_name": "geoutils", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "source_map": [ + 5, + 22, + 43, + 48, + 53, + 56, + 70, + 74, + 85, + 89 + ] + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index b03fb06bc..58e16bca2 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -36,9 +36,12 @@ "sphinxcontrib.programoutput", "sphinx_gallery.gen_gallery", # Examples gallery "sphinx.ext.intersphinx", - "myst_parser", # Form of Markdown that works with sphinx, used a lot by the Sphinx Book Theme + # "myst_parser", !! Not needed with myst_nb !! # Form of Markdown that works with sphinx, used a lot by the Sphinx Book Theme + "myst_nb" # MySt for rendering Jupyter notebook in documentation ] +nb_kernel_rgx_aliases = {".*geoutils.*": "python3"} + intersphinx_mapping = { "rasterio": ("https://rasterio.readthedocs.io/en/latest", None), "numpy": ("https://numpy.org/doc/stable", None), diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index c89bd9a9c..19ca7b1b2 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -1,3 +1,13 @@ (core-array-funcs)= # Masked-array NumPy interface + +NumPy possesses an [array interface](https://numpy.org/doc/stable/reference/arrays.interface.html) that allows to properly map their functions on objects +that depend on a {class}`~numpy.ndarray`. + +GeoUtils integrates this interface to work with any {class}`~geoutils.Raster` and their subclasses. + +## Universal functions + +The first category of NumPy functions that can be applied are [universal functions](https://numpy.org/doc/stable/reference/ufuncs.html) and +most other NumPy array functions, while logically casting `dtypes` and respecting `.nodata` values. \ No newline at end of file diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index a25bb9f47..0fb8afe6b 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -1,3 +1,91 @@ -(core-py-ops)= +--- +file_format: mystnb +kernelspec: + name: geoutils +--- +(core-py-ops)= # Support of pythonic operators + +GeoUtils integrates most of Python's operators for shorter, more intuitive code to consistently perform arithmetic and logical operations. + +Pythonic operators work on {class}`~geoutils.Raster` much as they would on {class}`~numpy.ndarray`, with some more details. + +## Arithmetic of {class}`~geoutils.Raster` classes + +Arithmetic operators (+, -, /, //, *, **, %) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, {class}`~numpy. +ndarray` or number. + +For an operation with another {class}`~geoutils.Raster`, the georeferencing ({attr}`~geoutils.Raster.crs` and {attr}`~geoutils.Raster.transform`) must match. +For another {class}`~numpy.ndarray`, the {attr}`~geoutils.Raster.shape` must match. The operation always returns a {class}`~geoutils.Raster`. + +```{code-cell} ipython3 +import geoutils as gu +import rasterio as rio +import numpy as np + +# Create a random 3 x 3 masked array +np.random.seed(42) +arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") +mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") +ma = np.ma.masked_array(data=arr, mask=mask) + +# Create an example Raster with only a transform +raster = gu.Raster.from_array( + data = ma, + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = None + ) + +raster +``` + +```{code-cell} ipython3 +# Arithmetic with a number +raster + 1 +``` + +```{code-cell} ipython3 +# Arithmetic with an array +raster / arr + +``` +```{code-cell} ipython3 +# Arithmetic with a raster +raster - (raster**0.5) +``` + +If an unmasked {class}`~numpy.ndarray` is passed, it will internally be cast into a {class}`~numpy.ma.MaskedArray` to respect the propagation of +{class}`~geoutils.Raster.nodata` values. Additionally, the {attr}`~geoutils.Raster.dtypes` are also reconciled as they would for {class}`~numpy.ndarray`, +following [standard NumPy coercion rules](https://numpy.org/doc/stable/reference/generated/numpy.find_common_type.html). + +## Logical comparisons cast to {class}`~geoutils.Mask` + +Logical comparison operators (==, !=, >=, >, <=, <) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, +{class}`~numpy.ndarray` or number. + +Those operation always return a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster` with a boolean {class}`~numpy.ma.MaskedArray` +as {class}`~geoutils.Raster.data`. + +```{code-cell} ipython3 +# Logical comparison with a number +mask = raster > 100 +mask +``` + +```{note} +A {class}`~geoutils.Mask`'s {attr}`~geoutils.Raster.data` remains a {class}`~numpy.ma.MaskedArray`. Therefore, it still maps unvalid values through its +{attr}`~numpy.ma.MaskedArray.mask`, but has no associated {attr}`~geoutils.Raster.nodata`. +``` + +## Logical bitwise operations on {class}`~geoutils.Mask` + +Logical bitwise operators (~, &, |, ^) can be used to combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`. + +```{code-cell} ipython3 +# Logical bitwise operation between masks +mask = (raster > 100) & ((raster % 2) == 0) +mask +``` + +## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask` \ No newline at end of file diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index c19ea926d..6107d213f 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -79,7 +79,7 @@ Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils ## Pythonic arithmetic and NumPy interface -All {class}`~geoutils.Raster` objects support Python arithmetic (+, -, /, //, *, ^, %) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or +All {class}`~geoutils.Raster` objects support Python arithmetic (+, -, /, //, *, **, %) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. For other {class}`~geoutils.Raster`, the georeferencing must match, while only the shape for other {class}`~numpy.ndarray`. ```{literalinclude} code/index_example.py @@ -97,8 +97,8 @@ most other NumPy array functions, while logically casting `dtypes` and respectin ## Casting to {class}`~geoutils.Mask`, indexing and overload -All {class}`~geoutils.Raster` classes also support Python logical operators (==, !=, >=, >, <=, <), or more complex NumPy logical functions. Those operations -automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. +All {class}`~geoutils.Raster` classes also support Python logical comparison operators (==, !=, >=, >, <=, <), or more complex NumPy logical functions. Those +operations automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. ```{literalinclude} code/index_example.py diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 712397cce..874a7af28 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -590,16 +590,26 @@ def to_rio_dataset(self) -> rio.io.DatasetReader: return mfh.open() def __repr__(self) -> str: - """Convert object to formal string representation.""" - return self.__str__() + """Convert formal Raster string representation.""" + + # Align left spaces for multi-line object representation (arrays) after return to lines + s = "Raster(\n"+\ + " data=" + "\n ".join(self.data.__str__().split("\n"))+\ + "\ntransform="+ "\n ".join(self.transform.__str__().split("\n"))+\ + "\ncrs=" + self.crs.__str__()+\ + "\nnodata=" + self.nodata.__str__() + + return s # L = [getattr(self, item) for item in self._saved_attrs] # s: str = "{}.{}({})".format(type(self).__module__, type(self).__qualname__, ", ".join(map(str, L))) # return s def __str__(self) -> str: - """Provide string of information about Raster.""" - return self.info() + """Provide simplified string for print() about Raster.""" + s = self.data.__str__() + + return s def __getitem__(self, value: Raster | Vector | list[float] | tuple[float, ...]) -> np.ndarray | Raster: """Subset the Raster object: calls the crop method with default parameters""" @@ -2790,6 +2800,7 @@ def proximity( # Subclass Mask for manipulating boolean Rasters class Mask(Raster): + def __init__( self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], @@ -2822,6 +2833,17 @@ def __init__( # Define in dtypes self._dtypes = (bool,) + def __repr__(self) -> str: + """Convert formal Raster string representation.""" + + # Over-ride Raster's method to remove nodata value (always None) + s = "Mask(\n"+\ + " data=" + "\n ".join(self.data.__str__().split("\n"))+\ + "\ntransform="+ "\n ".join(self.transform.__str__().split("\n"))+\ + "\ncrs=" + self.crs.__str__() + + return s + def reproject( self: Mask, dst_ref: RasterType | rio.io.Dataset | str | None = None, @@ -2932,29 +2954,29 @@ def polygonize( self._data = self.data.astype("uint8") return super().polygonize(in_value=in_value) - # Logical operations between mask objects: scale to the entire mask + # Logical bitwise operations def __and__(self: Mask, other: Mask) -> Mask: return self.from_array( - data=np.logical_and(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + data=(self.data & other.data), transform=self.transform, crs=self.crs, nodata=self.nodata ) def __or__(self: Mask, other: Mask) -> Mask: return self.from_array( - data=np.logical_or(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + data=(self.data | other.data), transform=self.transform, crs=self.crs, nodata=self.nodata ) def __xor__(self: Mask, other: Mask) -> Mask: return self.from_array( - data=np.logical_xor(self.data, other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + data=(self.data ^ other.data), transform=self.transform, crs=self.crs, nodata=self.nodata ) def __invert__(self: Mask) -> Mask: return self.from_array( - data=np.logical_not(self.data), transform=self.transform, crs=self.crs, nodata=self.nodata + data=~self.data, transform=self.transform, crs=self.crs, nodata=self.nodata ) From aacae89823b39d4562994c246ab2f32a306e6b0d Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 15 Feb 2023 13:24:38 -0800 Subject: [PATCH 025/184] Remove jupyter myst folder --- .gitignore | 3 + doc/jupyter_execute/core_py_ops.ipynb | 295 -------------------------- 2 files changed, 3 insertions(+), 295 deletions(-) delete mode 100644 doc/jupyter_execute/core_py_ops.ipynb diff --git a/.gitignore b/.gitignore index 0303cd60a..2d09d8b18 100644 --- a/.gitignore +++ b/.gitignore @@ -146,3 +146,6 @@ doc/source/gen_modules/ # Directories where example data is downloaded examples/data/ + +# Directory where myst_nb executes jupyter code +doc/jupyter_execute/ diff --git a/doc/jupyter_execute/core_py_ops.ipynb b/doc/jupyter_execute/core_py_ops.ipynb deleted file mode 100644 index 58bb93e09..000000000 --- a/doc/jupyter_execute/core_py_ops.ipynb +++ /dev/null @@ -1,295 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "d1ed8b4e", - "metadata": {}, - "source": [ - "(core-py-ops)=\n", - "# Support of pythonic operators\n", - "\n", - "GeoUtils integrates most of Python's operators for shorter, more intuitive code to consistently perform arithmetic and logical operations.\n", - " \n", - "Pythonic operators work on {class}`~geoutils.Raster` much as they would on {class}`~numpy.ndarray`, with some more details.\n", - "\n", - "## Arithmetic of {class}`~geoutils.Raster` classes\n", - "\n", - "Arithmetic operators (+, -, /, //, *, **, %) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.\n", - "ndarray` or number.\n", - "\n", - "For an operation with another {class}`~geoutils.Raster`, the georeferencing ({attr}`~geoutils.Raster.crs` and {attr}`~geoutils.Raster.transform`) must match. \n", - "For another {class}`~numpy.ndarray`, the {attr}`~geoutils.Raster.shape` must match. The operation always returns a {class}`~geoutils.Raster`." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "29de0559", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Raster(\n", - " data=[[[102 -- --]\n", - " [-- 179 61]\n", - " [234 203 --]]]\n", - "transform=| 0.33, 0.00, 0.00|\n", - " | 0.00,-0.33, 1.00|\n", - " | 0.00, 0.00, 1.00|\n", - "crs=None\n", - "nodata=None" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import geoutils as gu\n", - "import rasterio as rio\n", - "import numpy as np\n", - "\n", - "# Create a random 3 x 3 masked array\n", - "np.random.seed(42)\n", - "arr = np.random.randint(0, 255, size=(3, 3), dtype=\"uint8\")\n", - "mask = np.random.randint(0, 2, size=(3, 3), dtype=\"bool\")\n", - "ma = np.ma.masked_array(data=arr, mask=mask)\n", - "\n", - "# Create an example Raster with only a transform\n", - "raster = gu.Raster.from_array(\n", - " data = ma,\n", - " transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3),\n", - " crs=None\n", - " )\n", - "\n", - "raster" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "7f363e0c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Raster(\n", - " data=[[[103 -- --]\n", - " [-- 180 62]\n", - " [235 204 --]]]\n", - "transform=| 0.33, 0.00, 0.00|\n", - " | 0.00,-0.33, 1.00|\n", - " | 0.00, 0.00, 1.00|\n", - "crs=None\n", - "nodata=None" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Arithmetic with a number\n", - "raster + 1" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "f1afca04", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Raster(\n", - " data=[[[1.0 -- --]\n", - " [-- 1.0 1.0]\n", - " [1.0 1.0 --]]]\n", - "transform=| 0.33, 0.00, 0.00|\n", - " | 0.00,-0.33, 1.00|\n", - " | 0.00, 0.00, 1.00|\n", - "crs=None\n", - "nodata=None" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Arithmetic with an array\n", - "raster / arr\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "31186cf5", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Raster(\n", - " data=[[[91.90049506163793 -- --]\n", - " [-- 165.62091183974036 53.189750324093346]\n", - " [218.70294145922165 188.752193151225 --]]]\n", - "transform=| 0.33, 0.00, 0.00|\n", - " | 0.00,-0.33, 1.00|\n", - " | 0.00, 0.00, 1.00|\n", - "crs=None\n", - "nodata=None" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Arithmetic with a raster\n", - "raster - (raster**0.5)" - ] - }, - { - "cell_type": "markdown", - "id": "8a62d81a", - "metadata": {}, - "source": [ - "If an unmasked {class}`~numpy.ndarray` is passed, it will internally be cast into a {class}`~numpy.ma.MaskedArray` to respect the propagation of \n", - "{class}`~geoutils.Raster.nodata` values. Additionally, the {attr}`~geoutils.Raster.dtypes` are also reconciled as they would for {class}`~numpy.ndarray`, \n", - "following [standard NumPy coercion rules](https://numpy.org/doc/stable/reference/generated/numpy.find_common_type.html).\n", - "\n", - "## Logical comparisons cast to {class}`~geoutils.Mask`\n", - "\n", - "Logical comparison operators (==, !=, >=, >, <=, <) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, \n", - "{class}`~numpy.ndarray` or number.\n", - "\n", - "Those operation always return a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster` with a boolean {class}`~numpy.ma.MaskedArray` \n", - "as {class}`~geoutils.Raster.data`." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "4e8aa41a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Mask(\n", - " data=[[[True -- --]\n", - " [-- True False]\n", - " [True True --]]]\n", - "transform=| 0.33, 0.00, 0.00|\n", - " | 0.00,-0.33, 1.00|\n", - " | 0.00, 0.00, 1.00|\n", - "crs=None" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Logical comparison with a number\n", - "mask = raster > 100\n", - "mask" - ] - }, - { - "cell_type": "markdown", - "id": "2d8d4173", - "metadata": {}, - "source": [ - "```{note}\n", - "A {class}`~geoutils.Mask`'s {attr}`~geoutils.Raster.data` remains a {class}`~numpy.ma.MaskedArray`. Therefore, it still maps unvalid values through its \n", - "{attr}`~numpy.ma.MaskedArray.mask`, but has no associated {attr}`~geoutils.Raster.nodata`.\n", - "```\n", - "\n", - "## Logical bitwise operations on {class}`~geoutils.Mask`\n", - "\n", - "Logical bitwise operators (~, &, |, ^) can be used to combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "2217b60f", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Mask(\n", - " data=[[[True -- --]\n", - " [-- False False]\n", - " [True False --]]]\n", - "transform=| 0.33, 0.00, 0.00|\n", - " | 0.00,-0.33, 1.00|\n", - " | 0.00, 0.00, 1.00|\n", - "crs=None" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Logical bitwise operation between masks\n", - "mask = (raster > 100) & ((raster % 2) == 0)\n", - "mask" - ] - }, - { - "cell_type": "markdown", - "id": "3ff6fbb0", - "metadata": {}, - "source": [ - "## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask`" - ] - } - ], - "metadata": { - "file_format": "mystnb", - "kernelspec": { - "display_name": "geoutils", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - }, - "source_map": [ - 5, - 22, - 43, - 48, - 53, - 56, - 70, - 74, - 85, - 89 - ] - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file From 19035d97841448f1237c1bf0f231cca9375040c6 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 15 Feb 2023 16:34:21 -0800 Subject: [PATCH 026/184] Incremental commit on doc --- doc/source/core_array_funcs.md | 100 +++++++++++++++++++++++++++++++-- doc/source/core_composition.md | 38 +++++++++++-- doc/source/core_match_ref.md | 4 +- doc/source/core_py_ops.md | 24 ++++++-- doc/source/quick_start.md | 4 +- geoutils/georaster/raster.py | 14 ++--- 6 files changed, 160 insertions(+), 24 deletions(-) diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index 19ca7b1b2..cac7cddce 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -1,13 +1,105 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (core-array-funcs)= # Masked-array NumPy interface NumPy possesses an [array interface](https://numpy.org/doc/stable/reference/arrays.interface.html) that allows to properly map their functions on objects -that depend on a {class}`~numpy.ndarray`. +that depend on {class}`ndarrays`. -GeoUtils integrates this interface to work with any {class}`~geoutils.Raster` and their subclasses. +GeoUtils integrates this interface to work with all {class}`Rasters` and their subclasses. ## Universal functions -The first category of NumPy functions that can be applied are [universal functions](https://numpy.org/doc/stable/reference/ufuncs.html) and -most other NumPy array functions, while logically casting `dtypes` and respecting `.nodata` values. \ No newline at end of file +A first category of NumPy functions supported by {class}`Rasters` through the array interface is that of +[universal functions](https://numpy.org/doc/stable/reference/ufuncs.html), which operate on {class}`ndarrays` in an element-by-element +fashion. Examples of such functions are {func}`~numpy.add`, {func}`~numpy.absolute`, {func}`~numpy.isnan` or {func}`~numpy.sin`, and they number at more +than 90. + +Universal functions can take one or two inputs, and return one or two outputs. Through GeoUtils, as long as one of the two inputs is a {class}`Rasters`, +the output will be a {class}`~geoutils.Raster`. If there is a second input, it can be a {class}`~geoutils.Raster` or {class}`~numpy.ndarray` with +matching georeferencing or shape, respectively. + +These functions inherently support the casting of different {attr}`~geoutils.Raster.dtypes` and values masked by {attr}`~geoutils.Raster.nodata` in the +{class}`~numpy.ma.MaskedArray`. + +Below, we re-use the same example created in {ref}`core-py-ops`. + +```{code-cell} ipython3 +:tags: [hide-input, hide-output] + +import geoutils as gu +import rasterio as rio +import numpy as np + +# Create a random 3 x 3 masked array +np.random.seed(42) +arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") +mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") +ma = np.ma.masked_array(data=arr, mask=mask) + +# Create an example Raster with only a transform +raster = gu.Raster.from_array( + data = ma, + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = None + ) +raster +``` + +```{code-cell} ipython3 +# Universal function with a single input and output +np.sin(raster) +``` + +```{code-cell} ipython3 +# Universal function with a two inputs and single output +np.add(arr, raster) +``` + +```{code-cell} ipython3 +# Universal function with a single input and two outputs +np.modf(raster) +``` + +Similar to with Python operators, NumPy's [logical comparison functions](https://numpy.org/doc/stable/reference/ufuncs.html#comparison-functions) cast +{class}`Rasters` to a {class}`~geoutils.Mask`. + +```{code-cell} ipython3 +# Universal function with a single input and two outputs +np.greater(raster, raster + np.random.normal(size=np.shape(arr))) +``` + +## Array functions + +The second and last category of NumPy array functions supported by {class}`Rasters` through the array interface is that of array functions, +which are all other non-universal functions that can be applied to an array. Those function always modify the dimensionality of the output, such as +{func}`~numpy.mean`, {func}`~numpy.count_nonzero` or {func}`~numpy.nanmax`. Consequently, the output is the same as it would be with {class}`ndarrays`. + + +```{code-cell} ipython3 +# Traditional mathematical function +np.max(raster) +``` + +```{code-cell} ipython3 +# Masked-array function +np.ma.median(raster) +``` + +```{code-cell} ipython3 +# Expliciting an axis for reduction +np.count_nonzero(raster, axis=2) +``` + + +Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), +[masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). +A full list of supported array function is available in {attr}`geoutils.georaster.raster.handled_array_funcs`. + +## Respecting masked values + +TODO: finalize once masked-array recursion bug is fixed \ No newline at end of file diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index f79e15803..78377236b 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -1,10 +1,15 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (core-composition)= # Composition from Rasterio and GeoPandas ## A composition framework -GeoUtil's main classes {class}`~geoutils.Raster` and {class}`~geoutils.Vector` are linked to [Rasterio](https://rasterio.readthedocs.io/en/latest/) and +GeoUtils' main classes {class}`~geoutils.Raster` and {class}`~geoutils.Vector` are linked to [Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html), respectively, through class composition. They directly rely on their robust geospatial handling functionalities, as well of that of [PyProj](https://pyproj4.github.io/pyproj/stable/index.html), and @@ -14,15 +19,32 @@ add a layer on top for interfacing between rasters and vectors with higher-level The {class}`~geoutils.Raster` is a composition class with **four main attributes**: -1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, -2. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, -3. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, and +1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, +2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform` +3. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, and 4. a {class}`float` or {class}`int` as {attr}`~geoutils.Raster.nodata`. +```{code-cell} ipython3 +:tags: [hide-output] + +import geoutils as gu + +# Initiate a Raster from disk +raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +raster +``` + From these **four main attributes**, many other derivatives attributes exist, such as {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.res` to describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster. name` or {attr}`~geoutils.Raster.driver`. +```{code-cell} ipython3 +:tags: [hide-output] + +# Show summarized information +print(raster.info()) +``` + ```{important} The {class}`~geoutils.Raster` is not a composition of either a {class}`rasterio.DatasetReader`, a {class}`rasterio.MemoryFile` or a {class}`rasterio.DatasetWriter`. It is only linked to those objects to initiate a {class}`~geoutils.Raster` instance which first loads the metadata (notably the three main metadata attributes @@ -48,3 +70,11 @@ A {class}`~geoutils.Vector` is a composition class with a single main attribute: Because lazy loading is a lesser priority with vector data, a {class}`~geoutils.Vector` directly loads its {attr}`~geoutils.Vector.ds`. Besides, many higher-level geospatial methods are already available in {class}`~geopandas.GeoDataFrame`. We thus only wrap those directly into {class}`~geoutils.Vector`, in order to easily call them from the vector object, and build additional methods on top. + +```{code-cell} ipython3 +:tags: [hide-output] + +# Initiate a Vector from disk +vector = examples.get_path("exploradores_rgi_outlines") +vector +``` diff --git a/doc/source/core_match_ref.md b/doc/source/core_match_ref.md index f112c0b05..84b91eeab 100644 --- a/doc/source/core_match_ref.md +++ b/doc/source/core_match_ref.md @@ -18,10 +18,10 @@ operation. ## Matching with a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector` reference -The rules of using match-reference with a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector` are always the same. +The rules of using match-reference with a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector` are always the same: - If the **reference** passed is a {class}`~geoutils.Vector`, it can enforce a matching of its {attr}`~geoutils.Vector.bounds` and/or of its {attr}`~geoutils.Vector.crs` (its only two - georeferencing characteristics), + georeferencing attributes), - If the **reference** is a {class}`~geoutils.Raster`, it can also enforce a matching of any aspect of its {attr}`~geoutils.Raster.transform` (i.e, its {attr}`~geoutils.Raster.res`, {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.shape`) and/or of its {attr}`~geoutils.Raster.crs`. diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index 0fb8afe6b..b1cdc4d36 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -13,8 +13,8 @@ Pythonic operators work on {class}`~geoutils.Raster` much as they would on {clas ## Arithmetic of {class}`~geoutils.Raster` classes -Arithmetic operators (+, -, /, //, *, **, %) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, {class}`~numpy. -ndarray` or number. +Arithmetic operators (`+`, `-`, `/`, `//`, `*`, `**`, `%`) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, +{class}`~numpy.ndarray` or number. For an operation with another {class}`~geoutils.Raster`, the georeferencing ({attr}`~geoutils.Raster.crs` and {attr}`~geoutils.Raster.transform`) must match. For another {class}`~numpy.ndarray`, the {attr}`~geoutils.Raster.shape` must match. The operation always returns a {class}`~geoutils.Raster`. @@ -61,7 +61,7 @@ following [standard NumPy coercion rules](https://numpy.org/doc/stable/reference ## Logical comparisons cast to {class}`~geoutils.Mask` -Logical comparison operators (==, !=, >=, >, <=, <) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, +Logical comparison operators (`==`, `!=`, `>=`, `>`, `<=`, `<`) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. Those operation always return a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster` with a boolean {class}`~numpy.ma.MaskedArray` @@ -80,7 +80,7 @@ A {class}`~geoutils.Mask`'s {attr}`~geoutils.Raster.data` remains a {class}`~num ## Logical bitwise operations on {class}`~geoutils.Mask` -Logical bitwise operators (~, &, |, ^) can be used to combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`. +Logical bitwise operators (`~`, `&`, `|`, `^`) can be used to combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`. ```{code-cell} ipython3 # Logical bitwise operation between masks @@ -88,4 +88,18 @@ mask = (raster > 100) & ((raster % 2) == 0) mask ``` -## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask` \ No newline at end of file +## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask` + +Finally, indexing and index assignment operations (`[]`, `[] = `) are both supported by {class}`Rasters`. + +For indexing, they can be passed either a {class}`~geoutils.Mask` with the same georeferencing, or a boolean {class}`~numpy.ndarray` of the same shape. +For assignment, either a {class}`~geoutils.Raster` with the same georeferencing, or any {class}`~numpy.ndarray` of the same shape is expected. + +When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the indexed values of the {class}`~geoutils.Mask` **excluding those masked in its +{class}`~geoutils.Raster.data`'s {class}`~numpy.ma.MaskedArray` (i.e. nodata values present during logical comparison)**. To bypass this behaviour, simply +index without the mask using {attr}``. + +```{code-cell} ipython3 +# Indexing the raster with the previous mask +raster[mask] +``` \ No newline at end of file diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 6107d213f..37cb59e53 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -79,7 +79,7 @@ Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils ## Pythonic arithmetic and NumPy interface -All {class}`~geoutils.Raster` objects support Python arithmetic (+, -, /, //, *, **, %) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or +All {class}`~geoutils.Raster` objects support Python arithmetic (`+`, `-`, `/`, `//`, `*`, `**`, `%`) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. For other {class}`~geoutils.Raster`, the georeferencing must match, while only the shape for other {class}`~numpy.ndarray`. ```{literalinclude} code/index_example.py @@ -97,7 +97,7 @@ most other NumPy array functions, while logically casting `dtypes` and respectin ## Casting to {class}`~geoutils.Mask`, indexing and overload -All {class}`~geoutils.Raster` classes also support Python logical comparison operators (==, !=, >=, >, <=, <), or more complex NumPy logical functions. Those +All {class}`~geoutils.Raster` classes also support Python logical comparison operators (`==`, `!=`, `>=`, `>`, `<=`, `<`), or more complex NumPy logical functions. Those operations automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 874a7af28..f83e6feae 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -104,7 +104,7 @@ "equal", "not_equal", ] - +handled_array_funcs = _HANDLED_FUNCTIONS_1NIN + _HANDLED_FUNCTIONS_2NIN # Function to set the default nodata values for any given dtype # Similar to GDAL for int types, but without absurdly long nodata values for floats. @@ -595,9 +595,9 @@ def __repr__(self) -> str: # Align left spaces for multi-line object representation (arrays) after return to lines s = "Raster(\n"+\ " data=" + "\n ".join(self.data.__str__().split("\n"))+\ - "\ntransform="+ "\n ".join(self.transform.__str__().split("\n"))+\ - "\ncrs=" + self.crs.__str__()+\ - "\nnodata=" + self.nodata.__str__() + "\n transform="+ "\n ".join(self.transform.__str__().split("\n"))+\ + "\n crs=" + self.crs.__str__()+\ + "\n nodata=" + self.nodata.__str__()+")" return s # L = [getattr(self, item) for item in self._saved_attrs] @@ -1423,7 +1423,7 @@ def __array_function__( ) -> Any: """ Method to cast NumPy array function directly on a Raster object by applying it to the masked array. - A limited number of function is supported, listed in raster._HANDLED_FUNCTIONS. + A limited number of function is supported, listed in raster.handled_array_funcs. """ # If function is not implemented @@ -2839,8 +2839,8 @@ def __repr__(self) -> str: # Over-ride Raster's method to remove nodata value (always None) s = "Mask(\n"+\ " data=" + "\n ".join(self.data.__str__().split("\n"))+\ - "\ntransform="+ "\n ".join(self.transform.__str__().split("\n"))+\ - "\ncrs=" + self.crs.__str__() + "\n transform="+ "\n ".join(self.transform.__str__().split("\n"))+\ + "\n crs=" + self.crs.__str__()+")" return s From 0c223fceb59adaf4c91fcf5f2815c6b0fdde52e5 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 15 Feb 2023 18:16:30 -0800 Subject: [PATCH 027/184] Incremental commit on doc --- doc/source/core_inheritance.md | 39 ++++++++++++++++++++++++++++++++++ doc/source/core_lazy_load.md | 2 ++ doc/source/how_to_install.md | 2 +- geoutils/georaster/raster.py | 3 ++- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index da567ddaf..8ab532ba1 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -1,2 +1,41 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (core-inheritance)= # Inheritance to geo-images and beyond + +Inheritance is practical to naturally pass down parent methods and attributes to child classes. +In the case of {class}`Rasters`, many types of geospatial data exist with their own peculiarities, additional attributes, while +remaining a {class}`Rasters`. + +## Overview of {class}`~geoutils.Raster` inheritance + + +Below is a diagram showing current {class}`~geoutils.Raster` inheritance, which extends into other packages such as [xDEM](https://xdem.readthedocs.io/en/latest/index.html) +for analyzing digital elevation models. + +```{eval-rst} +.. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM + :top-classes: geoutils.georaster.raster.Raster +``` + +## The internal {class}`~geoutils.SatelliteImage` subclass + +GeoUtils subclasses {class}`Rasters` to {class}`SatelliteImages` for remote sensing users interested in parsing +metadata from space- or airborne imagery. + +Based on the filename, or auxiliary files, the {class}`~geoutils.SatelliteImage` class attempts to automatically parse a `.datetime`, `.sensor`, `tile_name`, +and other information. + + +```{code-cell} ipython3 +:tags: [hide-output] + +import geoutils as gu + +# Initiate a geo-image from disk +geoimg = gu.SatelliteImage(gu.examples.get_path("exploradores_aster_dem")) +geoimg +``` diff --git a/doc/source/core_lazy_load.md b/doc/source/core_lazy_load.md index e9e15c4d9..303719423 100644 --- a/doc/source/core_lazy_load.md +++ b/doc/source/core_lazy_load.md @@ -1,3 +1,5 @@ (core-lazy-load)= # Implicit lazy loading + +TODO \ No newline at end of file diff --git a/doc/source/how_to_install.md b/doc/source/how_to_install.md index a04d48d4c..ce1a51d39 100644 --- a/doc/source/how_to_install.md +++ b/doc/source/how_to_install.md @@ -28,7 +28,7 @@ Setting up GDAL and PROJ may require some extra steps, depending on your operati ## Installing for contributors -```shell +```bash git clone https://github.com/GlacioHack/xdem.git cd ./xdem mamba env create -f dev-environment.yml diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index f83e6feae..6f255da5f 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -593,7 +593,8 @@ def __repr__(self) -> str: """Convert formal Raster string representation.""" # Align left spaces for multi-line object representation (arrays) after return to lines - s = "Raster(\n"+\ + # And use class name for easier inheritance to subclasses (avoid overloading) + s = self.__class__.__name__+"(\n"+\ " data=" + "\n ".join(self.data.__str__().split("\n"))+\ "\n transform="+ "\n ".join(self.transform.__str__().split("\n"))+\ "\n crs=" + self.crs.__str__()+\ From dc5ada0bfce51829edfb29dcfa2f5110fe782694 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 16 Feb 2023 12:27:24 -0800 Subject: [PATCH 028/184] Incremental commit on doc --- doc/source/core_composition.md | 5 ++- doc/source/core_inheritance.md | 37 +++++++++++++++--- doc/source/core_lazy_load.md | 69 +++++++++++++++++++++++++++++++++- doc/source/core_match_ref.md | 8 ++-- doc/source/core_py_ops.md | 6 +-- geoutils/georaster/raster.py | 22 ++++++++--- 6 files changed, 127 insertions(+), 20 deletions(-) diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index 78377236b..e0eac186c 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -38,9 +38,12 @@ From these **four main attributes**, many other derivatives attributes exist, su describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster. name` or {attr}`~geoutils.Raster.driver`. +```{note} +By default, {attr}`~geoutils.Raster.data` is not loaded during instantiation. See {ref}`core-lazy-load` for more details. +``` + ```{code-cell} ipython3 :tags: [hide-output] - # Show summarized information print(raster.info()) ``` diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 8ab532ba1..1c7ecc8c8 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -8,7 +8,7 @@ kernelspec: Inheritance is practical to naturally pass down parent methods and attributes to child classes. In the case of {class}`Rasters`, many types of geospatial data exist with their own peculiarities, additional attributes, while -remaining a {class}`Rasters`. +remaining {class}`Rasters` that benefit from methods implemented in GeoUtils. ## Overview of {class}`~geoutils.Raster` inheritance @@ -21,6 +21,15 @@ for analyzing digital elevation models. :top-classes: geoutils.georaster.raster.Raster ``` +```{note} +The {class}`~xdem.DEM` class re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) (and more) to derive topographic attributes +(hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same results. +Among others, it also adds a {attr}`~xdem.DEM.vref` property to consistently manage vertical referencing (ellipsoid, geoids). + +If you are DEM-enthuisiastic, **[check-out our sister-package xDEM](https://xdem.readthedocs.io/en/latest/index.html)** for the analysis of digital +elevation models. +``` + ## The internal {class}`~geoutils.SatelliteImage` subclass GeoUtils subclasses {class}`Rasters` to {class}`SatelliteImages` for remote sensing users interested in parsing @@ -29,13 +38,31 @@ metadata from space- or airborne imagery. Based on the filename, or auxiliary files, the {class}`~geoutils.SatelliteImage` class attempts to automatically parse a `.datetime`, `.sensor`, `tile_name`, and other information. +```{code-cell} ipython3 +import geoutils as gu + +# Initiate a geo-image from an ASTER image +geoimg = gu.SatelliteImage(gu.examples.get_path("exploradores_aster_dem"), silent=False) +``` ```{code-cell} ipython3 -:tags: [hide-output] +# Initiate a geo-image from a Landsat 7 image +geoimg2 = gu.SatelliteImage(gu.examples.get_path("everest_landsat_b4"), silent=False) +``` -import geoutils as gu +Along these additional attributes, the {class}`~geoutils.SatelliteImage` possesses the same main attributes as a {class}`~geoutils.Raster`. + +```{code-cell} ipython3 -# Initiate a geo-image from disk -geoimg = gu.SatelliteImage(gu.examples.get_path("exploradores_aster_dem")) +# The geo-image main attributes geoimg ``` + +## And beyond + +Many types of geospatial data can be viewed as a subclass of {class}`Rasters`, which have more attributes and require their own methods: +**spectral images**, **velocity fields**, **phase difference maps**, etc... + +If you are interested to build your own subclass of {class}`~geoutils.Raster`, you can take example of the structure of {class}`geoutils.SatelliteImage` and +{class}`xdem.DEM`. Then, just add any of your own attributes and methods, and overload parent methods if necessary! Don't hesitate to reach out on GitHub if +you have a subclassing project. diff --git a/doc/source/core_lazy_load.md b/doc/source/core_lazy_load.md index 303719423..fadecb7b2 100644 --- a/doc/source/core_lazy_load.md +++ b/doc/source/core_lazy_load.md @@ -1,5 +1,72 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (core-lazy-load)= # Implicit lazy loading -TODO \ No newline at end of file +## Lazy instantiation of {class}`Rasters` + +By default, GeoUtils instantiate a {class}`~geoutils.Raster` from an **on-disk** file without loading its {attr}`geoutils.Raster.data` array. It only loads its +metadata ({attr}`~geoutils.Raster.transform`, {attr}`~geoutils.Raster.crs`, {attr}`~geoutils.Raster.nodata` and derivatives, as well as +{attr}`~geoutils.Raster.name` and {attr}`~geoutils.Raster.driver`). + +```{code-cell} ipython3 + +import geoutils as gu + +# Initiate a Raster from disk +raster = gu.Raster(gu.examples.get_path("everest_landsat_b4")) + +# This Raster is not loaded +raster +``` + +To load the data explicity during instantiation opening, `load_data=True` can be passed to {class}`~geoutils.Raster`. Or the {func}`~geoutils.Raster.load` +method can be called after. The two are equivalent. + +```{code-cell} ipython3 +# Initiate another Raster just for the purpose of loading +raster_toload = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +raster_toload.load() + +# This Raster is loaded +raster_toload +``` + +## Lazy passing of georeferencing metadata + +Operations relying on georeferencing metadata of {class}`Rasters` or {class}`Vectors` are always done by respecting the +possible lazy loading of the objects. + +For instance, using any {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a match-reference for a geospatial operation (see {ref}`core-match-ref`) will +always conserve the lazy loading of that match-reference object. + +```{code-cell} ipython3 +# Use a smaller Raster as reference to crop the initial one +smaller_raster = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) +raster.crop(smaller_raster) + +# The reference Raster is not loaded +smaller_raster +``` + +## Optimized geospatial subsetting + +```{important} +These features are a work in progress, we aim to make GeoUtils more lazy-friendly through [Dask](https://docs.dask.org/en/stable/) in future versions of the +package! +``` + +Some georeferencing operations be done without loading the entire array Right now, relying directly on Rasterio, GeoUtils supports optimized subsetting +through the {func}`~geoutils.Raster.`crop` method. + +```{code-cell} ipython3 +# The previously cropped Raster was loaded without accessing the entire array +raster +``` + + + diff --git a/doc/source/core_match_ref.md b/doc/source/core_match_ref.md index 84b91eeab..37ada2cb5 100644 --- a/doc/source/core_match_ref.md +++ b/doc/source/core_match_ref.md @@ -18,7 +18,7 @@ operation. ## Matching with a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector` reference -The rules of using match-reference with a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector` are always the same: +The rules of using match-reference with {class}`Rasters` or {class}`Vectors` are always the same: - If the **reference** passed is a {class}`~geoutils.Vector`, it can enforce a matching of its {attr}`~geoutils.Vector.bounds` and/or of its {attr}`~geoutils.Vector.crs` (its only two georeferencing attributes), @@ -29,7 +29,7 @@ Which of these attributes are eventually used to enforce the matching **depends ## Geospatial handling rules for match-reference -Core "handling" methods all support match-reference, and **always enforce the same georeferencing attributes** for either {class}`~geoutils.Raster` +Geospatial handling methods all support match-reference, and **always enforce the same georeferencing attributes** for either {class}`~geoutils.Raster` or {class}`~geoutils.Vector`: ```{list-table} @@ -57,10 +57,10 @@ or {class}`~geoutils.Vector`: 1Because a {class}`~geoutils.Vector` only possesses the {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Raster`'s {attr}`~geoutils.Raster.transform`. -## Other operations supporting match-reference arguments +## Other operations supporting match-reference There are **other geospatial operation that also support match-reference arguments**. Unlike the geospatial handling methods described above, these do not aim -at modifying the georeferencing of a {class}`~geoutils.Raster` or a {class}`~geoutils.Vector`. Instead, they simply require the georeferencing metadata. +at modifying the georeferencing of {class}`Rasters` or {class}`Vectors`. Instead, they simply require the georeferencing metadata. ### From vector to raster diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index b1cdc4d36..7ceb122c7 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -9,7 +9,7 @@ kernelspec: GeoUtils integrates most of Python's operators for shorter, more intuitive code to consistently perform arithmetic and logical operations. -Pythonic operators work on {class}`~geoutils.Raster` much as they would on {class}`~numpy.ndarray`, with some more details. +Pythonic operators work on {class}`Rasters` much as they would on {class}`ndarrays`, with some more details. ## Arithmetic of {class}`~geoutils.Raster` classes @@ -96,8 +96,8 @@ For indexing, they can be passed either a {class}`~geoutils.Mask` with the same For assignment, either a {class}`~geoutils.Raster` with the same georeferencing, or any {class}`~numpy.ndarray` of the same shape is expected. When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the indexed values of the {class}`~geoutils.Mask` **excluding those masked in its -{class}`~geoutils.Raster.data`'s {class}`~numpy.ma.MaskedArray` (i.e. nodata values present during logical comparison)**. To bypass this behaviour, simply -index without the mask using {attr}``. +{class}`~geoutils.Raster.data`'s {class}`~numpy.ma.MaskedArray` (for instance, nodata values present during a previous logical comparison)**. To bypass this +behaviour, simply index without the mask using {attr}`Mask.data.data`. ```{code-cell} ipython3 # Indexing the raster with the previous mask diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 6f255da5f..151b54b4e 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -265,7 +265,7 @@ def __init__( | rio.io.MemoryFile | dict[str, Any], indexes: None | int | list[int] = None, - load_data: bool = True, + load_data: bool = False, downsample: AnyNumber = 1, masked: bool = True, nodata: int | float | list[int] | list[float] | None = None, @@ -278,7 +278,7 @@ def __init__( :param indexes: The band(s) to load into the object. Default is to load all bands. - :param load_data: Load the raster data into the object. Default is True. + :param load_data: Load the raster data into the object. Default is False. :param downsample: Reduce the size of the image loaded by this factor. Default is 1. @@ -592,10 +592,16 @@ def to_rio_dataset(self) -> rio.io.DatasetReader: def __repr__(self) -> str: """Convert formal Raster string representation.""" - # Align left spaces for multi-line object representation (arrays) after return to lines - # And use class name for easier inheritance to subclasses (avoid overloading) + # If data not loaded, return and string and avoid calling .data + if not self.is_loaded: + str_data = "not_loaded" + else: + str_data = "\n ".join(self.data.__str__().split("\n")) + + # Above and below, we align left spaces for multi-line object representation (arrays) after return to lines + # And call the class name for easier inheritance to subclasses (avoid overloading to just rewrite subclass name) s = self.__class__.__name__+"(\n"+\ - " data=" + "\n ".join(self.data.__str__().split("\n"))+\ + " data=" + str_data +\ "\n transform="+ "\n ".join(self.transform.__str__().split("\n"))+\ "\n crs=" + self.crs.__str__()+\ "\n nodata=" + self.nodata.__str__()+")" @@ -970,6 +976,9 @@ def is_modified(self) -> bool: :returns: True if Raster has been modified. """ + if not self.is_loaded: + return False + if not self._is_modified: new_hash = hash( (self._data.tobytes() if self._data is not None else 0, self.transform, self.crs, self.nodata) @@ -1259,7 +1268,8 @@ def info(self, stats: bool = False) -> str: f"Driver: {self.driver} \n", f"Opened from file: {self.filename} \n", f"Filename: {self.name} \n", - f"Raster modified since disk load? {self._is_modified} \n", + f"Loaded? {self.is_loaded} \n", + f"Modified since load? {self.is_modified} \n", f"Size: {self.width}, {self.height}\n", f"Number of bands: {self.count:d}\n", f"Data types: {self.dtypes}\n", From fbf8af30752868cf7795e2e9e2a4e35e9bb8ff97 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 16 Feb 2023 23:12:41 -0800 Subject: [PATCH 029/184] Incremental commit on documentation --- doc/source/api.md | 2 + doc/source/conf.py | 1 + doc/source/core_array_funcs.md | 8 +- doc/source/core_composition.md | 4 +- doc/source/core_inheritance.md | 11 +- doc/source/core_lazy_load.md | 4 + doc/source/core_match_ref.md | 2 + doc/source/core_py_ops.md | 24 +++-- doc/source/quick_start.md | 9 +- doc/source/raster_class.md | 191 +++++++++++++++++++++++++-------- geoutils/georaster/raster.py | 43 ++++---- 11 files changed, 215 insertions(+), 84 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index e6da9c7ce..530dc6a8a 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -35,6 +35,8 @@ documentation. Raster.from_array ``` +(api-raster-attrs)= + ### Unique attributes ```{eval-rst} diff --git a/doc/source/conf.py b/doc/source/conf.py index 58e16bca2..8d975cffe 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -43,6 +43,7 @@ nb_kernel_rgx_aliases = {".*geoutils.*": "python3"} intersphinx_mapping = { + "python": ("https://docs.python.org/", None), "rasterio": ("https://rasterio.readthedocs.io/en/latest", None), "numpy": ("https://numpy.org/doc/stable", None), "matplotlib": ("https://matplotlib.org/stable", None), diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index cac7cddce..b3148d5d3 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -10,7 +10,7 @@ kernelspec: NumPy possesses an [array interface](https://numpy.org/doc/stable/reference/arrays.interface.html) that allows to properly map their functions on objects that depend on {class}`ndarrays`. -GeoUtils integrates this interface to work with all {class}`Rasters` and their subclasses. +GeoUtils utilizes this interface to work with all {class}`Rasters` and their subclasses. ## Universal functions @@ -33,6 +33,7 @@ Below, we re-use the same example created in {ref}`core-py-ops`. import geoutils as gu import rasterio as rio +import pyproj import numpy as np # Create a random 3 x 3 masked array @@ -41,11 +42,12 @@ arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") ma = np.ma.masked_array(data=arr, mask=mask) -# Create an example Raster with only a transform +# Create an example Raster raster = gu.Raster.from_array( data = ma, transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), - crs = None + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 ) raster ``` diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index e0eac186c..c7224b5e8 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -7,8 +7,6 @@ kernelspec: # Composition from Rasterio and GeoPandas -## A composition framework - GeoUtils' main classes {class}`~geoutils.Raster` and {class}`~geoutils.Vector` are linked to [Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html), respectively, through class composition. @@ -36,7 +34,7 @@ raster From these **four main attributes**, many other derivatives attributes exist, such as {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.res` to describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster. -name` or {attr}`~geoutils.Raster.driver`. +name` or {attr}`~geoutils.Raster.driver`, see {ref}`raster-class` for a summary, or the {ref}`dedicated sections of the API` for a full listing. ```{note} By default, {attr}`~geoutils.Raster.data` is not loaded during instantiation. See {ref}`core-lazy-load` for more details. diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 1c7ecc8c8..bd59916d5 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -6,9 +6,10 @@ kernelspec: (core-inheritance)= # Inheritance to geo-images and beyond -Inheritance is practical to naturally pass down parent methods and attributes to child classes. -In the case of {class}`Rasters`, many types of geospatial data exist with their own peculiarities, additional attributes, while -remaining {class}`Rasters` that benefit from methods implemented in GeoUtils. +Inheritance is practical to naturally pass down parent methods and attributes to child classes. + +Many subtypes of {class}`Rasters` geospatial data exist that require additional attributes and methods, yet might benefit from methods +implemented in GeoUtils. ## Overview of {class}`~geoutils.Raster` inheritance @@ -64,5 +65,5 @@ Many types of geospatial data can be viewed as a subclass of {class}`Rasters` By default, GeoUtils instantiate a {class}`~geoutils.Raster` from an **on-disk** file without loading its {attr}`geoutils.Raster.data` array. It only loads its diff --git a/doc/source/core_match_ref.md b/doc/source/core_match_ref.md index 37ada2cb5..098593a68 100644 --- a/doc/source/core_match_ref.md +++ b/doc/source/core_match_ref.md @@ -1,6 +1,8 @@ (core-match-ref)= # Match-reference functionality +Match-reference functionalities lie at the core of GeoUtils' focus to provide accessible and intuitive tools for end-user geospatial analysis. + ## What is match-reference ? Match-reference means to **match** the georeferencing of a dataset to that of another **reference** dataset. diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index 7ceb122c7..08d1ff3f7 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -7,13 +7,14 @@ kernelspec: (core-py-ops)= # Support of pythonic operators -GeoUtils integrates most of Python's operators for shorter, more intuitive code to consistently perform arithmetic and logical operations. +GeoUtils integrates pythonic operators for shorter, more intuitive code, and to perform arithmetic and logical operations consistently. -Pythonic operators work on {class}`Rasters` much as they would on {class}`ndarrays`, with some more details. +These operators work on {class}`Rasters` much as they would on {class}`ndarrays`, with some more details. ## Arithmetic of {class}`~geoutils.Raster` classes -Arithmetic operators (`+`, `-`, `/`, `//`, `*`, `**`, `%`) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, +Arithmetic operators ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, +{func}`**`, {func}`%`) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. For an operation with another {class}`~geoutils.Raster`, the georeferencing ({attr}`~geoutils.Raster.crs` and {attr}`~geoutils.Raster.transform`) must match. @@ -22,6 +23,7 @@ For another {class}`~numpy.ndarray`, the {attr}`~geoutils.Raster.shape` must mat ```{code-cell} ipython3 import geoutils as gu import rasterio as rio +import pyproj import numpy as np # Create a random 3 x 3 masked array @@ -30,11 +32,12 @@ arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") ma = np.ma.masked_array(data=arr, mask=mask) -# Create an example Raster with only a transform +# Create an example Raster raster = gu.Raster.from_array( data = ma, transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), - crs = None + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 ) raster @@ -61,8 +64,9 @@ following [standard NumPy coercion rules](https://numpy.org/doc/stable/reference ## Logical comparisons cast to {class}`~geoutils.Mask` -Logical comparison operators (`==`, `!=`, `>=`, `>`, `<=`, `<`) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, -{class}`~numpy.ndarray` or number. +Logical comparison operators ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, +{func}`<`) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or +number. Those operation always return a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster` with a boolean {class}`~numpy.ma.MaskedArray` as {class}`~geoutils.Raster.data`. @@ -80,7 +84,8 @@ A {class}`~geoutils.Mask`'s {attr}`~geoutils.Raster.data` remains a {class}`~num ## Logical bitwise operations on {class}`~geoutils.Mask` -Logical bitwise operators (`~`, `&`, `|`, `^`) can be used to combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`. +Logical bitwise operators ({func}`~ `, {func}`& `, {func}`| `, {func}`^ `) can be used to +combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`. ```{code-cell} ipython3 # Logical bitwise operation between masks @@ -90,7 +95,8 @@ mask ## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask` -Finally, indexing and index assignment operations (`[]`, `[] = `) are both supported by {class}`Rasters`. +Finally, indexing and index assignment operations ({func}`[] `, {func}`[]= `) are both supported by +{class}`Rasters`. For indexing, they can be passed either a {class}`~geoutils.Mask` with the same georeferencing, or a boolean {class}`~numpy.ndarray` of the same shape. For assignment, either a {class}`~geoutils.Raster` with the same georeferencing, or any {class}`~numpy.ndarray` of the same shape is expected. diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 37cb59e53..47af00aeb 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -79,7 +79,8 @@ Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils ## Pythonic arithmetic and NumPy interface -All {class}`~geoutils.Raster` objects support Python arithmetic (`+`, `-`, `/`, `//`, `*`, `**`, `%`) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or +All {class}`~geoutils.Raster` objects support Python arithmetic ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, +{func}`**`, {func}`%`) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. For other {class}`~geoutils.Raster`, the georeferencing must match, while only the shape for other {class}`~numpy.ndarray`. ```{literalinclude} code/index_example.py @@ -97,8 +98,8 @@ most other NumPy array functions, while logically casting `dtypes` and respectin ## Casting to {class}`~geoutils.Mask`, indexing and overload -All {class}`~geoutils.Raster` classes also support Python logical comparison operators (`==`, `!=`, `>=`, `>`, `<=`, `<`), or more complex NumPy logical functions. Those -operations automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. +All {class}`~geoutils.Raster` classes also support Python logical comparison operators ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, +{func}`<`), or more complex NumPy logical functions. Those operations automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. ```{literalinclude} code/index_example.py @@ -106,7 +107,7 @@ operations automatically casts them into a {class}`~geoutils.Mask`, a subclass o :language: python ``` -Masks can then be used for indexing a {class}`~geoutils.Raster`. +Masks can then be used for indexing a {class}`~geoutils.Raster`, which returns a {class}`~numpy.ma.MaskedArray` of indexed values. ```{literalinclude} code/index_example.py :lines: 32-33 diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index d80855c36..a17eba632 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -1,84 +1,191 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (raster-class)= -# The georeferenced raster (`Raster`) +# The georeferenced raster ({class}`~geoutils.Raster`) -## Object definition +Below, a summary of the {class}`~geoutils.Raster` object and its methods. -A {class}`geoutils.Raster` is a georeferenced raster read, written or reprojected by `rasterio`. +(raster-obj-def)= -It contains: -- an array `.data` as a {class}`numpy.ma.MaskedArray`, -- a geotransform `.transform` as a {class}`affine.Affine`, -- a coordinate reference system (CRS) `.crs` as a {class}`pyproj.CRS`, -- a nodata value `.nodata` as ``float`` or ``int``. +## Object definition and attributes +A {class}`~geoutils.Raster` contains **four main attributes**: + +1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, +2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform` +3. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, and +4. a {class}`float` or {class}`int` as {attr}`~geoutils.Raster.nodata`. + +For more details on {class}`~geoutils.Raster` class composition, see {ref}`core-composition`. + +A {class}`~geoutils.Raster` also contains many derivative attributes, with naming generally consistent with that of a {class}`rasterio.DatasetReader`. + +A first category includes georeferencing attributes directly derived from {attr}`~geoutils.Raster.transform`, namely: {attr}`~geoutils.Raster.shape`, +{attr}`~geoutils.Raster.height`, {attr}`~geoutils.Raster.width`, {attr}`~geoutils.Raster.res`, {attr}`~geoutils.Raster.bounds`. + +A second category concerns the attributes derived from the raster array shape and type: {attr}`~geoutils.Raster.count`, {attr}`~geoutils.Raster.indexes` and +{attr}`~geoutils.Raster.dtypes`. The two former refer to the number of bands loaded in a {class}`~geoutils.Raster`, and their indexes. + +```{important} +The {attr}`~geoutils.Raster.indexes` of {class}`rasterio.DatasetReader` start from 1 and not 0, be careful when instantiating or loading from a +multi-band raster! +``` + +Finally, the remaining attributes are only relevant when instantiating from a **on-disk** file: {attr}`~geoutils.Raster.name`, {attr}`~geoutils.Raster.driver`, +{attr}`~geoutils.Raster.count_on_disk`, {attr}`~geoutils.Raster.indexes_on_disk`, {attr}`~geoutils.Raster.is_loaded` and {attr}`~geoutils.Raster.is_modified`. + + +```{note} +The {attr}`~geoutils.Raster.count` and {attr}`~geoutils.Raster.indexes` attributes always exist, while {attr}`~geoutils.Raster.count_on_disk` and +{attr}`~geoutils.Raster.indexes_on_disk` only refers to the number of bands on the **on-disk** dataset, if it exists. + +For example, {attr}`~geoutils.Raster.count` and {attr}`~geoutils.Raster.count_on_disk` will differ when a single band is loaded from a +3-band **on-disk** file, by passing a single index to the `indexes` argument in {class}`~geoutils.Raster` or {func}`~geoutils.Raster.load`. +``` + +The complete list of {class}`~geoutils.Raster` attributes with description is available in {ref}`dedicated sections of the API`. ## Open and save -```{literalinclude} code/raster-basics_open_file.py -:lines: 2-8 +A {class}`~geoutils.Raster` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path` or a {class}`rasterio.DatasetReader`. + + +```{code-cell} ipython3 +import geoutils as gu + +# Initiate a raster from disk +raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +raster ``` -````{margin} -**Example of raster from {func}`~geoutils.Raster.info`:** +Detailed information on the {class}`~geoutils.Raster` is printed using {class}`~geoutils.Raster.info()`, along with basic statistics using `stats=True`: -```{literalinclude} code/index_rast_info.py - :lines: 11-12 - :language: python +```{code-cell} ipython3 +# Print details of raster +print(raster.info(stats=True)) ``` -```{eval-rst} -.. program-output:: $PYTHON code/index_rast_info.py - :shell: + +```{note} +Calling {class}`~geoutils.Raster.info()` with `stats=True` automatically loads the array in-memory, like any other operation calling {attr}`~geoutils.Raster.data`. ``` -```` +A {class}`~geoutils.Raster` is saved to file by calling {func}`~geoutils.Raster.save` with a {class}`str` or a {class}`pathlib.Path`. -To print information directly to your console: +```{code-cell} ipython3 +# Save raster to disk +raster.save("myraster.tif") +``` +```{code-cell} ipython3 +:tags: [remove-cell] +import os +os.remove("myraster.tif") +``` -```python -print(image) +## Create from array + +A {class}`~geoutils.Raster` is created from an array by calling the class method {func}`~geoutils.Raster.from_array` and passing the +{ref}`four main attributes`. + +```{code-cell} ipython3 +import rasterio as rio +import pyproj +import numpy as np + +# Create a random 3 x 3 masked array +np.random.seed(42) +arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") +mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") +ma = np.ma.masked_array(data=arr, mask=mask) + +# Create a Raster from array +raster = gu.Raster.from_array( + data = ma, + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 + ) +raster ``` -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_open_file.py').read()); print(image)" - :shell: +```{important} +The {attr}`~geoutils.Raster.data` attribute can be passed as an unmasked {class}`~numpy.ndarray`. That array will be converted to a {class}`~numpy.ma.MaskedArray` +with a {class}`~numpy.ma.MaskedArray.mask` on all {class}`~numpy.nan` and {class}`~numpy.inf` in the {attr}`~geoutils.Raster.data`, and on all values +matching the {attr}`~geoutils.Raster.nodata` value passed to {func}`~geoutils.Raster.from_array`. ``` -If you'd like to retrieve a string of information about the raster to be saved -to a variable, output to a text file etc: +## Get array -```{literalinclude} code/raster-basics_open_file.py -:lines: 10 +The array of a {class}`~geoutils.Raster` is available in {class}`~geoutils.Raster.data` as a {class}`~numpy.ma.MaskedArray`. + +```{code-cell} ipython3 +# Get raster's masked-array +raster.data ``` -With added stats: +For those less familiar with {class}`MaskedArrays` and the associated functions in NumPy, an unmasked {class}`~numpy.ndarray` filled with +{class}`~numpy.nan` on masked values can be extracted using {func}`~geoutils.Raster.get_nanarray`. -```{literalinclude} code/raster-basics_open_file.py -:lines: 12 +```{code-cell} ipython3 +# Get raster's nan-array +raster.get_nanarray() ``` -Then to write a file: +```{important} +Getting a {class}`~numpy.ndarray` filled with {class}`~numpy.nan` will automatically cast the {class}`dtype` to {class}`numpy.float32`. This +might result in larger memory usage than in the original {class}`~geoutils.Raster` (if of {class}`int` type). + +Thanks to the {ref}`core-array-funcs`, **NumPy functions applied directly to a {class}`~geoutils.Raster` will respect {class}`~geoutils.Raster.nodata` +values** as well as if computing with the {class}`~numpy.ma.MaskedArray` or an unmasked {class}`~numpy.ndarray` filled with {class}`~numpy.nan`. -```{literalinclude} code/raster-basics_open_file.py -:lines: 14-15 +Additionally, the {class}`~geoutils.Raster` will automatically cast between different {class}`dtypes`, and possibly re-define missing +{class}`nodatas`. ``` -Or just print nicely to console: +## Arithmetic -```python -print(information) +A {class}`~geoutils.Raster` can be applied any pythonic arithmetic operation ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, +{func}`**`, {func}`%`) with another {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. It will output one or two {class}`Rasters`. NumPy coercion rules apply for {class}`dtypes`. + +```{code-cell} ipython3 +# Add 1 and divide raster by 2 +(raster + 1)/2 ``` -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_open_file.py').read()); print(information)" - :shell: +A {class}`~geoutils.Raster` can also be applied any pythonic logical comparison operation ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, +{func}`<`) with another {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. It will cast to a {class}`geoutils.Mask`. + +```{code-cell} ipython3 +# What raster pixels are less than 100? +raster < 100 ``` -## Arithmetic +See {ref}`core-py-ops` for more details. ## Array interface +A {class}`~geoutils.Raster` can be applied any NumPy universal functions and most mathematical, logical or masked-array functions with another +{class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. + +```{code-cell} ipython3 +# Compute the element-wise sqrt +np.sqrt(raster) +``` + +Logical comparison functions will cast to a {class}`geoutils.Mask`. + +```{code-cell} ipython3 +# Is the raster close to another within tolerance? +np.isclose(raster, raster+0.05, atol=0.1) +``` + +See {ref}`core-array-funcs` for more details. + + ## Crop Comparing multiple rasters can often be a burden if multiple coordinate systems, bounding boxes, and resolutions are involved. diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 151b54b4e..18e118608 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1281,22 +1281,24 @@ def info(self, stats: bool = False) -> str: ] if stats: - if self.is_loaded: - if self.count == 1: - as_str.append(f"[MAXIMUM]: {np.nanmax(self.data):.2f}\n") - as_str.append(f"[MINIMUM]: {np.nanmin(self.data):.2f}\n") - as_str.append(f"[MEDIAN]: {np.ma.median(self.data):.2f}\n") - as_str.append(f"[MEAN]: {np.nanmean(self.data):.2f}\n") - as_str.append(f"[STD DEV]: {np.nanstd(self.data):.2f}\n") - else: - for b in range(self.count): - # try to keep with rasterio convention. - as_str.append(f"Band {b + 1}:") - as_str.append(f"[MAXIMUM]: {np.nanmax(self.data[b, :, :]):.2f}\n") - as_str.append(f"[MINIMUM]: {np.nanmin(self.data[b, :, :]):.2f}\n") - as_str.append(f"[MEDIAN]: {np.ma.median(self.data[b, :, :]):.2f}\n") - as_str.append(f"[MEAN]: {np.nanmean(self.data[b, :, :]):.2f}\n") - as_str.append(f"[STD DEV]: {np.nanstd(self.data[b, :, :]):.2f}\n") + if not self.is_loaded: + self.load() + + if self.count == 1: + as_str.append(f"[MAXIMUM]: {np.nanmax(self.data):.2f}\n") + as_str.append(f"[MINIMUM]: {np.nanmin(self.data):.2f}\n") + as_str.append(f"[MEDIAN]: {np.ma.median(self.data):.2f}\n") + as_str.append(f"[MEAN]: {np.nanmean(self.data):.2f}\n") + as_str.append(f"[STD DEV]: {np.nanstd(self.data):.2f}\n") + else: + for b in range(self.count): + # try to keep with rasterio convention. + as_str.append(f"Band {b + 1}:") + as_str.append(f"[MAXIMUM]: {np.nanmax(self.data[b, :, :]):.2f}\n") + as_str.append(f"[MINIMUM]: {np.nanmin(self.data[b, :, :]):.2f}\n") + as_str.append(f"[MEDIAN]: {np.ma.median(self.data[b, :, :]):.2f}\n") + as_str.append(f"[MEAN]: {np.nanmean(self.data[b, :, :]):.2f}\n") + as_str.append(f"[STD DEV]: {np.nanstd(self.data[b, :, :]):.2f}\n") return "".join(as_str) @@ -1345,8 +1347,13 @@ def get_nanarray(self, return_mask: bool = False) -> np.ndarray | tuple[np.ndarr :returns Array with masked data as NaNs, (Optional) Mask of valid data """ - # Get the array with masked value fill with NaNs - nanarray = self.data.filled(fill_value=np.nan).squeeze() + # Cast array to float32 is its dtype is integer (cannot be filled with NaNs otherwise) + if "int" in str(self.data.dtype): + # Get the array with masked value fill with NaNs + nanarray = self.data.astype("float32").filled(fill_value=np.nan).squeeze() + else: + # Same here + nanarray = self.data.filled(fill_value=np.nan).squeeze() # The function np.ma.filled() only returns a copy if the array is masked, copy the array if it's not the case if not np.ma.is_masked(self.data): From 98d643840ae32dcc2a3dd156332663eee948f537 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 16 Feb 2023 23:43:49 -0800 Subject: [PATCH 030/184] Incremental commit on doc --- doc/source/api.md | 2 ++ doc/source/raster_class.md | 67 ++++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index 530dc6a8a..5d4dbe11a 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -71,6 +71,8 @@ documentation. Raster.driver ``` +(api-geo-handle)= + ### Geospatial handling methods ```{eval-rst} diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index a17eba632..f99a34725 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -157,7 +157,7 @@ A {class}`~geoutils.Raster` can be applied any pythonic arithmetic operation ({f ``` A {class}`~geoutils.Raster` can also be applied any pythonic logical comparison operation ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, -{func}`<`) with another {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. It will cast to a {class}`geoutils.Mask`. +{func}`<`) with another {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. It will cast to a {class}`~geoutils.Mask`. ```{code-cell} ipython3 # What raster pixels are less than 100? @@ -176,7 +176,7 @@ A {class}`~geoutils.Raster` can be applied any NumPy universal functions and mos np.sqrt(raster) ``` -Logical comparison functions will cast to a {class}`geoutils.Mask`. +Logical comparison functions will cast to a {class}`~geoutils.Mask`. ```{code-cell} ipython3 # Is the raster close to another within tolerance? @@ -185,55 +185,52 @@ np.isclose(raster, raster+0.05, atol=0.1) See {ref}`core-array-funcs` for more details. +## Reproject -## Crop - -Comparing multiple rasters can often be a burden if multiple coordinate systems, bounding boxes, and resolutions are involved. -The {class}`geoutils.Raster` class simplifies this using two methods: `Raster.crop()` and `Raster.reproject()`. +Reprojecting a {class}`~geoutils.Raster` means to enforce a new {attr}`~geoutils.Raster.transform` and/or {class}`~geoutils.Raster.crs`. This is done by the +{func}`~geoutils.Raster.reproject` function. -{func}`geoutils.Raster.crop` +```{important} +As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils. +Vector` as argument. -If a large raster should be cropped to a smaller extent without changing the uncropped data, this is possible through the crop function. +A {class}`~geoutils.Raster` reference will enforce to match its {attr}`~geoutils.Raster.transform` and {class}`~geoutils.Raster.crs`. +A {class}`~geoutils.Vector` reference will enforce to match its {attr}`~geoutils.Vector.bounds` and {class}`~geoutils.Vector.crs`. -```{literalinclude} code/raster-basics_cropping_and_reprojecting.py -:lines: 4-6 +See {ref}`core-match-ref` for more details. ``` -```python -print(large_image.shape) +The {func}`~geoutils.Raster.reproject` function can also be passed any individual arguments such as `dst_bounds`, to enforce specific georeferencing +attributes. For more details, see the {ref}`specific section and function descriptions in the API`. -print(smaller_image.shape) +```{code-cell} ipython3 +# Original bounds and resolution +print(raster.res) +print(raster.bounds) ``` -prints: - -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_cropping_and_reprojecting.py').read());print(large_image_orig.shape);print(smaller_image.shape)" - :shell: +```{code-cell} ipython3 +# Reproject to smaller bounds and higher resolution +raster = raster.reproject( + dst_res=0.25, + dst_bounds={"left": 0, "bottom": 0, "right": 0.75, "top": 0.75}, + resampling="cubic") +raster ``` -If we want to crop the larger image to fit the smaller one: - -```{literalinclude} code/raster-basics_cropping_and_reprojecting.py -:lines: 9 +```{code-cell} ipython3 +# New bounds and resolution +print(raster.res) +print(raster.bounds) ``` -Now, they have the same shape, and can be compared directly: - -```python -print(large_image.shape) - -print(smaller_image.shape) +```{note} +In GeoUtils, `"bilinear"` is the default resampling method. A simple {class}`str` matching the naming of a {class}`rasterio.enums.Resampling` method can be +passed. ``` -prints: -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/raster-basics_cropping_and_reprojecting.py').read());print(large_image.shape);print(smaller_image.shape)" - :shell: -``` - -## Reproject +## Crop {func}`geoutils.Raster.reproject` From f2d2db1897f270bbcec5a237e00e4fa41dfed676 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 17 Feb 2023 18:19:19 -0800 Subject: [PATCH 031/184] Incremental commit on doc --- doc/source/api.md | 20 ++--- doc/source/conf.py | 1 + doc/source/core_py_ops.md | 2 + doc/source/mask_class.md | 151 +++++++++++++++++++++++++++++++++-- doc/source/raster_class.md | 127 ++++++++++++++++++++++------- doc/source/vector_class.md | 67 +++------------- geoutils/georaster/raster.py | 36 ++++++++- 7 files changed, 304 insertions(+), 100 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index 5d4dbe11a..ba60ee9f0 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -122,16 +122,6 @@ documentation. Raster.to_xarray ``` -### Logical methods - -```{eval-rst} -.. autosummary:: - :toctree: gen_modules/ - - Raster.raster_equal - Raster.equal_georeferenced_grid -``` - ### Coordinate and extent methods ```{eval-rst} @@ -147,6 +137,16 @@ documentation. Raster.outside_image ``` +### Testing methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Raster.raster_equal + Raster.equal_georeferenced_grid +``` + ### Arithmetic with other rasters, arrays or numbers ```{eval-rst} diff --git a/doc/source/conf.py b/doc/source/conf.py index 8d975cffe..99aa20ef8 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -49,6 +49,7 @@ "matplotlib": ("https://matplotlib.org/stable", None), "pyproj": ("https://pyproj4.github.io/pyproj/stable", None), "geopandas": ("https://geopandas.org/en/stable", None), + "xarray": ("https://docs.xarray.dev/en/stable/", None), "xdem": ("https://xdem.readthedocs.io/en/latest", None), } diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index 08d1ff3f7..09e831d88 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -93,6 +93,8 @@ mask = (raster > 100) & ((raster % 2) == 0) mask ``` +(py-ops-indexing)= + ## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask` Finally, indexing and index assignment operations ({func}`[] `, {func}`[]= `) are both supported by diff --git a/doc/source/mask_class.md b/doc/source/mask_class.md index f25287113..40c62e2e1 100644 --- a/doc/source/mask_class.md +++ b/doc/source/mask_class.md @@ -1,14 +1,155 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (mask-class)= -# The georeferenced mask (`Mask`) +# The georeferenced mask ({class}`~geoutils.Mask`) +Below, a summary of the {class}`~geoutils.Mask` object and its methods. -## Object definition +## Object definition and attributes + +A {class}`~geoutils.Mask` is a subclass of {class}`~geoutils.Raster` that contains **three main attributes**: + +1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data` of {class}`~numpy.boolean` {class}`~numpy.dtype`, +2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, and +3. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`. + +A {class}`~geoutils.Mask` also inherits the same derivative attributes as a {class}`~geoutils.Raster`. + +```{note} +There is no {class}`~geoutils.Raster.nodata` value defined in a {class}`~geoutils.Mask`, as it only take binary values. However, the +{class}`numpy.ma.MaskedArray` still has a {class}`~geoutils.Raster.data.mask` for unvalid values. +``` + +## Open and save + +{class}`Masks` can be created from files through instantiation with {class}`~geoutils.Mask`, and inherit the {func}`~geoutils.Raster.save` +method from {class}`~geoutils.Raster`. + +```{important} +Most raster file formats such a [GeoTIFFs](https://gdal.org/drivers/raster/gtiff.html) **do not support {class}`bool` array {class}`dtypes` +on-disk**, and **most of Rasterio functionalities also do not support {class}`bool` {class}`dtypes`**. + +To address this, during opening, saving and geospatial handling operations, {class}`Masks` are automatically converted to and from {class}`numpy.uint8`. +The {class}`~geoutils.Raster.nodata` of a {class}`~geoutils.Mask` can now be defined to save to a file, and defaults to `255`. +``` + +On opening, all data will be forced to a {class}`bool` {class}`numpy.dtype`. + +```{code-cell} ipython3 +import geoutils as gu + +# Initiate a raster from disk +mask = gu.Mask(gu.examples.get_path("exploradores_aster_dem"), load_data=True) +mask +``` + +## Cast from {class}`~geoutils.Raster` + +{class}`Masks` are automatically cast by a logical comparison operation performed on a {class}`~geoutils.Raster` with either another +{class}`~geoutils.Raster`, a {class}`~numpy.ndarray` or a number. + +```{code-cell} ipython3 +# Initiate a raster from disk +raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem"), load_data=True) + +# Which pixels are below 1500 m? +raster < 1500 +``` + +See {ref}`core-py-ops` for more details. + + +## Create from {class}`~numpy.ndarray` + +The class method {func}`geoutils.Raster.from_array`, inherited by the {class}`~geoutils.Mask` subclass, automatically casts to a {class}`~geoutils.Mask` if +the input {class}`~numpy.ndarray` is of {class}`bool` {class}`~numpy.dtype`. + +```{code-cell} ipython3 +import rasterio as rio +import pyproj +import numpy as np + +# Create a random 3 x 3 masked array +np.random.seed(42) +arr = np.random.randint(0, 2, size=(3, 3), dtype="bool") +mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") +ma = np.ma.masked_array(data=arr, mask=mask) + +# Cast to a mask from a boolean array through the Raster class +mask = gu.Raster.from_array( + data = ma, + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 + ) +mask +``` + +When creating with {func}`geoutils.Mask.from_array`, any input {class}`~numpy.ndarray` will be forced to a {class}`bool` {class}`numpy.dtype`. + +```{code-cell} ipython3 +# Create a mask from array directly from the Mask class +mask = gu.Mask.from_array( + data = ma.astype("float32"), + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 + ) +mask +``` + +## Create from {class}`~geoutils.Vector` + +{class}`Masks` can also be created from a {class}`~geoutils.Vector` using {class}`~geoutils.Vector.create_mask`, which rasterizes all input +geometries to a boolean array through {class}`~geoutils.Vector.rasterize`. + +Georeferencing attributes to create the {class}`~geoutils.Mask` can also be passed individually, using `bounds`, `crs`, `xres` and `yres`. + +```{code-cell} ipython3 +# Open a vector of glacier outlines +vector = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) + +# Create mask using the raster as reference to match for bounds, resolution and projection +mask_outlines = vector.create_mask(raster) +mask_outlines +``` + +See {ref}`core-match-ref` for more details on the match-reference functionality. ## Arithmetic -## Cast from `Raster` +{class}`Masks` support Python's logical bitwise operators ({func}`~ `, {func}`& `, {func}`|`, +{func}`^ `) with other {class}`Masks`, and always output a {class}`~geoutils.Mask`. + +```{code-cell} ipython3 +# Combine masks +~mask | mask +``` + +## Indexing and assignment + +{class}`Masks` can be used for indexing and index assignment operations ({func}`[] `, {func}`[]= `) with +{class}`Rasters`. + +```{important} +When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the indexed values of the {class}`~geoutils.Mask` **excluding those masked in its +{class}`~geoutils.Raster.data`'s {class}`~numpy.ma.MaskedArray`**. +``` + +```{code-cell} ipython3 +# Index raster values on mask +raster[mask_outlines] +``` + +See {ref}`py-ops-indexing` for more details. + +## Polygonize (overloaded from {class}`~geoutils.Raster`) + +Some methods -## Create from `Vector` +## Proximity (overloaded from {class}`~geoutils.Raster`) -## Indexing \ No newline at end of file diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index f99a34725..b4461b8b3 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -15,8 +15,8 @@ Below, a summary of the {class}`~geoutils.Raster` object and its methods. A {class}`~geoutils.Raster` contains **four main attributes**: -1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, -2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform` +1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, of either {class}`~numpy.integer` or {class}`~numpy.floating` {class}`~numpy.dtype`, +2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, 3. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, and 4. a {class}`float` or {class}`int` as {attr}`~geoutils.Raster.nodata`. @@ -86,7 +86,7 @@ import os os.remove("myraster.tif") ``` -## Create from array +## Create from {class}`~numpy.ndarray` A {class}`~geoutils.Raster` is created from an array by calling the class method {func}`~geoutils.Raster.from_array` and passing the {ref}`four main attributes`. @@ -187,8 +187,8 @@ See {ref}`core-array-funcs` for more details. ## Reproject -Reprojecting a {class}`~geoutils.Raster` means to enforce a new {attr}`~geoutils.Raster.transform` and/or {class}`~geoutils.Raster.crs`. This is done by the -{func}`~geoutils.Raster.reproject` function. +Reprojecting a {class}`~geoutils.Raster` is done through the{func}`~geoutils.Raster.reproject`, which enforces new {attr}`~geoutils.Raster.transform` and/or +{class}`~geoutils.Raster.crs`. ```{important} As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils. @@ -211,11 +211,11 @@ print(raster.bounds) ```{code-cell} ipython3 # Reproject to smaller bounds and higher resolution -raster = raster.reproject( - dst_res=0.25, +raster_reproj = raster.reproject( + dst_res=0.1, dst_bounds={"left": 0, "bottom": 0, "right": 0.75, "top": 0.75}, resampling="cubic") -raster +raster_reproj ``` ```{code-cell} ipython3 @@ -227,40 +227,113 @@ print(raster.bounds) ```{note} In GeoUtils, `"bilinear"` is the default resampling method. A simple {class}`str` matching the naming of a {class}`rasterio.enums.Resampling` method can be passed. -``` +Resampling methods are listed in **[the dedicated section of Rasterio's API](https://rasterio.readthedocs.io/en/latest/api/rasterio.enums.html#rasterio.enums.Resampling)**. +``` ## Crop -{func}`geoutils.Raster.reproject` +Cropping a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.crop` function, which enforces new {attr}`~geoutils.Raster.bounds`. -For rasters with different coordinate systems, resolutions or grids, reprojection is needed to fit one raster to another. -`Raster.reproject()` is apt for these use-cases: +```{important} +As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils. +Vector` as argument. -```{literalinclude} code/raster-basics_cropping_and_reprojecting.py -:lines: 11 +See {ref}`core-match-ref` for more details. ``` -This call will crop, project and resample the `larger_raster` to fit the `smaller_raster` exactly. -By default, `Raster.resample()` uses nearest neighbour resampling, which is good for fast reprojections, but may induce unintended artefacts when precision is important. -It is therefore recommended to choose the method that fits the purpose best, using the `resampling=` keyword argument: +The {func}`~geoutils.Raster.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). +For more details, see the {ref}`specific section and function descriptions in the API`. -1. `resampling="nearest"`: Default. Performant but is not good for changes in resolution and grid. -2. `resampling="bilinear"`: Good when changes in resolution and grid are involved. -3. `resampling="cubic_spline"`: Often considered the best approach. Not as performant as simpler methods. -All valid resampling methods can be seen in the [Rasterio documentation](https://rasterio.readthedocs.io/en/latest/api/rasterio.enums.html#rasterio.enums.Resampling). - -```{eval-rst} -.. minigallery:: geoutils.Raster - :add-heading: - :heading-level: - +```{code-cell} ipython3 +# Crop to smaller bounds +raster_crop = raster.crop(cropGeom=(0.3, 0.3, 1, 1), inplace=False) +raster_crop ``` ## Polygonize +Polygonizing a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.polygonize` function, which converts target pixels into a multi-polygon +{class}`~geoutils.Vector`. + +By default, values equal to `1` are used. + +```{note} +For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly targets `True` values and thus does not require target pixels. +``` + +```{code-cell} ipython3 +# Polygonize all values lower than 100 + +vector_lt_100 = (raster < 100).polygonize() +vector_lt_100 +``` + ## Proximity +Computing proximity from a {class}`~geoutils.Raster` is done through by the {func}`~geoutils.Raster.proximity` function, which computes the distance to any +target pixels in the {class}`~geoutils.Raster`. + +```{note} +For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.proximity` implicitly targets `True` values and thus does not require target pixels. +``` + +```{code-cell} ipython3 +# Compute proximity from mask for all values lower than 100 +proximity_lt_100 = (raster < 100).proximity() +proximity_lt_100 +``` + +Optionally, instead of target pixel values, a {class}`~geoutils.Vector` can be passed to compute the proximity from the geometry. + +```{code-cell} ipython3 +# Compute proximity from mask for all values lower than 100 +proximity_lt_100_from_vector = raster.proximity(vector=vector_lt_100) +proximity_lt_100_from_vector +``` + ## Interpolate or extract to point -## Export \ No newline at end of file +Interpolating or extracting {class}`~geoutils.Raster` values at specific points can be done through: +- the {func}`~geoutils.Raster.value_at_coords` function, who extracts the single closest pixel or a surrounding window for each coordinate, on which + can be applied reducing any function ({func}`numpy.ma.mean` by default), or +- the {func}`~geoutils.Raster.interp_points` function, who interpolates the {class}`~geoutils.Raster`'s regular grid to each coordinate using a + resampling algorithm. + +```{code-cell} ipython3 +# Extract median value in a 3 x 3 pixel window +raster_reproj.value_at_coords(x=0.5, y=0.5, reducer_function=np.ma.median) +``` + +```{code-cell} ipython3 +# Interpolate coordinate value with quintic algorithm +raster_reproj.interp_points([(0.5, 0.5)], mode="quintic") +``` + +```{note} +Both {func}`~geoutils.Raster.value_at_coords` and {func}`~geoutils.Raster.interp_points` can be passed a single coordinate as {class}`floats`, or a +{class}`list` of coordinates. +``` + +## Export + +A {class}`~geoutils.Raster` can be exported to different formats, to facilitate inter-compatibility with different packages and code versions. + +Those include exporting to: +- a {class}`xarray.Dataset` with {class}`~geoutils.Raster.to_xarray`, +- a {class}`rasterio.DatasetReader` with {class}`~geoutils.Raster.to_rio_dataset`, +- a {class}`numpy.ndarray`, {class}`geopandas.GeoDataFrame` or {class}`geoutils.Vector` as a point cloud with {class}`~geoutils.Raster.to_points`. + +```{code-cell} ipython3 +# Export to rasterio dataset-reader through a memoryfile +raster_reproj.to_rio_dataset() +``` + +```{code-cell} ipython3 +# Export to geopandas dataframe +raster_reproj.to_points(as_frame=True) +``` + + + diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index 389126b6e..8050b83b2 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -1,70 +1,23 @@ (vector-class)= -# The georeferenced vector (`Vector`) +# The georeferenced vector ({class}`~geoutils.Vector`) -## Object definition +Below, a summary of the {class}`~geoutils.Vector` object and its methods. -The Vector class builds upon the great functionalities of [GeoPandas](https://geopandas.org/), with the aim to bridge the gap between vector and raster files. -It uses `geopandas.GeoDataFrame` as a base driver, accessible through `Vector.ds`. +## Object definition and attributes -## Open and save - -```{literalinclude} code/vector-basics_open_file.py -:lines: 2-6 -``` - -Printing the Vector shows the underlying `GeoDataFrame` and some extra statistics: - -```python -print(outlines) -``` +A {class}`~geoutils.Vector` contains **a single main attribute**: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`. -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/vector-basics_open_file.py').read()); print(outlines)" - :shell: +All other attributes are derivatives of the {class}`~geopandas.GeoDataFrame`. -``` - -````{margin} -**Example of vector from {func}`~geoutils.Vector.info`:** - -```{literalinclude} code/index_vect_info.py - :lines: 11-12 - :language: python -``` +```{important} +In short, {class}`~geoutils.Vector` is a "convenience" composition class built on top of GeoPandas, to facilitate interfacing with {class}`~geoutils.Raster`, +and to allow the addition of more complex vector functionalities. -```{eval-rst} -.. program-output:: $PYTHON code/index_vect_info.py - :shell: +**All of {class}`~geopandas.GeoDataFrame`'s methods are available directly from a {class}`~geoutils.Vector`**, as if working directly on the {class}`~geopandas.GeoDataFrame`. ``` - -```` - -Masks can easily be generated for use with Rasters: - -```{literalinclude} code/vector-basics_open_file.py -:lines: 8-13 -``` - -We can prove that glaciers are bright (who could have known!?) by masking the values outside and inside of the glaciers: - -```python -print(f"Inside: {image.data[mask].mean():.1f}, outside: {image.data[~mask].mean():.1f}") -``` - -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/vector-basics_open_file.py').read()); print(f'Inside: {image.data[mask].mean():.1f}, outside: {image.data[~mask].mean():.1f}')" - :shell: -``` - -TODO: Add rasterize text. - -```{eval-rst} -.. minigallery:: geoutils.Raster - :add-heading: - :heading-level: - -``` +## Open and save ## Arithmetic diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 18e118608..a7740b950 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -2854,9 +2854,15 @@ def __init__( def __repr__(self) -> str: """Convert formal Raster string representation.""" + # If data not loaded, return and string and avoid calling .data + if not self.is_loaded: + str_data = "not_loaded" + else: + str_data = "\n ".join(self.data.__str__().split("\n")) + # Over-ride Raster's method to remove nodata value (always None) s = "Mask(\n"+\ - " data=" + "\n ".join(self.data.__str__().split("\n"))+\ + " data=" + str_data +\ "\n transform="+ "\n ".join(self.transform.__str__().split("\n"))+\ "\n crs=" + self.crs.__str__()+")" @@ -2965,13 +2971,41 @@ def polygonize( self, in_value: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = 1 ) -> Vector: + # If target values is passed but does not correspond to 0 or 1, raise a warning if not isinstance(in_value, (int, np.integer, float, np.floating)) or in_value not in [0, 1]: warnings.warn("In-value converted to 1 for polygonizing boolean mask.") in_value = 1 + # Convert to unsigned integer and pass to parent method self._data = self.data.astype("uint8") return super().polygonize(in_value=in_value) + def proximity( + self, + vector: Vector | None = None, + target_values: list[float] | None = None, + geometry_type: str = "boundary", + in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", + distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", + ) -> Raster: + + # By default, target True values of the mask + if vector is None and target_values is None: + target_values = [1] + + # TODO: Adapt target_value into int | list in Raster.proximity + # If target values is passed but does not correspond to 0 or 1, raise a warning + # if target_values is not None and not isinstance(target_values, (int, np.integer, float, np.floating)) or target_values not in [0, 1]: + # warnings.warn("In-value converted to 1 for polygonizing boolean mask.") + # target_values = [1] + + # Convert to unsigned integer and pass to parent method + self._data = self.data.astype("uint8") + + # Need to cast output to Raster before computing proximity, as output will not be boolean (super() would instantiate Mask() again) + raster = Raster({"data": self.data, "transform": self.transform, "crs": self.crs, "nodata": self.nodata}) + return raster.proximity(vector=vector, target_values=target_values, geometry_type=geometry_type, in_or_out=in_or_out, distance_unit=distance_unit) + # Logical bitwise operations def __and__(self: Mask, other: Mask) -> Mask: From 683ff9aca96dfd94e2d4191ab66290415cb28ab7 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Feb 2023 15:03:31 -0800 Subject: [PATCH 032/184] Fix behaviour of load() with load_data=False as default, add and modify tests --- geoutils/georaster/raster.py | 117 +++++++++++++++++++++++------------ tests/test_georaster.py | 41 +++++++----- 2 files changed, 102 insertions(+), 56 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index a7740b950..ada1a1023 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -193,17 +193,21 @@ def _load_rio( * window : to load a cropped version * resampling : to set the resampling algorithm """ - if transform is not None and shape is not None: - if transform == dataset.transform: - row_off, col_off = 0, 0 - else: - row_off, col_off = (round(val) for val in dataset.index(transform[2], abs(transform[4]))) - - window = rio.windows.Window(col_off, row_off, *shape[::-1]) - elif sum(param is None for param in [shape, transform]) == 1: - raise ValueError("If 'shape' or 'transform' is provided, BOTH must be given.") - else: + # If out_shape is passed, no need to account for transform and shape + if kwargs['out_shape'] is not None: window = None + else: + if transform is not None and shape is not None: + if transform == dataset.transform: + row_off, col_off = 0, 0 + else: + row_off, col_off = (round(val) for val in dataset.index(transform[2], abs(transform[4]))) + + window = rio.windows.Window(col_off, row_off, *shape[::-1]) + elif sum(param is None for param in [shape, transform]) == 1: + raise ValueError("If 'shape' or 'transform' is provided, BOTH must be given.") + else: + window = None if indexes is None: data = dataset.read(masked=masked, window=window, **kwargs) @@ -211,7 +215,7 @@ def _load_rio( data = dataset.read(indexes=indexes, masked=masked, window=window, **kwargs) if len(data.shape) == 2: data = data[np.newaxis, :, :] - return np.ma.masked_array(data) + return data class Raster: @@ -303,6 +307,7 @@ def __init__( self._indexes = indexes self._indexes_loaded: int | tuple[int, ...] | None = None self._masked = masked + self._out_shape: tuple[int, int, int] | None = None self._disk_hash: int | None = None self._is_modified = True self._disk_shape: tuple[int, int, int] | None = None @@ -342,8 +347,11 @@ def __init__( elif isinstance(filename_or_dataset, rio.io.DatasetReader): ds = filename_or_dataset self.filename = filename_or_dataset.files[0] - else: # This is if it's a MemoryFile + # This is if it's a MemoryFile + else: ds = filename_or_dataset.open() + # In that case, data has to be loaded + load_data = True self.filename = None self.transform = ds.transform @@ -381,16 +389,23 @@ def __init__( res = tuple(np.asarray(self.res) * downsample) self.transform = rio.transform.from_origin(self.bounds.left, self.bounds.top, res[0], res[1]) + # This will record the downsampled out_shape is data is only loaded later on by .load() + self._out_shape = out_shape + if load_data: # Mypy doesn't like the out_shape for some reason. I can't figure out why! (erikmannerfelt, 14/01/2022) - self._data = _load_rio(ds, indexes=indexes, masked=masked, out_shape=out_shape) # type: ignore - if isinstance(filename_or_dataset, str): - self._is_modified = False - self._disk_hash = hash((self.data.tobytes(), self.transform, self.crs, self.nodata)) + # Don't need to pass shape and transform, because out_shape overrides it + self.data = _load_rio(ds, indexes=indexes, masked=masked, out_shape=out_shape) # type: ignore - # Set nodata - if nodata is not None: - self.set_nodata(nodata) + # Probably don't want to use set_nodata that can update array, setting self._nodata is sufficient + # Set nodata only if data is loaded + # if nodata is not None and self._data is not None: + # self.set_nodata(self._nodata) + + # If data was loaded explicitly, initiate is_modified and save disk hash + if load_data and isinstance(filename_or_dataset, str): + self._is_modified = False + self._disk_hash = hash((self.data.tobytes(), self.transform, self.crs, self.nodata)) # Provide a catch in case trying to load from data array elif isinstance(filename_or_dataset, np.ndarray): @@ -432,6 +447,10 @@ def width(self) -> int: @property def shape(self) -> tuple[int, int]: """Return a (height, width) tuple of the data shape in pixels.""" + # If a downsampling argument was defined but data not loaded yet + if self._out_shape is not None and not self.is_loaded: + return self._out_shape[1], self._out_shape[2] + # If data is not loaded, pass the disk shape if not self.is_loaded: return self._disk_shape[1], self._disk_shape[2] # type: ignore return int(self.data.shape[1]), int(self.data.shape[2]) @@ -503,6 +522,7 @@ def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: # Save which indexes are loaded self._indexes_loaded = valid_indexes + # If a downsampled out_shape was defined during instantiation with rio.open(self.filename) as dataset: self.data = _load_rio( dataset, @@ -510,9 +530,19 @@ def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: masked=self._masked, transform=self.transform, shape=self.shape, + out_shape=self._out_shape, **kwargs, ) + # Probably don't want to use set_nodata() that updates the array + # Set nodata value with the loaded array + # if self._nodata is not None: + # self.set_nodata(nodata=self._nodata) + + # To have is_modified work correctly when data is loaded implicitly (not in init) + self._is_modified = False + self._disk_hash = hash((self.data.tobytes(), self.transform, self.crs, self.nodata)) + @classmethod def from_array( cls: type[RasterType], @@ -614,7 +644,11 @@ def __repr__(self) -> str: def __str__(self) -> str: """Provide simplified string for print() about Raster.""" - s = self.data.__str__() + + if not self.is_loaded: + s = "not_loaded" + else: + s = self.data.__str__() return s @@ -1133,8 +1167,8 @@ def data(self) -> np.ma.masked_array: :returns: data array. """ - if not self.is_loaded and self._data is None: - raise ValueError("Data are not loaded") + if not self.is_loaded: + self.load() return self._data @data.setter @@ -2291,31 +2325,32 @@ def format_value(value: Any) -> Any: rio_window = rio.windows.Window(col, row, width, height) if self.is_loaded: - data = self.data[slice(None) if index is None else index, row : row + height, col : col + width] + data = self.data[slice(None) if index is None else index - 1, row : row + height, col : col + width] if not masked: data = data.filled() value = format_value(data) win: np.ndarray | dict[int, np.ndarray] = data else: - if self.count == 1: - with rio.open(self.filename) as raster: - data = raster.read( - window=rio_window, fill_value=self.nodata, boundless=boundless, masked=masked - ) - value = format_value(data) - win = data - else: - value = {} - win = {} - with rio.open(self.filename) as raster: - for b in self.indexes: - data = raster.read( - window=rio_window, fill_value=self.nodata, boundless=boundless, masked=masked, indexes=b - ) - val = format_value(data) - value[b] = val - win[b] = data # type: ignore + # TODO: if we want to allow sampling multiple bands, need to do it also when data is loaded + # if self.count == 1: + with rio.open(self.filename) as raster: + data = raster.read( + window=rio_window, fill_value=self.nodata, boundless=boundless, masked=masked, indexes=index, + ) + value = format_value(data) + win = data + # else: + # value = {} + # win = {} + # with rio.open(self.filename) as raster: + # for b in self.indexes: + # data = raster.read( + # window=rio_window, fill_value=self.nodata, boundless=boundless, masked=masked, indexes=b + # ) + # val = format_value(data) + # value[b] = val + # win[b] = data # type: ignore list_values.append(value) if return_window: diff --git a/tests/test_georaster.py b/tests/test_georaster.py index bbddf1e8e..653d9cee3 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -102,13 +102,21 @@ def test_init(self, example: str) -> None: r4 = gr.Raster(memfile) assert isinstance(r4, gr.Raster) + # All rasters should be equal assert all( [r0.raster_equal(r1), r0.raster_equal(r1), r0.raster_equal(r2), r0.raster_equal(r3), r0.raster_equal(r4)] ) - # The data will not be copied, immutable objects will + # For re-instantiation via Raster (r2 above), we check the behaviour: + # By default, raster were unloaded, and were loaded during raster_equal() independently + # So the instances should not be pointing to the same data and not mirror modifs r0.data[0, 0, 0] += 5 - assert r2.data[0, 0, 0] == r0.data[0, 0, 0] + assert r2.data[0, 0, 0] != r0.data[0, 0, 0] + + # However, if we reinstantiate now that the data is loaded, it should point to the same + r5 = gr.Raster(r0) + r0.data[0, 0, 0] += 5 + assert r5.data[0, 0, 0] == r0.data[0, 0, 0] # With r.count = 2 r0._data = np.repeat(r0.data, 2).reshape((2,) + r0.shape) @@ -129,12 +137,9 @@ def test_info(self, example: str) -> None: for attr in _default_rio_attrs: assert r.__getattribute__(attr) == dataset.__getattribute__(attr) - # Check summary matches that of RIO - assert str(r) == r.info() - # Check that the stats=True flag doesn't trigger a warning with warnings.catch_warnings(): - warnings.simplefilter("error") + warnings.filterwarnings("ignore", message="New nodata.*", category=UserWarning) stats = r.info(stats=True) # Check the stats adapt to nodata values @@ -161,10 +166,17 @@ def test_loading(self) -> None: """ Test that loading metadata and data works for all possible cases. """ + # Test 0 - test that loading explicitly via load() or implicitly via .data is similar + r_explicit = gr.Raster(self.landsat_b4_path, load_data=True) + r_implicit = gr.Raster(self.landsat_b4_path) + + assert r_explicit.raster_equal(r_implicit) + # Test 1 - loading metadata only, single band # For the first example with Landsat B4 - r = gr.Raster(self.landsat_b4_path, load_data=False) + r = gr.Raster(self.landsat_b4_path) + assert not r.is_loaded assert r.driver == "GTiff" assert r.width == 800 assert r.height == 655 @@ -178,11 +190,11 @@ def test_loading(self) -> None: assert np.array_equal(r.res, [30.0, 30.0]) assert r.bounds == rio.coords.BoundingBox(left=478000.0, bottom=3088490.0, right=502000.0, top=3108140.0) assert r.crs == rio.crs.CRS.from_epsg(32645) - assert not r.is_loaded # And the second example with ASTER DEM - r2 = gr.Raster(self.aster_dem_path, load_data=False) + r2 = gr.Raster(self.aster_dem_path) + assert not r2.is_loaded assert r2.driver == "GTiff" assert r2.width == 539 assert r2.height == 618 @@ -196,7 +208,6 @@ def test_loading(self) -> None: assert np.array_equal(r2.res, [30.0, 30.0]) assert r2.bounds == rio.coords.BoundingBox(left=627175.0, bottom=4833545.0, right=643345.0, top=4852085.0) assert r2.crs == rio.crs.CRS.from_epsg(32718) - assert not r2.is_loaded # Test 2 - loading the data afterward r.load() @@ -1456,9 +1467,9 @@ def test_value_at_coords(self) -> None: itest = itest[0] jtest = jtest[0] # Extract the values - z_band1 = r_multi.value_at_coords(xtest0, ytest0, index=0) - z_band2 = r_multi.value_at_coords(xtest0, ytest0, index=1) - z_band3 = r_multi.value_at_coords(xtest0, ytest0, index=2) + z_band1 = r_multi.value_at_coords(xtest0, ytest0, index=1) + z_band2 = r_multi.value_at_coords(xtest0, ytest0, index=2) + z_band3 = r_multi.value_at_coords(xtest0, ytest0, index=3) # Compare to the Raster array slice assert list(r_multi.data[:, itest, jtest]) == [z_band1, z_band2, z_band3] @@ -1471,7 +1482,7 @@ def test_value_at_coords(self) -> None: # 4/ Window argument val_window, z_window = r_multi.value_at_coords( - xtest0, ytest0, index=0, window=3, masked=True, return_window=True + xtest0, ytest0, index=1, window=3, masked=True, return_window=True ) assert ( val_window @@ -1482,7 +1493,7 @@ def test_value_at_coords(self) -> None: # 5/ Reducer function argument val_window2 = r_multi.value_at_coords( - xtest0, ytest0, index=0, window=3, masked=True, reducer_function=np.ma.median + xtest0, ytest0, index=1, window=3, masked=True, reducer_function=np.ma.median ) assert val_window2 == np.ma.median(r_multi.data[0, itest - 1 : itest + 2, jtest - 1 : jtest + 2]) From 56fae7cc5065f999d29b985cf7eb52f9ce4718ab Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Feb 2023 15:59:34 -0800 Subject: [PATCH 033/184] Add _repr_html_ functions --- doc/source/raster_class.md | 1 - geoutils/georaster/raster.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index b4461b8b3..c39d53913 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -69,7 +69,6 @@ Detailed information on the {class}`~geoutils.Raster` is printed using {class}`~ print(raster.info(stats=True)) ``` - ```{note} Calling {class}`~geoutils.Raster.info()` with `stats=True` automatically loads the array in-memory, like any other operation calling {attr}`~geoutils.Raster.data`. ``` diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index ada1a1023..6b20dafc3 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -642,6 +642,24 @@ def __repr__(self) -> str: # return s + def _repr_html_(self) -> str: + """Convert to HTML representation for notebooks and docs""" + + # If data not loaded, return and string and avoid calling .data + if not self.is_loaded: + str_data = "not_loaded" + else: + str_data = "\n ".join(self.data.__str__().split("\n")) + + # Over-ride Raster's method to remove nodata value (always None) + # Use
 to keep white spaces,  to keep line breaks
+        s = '
'+self.__class__.__name__+'(\n' + \
+            "  data=" + str_data + \
+            "\n  transform=" + "\n            ".join(self.transform.__str__().split("\n")) + \
+            "\n  crs=" + self.crs.__str__() + ")
" + + return s + def __str__(self) -> str: """Provide simplified string for print() about Raster.""" @@ -2903,6 +2921,24 @@ def __repr__(self) -> str: return s + def _repr_html_(self) -> str: + """Convert to HTML representation for notebooks and docs""" + + # If data not loaded, return and string and avoid calling .data + if not self.is_loaded: + str_data = "not_loaded" + else: + str_data = "\n ".join(self.data.__str__().split("\n")) + + # Over-ride Raster's method to remove nodata value (always None) + # Use
 to keep white spaces,  to keep line breaks
+        s = '
Mask(\n' + \
+            "  data=" + str_data + \
+            "\n  transform=" + "\n            ".join(self.transform.__str__().split("\n")) + \
+            "\n  crs=" + self.crs.__str__() + ")
" + + return s + def reproject( self: Mask, dst_ref: RasterType | rio.io.Dataset | str | None = None, From d747d62257e49807c8e4ded1a2dec17c3fb5a7d8 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Feb 2023 16:05:01 -0800 Subject: [PATCH 034/184] Fix behaviour with load_data=False in _overloading_check and tests in satimg --- geoutils/georaster/raster.py | 7 ++++--- tests/test_satimg.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 6b20dafc3..69cff1ce7 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -733,9 +733,10 @@ def _overloading_check( # Case 1 - other is a Raster if isinstance(other, Raster): - # Check that both data are loaded - if not (self.is_loaded & other.is_loaded): - raise ValueError("Raster's data must be loaded with self.load().") + # Not necessary anymore with implicit loading + # # Check that both data are loaded + # if not (self.is_loaded & other.is_loaded): + # raise ValueError("Raster's data must be loaded with self.load().") # Check that both rasters have the same shape and georeferences if (self.data.shape == other.data.shape) & (self.transform == other.transform) & (self.crs == other.crs): diff --git a/tests/test_satimg.py b/tests/test_satimg.py index b13bb886a..6662cd075 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -135,7 +135,7 @@ def test_copy(self, example: str) -> None: satimg_attrs = ["satellite", "sensor", "product", "version", "tile_name", "datetime"] # using list directly available in Class attrs = georaster_attrs + satimg_attrs - all_attrs = attrs + gr.satimg_attrs + all_attrs = attrs + gr.satimg.satimg_attrs for attr in all_attrs: assert r.__getattribute__(attr) == r2.__getattribute__(attr) From caea527016a563dd2bdc2ae89b1c9e1e718253ac Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Feb 2023 18:37:36 -0800 Subject: [PATCH 035/184] Update quick start with myst-nd and start new gallery of examples --- .../analysis_examples_jupyter.zip | Bin 0 -> 22 bytes .../analysis_examples_python.zip | Bin 0 -> 22 bytes doc/source/analysis_examples/index.rst | 38 +++ doc/source/analysis_examples/searchindex.bak | 3 + doc/source/analysis_examples/searchindex.dat | Bin 0 -> 84326 bytes doc/source/analysis_examples/searchindex.dir | 3 + doc/source/api.md | 1 + doc/source/code/index_example.py | 49 ---- doc/source/code/index_rast_info.py | 12 - doc/source/code/index_vect_info.py | 12 - ...raster-basics_cropping_and_reprojecting.py | 11 - doc/source/code/raster-basics_open_file.py | 15 -- doc/source/code/satimg-basics_open_file.py | 6 - doc/source/code/vector-basics_open_file.py | 16 -- doc/source/conf.py | 8 +- doc/source/core_composition.md | 2 +- .../handling_examples_jupyter.zip | Bin 0 -> 7010 bytes .../handling_examples_python.zip | Bin 0 -> 1580 bytes .../images/sphx_glr_read_raster_001.png | Bin 0 -> 213447 bytes .../images/sphx_glr_read_satimg_001.png | Bin 0 -> 151024 bytes .../images/sphx_glr_read_vector_001.png | Bin 0 -> 100827 bytes .../thumb/sphx_glr_read_raster_thumb.png | Bin 0 -> 92341 bytes .../thumb/sphx_glr_read_satimg_thumb.png | Bin 0 -> 70802 bytes .../thumb/sphx_glr_read_vector_thumb.png | Bin 0 -> 53375 bytes doc/source/handling_examples/index.rst | 37 +++ .../handling_examples/read_raster.ipynb | 108 +++++++++ .../source/handling_examples}/read_raster.py | 0 .../handling_examples/read_raster.py.md5 | 1 + doc/source/handling_examples/read_raster.rst | 131 +++++++++++ .../read_raster_codeobj.pickle | Bin 0 -> 975 bytes .../handling_examples/read_satimg.ipynb | 108 +++++++++ .../source/handling_examples}/read_satimg.py | 0 .../handling_examples/read_satimg.py.md5 | 1 + doc/source/handling_examples/read_satimg.rst | 130 +++++++++++ .../read_satimg_codeobj.pickle | Bin 0 -> 991 bytes .../handling_examples/read_vector.ipynb | 108 +++++++++ .../source/handling_examples}/read_vector.py | 0 .../handling_examples/read_vector.py.md5 | 1 + doc/source/handling_examples/read_vector.rst | 150 ++++++++++++ .../read_vector_codeobj.pickle | Bin 0 -> 2764 bytes doc/source/handling_examples/searchindex.bak | 3 + doc/source/handling_examples/searchindex.dat | Bin 0 -> 84326 bytes doc/source/handling_examples/searchindex.dir | 3 + .../handling_examples/sg_execution_times.rst | 16 ++ doc/source/index.md | 4 +- .../images/sphx_glr_read_raster_001.png | Bin 0 -> 213447 bytes .../images/sphx_glr_read_satimg_001.png | Bin 0 -> 151024 bytes .../images/sphx_glr_read_vector_001.png | Bin 0 -> 100827 bytes .../thumb/sphx_glr_read_raster_thumb.png | Bin 0 -> 92341 bytes .../thumb/sphx_glr_read_satimg_thumb.png | Bin 0 -> 70802 bytes .../thumb/sphx_glr_read_vector_thumb.png | Bin 0 -> 53375 bytes doc/source/io_examples/index.rst | 96 ++++++++ .../io_examples/io_examples_jupyter.zip | Bin 0 -> 8208 bytes doc/source/io_examples/io_examples_python.zip | Bin 0 -> 2205 bytes doc/source/io_examples/read_raster.ipynb | 144 ++++++++++++ doc/source/io_examples/read_raster.py | 32 +++ doc/source/io_examples/read_raster.py.md5 | 1 + doc/source/io_examples/read_raster.rst | 217 ++++++++++++++++++ .../io_examples/read_raster_codeobj.pickle | Bin 0 -> 1481 bytes doc/source/io_examples/read_satimg.ipynb | 108 +++++++++ doc/source/io_examples/read_satimg.py | 23 ++ doc/source/io_examples/read_satimg.py.md5 | 1 + doc/source/io_examples/read_satimg.rst | 130 +++++++++++ .../io_examples/read_satimg_codeobj.pickle | Bin 0 -> 991 bytes doc/source/io_examples/read_vector.ipynb | 108 +++++++++ doc/source/io_examples/read_vector.py | 25 ++ doc/source/io_examples/read_vector.py.md5 | 1 + doc/source/io_examples/read_vector.rst | 150 ++++++++++++ .../io_examples/read_vector_codeobj.pickle | Bin 0 -> 2764 bytes doc/source/io_examples/searchindex.bak | 3 + doc/source/io_examples/searchindex.dat | Bin 0 -> 84326 bytes doc/source/io_examples/searchindex.dir | 3 + doc/source/io_examples/sg_execution_times.rst | 16 ++ doc/source/mask_class.md | 22 +- doc/source/quick_start.md | 103 ++++++--- doc/source/raster_class.md | 18 +- doc/source/satimg_class.md | 59 +++-- examples/README.rst | 4 - examples/analysis/README.rst | 5 + examples/handling/README.rst | 4 + examples/io/README.rst | 4 + examples/io/read_raster.py | 32 +++ examples/io/read_satimg.py | 23 ++ examples/io/read_vector.py | 25 ++ geoutils/georaster/raster.py | 10 +- 85 files changed, 2144 insertions(+), 200 deletions(-) create mode 100644 doc/source/analysis_examples/analysis_examples_jupyter.zip create mode 100644 doc/source/analysis_examples/analysis_examples_python.zip create mode 100644 doc/source/analysis_examples/index.rst create mode 100644 doc/source/analysis_examples/searchindex.bak create mode 100644 doc/source/analysis_examples/searchindex.dat create mode 100644 doc/source/analysis_examples/searchindex.dir delete mode 100644 doc/source/code/index_example.py delete mode 100644 doc/source/code/index_rast_info.py delete mode 100644 doc/source/code/index_vect_info.py delete mode 100644 doc/source/code/raster-basics_cropping_and_reprojecting.py delete mode 100644 doc/source/code/raster-basics_open_file.py delete mode 100644 doc/source/code/satimg-basics_open_file.py delete mode 100644 doc/source/code/vector-basics_open_file.py create mode 100644 doc/source/handling_examples/handling_examples_jupyter.zip create mode 100644 doc/source/handling_examples/handling_examples_python.zip create mode 100644 doc/source/handling_examples/images/sphx_glr_read_raster_001.png create mode 100644 doc/source/handling_examples/images/sphx_glr_read_satimg_001.png create mode 100644 doc/source/handling_examples/images/sphx_glr_read_vector_001.png create mode 100644 doc/source/handling_examples/images/thumb/sphx_glr_read_raster_thumb.png create mode 100644 doc/source/handling_examples/images/thumb/sphx_glr_read_satimg_thumb.png create mode 100644 doc/source/handling_examples/images/thumb/sphx_glr_read_vector_thumb.png create mode 100644 doc/source/handling_examples/index.rst create mode 100644 doc/source/handling_examples/read_raster.ipynb rename {examples => doc/source/handling_examples}/read_raster.py (100%) create mode 100644 doc/source/handling_examples/read_raster.py.md5 create mode 100644 doc/source/handling_examples/read_raster.rst create mode 100644 doc/source/handling_examples/read_raster_codeobj.pickle create mode 100644 doc/source/handling_examples/read_satimg.ipynb rename {examples => doc/source/handling_examples}/read_satimg.py (100%) create mode 100644 doc/source/handling_examples/read_satimg.py.md5 create mode 100644 doc/source/handling_examples/read_satimg.rst create mode 100644 doc/source/handling_examples/read_satimg_codeobj.pickle create mode 100644 doc/source/handling_examples/read_vector.ipynb rename {examples => doc/source/handling_examples}/read_vector.py (100%) create mode 100644 doc/source/handling_examples/read_vector.py.md5 create mode 100644 doc/source/handling_examples/read_vector.rst create mode 100644 doc/source/handling_examples/read_vector_codeobj.pickle create mode 100644 doc/source/handling_examples/searchindex.bak create mode 100644 doc/source/handling_examples/searchindex.dat create mode 100644 doc/source/handling_examples/searchindex.dir create mode 100644 doc/source/handling_examples/sg_execution_times.rst create mode 100644 doc/source/io_examples/images/sphx_glr_read_raster_001.png create mode 100644 doc/source/io_examples/images/sphx_glr_read_satimg_001.png create mode 100644 doc/source/io_examples/images/sphx_glr_read_vector_001.png create mode 100644 doc/source/io_examples/images/thumb/sphx_glr_read_raster_thumb.png create mode 100644 doc/source/io_examples/images/thumb/sphx_glr_read_satimg_thumb.png create mode 100644 doc/source/io_examples/images/thumb/sphx_glr_read_vector_thumb.png create mode 100644 doc/source/io_examples/index.rst create mode 100644 doc/source/io_examples/io_examples_jupyter.zip create mode 100644 doc/source/io_examples/io_examples_python.zip create mode 100644 doc/source/io_examples/read_raster.ipynb create mode 100644 doc/source/io_examples/read_raster.py create mode 100644 doc/source/io_examples/read_raster.py.md5 create mode 100644 doc/source/io_examples/read_raster.rst create mode 100644 doc/source/io_examples/read_raster_codeobj.pickle create mode 100644 doc/source/io_examples/read_satimg.ipynb create mode 100644 doc/source/io_examples/read_satimg.py create mode 100644 doc/source/io_examples/read_satimg.py.md5 create mode 100644 doc/source/io_examples/read_satimg.rst create mode 100644 doc/source/io_examples/read_satimg_codeobj.pickle create mode 100644 doc/source/io_examples/read_vector.ipynb create mode 100644 doc/source/io_examples/read_vector.py create mode 100644 doc/source/io_examples/read_vector.py.md5 create mode 100644 doc/source/io_examples/read_vector.rst create mode 100644 doc/source/io_examples/read_vector_codeobj.pickle create mode 100644 doc/source/io_examples/searchindex.bak create mode 100644 doc/source/io_examples/searchindex.dat create mode 100644 doc/source/io_examples/searchindex.dir create mode 100644 doc/source/io_examples/sg_execution_times.rst delete mode 100644 examples/README.rst create mode 100644 examples/analysis/README.rst create mode 100644 examples/handling/README.rst create mode 100644 examples/io/README.rst create mode 100644 examples/io/read_raster.py create mode 100644 examples/io/read_satimg.py create mode 100644 examples/io/read_vector.py diff --git a/doc/source/analysis_examples/analysis_examples_jupyter.zip b/doc/source/analysis_examples/analysis_examples_jupyter.zip new file mode 100644 index 0000000000000000000000000000000000000000..15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7 GIT binary patch literal 22 NcmWIWW@Tf*000g10H*)| literal 0 HcmV?d00001 diff --git a/doc/source/analysis_examples/analysis_examples_python.zip b/doc/source/analysis_examples/analysis_examples_python.zip new file mode 100644 index 0000000000000000000000000000000000000000..15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7 GIT binary patch literal 22 NcmWIWW@Tf*000g10H*)| literal 0 HcmV?d00001 diff --git a/doc/source/analysis_examples/index.rst b/doc/source/analysis_examples/index.rst new file mode 100644 index 000000000..9c2be7621 --- /dev/null +++ b/doc/source/analysis_examples/index.rst @@ -0,0 +1,38 @@ +:orphan: + +Analysis +======== + +Examples about numerical operations on rasters, computing proximity and buffer without overlap. + + + + +.. raw:: html + +
+ + +.. raw:: html + +
+ + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-gallery + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download all examples in Python source code: analysis_examples_python.zip ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download all examples in Jupyter notebooks: analysis_examples_jupyter.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/analysis_examples/searchindex.bak b/doc/source/analysis_examples/searchindex.bak new file mode 100644 index 000000000..38a7775ad --- /dev/null +++ b/doc/source/analysis_examples/searchindex.bak @@ -0,0 +1,3 @@ +'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) +'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) +'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/analysis_examples/searchindex.dat b/doc/source/analysis_examples/searchindex.dat new file mode 100644 index 0000000000000000000000000000000000000000..a179f4911b395951a6ae047c77b42ea62fd8164a GIT binary patch literal 84326 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 literal 0 HcmV?d00001 diff --git a/doc/source/analysis_examples/searchindex.dir b/doc/source/analysis_examples/searchindex.dir new file mode 100644 index 000000000..38a7775ad --- /dev/null +++ b/doc/source/analysis_examples/searchindex.dir @@ -0,0 +1,3 @@ +'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) +'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) +'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/api.md b/doc/source/api.md index ba60ee9f0..cf9f2c234 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -244,6 +244,7 @@ And reverse operations. Mask.crop Mask.reproject Mask.polygonize + Mask.proximity ``` ## Vectors diff --git a/doc/source/code/index_example.py b/doc/source/code/index_example.py deleted file mode 100644 index dd1f602f2..000000000 --- a/doc/source/code/index_example.py +++ /dev/null @@ -1,49 +0,0 @@ -import geoutils as gu - -# Examples files: infrared band of Landsat and glacier outlines -filename_rast = gu.examples.get_path("everest_landsat_b4") -filename_vect = gu.examples.get_path("everest_rgi_outlines") - -# Open files -rast = gu.Raster(filename_rast) -vect = gu.Vector(filename_vect) - -# Open file as a satellite image -import os -print(os.path.basename(filename_rast)) -rast = gu.SatelliteImage(filename_rast, silent=False) - -# Crop raster to vector's extent -rast.crop(vect) - -# Compute proximity to vector on raster's grid -rast_proximity_to_vec = vect.proximity(rast) - -# Add 1 to the raster array -rast += 1 - -# Apply a normalization to the raster -import numpy as np -rast = (rast - np.min(rast)) / (np.max(rast) - np.min(rast)) - -# Get mask of an AOI: infrared index above 0.7, at least 200 m from glaciers -mask_aoi = np.logical_and(rast > 0.7, rast_proximity_to_vec > 200) - -# Index raster with mask to extract a 1-D array -values_aoi = rast[mask_aoi] - -# Polygonize areas where mask is True -vect_aoi = mask_aoi.polygonize() - -# Plot result -import matplotlib.pyplot as plt -fig = plt.figure() -ax = plt.gca() -rast.show(ax=ax, cmap='Reds', cb_title='Normalized infrared') -vect_aoi.ds.plot(ax=ax, fc='none', ec='k', lw=0.5) - -# Save final raster and vector to files -# rast.save() -# vect_aoi.save() - -#TODO: maybe add glacier outlines also above, once the plotting through Vector is simplified diff --git a/doc/source/code/index_rast_info.py b/doc/source/code/index_rast_info.py deleted file mode 100644 index 44914008d..000000000 --- a/doc/source/code/index_rast_info.py +++ /dev/null @@ -1,12 +0,0 @@ -import geoutils as gu - -# Examples files -filename_rast = gu.examples.get_path("everest_landsat_b4") -filename_vect = gu.examples.get_path("everest_rgi_outlines") - -# Open files -rast = gu.Raster(filename_rast) -vect = gu.Vector(filename_vect) - -# Print raster metadata info -print(rast.info()) \ No newline at end of file diff --git a/doc/source/code/index_vect_info.py b/doc/source/code/index_vect_info.py deleted file mode 100644 index 2fce27336..000000000 --- a/doc/source/code/index_vect_info.py +++ /dev/null @@ -1,12 +0,0 @@ -import geoutils as gu - -# Examples files -filename_rast = gu.examples.get_path("everest_landsat_b4") -filename_vect = gu.examples.get_path("everest_rgi_outlines") - -# Open files -rast = gu.Raster(filename_rast) -vect = gu.Vector(filename_vect) - -# Print vector metadata info -print(vect.info()) \ No newline at end of file diff --git a/doc/source/code/raster-basics_cropping_and_reprojecting.py b/doc/source/code/raster-basics_cropping_and_reprojecting.py deleted file mode 100644 index 4be5f6a95..000000000 --- a/doc/source/code/raster-basics_cropping_and_reprojecting.py +++ /dev/null @@ -1,11 +0,0 @@ -"""Exemplify uses of crop and reproject.""" -import geoutils as gu - -large_image = gu.Raster(gu.examples.get_path("everest_landsat_b4")) - -smaller_image = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) -# TEXT -large_image_orig = large_image.copy() # Since it gets modified inplace, we want to keep it to print stats. -large_image.crop(smaller_image) -# TEXT -large_image_reprojected = large_image_orig.reproject(smaller_image, dst_nodata=0) diff --git a/doc/source/code/raster-basics_open_file.py b/doc/source/code/raster-basics_open_file.py deleted file mode 100644 index eb10fc13b..000000000 --- a/doc/source/code/raster-basics_open_file.py +++ /dev/null @@ -1,15 +0,0 @@ -"""Example scripts to open and print information about a raster.""" -import geoutils as gu - -# Fetch an example file -filename = gu.examples.get_path("everest_landsat_b4") - -# Open the file -image = gu.Raster(filename) -#### TEXT -information = image.info() -#### TEXT -information = image.info(stats=True) -#### TEXT -with open("file.txt", "w") as fh: - fh.writelines(information) diff --git a/doc/source/code/satimg-basics_open_file.py b/doc/source/code/satimg-basics_open_file.py deleted file mode 100644 index 80613b8a2..000000000 --- a/doc/source/code/satimg-basics_open_file.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Example script to load a satellite image.""" -import geoutils as gu - -filename = gu.examples.get_path("everest_landsat_b4_cropped") - -satimg = gu.SatelliteImage(filename) diff --git a/doc/source/code/vector-basics_open_file.py b/doc/source/code/vector-basics_open_file.py deleted file mode 100644 index a1d683f62..000000000 --- a/doc/source/code/vector-basics_open_file.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Example script to load a vector file.""" -import warnings - -warnings.simplefilter("ignore") # Temporarily filter warnings since something's up with GeoPandas (2021-05-20). -import geoutils as gu - -filename = gu.examples.get_path("everest_rgi_outlines") - -outlines = gu.Vector(filename) -# TEXT -# Load an example Landsat image -filename = gu.examples.get_path("everest_landsat_b4") -image = gu.Raster(filename) - -# Generate a boolean mask from the glacier outlines. -mask = outlines.create_mask(image) diff --git a/doc/source/conf.py b/doc/source/conf.py index 99aa20ef8..b7336b425 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -54,8 +54,12 @@ } sphinx_gallery_conf = { - "examples_dirs": os.path.join(os.path.dirname(__file__), "../", "../", "examples"), # path to your example scripts - "gallery_dirs": "auto_examples", # path to where to save gallery generated output + "examples_dirs": [ + os.path.join(os.path.dirname(__file__), "../", "../", "examples/io"), + os.path.join(os.path.dirname(__file__), "../", "../", "examples/handling"), + os.path.join(os.path.dirname(__file__), "../", "../", "examples/analysis"), + ], # path to your example scripts + "gallery_dirs": ["io_examples", "handling_examples", "analysis_examples"], # path to where to save gallery generated output "inspect_global_variables": True, # Make links to the class/function definitions. "reference_url": { # The module you locally document uses None diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index c7224b5e8..c5fb753d8 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -47,7 +47,7 @@ print(raster.info()) ``` ```{important} -The {class}`~geoutils.Raster` is not a composition of either a {class}`rasterio.DatasetReader`, a {class}`rasterio.MemoryFile` or a {class}`rasterio.DatasetWriter`. +The {class}`~geoutils.Raster` is not a composition of either a {class}`rasterio.io.DatasetReader`, a {class}`rasterio.io.MemoryFile` or a {class}`rasterio.io.DatasetWriter`. It is only linked to those objects to initiate a {class}`~geoutils.Raster` instance which first loads the metadata (notably the three main metadata attributes {attr}`~geoutils.Raster.crs`, {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.nodata`). Then, explicity or implicitly, it can also {class}`~geoutils.Raster.load` the array data when an **on-disk** dataset exists, or {class}`~geoutils.Raster.save` the **in-memory** diff --git a/doc/source/handling_examples/handling_examples_jupyter.zip b/doc/source/handling_examples/handling_examples_jupyter.zip new file mode 100644 index 0000000000000000000000000000000000000000..2711715392bf5fbf7a6302e1f134af6af24c15b1 GIT binary patch literal 7010 zcmeHMPiqu06t7lAdJ}r`6e7Y_bX4laLJuB9OGOqHFK!vLGuhc-CJ9NV-BEfd9_mfO zn`gg<_!+$UaeT@A-L;Eds^Xv7c6Miyyu9S)<-K2i)8Ae`v%2Iy_Ya>u{qp|c@ZI^P zC3jtbGEGSwYGN$a0aLjczFI*DN0f7eH_$V9K*$xjk1q~wt|(ODNX8Ujt*YpRMww+& zgptgIg$5$yysl2EB{8uC%B$X7f0A>e42_|9M7W`4Z~L{J%&f|+AIeT$Xk?}%H-=(( zerT6dVilK`vms)FGeL3L*^d7$yR4K?PbDS#MJ)G3GoAK$o6#JlcpxsJ2a?20B#4L@ zWg@29Sh#g1kGy5gKn%o_F*6AMdzvLvcPxCwJA2qh8xZTn*TIYCukE)SGr9P?n>7)K|*b)h#mI^nC?W_(WZ=MUoh}5VOlzsb)aA^n6j*f$xz~-P)&RMupo5#uzZ6H7(e1VhlQg>lNjAZ4Dm(Cqr;+D!krQv6-{Fp>gPTj<`!WvXES zc(#?wlavZ;LJo=FwBNZ0F) zmHqw{e0lkG`QZnEF7B$~3xp`=Jso_h5mI|QG)^-QzC0!t@E)^tD+R|#9%7&&F)aFp zz-3x#2VjalF%N8?B%yWy1^`SLX{i*AJ0OGbu@6-pkZFSFv5nRNnGVQQ$mVDuvjsfN z=IaY*z5irrcltqm=4AqhpkLFi5ML3 z3Nohfoga}gI$F4vp{{koD=;-h&DoqExdUb1p*aUE3 z1X5$q^wxlRawNQ?X&e#NyHZL3rzqhls&il&p)rHpHpx3a=RfCjPMuy|p8rD1zo~++ zj|cAVyVq0w?P>8@LZ}h{49Lohx9Op__)##d5q|@y%!{`{S%3TNIoD(v{yxF)+l%h$ E8wTNBGynhq literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/handling_examples_python.zip b/doc/source/handling_examples/handling_examples_python.zip new file mode 100644 index 0000000000000000000000000000000000000000..3857f7d7b3ea25ec013f56fd9616ff553e959bc9 GIT binary patch literal 1580 zcmd5+&x+JA7@u`q4>^d3eSnY3LMzgfr@|aOh%Vz{&}G3>H`8X)K$?Ukv$H*VQ1Jmg z3nD&%c=PC41mDAV@Jrk2GK0Hw8Zd3zFUj{W`Q`V`rsLa3qtKqcJ3agH=EIk_2cuEw zcX7@#TIQBI&unU%D2m8)O-rFTP*uWGmCQPiVwic+syQ;*)d)GQgadqID|qEW=~V(0 z%4J2$r7FD8ib~-d&Th4L}_!-aLU$C#4#ZIm=@IxcEzCec&N8kgHB zA((NukdWC1VxNBg{{9}(3VlEfL!u8?<{L|GfHzfQ3GQcEJj-chl^wRl5Cd=B@*7?e-s(3w0)c2)zafwH94kx)f<+tzNk}$p~NzJPcHgAM^%DeqA$f z8!QK?D-glUW{a-}w~ofw*VwlB;^*`0AZ=FM*>ux(o9$P&U-2rnwgdOKZMQGol`S9l Vw!;;iO%Lyc^)Wu*@IOBcqrWz_L9{ErBPM`c* zmblq#S%G`$zaQb8#^?yE`)1@xo z{ofZLc!uKS_|J#Fr8=g%)PFu^`9hh3@c#ReMn#6E_21v|x##u&`xXChp~(JkcN9V6 zZ{q*ikgMw4X&NQkfvX3%sAG9EdedgET(6`5pRL@|c=>s^#e1DH^yuj5#y#I~X~ui^ z?s1SFI+6X)-aVy}s?uwpn`5%Hw48AlnchAzuh4aW@+3#EgcJS$_`cBA*`U^0uTIHn zdqJMr0u3)o>D$5o-T$YWhuw{*D*E1wDbB*QIGE^%Ulk-m9u%nF^zF5h*rlqhtmHab z{#{&Z`1hr9eu(5@sAN$8a%it*>!jy&?AX}Y!A`Nh;QyYX*|e6vEYWS>#slBcT2G!= z0mn8!)}H)w?Y_xDhSzQAIpI)kVqzk7est6y*zjjl@}Q=Fwycw`wUt;hCe6?X_sEaV zbIO~YI<~|2=m|IeJJOH;4t7i9ErE<)qoSA-kHEiO?AiK%FNnpqQ0T!ZB3j{~wGXwb z=fBMP7|HOm4#fG=2^#iGoMuI*kBA(kR8JiXHioxkNL~M* zqrNOBm|^f&Enm}HMKZ0+9EEalaOgfi4W}@?%RtgJGsAFx)_8I7R6cLV6Pt+kZi#m5 z&FWuFfj_6F;;QUM<`&b88Zf8jrSSi^^7KW2_`v;HpZjQ;=VFJ4=a|3O@=Er_>9R}j z?GfYk@v5EW>gfQ{^L?X>tNr2!wCBqgr`cZn!;iL`i38LX7vYh{$TN%4^_L&uV}+)% z{FFDjLxxr-J6dH&vGHY%yRy1EFF*g12FU}9r&W`F2rlN`c zS7dZ*J#HeuLu|Y43gxwH?fn6NE20C^Qd3hs19-{$@E74a;7NX-dv;@MqVa6va797# zIBse_{(L2VE86H3J@9NKF!ssXPsZdf2D*5oIklpH8-D92vPHFD&Cs7V)6cL=9^rbd zkB9g5^$Gbq2-C`|sS&GYOAyZ3Z@E|L$#JIWy+xPL)3BdzILq=!3&)qf+@umV;na8y z*6rldMT1_+T>tr>{;krB<5I^VP&i6~_BWN5^sF;|Tne&s)&N8#kkG8`_2P+S5 z{M+lNJJy03uze3P#Qu(&dCi7Xd^Bx;7JYt?DY2=ihoEJa7iGM=)NAeF&_1Ll85OpWZA9xz?xxZ@^r_MTafCD$)7&}?(IX@do-;v0yuvHfCPf$JSk@3fm zF7VW4Z-0NbK=SOm_IsV+sVPIhzZKbXeCelG16`306n-({T&UdSbQR7>W8`MQLG9tx zK?8PVYjAtJ92Yltt`;X6YUfC-VJn=+xc`;-i_FY6e4e_X+Kn&x64}HDzubsXO^dFx z(`U7Tk&;KrlFb$U649a9#B<5nrz}599@~Nj&h^K3pAr=pF7m0Nijv@A&!SGs0R7kzgb<`o$ zfEL;ugsdV*it^{}4w~(Cr$8E?`t$lzk+e%>aCKYbcBMIE_NLCx&STk+IInuu8={&9 z2T3_o@Q`cqo~&-zziB^JM^&JjrD8uM~7N42)LqJmaeZT@>wr}(DU z7_7rD4*vaAER9g^fHebk>>ON|_@Auip*!>K_Sd>0}~q?+<#jl!>QqjFyHKd(25aO5&j{m$;J8E z=w}yZLC0S>sKe7`$#}TlxyG~V3#^&yg)-Gv5DzpE|-<;xNeAMfq z)vd$%_}1m!*)MA!S|cU)7zv1pk&Szv;?Mq{{V23;=!tgYv@tUEtw{HO;LinHLa*hl zYYjOj?$K1|<^gkZ*Qw@a-qv}z=KmjK5&s8#L5Y6HZ5$pgb-Bq1T}4IZMRs;% z>#TZc(LXpgMFusnMxSVXef|5Y7MkkpBPf@FxIO*-Dpw-^Q{o>t!zmOI6%8Dwmzm{@ zWD07;6mEdtwv@{@rdJYSTJ}^rCrGm}EX^o@>dKWX$etF-lIKj37Nb+h8wgS@kl`R} zNqJ-#_1LNYDh37yDyXLB&go&NWXEi4WMZ!+8p^PgV#L7n+P`uR9k)Bcs$#6|&mXS8 z8gj4HkR9wl%PutKHIjo=^h^W{Q5Jl50uSXminSrMhq5f&Fm^rF}D^Q5CJ)8*&N ztb2QVaCmdHiy2dtXGhD-woc9te-ucB69;UCLg`64m^&!c;uH=z6<%6eQZF8UJ+h`z zIs%`!#KmKw8X?`(|6ua-(n~vI0qUHg)$jI#iOTuBw{D>o3`JdGS2gzYqlFt)Ex5v# zjJxec+irRp)_vdST8i=|^5{!9Z{Fmkje__u z8$Ri%kgqAlMJ`jQm1Op~co?ojor6rhbmTHoWX{N%iC~6Cp;p2@w#5GB%dzqroGHc? zV>8ae@C_!slD`A6>1p^W+1<(OlN^UzLR-#*jfNyTcddR4dz4cG`MN?679q; z^Y7LIaQFu71s6{+td@h8cD-RgG1JB8X%|POi)Z(@&TkuATjLaJ;lb@YR@1;|MMVX` zoGmZ0ua*@bb+}f3|CTP%mVusAqaV>adzUHkF8ulNV;X3{=Uc=VB%z_9uvSvg^QX@y zr)Q6rv(tPh>;=1zswUPCtBg)=|8gI?4-4A8v4%8-cDvGgtc+f^5j{J*8+wl)!=)lS z0pP$d-_3gXh3~XD-)R@aCltj)X!k)BMt+xvZ6AJEgvS7NOQ%G8@8F=-w5-XtYK}kq zh~UHLIlLdXw`nBN`U!O0J=(`bqa+ttM?s6aK_bWuM5lT3q zK9i9T22a~fc_|5y&x~H==JPcZt#m5JWZ9CX;OSM2J)i#pOKx?K?aH2yxS+%Mzs@X} zu};{@nbDhDTx>VCQsIeTg!$h276%@%L4C*b6#3I(Jz}6X!p}18}V&$nIyqaDWq3qK)i+ zzI0duI1FK01w%HJvGPzu;2PlqDG9K2Y2hy5`%K{Axr@LFL=H`r9&%!=qT89^I6?Uc z+uG8HCM!T~4WD*)GO#CYt6nCQLJ@_n2FU*FvLHO=^n2wWt3Q2eG3Uc=R%1*4YR(5h z3GM>fsIXl}*5JpbsSUW(NQ%A0;`F)M1)qNUB>3VU8|+K2n>TZ`IGgPS^TzC%=;B+A zc^OD>Uh8w?>n`rYPr`QP7%^iY37X!F>|d7QOAqT_l&&{SlM{vqrNzkzKe4u-3t)D4 zch}m@KoE(Emyr`;%zL+V#LU^5gr|Ow)LrBnY(32B5U_5xiHpl+z#A=JzdqGJs1t-&n{lm~O1hb^&uzBcmxXk- zp;cK1lDkkKYxFySPCUEI@bS++3)%7wgB@yH!Hn)#*vqXiZ`(QV_6p4pDA2d($VBqq zz8!8`^{jTY!I!EgpnBG$c3^Tc7GV$Do%Eeu$?S`I*bD~_@R}vs$-Y)LHcEnR&|RRb zY#6{Ac!?2Jj1i=DKWUyW;gZ4I#0K1fyQS@+tQ8J>9K|)^K$?}6HTubchMb&yX5R-2 z^fokoX3{7I*rMt8a>wjj>;<85HLtqQP5ExhIXP{XW8 zg@aQ?yZ9>nAW!3Ix!89lOJvEny~4OUOm9K68w*QVXHv*1AhARr#7PjQKU>9eQ6@lorQ$b5zYh@bV~TSu#Z9+_u(bS z)SYh?kF3FENgLh?APMqg&*;SpZDv?_X8VLQC1hkRyxBOY*HT6>L#|MZG@m!SAOF^Z z3u`kY6~pY(nrlraM;tVNx_InxX|yqWa>0!I&sB?upZQ4;CwGOxNjELy7J3VB{Ov9RG<5z- z_912*?VS+dLS|3~0rr*Og393}>^Y970F6~5I}%nXDZ6`d#-lc+Uv1W(P!7d|2JGg& z_F8%Sk+fGumJ6&C6vUX28)SG;13D3i3f1n z+YS#uKX1be4|%@VW64iWL1B@zbL0nG%!$Ky%Wb<^&GlpiUI`O|v4lIsuZTZk)n;?QfydW%S}*uhJ_i@2=J>aTdvXH*EVNBjd_qziAoH^+hYw z!cwS!t0lv>&_)2}$Z@BILj5V!3W2W-BqXk_n*|18m2Y^`x~1Su!XZq0p`8E5@59h4 z;Fa zgLbiNPvIMQE{u92Swz>aU6Up)bT4^at@>Oc>2ARgZhU8`Z519I6gbtgOlUKI+$cE#nTVV959(ivyW8V@Ji=i;| zLTg$}#d*biE{W23g7$UkwP7i9in58LBOGfYwq&Ibo_PZ}@tqir)5NfTtbvYn-hDlm z6$$rWNoJ`=y4Ltgze+wvrv(_xjgm9|40makE>yS=qZ@rQ*ZE8RrX{1<_$D`|4stB(a#SCH_q?O z{K?hiOhMpnuVs6h^kQB?fx?eeYXQz^bAZHlcEu!pz!pG`Q@oDtHII>vPt^4iDh2@A z2tt}I`t#PnCV)5qiKn*24+CKT7iuYg|n< z9ip3Lcu)hNo*{oTSIO#-^is2s<;_>yD(=Ty!4+-r?a%ZSTKdD$gp1$GFD)f?qsRzaFGLCVszf4)Npuxe3$QDN_uDHLi^@HcT4`m zBLGXVkD$(eg!cTcVtO-;)I%^Mh4hZf=VoJdvim-ag~CJC@}`ft$Ss=TU~r^$1DtV< z`OIjn8_UVAvglEJEknqOym1j2+i8I0`1m;NTY&yyhdT!;%BujrF*kuK|VV?$j$ zI`ZDu@9XVtHs@=$7Wh^r-N>AB?R+eq-^zL6Yodj0>Oo<%G0C9H;*a@lt_S87V_eXm z5%NVWkj0f3e?q{^)c_^6U!HHIXpr z9G!H&1dFazh`a)|DdYT=3ZWx`5NxWP9LAo$K2vD^Ac&>h=P~MB+yB)?1-uGqmcPG0 z#lB3#ggV{b@aS2C$ydJuFV1ut1M1&lPkeG{y`m=O(mcY$#Wf=oxGPjZg-O{FeB$y<4Y^{P%}9SA3H) zzq893O=isc2~l+8SLqSLwL$~^imuWmaQShGHc(4ANNw{j2vZRC+-O5UMHR@YhNX{s zR^ejP?_q&@1T9NG?=n0;07V`jxNc#P$OTWwE<`WV0n7kYcZz6{+p$i57*IOv8IFpz zi-9S_J93t#l2wxom*7%f~ke5|ak$-rLgoDZH@C%?^0OVkS0Ja}YnA3Az@ zuwuR@Aa`jJn%Ias&~&9hBLG&HqkcDU0O(y)TN`Fl*P8@`q;?&y2t^hqyemrmt3w5s z<2&=WO-)SDfFRE0n=|9}mrXjR?TF%DL)8RKjE}=<{tk$(p+N$9WRLV+E*+no%sLBy z9~fZ7jBA^=tjHO#{#qp>B*ebxm)6btjRUpamt<_`pIFJiW%!-*r9C=Y(D``)fGHcU zZ;P%^WtpCK&I8jjbwOlztWekfB*h*6?c*HV>G5$+Jl?LHE_2fTW7GckI$UfAx1B$2 zhqEpNy$5}Y`vLlxuzYQEcvD2i#aZKp5Om<3x1~>NCl3)-7ZIJLfx-ev4gj%t2IO@h zxc)<%TaF-4AQ}LOBZmv@l2M4bOG!ru#5ch=yPw;Ua&@*!hYJwfpyJ!tTLB%lMJNvs zkGazG-=*KDr-@NsQ2n=Oi32Htsao!UB9&PQ*Yj+Z%3Wi zFb9(PMV#+O4K@NpB+j?v=kwDHTR@&fLzOlh|1QTK0fF`(MmD2A87)8fvi@lTR7v8m z7mW`#qmacPSy|zLj^qNKL46_ai$&p*@#Z9B&}E=pNP|2D400B@4;T6Nw;QFXpy$*N z3dsz8*KdQ|0*pZ=iYHM~u`msPV`k>{b8!bjnlE3zd@%)_wdw#n-L44yn^Hzsx}%}>KBsS>7^dgEE?|_V%sQsK?0SdM+fR{( zAj0DJH6?v$2JTz7T`Uy#sB(r~&+Ih^Oa_*u`?n>s)yVZpSv`(yDHMu~Sx8~yrr(o) zijK>!B$UA;A|hhW{Q4p7yU&_7X5I!9`ne8Mvo6D_Bf4>{QVd6z4B18Oe!dR|2+Ez7 z+&^h8AUmL(uLfy=EHFwgLe9_6h5X6&O0*GGW#!MGw)uF;xb$=?v5V7PAk2r~BPCd9N{lW6 z?8CysLJlN!d;p3xLqkKFOP1`CSFI#&{N0?62hec!%G<4mj#_2ZCg`3P7Z<~&0apW}tWivWE5imU83PojXzb+|^uvgKQ&5*4n|RxVW5s zy1+iYb!~MyH{`1(rVwWC+yt^7#vsprPWc=*6WqkJ{^O~Rsj zbbEQscXGT-@Cz9=PXqj3h-Rrq@tV|(t$UaOA^j2wTT4zZkMF0wpj^%~`8=pw<*%We zZleIYL0w~%(Yke?E&1IiE*ienu=|(AI`o_R&xE4LYssq!sY*&pz&DzsSeUNW=f*@w zgWs~?ddEhe!Q~Req8>^*$h&7{^@n_m^Mlr;I~Or;UX2jpZi6tW&zvdkYdf9rlKWo=mg>cK&hScS!c>9S!t2QyaA56?aude>#H=D-0sQglIsvxbNSY_>05%w6ifpqBwzlTvu8O_;ACsF}gqSeAnso zWh1ZIOYkHBIw6YJ;qOO{$EkF$mgj&Qf`I{arA39OJ`C6icnPx~YBxxw-(DJKlX$HY z@@CuuXxQi^n2qqy(6nWl%3@GkQd!-L0Gnrbk4SJZSvWhODQa;>%{|~Cd$T}>7e2Fl z#48~YXVw`9uqj-9X!TE{a!dD4QL zm))f1HiuI`9qAT+#|)JM_HAQZro;7h(G}Y5$1!_YtihangY6yQaWHi%WYl)9DXsPY zC<@fO4fI!8j9@yH9I@nV6lXV`Kt1D3A!X|2lV}vh<*EG)=)&t>yd_#apEpB(l_kMKC!zIhQxTY!4c$np*txWcWS6#A%5lpF38WKYMeX1gB|v z7jOduX_R6EOGb>fZ+82LZ@7|ZkZ0jn7#IC9F+aIDnWESlxqzwFy?o0 z@Y6|(&JwPzUch}>LGV1Cmpo?%gJYv^S9NSOlemr>8%pc+G!JIPZ}H7qkS#$T0`l{l zCf*3-If-lv5L=VcoIRM$t0!172EiAiB*?9(xB`c9+$BN>3-!^IF{xXI1UL`lGXQen)? zab&VQ2lV~&;HrScZhAl0$u43716=6*4m}S1Hov28qYf}Hr4X|( zZy`v~HAfZS*z7`1`fYjexy z!LUi;M-UXAi#l-0^QB{=7+v=Iz|^#vVJNexjQRBAtFt+l^^ot|hxb(rNTm&~xHbv;4WGF{Ah+B1ZVv@qfLu9v-t7#0To`i`a~Q6@WeKxinO*9%7c}$o zqV)Xb(&_F0Sr0BKQ_PzbDFZ)ROG73MZm+6(bPR7T?Ag=wg!;hIVlh&cV zJ%RJai_MExpi>d?{(sC6I>@58BK_URpsJV5UfMM8QlJsg0-yrm{DBa~ner@b&CWl$ zu0vO(z_TB$1F%g&c>%8&+#%B8!~?#qFGWkv!XI7cpSXZ9oSw z_VPM>^cJ0yqmF9o3HoPXd590Z>P{Umam*nBt_`9bHZdvzLAKjcThoA-a1m(N4X z?vD%(MgxHsH0me;CAMVH^?;%QosyCBU`w_j zC>05VAnoPE@5c8fJxe|+xbT?RSQAjzp+$hCfN(rWaxwL~B`RXV!GX1hwPt2!Mnnwx z5~%58W4eDPs?~~cfLMbv0Hg}Z4j=|P+|V7Ox(fhJ5s{IX@xqayhKY}lUY{;t0*Jdn z@paSCJa@dC5_FY2!ZQm6Qlt&~80=p||NVy`*PardLyBbkTD=hmnNR3AMXdbvYdrL( zA3w-mc`s3&ZbeGgt(-)(njpt%Z*_>mua~0nP|ojsH#_aZN?`*E3>!N;m>6Jbg5(c; zhMBkrUp!%LUqoJB-k6t?geWrYQruwnf=BIkl$swpvE(T)g1Mo(S**>@ngFWN_)htn zHda1ACl4gQw~Wr;E~)r~`9`bV5L4E6CjOl=Y{UEQVRQ> zl(miTju5U^!^#Mg)OR1Qv6}Y|aRU*Qb{V;q0UQ8lStSJhV0**91SN#o^a!JFrGs4dFku?Kt>Yo4U|u z&NmB0`#A?4XN{gkx-GqU@pzx_I9=s(Qron(0C#kW_l+`Ua7H@guE}tbhqjw0b`Y1K ztH~uj@VEjtcI8wmFKQ4t4h!$XCI*Zoc=raKUp2PzxnQlReZw2Wn`7GTcq z3d7wZkqmf%bL|FIwpT&;ZdYwyi|mw!oWi&aDKQE(s+1%N=TEc}fGeREQ`HMYN&-qj zOkA8PbeF^PgT{CO-blJ}6yOvn01F~-l)sp?V!;*x%y8GQnd{2tXxaSD#WpD4Xny#?Bc40M3d3!9K~pZ{|SxZ38)j`ZqCTiAAgsdPRoCZpJE?8GIZE4$3Yg- zy_mG|^G0c%<~Ktp`EyV(;S=ibV(`GAtA{Jto=NRL);;Etun*Em#Ew_;!2AswvybRs zZUyx@sKT%a&K*2k0wSVkA-BLVbQiff{|#|8A3uge_|bJy`MqA%Z$bkPIIy6k!8u20 z#FyGyP)N7KdHm3z?7evT5;{4oy_AvBG@nHj9x;V$QaVJAlp<$E(T3%@JM>=!R}@{& zxKn4yqt8w8tepuiG^vXP>df?>ca~Xqh=n$fGTEZC(-wfGEpZ+B!>8 zF3)tAI2~Ynna*b&VZ+Aw1}eKWYyoC};tV(>!j$N(9f^bM2Y< z(_;=PZ5(E9x9sqhQjkg?(2GL(tJih<;~A=!beTcWLTvdV9+n=~TSPZPyJLt1%l z7d1tCG=Ccwb6|LwH7s?6ub(GdMvR!{^`E<7^MJu4!$OL{Ynlz44!&*iJGqG!A6FN- z(z-#$8?E=P{`Cl81C&|>5dx}(X6byqX9JOxXwnfxCfxfq-8+w4M~4^KVD`F~?+<`= zPtWRvQ;@MitI53MhQ+R$*#zDzQz8yxswK0l%|X_+4WO*cT1hqVf3*&Ls3dUO0YuS? zd8vOGMvNFx^uYb_Qdw4%nyqgE#iMaApJ(>(gpiT->b6?&y>`0T(J~+Y z0BwEiy9{wxKY~(F<~b7XDfIzR2dprt=sQGjF+qD1icuc|*r%=66vs#5FljA==gnPy*O7D0@JVKU#E? zqevb;%j<=t2a?Hy%>p$Y)QKpGdG&cCHpD1zC=dTRl##q=U@p? z6g)y$M@U}oyt{}+(IvM(uqiok^?xa9QU=^FrB}jA(R<~aT2Q(0wxr~KId1x;uLDk} zz9o`_K$`$uLhQo1b`rK*vYG0`cx8@m5AL)z!Xi4ehj3$UFL`@1-}+58YTX{o@1X@z zn(2NeNUI?XT0BrE4L*|PVWK-%!npZC9fEh}`tO^ChiSzd6-E#Lz6nE5pv zS~#7+g1be&_JV^7nE{X5c9kyQq&Ly8jD3E5US;T7uE~Gq)v%HqZ33B##*33iVpR8B z(n@;%#fyI9)ZsDND@2jg?mL%<-7KH%__~;zf+YzR-!5UMqe!!GW^1+;0@I&Y-)1;N zuvz89{Nvc_8g=H*@Fs2#&VgSzPo6w60oV?A*?M>=98@8(mnG_HI^Ez8h_Yk7dYU@` zd3gL$V31U)hv;3UyXU8qON91_8Kf+<)B2nnd#PC1*2 z@AmM6-O;gm+P7IZ>$VCxQgI(LV(~o^AfX`6No)lZ&m=0)B{KY|#;BgS$?tw61rJf3 ze|axw0kT{6uNS0=TO_i?-7yKs$ts>$?|^Le4GZe}^Zi?Ls5e?QU`BrW^a(%Cyq0A2 z5_q87ImG@rk3ZQrkB(9T8F~IRwD~m&zC$MF*)#phD)XhRklIybcMH2M#fZHFP==)2 z!NUg;MNxMTakpTS08CjfJR*N%ttA9}fW#IF_69KMXaR}>*bsPZ%$h3&=4^B+(S8z* z_T0BpxFbX!$k_l5mdNhx>4_1*SGO@gjs8@e7R~;UJGqPH!S%}K*}7Nn5;U9PyHYa0 z0Fbt5?)hcU+ZgkWHlDTKiz}ESS`J`mm^33qi4YuTdL1-spbJ16a_d|e*^YR~@KAkI zfnbsix@0NmL$>ctSI}-6n&JX}7gi%rV<6-#bJP=z-IJt~I0KwK8hN<6x$P8lT-Th1 zd7+a#3v&^2iN&2}(PF$?H~$%-Rk3R2;i0W~i$(y1l>g**q;nGN6IPhb*>XPBm9CG! zEzggAH0V*&IIMVOhHKTg!cRm3qsqd`;SXNQ0{)qVpvSftTEd3s5@ES+krtM zxgdyXBQiBS2Jaz#nIy7vahilF+;^N9rJjWRkCG(1LFWR90x>ZNq#zN?>B9~o=b`&T zvk<4bO234_W&mP-2es2FI|qORkP(XdyngUe9AD=bTAzzTt|lDPq!5TUgDjb18fPbN zSu&B=8QQSDU^|5;7~wF!fecDO`P__FErV(T2wkHDPA4xPUr1jcQujf}LaIOKRjRzy z{^c@=$3ewen8^ysp8^XL4DLB3rtvE99B~3QD65G5i&Vz5H`msXKvIHg0WiMy&DM7C zlv);DkuXqO)D?gXA&3QR0?Co*+`4gA*Iz6e`|N{Sl#u48&^l~;dzF_Z9SR00)>lLN zK8!(9(U=zpVgw-k>2P6zObtlo{-wQ*ZT^pAZ@{;sC4zE`t2DDcutf9I_BLb7o_cxa zS4~cpxY(Yap4WiXPxJRm6HhONO06yo3rYeST76zX zs>hkKP+Jz7R7A%d`wm4OEeC-8I-1)R^D-R>XN6gR{P7LzntZEz$;RfU0tXoYpw_*; z${1DiKSXYrW0(8gkfbZ<>?kWC{Xca1^iO~ya;M=#Oz36AyD2o1ay4nq<8!tV3DWEaD_5^9UU{)ke=oJqmp>G(I z_-c8k8M-$3>(?)yeNiOeAtEw0++Va+9RBi>A1-nXAb|ss)|}c}8pv(NdL`)C{_r4H zg=7$no?xL?IejG)BvX~*uZc!zafYX+lCQptmCpl*5n1XsIcr?XTHR@)%GoX?l?|=~ z*qz%j&|~W7r|%~)?kDP?z|%!Yp+Q;*`>m(97kBox=QI3T!SUeDUfLSa$N{EbC${3F zTK#UJ!)rw_R2i^w$DAKWlAWJUsRy9rACKC$tdC|H%HXx8W-kg*_Fej!8mL=twwhMN zTl-1LYxGv{2W$E1EvrK7ZQ33Fe9i0N&*jzEUjs(O&(HsTZJ&~cMn)uy9KHPW=g(Ye ziAChYlpdxPxDn4*!9xOU!qNGQQe&Ih#^MSJm)GrVC96V*%e8U)OYX}eRyTsNw}IeW zGC_7Hq^rCX8U|mB{1vFpvDfx`Z>{lA5=cXI5QKNwaikV;vGQ8YtCMe}JHDaO1bJQC zsv1|i&7w4JNk4UkWs(BDyTj@2xPdbG^zR>66B(WFq?Or?a3k{u0CY_WXDSjPqM4b+ z+4eRS@+ViIwwdP4Zi}Vi>>T?Pw`re9oSunI`!(X%Q(T<_DhfyqMvqLOVn75Jf&t>t zM&jT}l?Ln=udJ*z8F)^@7>ugBr)M-zYJbiF-hXqmUbxQPH>;6K<9o1?qDa=E?W`Om z=o-@S23{1rD4}b~dxoGL8v}Ad>TD-{AT5HLSR-Q>#gbIcCxE;83Rg_L2#d#y68scF>`|ogr{RZ(ifdVHyD6w3 z5FKrWpofpIFT8LxylU4LMi5*D?DA&{vA;PwU)@R#!*2x3_Ji~B;5%hF@>Ii}`gfDG z4C#Bi@&=`{=^-1^mSzEgx>$2FobCz;E>IhT<_zQp2@X1H<-Hr+hhY4wJ7ln$Eh~^1 z5n_0Jw@6BYM4xP)M%Tl67i6GOZ11ggRdA}jinc%HH}22ck=l4JEsPxLYo+*uRKMT zcCtcPryo1kTtfOcA~`~wTceMrKkKI;Kv(BhLLuY*-@~qIHmx6~xinSq9X>Jn$H1-! zc^=_GR#xoF`AMUq=)q0pINXLtzwYjMZs`nScgco6GW5O#VGU4aK(i6{_(KV_xy*al z5=n0Z=s~64{&1b6jvgjgSYb--F4XRx#)S~VyKMH*k8SuMr>X!ftD~bsos0aIu<+rH zvr|w|5rP4klwdg0j7Ii{Gcsi$+>48g1K`Y6O-O+Drc?6MqC%z0xG<7<(JI@Y96`lg zRGwOum6F}AY!t#t;+$y9)YV7RT4d>w1!tM3;^18(oB?R0q0cHS5|4l?56Y+%L~93E z_sX%JLMWabB4Hs0$z5+={9Ls#%fsr<=6dX!W7(A)mhz1JDi3G-pO1@}q}&lKzT=7% zoN{~bH3l#Y4`MVO%w?1SAy^W5+^RSVMjOl&@`YI&XcmxaeO3e{B0sY?iUIsg$RRZv z@nJ-_=X&Yy`-lUzg@Ll0tYrWpmeWS49~!)JBBkS0M7?PaH@qJgY$k^PPz^>4%rFYw zZMzSf)`CY_ChSpj)(tQSaSk~?4&A`LjazFUi;GooSnGphaA!SdH(7G>;s<7(6}b&S z3+mJNNUAW)Qo`hN*?qb6r2k+^{?8;=bi-BWZ(lnl&s|`6Q@T&QzzSv6zj*bLelP%2 z-8rMmDftrV14{^c0crNyZl@@7TULckD8yo!vQ$-0N&Iv6q+k|={yF9)C}E#)rt-u- z=X=)kFjCdBysLD6wN|Ek zvAieRoTUv;uVx_`P(eVz&S(20t@eVAR&-fz&4*DS4dj5a49@pz_rW4N4Y)UmOsxg^m1Yx{)4?pu~-RfJhdaqz_XQsLa7i zla;g5t%eW#as$~WGgKnP27@)3@*9LSC^=wtp>U~^D%gLo@C_{AhGJuW9 z93)68hlPhI6TdcqAE1%L&=d;rEv);q@eTss61Bw=F@mb1QPH#0`uchh_5rNJ*Icao zs;baIO?zM7#tE06s~Y5P0MCy67r+TH3A-+Hk>LTMqon_rR!43C0`?keif=2=Z|3Etr7-0Jirn?Z@`Z0n^tCc$R7xR3pu%9eKv*;`F3!TBUT>Nqc0+7KE2y%J zMa)D0o#Hnol2Hz+7N`NN3=?bnAPOp3efh+n&hl2BvZ9zAT* zaDhZH;64bN^Fip3g-)bz>~=;k%shY8E77E5^Cx>fD1hq>xgI}=FS0j(kEvmF znYa33*;JMuHiFt+J*3k4ZAD9fI{F1oOrExUtsasepcsJmrWvs0%{YeLS7g{T{ia&2 z7Wa))!n5quh22fX7hX>4DL#!!cW3qq8sNy8z%p+>-O9puB!#-E`bzA2);UCd!5_%| z?c!$+-4d*Sm-$$sXTLk|xBVxMAB-rGgm!qz@K?4ian<0dtDd?Db zOGon^ql>PEyw>3dSCOG%a2Ku~WnYUE$aCA%0j~Gs$Mbs+k#V6i&{Y8BL>103y}s#N zmogaXA-FPe+jq#Wt;_-31HNl8=3+ct^bk~)L4U;roCs;gHq%=gb07^KwnEj}3Ow(> z0Mh#6xfYdKOZzj-7n=%voss1~BV`sN4N|mbt+AHZKCieIs9Ho{p^;ZmSoE!~u9ktk z`%H6a((R7^YEU#0XD?CFJe>(ZF>~v}9m^ufVW%kP=yUUAOf{EtbzbIUPi$c1)&Y?V zCgHo4U$Z2z9I^1QVD530N0WK^OD){87(!F|@o&QG@eVBoIXN8bfvG9(yltKH{AHWT zFHaCr2$J|Nr-v?pv*rQ&%0oSaa0y(LoSeljGR^_b3NhbKcl(hzJ}F^KoDCfX%;bXe zZSW1URb+$e9GLN)750|g!wN~Pqy^9?MnXQ_sDbMxjl%J^psb&CgmFQ=pNZc_*hz7L zCVsW^F5zoU&cG+tD{=;D5QJw(ZvUH$A7lS|jm5wL z|7MyyW`o_8w(P+nCOV64KZs(tMIn8cA(h2s)eibpOz8Q z7Tgiwuv)6aWeU^?Jb_tfou041&fGAs(i=k}5-4H|_$$C3Z%a%Gy${^7aj&g3C+M}ND2iR2PyFo@C14emOtY|t^wCfYE+cR zwwD+LO>#G^MWZ3Rhs;B<>T<$>iFzEyng1zQ-QRR2c+Y`0tY*8|BEK4a@@&Sd4^y3$ zP~O}i(?Sl#ypF2t8ZIx5n3of;^i$a@Uj9>DN^ED*jhwY#PcFyk&py94&ZhHlb$uO1 z7*qYe!-x^gIi3(c#DbN)dPDm)>;+^p3MS z+!@H~aM|k%uh8U4OPQTMzFw#voLiHL<#v8eg>p~Xz`864J3`{Rhf`mF|G%jyB%jRh z7W_j#;p2Q8kEG#857fYq+8PK7p$fw&b@@L#xlb zh{pTxKZ&>KgV~URq3e0Lg%M?!!|Cc>YwEa2!|1rHCkSB@rFoKeTmGYsg^v$)@6y;^ zN5O7u8dXIBrFW}pKT?MYu|gA!1hL98dj_nbD=V)N#4lCB-VI-I#pojC#pRA7-1=!{ zWkdus6Nsr9NB_@I1FtYjhnkwY1qkPx0ZZh~4eacai!f?;s~E0>rGW&g0h&RyO9Y~6 zjO5WK5X6KLi-cPlPs_O!oo>N~6CC*8X- zz|AopBU$vo>2Cwnckr|Mq|9IircalIkhoJUCfA5Y@PNEe!Vl|$4D0S!dgZ2q8D$>p zdLRISgkS~{d(GcU`I^Yv3%Ja+;o9gOPEMa0zyU^#^}{Z;048$?`Zhy0Z}^ds99wcT z3}zsi zSSiz$IUn9l;!e7s8eSMyPTSWWUc_69YwXQ_TQpYbuu$Gc@)V0S>;<$&EG zT?aB5M}D-yKY#v=fOw6*=j0P4%39laA}R*M1^}u^%RoHm6%fFLSsAdgikLfVEh=cbE7H-A~|2|3gi($ zDksN>w6=n$rx55sUb0mDyM|IoE&$$$iRYHr(7<~MNy&H^BeZ#nz*A5&KL%a|z;Fso zD_pNG7wFW4Y+EZ#=)(h#1gjQ`XBQ1Wbu9F}^3l47azXO0s)et8q?w^Oc0f{#rSrRK z84|e3t$hT_BdloM;av1#THp}{qPi_JZTV1^cntjT5aY`YHArjHt?-987lGNLkY)z$ z5BgDnp!e5CkX;7K&C2FxTDdE{$qN8=L8qyIV@*K1tEYU5@A<(M9ImPbK7%QiwC-?_ z2y7aV77D}0K)+x^SX?H;3WY2g>ARjJ{#s$!&s~snwiEezx01{uImfe9tEEWW@Ok76G?=BoY%f5aa-X<1RyV||0+<$nlq-cImt(?0+l(;cT8y=z?r8 zB?R%8F$T2mtNNG14=~{UKq&5Fh*)*GKBw$$B6u-OO82rB-u25p+LoOr;SlXEFD04U zL`~yCZ^#Rs)kTgtJV0dU!3+|-*9xPXQ9RhDC!3A*5U~Z53wghch=_=(!2?0+ zSfodxaE^wkCzXyXx7qYkdLZgmP=F2X3%qNHL_jk52Gn(!g~zO&jDxxki8YY?LA`_V z=6N8kupwX^{@=T;fGBl+?P|0fV)`K92&@}F33^c9{u3w@(B@tHP*L%q!Rhhi46i#i zPu*tO(k-qhd{cwBtKdvYvSDDcgR#**#_41YzHwEPq(JVU@J

sk=k87#@XAJKN1j1p^#JYT7U}IYrukgz|!2lAGVh&NPTThyF z`I<@7AsmjxDkAB8a9|?W)es;LB$auXS1JuZ!$cOM^O~>?Y#rN+ZvER+*dWJp4cu_^T&hqqMMtWYd!Cuj8~6H4-dY`JchSx zqG-MF^}INt^=q;X^)oAb@w^t$02j;C{F@$E_)K2uT5<^u>TEzuQf@u+3(z0!Q$PXQ z3D`-_o+&mu-o6r zQxB7J$lK9G!6IKjNu>Q)Q3)wZm~*!lXqn$ue9hymJvn;>4|m6S`OcEeC+%V-FM$-! zfJWdGLmV&eVAs*nx6P}xN_IW{l#za|F_Tm(q-%u?4lE>b;WZg!xC}td^;yO*yyXV+`aH>lCih7(|vBvE9+)m4$hl-=LG42WsazCA|Ohft{q z{7HS{Uz8t}6eiI6l0=p-_*TSeiK2-UWcgE+yXvfxEJ)1+?aKB(I1uCzr?HwjKAp1J z?SxP!@(`Z(I|CU5I=ORlC^j&DX{!7CTs86zBp@ZE6Mwjw3_%7>;0MUk7Ar9X7#RTMF7`z6KBn#A|sVV54qZT?< zMsr65&`T5?_=Gsg2>lBEc>w32Q9nTUA`*^FBNWvS^X?f7?)@OM^t5#-` zjb<^b)q(EL$oz+n4R@g?fAc#uxVh%*%=Z5uYhU8d<=VFUBgvRj2$f`(3KfcwkfD^R z63Hw>LM5W8NJ3_kDN~^&B$S!VLy4r6c__&o%COJ7^&Q{df53i^j&-cH-WB;h_jBFX zbxv17^uvdt!Dk~?P)~~P`}1=PQV2)8iHl1Kvo0EE{-Q28I~pQ);)$SXEZE!uWd)8C zQCx?lx%`~@I66x_IJo)osf4KU^FQbqpR4S3BsxU=3_8tH(MOe%IN9`eMMdos&Mx=i z54zbHt61q1sjF0Qv+wG_K3wx;;z@P;mO!z%$0(cRwq$qv=F*MQPa6>3MvTl-F;T14 z!uI~gGdm*l=%)jn4lNvHdw(FO{{j~@lKDUHhz z9g!ph#SiLZH6i-kb6G|K)iW`qZ#6cGZ0^JC*zj?TLkE! zV+Ry3ti2D1d}`aOZ!(u=lo&}W2SG-0r93luMcKWmYvI!P?X5c<%fsZ`zjW>jrblOK29ox!Ht0KF3 zEq$=nqkS!hb2~Q*1k)7$%XHZm(}au?$ar+`7q<+rqG;cT5#cgB%i6U`phmZB*@A(? zdDqJr?i&t~#d+SxvpO2i%d;L--IN`W7N=BRYP}~ol45+6ZJ=1oPy7WBh!<8o0xQx{%OG7-BW4iPPznnINF^%m995Id^~uBtPvGDoLAF;>PUZc za zJY@L1y*DDWPopq~ujI7VK+^G6mG&TpwL$yT(JY4jy1;4_Nv*yq{qy8sPQ*gZJ@BcW zyry@9Qak!mgSmx=;`cCdtZaO4l~+~TiT0F3aa%tCU7I`%)RR;tbUNv}Pr#VJ>+JM{ zv;bpu{k~6~ogx|=BP;-6!w6;MK9*MipD4)%0x3?4bm%3`TPP&{_)3Gd%G-JQ@AHcw zo#B1|^|ykXMcecw(jQFDgAu@R%2`+=xZXI1_V%G9MIx*IjH?IKD*-j~8qagP+z+$A za{00epjk2||GG|bUOv4&|I3)<7Uh^z57hXBkzxV5;OZXEN1z`hQ^)F`BQ~Mxir;BDDD@A;qDy zjH|r(`zMYKX>(x|n#99a+a-ALFgnr&OkFk>g4paVQn zHZYK~jw(~5JT3!+yWm~@rzd&W(be91x-a3}`TVSXTQ)!{;QG7MnqXc~iug@EirduM z-5mt80>`PdS0ZTo?L~!WRzKpvFI1pB!JPVt_X}2zLpGmdiXG;|nLv?%fu1;Ap!3jIdnrJBRc|2TRuSoSf(#lLzvSA0J0336gD}Ag#fL<5xWAZX*E*R@uRgN6b<{fhH2+bxJ@k0IooH#Jk9oQ%eiMh_kyVn- z0#J;CV7^u^vJPJZgY{ho&89O-&7Lylhre8@pHAJmDtGcwRtMkawG9@`NKRtxh1rb+ zvVooEd>^UCuYAbx%mGG5z1?E%7pffB_?gio4_85;Hamm&WJ(a>mo z;cfa^g=DwF;Kok~!ch{Ys%@G4W**DT=bl!kbF6RJDq0JQ$S6lAC+$8nK-VO*jig`v za=-A*A1*eCJumyOp{7EMTl;s9v$`Ee-Jo+ro0Wrofi}_kf#VQkCqN5Hp>UOpxBTtJ zcr%6xqb@{4*NWHTZ`z-p>YtzNXANznO~kYT!jcl~{@v)@WGeMIb7(jI)!3JOzIAll z5cFbUL4!8OS$=AvXmcrWW)om>aE)$kH~w;E>14mdNCd#!{DJ+>x+^Y~G3c`JM}}XF zfOm@!*6si@78ST#4@N*mgu7CzgI`!E3HY)sqP?g~`|#%hT#__jsn;dB$n)?dn>@Jm z_T9PVvziT~)8aRC2S)LygdCEIiS~9{sF*vBAqmU42^E{q-4LG7>-C3SGMy3=`(~}) zX^Hgqg#yTTx2<%_JdbK@7jim6Rg$4JCwi3+g@GK_;3M6tU$8+!XO6=uAmSv>s z#`{@ZoSgVMkaDC90NZ{W8RQhVd9B&%O2NmSbd58x+VX6)-lWHxAHsRYW98X=n&#-Pm708Zn8XNz()3Qp0W5JGl0ll(ij~dE56%MBe^fx5^x34 zXXI?BcR~wmw5ua0F!1*>K9fG~#^LECF3FXk;gJ6M`7QXEEvP~la}5hIJE8czt_90B zuuQH-ho2rjciJ%508m3aY~37t=+vpGz21REOYz zhmC;&T71~HThCj9T%mmdg%w_9fY5}xf{hzgABh--gg#})oybccy?J9OIQXvRn*7xK z$u}?5MYCT25cTU3VfP=mQ9tQM`44ZTB6#b_J6`gkOf95+0^ZLX)j-~TF4eG{It!(3 z>w*;wPoEB6E5~~1*RRg^bVc&(;=R573nRsV<`9urQrf$Tcr|{I0Z#BD6DO)JTeq01 zsy|e;(GDEc#p?WuOcj=N7x`JsC;MvvYh8T=4b2;uvCu6t9K#s0?>g7~GNyo2pU>?6 zvlB2U5fGE?MwNZZ9rrp-W?o%!aY+Jej1#a?Myu=5E#*A0o)r%H=6JWAe;2rSG@A}) zTYr+QqtVwbIr)Zn+aYr!HDpvvjonTa4?P^u!|MJgN$t*vUo1kWE&}9TO#u~#h*whR zzdbpVSwFu|ieG}&H>Cl2d|QJ6KbT*>ym4u1$=RNKZXP3tc`Bd@fmT4cv*?vNtJ@an z;E}n7R;d5g7l%q*hCT(0h<2_ZmZBGAEH=F$M_yzGA4kCL+Cyo!c9pqq zvFXp?K!f*=Aec2$d?!EIJik!U>ty!i1{kdf;}}jg%g`;&_Dp7O@gI!UXbKCbt`X^h zlAg`_cb}n2zZ33>*t3+xfH-(lxeyPM5x%2Sc-m^N5A?FVBETBp#_uqMeSOgw+%`H( zI8>iV(a|@lMw43;MeF|Fn!AluKD1G4JPvz-1_J6KgCZeevlHUuX&p}%$|08lY~nvv zzuA_8cayz$vfrP-TVH!Aqe*hm`rzDn^BRce?4B3W0s1DnZT=riinKNp z9CIM8zz>}J*%(#>|BOERNWb&t_hsO+An<|w@7!sfn>u2--R*-*cnIa4b&9}^#y8g% zCu@l2Qo}kE(4=GmUR=^4>AdbsS~z)2yRgUhaD2;}3}2734;!x!)UF5MhuG<(a_;D< z&?%5i32*>DRa+n*#YN}?vUFdE(QPOR>JCRSbD((#-%DhGV5y?g70@?8BZgn*=H?iR zAWKJ$!ld@w7az5d_)_@wR zJFU@-imDT5(OxM%LV8Zhz&C{2gqeTZUti4zFg@tTO!CODUy-4s$Pc_n1c^RMhcreO zrAzr%MY;ong>3)(NNTz#LeOAiuTFP*k8gTU^pX^3@!=$WF?JtUysqCrqvgcUhJy(W zN9&17cW?gHUVA-IE^4n-e2RlAIgA$%5hDdZ#|!kIchmD>pSGT_StoG0HN<*rweHpoWIxS0= zwjPeZ^zHD>5)cKvcX2>A zDC;57Bj!(dz~+$?%}dq#+4?iOk;{Leu_zpx|M{q*!86y?kGp$LGv)p*Y9jJ0@9RrS zJNr{A*}0r?N({-2ogd_b=ViS*R++Z!yb2je$_JZh{XbTZB+Grf{dbq#J;HNc@3n|D zZXZBbkWSP8K5Jw|LlFriClDtxw5gi2Bz6`kHi?5n78~g_w+8t?@f&yrMFb)fv!mnV z@8DabpFWvo%nU8t;LZ}EcKOIBdA&`GvL(9}c2nFCc3K4;1`rGI@t0rc_BV~>o71QP z>>u=J+!=EuO*JGOR|!ZV3Uc2<=ee~aoDh7V_&%&wl8-F~(MTOz*t9ajZ2<)UvVS|Na zl5%#{7qE1Qa71o1#?614(DR2|ws`L5I2ZXNFJpbf9kGZowbqMJ7e%eM%jGt7_teg| zE{tH-u@CJV?sZj>iW9#WI`{{kHV_E-LTIVX=YD;KzVhE*1%NyxP9Br?loCwE$Th3V zuNS|#{L4Rr&)~OIOcMwV5?qfioA8E^L=h1(g|JO)ufWj*(i|>#TH-;#S0#7~i5a*5 z@@njudVtF%T`1~0f;$9uY%dy`|AQGC5+-hGm&4Pat+X=QZGAp`uL79>pk_T2t%&^{$K+GcJm%F27UtPs;X{}nMxBtr+PqEg<==OCR%d!19KaCKRF2Md28E^cz! zjn5lP-TYt8A);{`4qvpgQmv&}hv>YY`n%MZp2*|PvIAWuqD7Gd*faa}ah*k|hq$OM zyiVO&dZHu>1t32eGvB_*V$RO&7$r3!r=@Tk=LM-4^dU;dFS9Hw%i#v|knc8}s>E1} zp`+;cXFG%w7a-FFAQ(^#CV=BW1I*zbLh@foYcrzdD6wDTf)EH-zJ{j?)rJpUahX|m z4|3OuR8|aR3Jc1RmS1XE|?TZ7o5MAkffF z9J{8N!kP!2F$Ch1_x|u5Ltf;f1W5f4J7lgX+c7?R_;}}`$)XpH$B%E~IIi{nk?#vk z9vC`YxR1O>t_4H3S%-l7+J=-#SEJ|Bto%J<3!K=ofSVX8H&y*7^pm;l^1r0oxucLu zZPr^PDfmCT3U#^6(=O*g1}hBQfqc?Ghj2IvScrk1sKeE{&M3jQ2;aF0B00W<-V+Hz zd}x@F<{cWlevg6&5HNF^4?D_K?NJYUBGs&7_m;#uqZb-iR1gUY`52>iap3vcH}9bC zMl92MyhhLap)mv|I@$R9_wS1FIfN>kV-Vbkcu8gwL;Lssq^O56*>lh=J6?szFL98( z*70t%;2|35MiDHwhZ5{%|9M|pn=uox1hd0ER(_YByERFEz8h1C*tKp=cX>5qHz3fm zs7g?#5IHyM5LwfKteTQXTLv>dtTL8>VYVJUg|u(rn^+(rPNhD|+?J8mMMKmmjg17+ zmhqSuhrmIQK}gyWQ|iBLT!Nk7(?eG+vpMpeeq=1fDVO7&&&%=f8x@^i?%5R*7t4QZ zn5+2vk=~SmN?#U4SK{ME0ZT^KEdAuQVxR&A$kismFU#qjiEa05%r@37EGo>LIQuN6 z{OdBZBY^8b!3se4E+LfhI+9!{*plSQ1pUzNdHMU~Ba`42G~70MHJ&gwG9pp)Bvu!b z`;+)<8ntY)xACf>vl-z1_U+qRQ2$kR4*8k`&8UdP^@HUNu<2t(fGO^!q!*-}f2jX2 z3hJJazjxA^ObzuQ14sUTu?6a2=Yw^;N20PXYqY-I^5&Iclz_>F2jZHR9?Y7TX0)98 z?!MZ8lbg9Z2xGWNTy{!Jy2$(Ude5n(-aFo6;JB#z1o;K1vE;)?`*#>Y$>D{2qM2Y- zSpJ@{Os{~X@)X8X?#$DFwbikQg;foUVqRD^ap4{#Z=>X#G$6NZr#D|Q0h0K944PF{ z-d#e&e_p3OTJVss`;*)f5VH@VV2Qo_wQ|Qln?IY)w=FhBuk<9qF`P%5)qd@&<{{R2 zcwhlfkykYK;sxpbF+RlO4n_ms?pPSyHKq|92*en0;eqjkwd0j`5cT>kTio$?%+)NDL&|d62?^C^lOy!-E|akDojt2{Z8Fu0y{C z#u93hj}!pq7CMJ|mnbCXHth@q=M}1@Yz^9C((n0HSM4pY z4k5J*#|K|I)a`M{C{7d|X!M0^IQs=K&{_&LOR7NY(`nBxsW@Km(>MqS7`B-Py6S~s zjZ{ad8V%^sc4V}R8hcEt7|?AB;9~GW0uxe?CrOwHpveDnMW@zTrXjLS8Nbjfzm3nJ zqL<)^LQokL(oJmk&(M;CMc%N|f?`0vEK1BtcKxya=5s^(L6gxEj#r z_@cy^fOo60%<>mcci27gcKHkW*;1b(;}1}hF6$U19lw_?!F0!pBirTqt|fD$;L}{! zaqVEZvg|TO{?36o;TF}N7!|8$h@`da(>(YXs9yVTdS zsY6}`xEAI)!U7^~*q4Q;8c`7#W0C0ii4*8Fv0MXy!6kudGFvG)<%4m#c~rw-_z z(fk+3H-4r;p0rqtg#+c&V&$<46cm*_pqSNYTjv$Oi?qAkFa6I7i=`Wy6t34WKERI! zz6nSaeAjDW7WMabdAVWLqlHJN8&|PKqW_p2csG*u^*gEwnM+BUI#OFKd!KcTCaD1$ zm#B=vfR2DvFs3%u2MqxPF%|etpeG^r!e!lO$^rVurrQxgL0Icsu>IGLi-?=W_Py<;!<@ zix8W1_Y~gDOzw}R&dLvH$t1#`|pHFkU7*7?a%LD}m`q2ZM zCM#0F)Z*16vN=q)VCnw3I{%Dj`67Q2g$&|r)WLru0^o}VQkNdo+^^rW`OD@1(cK_n^=tBGrmiIv7fMHkW z%8&5iX5f-#R=K^^+!wvuZ6nedjl54&zag0fV3^204;7?95!V)un*(Ra(D2OPCH!92 z$48a+qJhXr$8TD(F7&G@h5FbXLlu7EV>1Ub4^tMj-1vnYHl;M1Wm*!R8@-b$cBkPS zgiqcqW8v(g^e8BObA8*5#MqHl>a!{20VV5i6Q$Oyd9F>%!t^#xvA0+%)jC;4u$(J= zei5%Kv8jU5;4=gqCnz6)nM$MsAT|=i?)=C65+5Lb=*#+%7cjM;?r=JC z(7L_)6>gh8oZK3+u!3M8OT;~@C%XD+I|GX6zl{I;2*QSzP5Y_aBI-m{$B=X3z#BWZ zEeHljY%Q}aM%Cqjj@`+ol-gPOg9n+RzUZgLXiKznD4U0f&>rGY00l}vZ|0`j!dr({ z5u^6karkMcR_}ipWPx->5>|n-oa$b=4)eNh~n$LCKfb{pY>K(Gp~z1grB3D!9bnW#C05I`3^JiSvloAW&e z9tm-AFHBhtt_677YW>JFFaK8TPWL!u8XdHZb=WqQp5(Pv}cDSCjjEB+s;G=hX_P2bNV> zJQRBWbmXjB8g$x-BqD?UmPEuD3LTICx2_99ee7@n*4*PK6kk?;dkta{Ej~ITLIlx? z_{XRPmd>}4M+LKibwIU3MmoF&9yhUZ!|2_Lvpg0RV20PAp^MRddHT$m3K(HYn+$Xl zs1tZ<1glX<5+1pz5&mr8cpQE=hR3h$2a!tr@gptrtXi)Bx>NvPBiX!V=y3yegQQ=k zSz~@rs>NHW!^OX8u3YU7bbDw%Ycff8ohafkwQoD#GS#Z;`tkhox*p+ zq0K2b(POx@9zBthXq4PkEgz(?I@OO$?; zB=`4cF7h!jwxh`*UdmiI7ddk%H2`J&6GPh=K9!e@Ek>Ri8tKdS~n0M0~EQA8pX zvUd(<3V42=p0)B0&a&BSoN!5#N1&lWFwDk4NG9m*f7UEv&IEgZ zPP_!57Yoo*lXu09J=^rBufig)y;MvWAN22NcvSIccy+NGzEzjWH48ChCkpuLPr24C z2nX4#gFjPQXL?*ya z>x8#xF;X?d1+-|pa*9a*rmg?7zxDKE^Ao$c<<$BX6rXSei^jNI=2+5*&1?9?!^$Xi z^M!H^>!v4HPZX!#s`5CvE2pu9kG}0%SK2-5b$MYo+8_-;Xx;RM@1!Y230rq1SUL8C z5N1^@VZhHW4jm7>4?1`3SgE~|xpOvNQ;;t6M+d@gfSVAFnxtpc578E8zkPn0$N)jY z6BJMAv?MSnW3Z0as0gtaYtj9hay%CV(IvV!H`D92-4M=|ByQI*0h-2{4fi3!igz$b za+{Nv(%}}9v@|rC3KU$sN>sxB4n>(hj z4bc|B``YL5()3%=5Q+lWD}c)g-gA7*cFF{g$V|4kv?J4S4PN|&<(VfyMWD#?t*S;b zl3N%EBO&qCO%%M6Wu zxyd@rK01FAJ

gRy;Cr?mt|DTnQNbm8zYOVL=T67|3OZD6Vxx*7>t@6$~Jl!~IXG zk8(57P_qVLQ42|%#jSzq=KT&M69k1Qzd!{m2b__aIaUN3B^DqZMoTasz(4EsKbQe; z-d!@kop1*Wo)qF6gJ?Dg0zZn(-*@rYhjB&Y%S>6w)!`fL83*7sX}H02PWNg13(SF} zSi6PakO0v6VKdw|yweeYnKfzN}Ig_Kf;HXFIm8@#`0)oxx|Ra=~(QGiD@&D_TrC@l`T^1Wiqn z#5%W0T>FkmS!n|43P+1fgrxnTaag~?U;He#2-bCE81vdb#eO6VidMqt$>G2a(t1*f z2tSwcZ-Lwn1ysluea?~D;)HvMJbwg0{eu+GpI5Uw(gNukoeau#g;_RHevqMFDu&G` zswOz)!Q48{7pOB_vIOM7_h}OQY{i!Gq)Qy10g&{`>pFu&sthvR(S4T>49>r=c_zSJ zB;Pq-;c@))G1sxlKW$PEf=@V$bwda@xc?FexY$#5=C>O>{~TYTr=)m{Lpq7{G!pu= zy1HVI-boDf4m{~0CI0PhO<$9sP%-!al09CB zJ6FM#Uq|NurUv8J6;;>Z#07%RPt1QY3VuY8wN?1d&(fi z78?LIiL1KrHZMvI$sey1HMjRew0DO7Q^%PRZ8XQ!xTwmJ=rYh-S4|77z~4FV%vttw zsv?PwQKMUEnu#nGWH%*i&6!JPbnT}jAAK+FPkOGu?X-qP2M^ytBvJ)KTNi-oa=R^@NxcV8Dsp%JdhO%R~v!#Eg2t=DEQz@8ZN<~(Ou0lhU z(V}ada9zy4ov+ zuL>L`wR}?o3;md0{JvIPF#FLExofK-^n%U+zY)+E($BTC(xjYp=vsF8Ne0{Q6+8SC4$ue<1kayq>Lb;P^IL{w5WGXH?uCTNhBsB6aUR?j{ zf+R`f3C_l*_2t&KXnKL(BmI8T6Zy1n$wpU#Op(-U3x~WWt1u0^Ebg&bn+w}YTwV&S zU-x z0qYFWFoPdi@y4UT7NNfJFc6hg}oqaLS)n?R(A z4qPPgDd^1bSM-nyD+?`< z(TuXh&}{}QD1gxOLRmX7%ftz(u}(&EMG{Po)mKZVw;&i&gKPb#^tXEL@6W@ z%k8~YZe3*zzkf*~)OhIeNvA~ijQ2SpA21@4E`i>VZ@1nW(>Dycvx@Hppx;VG8-0g&I$7EGXC5i4RnrYP} zqf(IrnrqjgXzPKMma}QgH7oNx^XFDZJ$t`&FH7Nio#gPGn zGbsM2_$4EK!w4m1#^`A~yomfYcQWWS-xuQVp&w@Tvr`=vEbns?`h6)$=i1`PXJI=jU3-|c)5gM>*9^Il) zARPuT@*co-M@(#(Eo`=JPh-Fs0u75f*0pX8IzqQIi)yUpyyjam%Zp!LF=O4%Tghp; z?kEPXB}cTzLp$xVG{;&(m~uDQ*@!h+=?+P~`Rahrz*76D*!!*W9;fN{NL%9LJR-B1 zT_u6@>SyMSMF(tOqd!K6n)!_yw47f zH$06Ei=Knef#DAjx3B?I4VGoLeuC>2hETHiXHHc1GkSEq_IuZ7iy38gWJ8coWE&gL zJKRUC10DlGqw8uJX{en`VTrSOiJ8*uoSW<;R$Iwv(nr&258erDzfDFVNS^vEFTeS! zbS6D*`k_VJ2B8v4@UiU*vPUl3?xX;V2Kj(y(bKT|(4pS1RsX*hn?!YRGTmXcQrs7F ztP6`A=dlq>cM)-ATwb{Yd(l~v+{9(F{ruvn`xLKQ_c^%CX|c-^7t!Z6c6k5xC!oII z?XCf)fgQy=8K1pBA08)2`Tk?@yhtd2=q)1e0N_WgdJx@%D+FmlcA^5b+5NY%D=~+r z6^mRjbk`}$BH~av25l8~c@frmSiqCy2?z1iAd0gT=pI91>JN~<7N%Ht1FFJMO2g^p?MfEh7st09NB82dzx@OmLQpW$HFP5?x(n9H0CLJ_sOV zu-xK~-JqCOFThIwy@J0}sM9PAHt<7oTcb{MV(BJI(s0a;LoVvh*|>uw-yVM2W^>rL z@WIqyyN1Vm+jK*EtiS*K=?yusDqHi7rr~Lwsp0)5pK{PT!{7tI8Jg+H)xt4H1xf#h z)(#L+i`;a;W+4LK;rgdg=n8vOA%dzmdwpZ+8vQS3A0>d0O0LbbH>VyZfO_}dy1$YfmcG!L;M*8B?Fo&-vuxoePG5XunK!WfP=Mh{#@XcTs1v$AX!2G=|GNz z_oPzn?W4}e1QL#^1LWIo(A0sZqty^ebH+fIJH@L`ljjnM$BjM()11B3-&du+ zw<_NF%r&n1g!qKbZ6NZ#P5ve1gy&+7XVK3VhvM0f(&6|1;bkAtrcUN%U1vs%K-sol zAivAmzvvre_yk5p;?CkLdJ`N4UuO(QEF3(o{5Br7?*>|Ye~}P9TrTUN6(dU~i5d%Y zX6mj`wug!7>>(cF)>&sV`$%7m#_z*%g+Q1LiCQWt*$1fdJN@QAm{P$MAiAT^Cg)71 zq;`K-VOiOzK+Vdu=Q0b(Me;K3KlfSN3V*buq5`AmmQ8#ZU;bN8OGkhx0G_p45AQJ! z``d0}a+wIi7p^A}Xl)Kpp{9m!|DNYMbqt3?DE_xq0)KrjV zQP*}C>>thwFn`{=Gd!)ApN~}yZrAW!r%o5-_ff2cmNIrLOW)_ap&1QrdoLhv)Mz3A z#-_}$I=b&jiL$|$rco;M-hCh~5uyi=d2i(A^-+E#l?7oVfydHnOG<$jI7>#Wu9$6eT~5!MC+ z2ei0#55(^mCeg=b_0KT6JfgO4Q0Es3xSD?QDMBO>WvT>bj+o|Q6ma3}?fVrY%A$D| z7k|=TFni&y^KqvSv_}ep4~LOmmdB(y_aV}A+iUZ=l7Xg)1%p9_!w-G)LXl}|8%Vr>P z)RF2-6BE72zh?7cb~+l^bol8`%l;C>cC*4uGMk+}Iwu=f`!64)utt}+q;C<3X4M*mjzMGxK$Ul6HTKdxY?v$Vn$&=sb@mg6b(>Ra3quJ zNV54oLbZCEfWp?@THOe_>06W1SN-??xBa+=>IJV~h1~%5iHdL`K|zw-Cl3AYYk;LB zSJ0yz-OCgw=5w+@4PD}Q+M%lg>&?T60}<10WZ-S{QzE&_h4}is^^1#m7Hzw?nmaVU z+%l$T<8~H8I`DoAi$&)UI@YK^e4HMUTCXqF?6aK!`@AoVUPCi9?i0Usp)i0z9AkdRs z^%O;=Ocq3KwOD>ucjd{}bO&_?*o+|DPG7Hd^e+D$Ur8-(F}%5*xVztIQBGx^@c|oy zqwp^z20`OCP(V#bZNE9JoA}c6@dZhPe1|Q>u}PBTh`MVS6K4LR{BYlHtdQ$|XGE>I zUEw8y;MBx=#ieetoEY2a4Z(t&JzglJ9|lixwTor8c*`G%RDCwjuo2i_hY9RGoESLR%1#PtY}gKRXUYpWDx>T{20uPLViy>%U=O6t~?U^#pYQ1 zy&ppK%XuBfJASm0aXtx;3`_lpu?7)~<0ObTT3APT;|b9S{s(W~pH~y3Q{NRd2?D%i zI&QoNrK8oU*z7F>>NjsplZHWKesFNF(~(d zTm!gQZIjpu8VFK=NF8_V;e_N9_A_r~fMkW$g^6fLa_V{%7T5B(V8})@&voo&45jCa zC-2jBvHP1gYf+rF$O6v%FAyyXyb0>O3w1J=GMa02;sl1T3SICL3)3AGw9$C-0U!=p zRtky@NiPKG1eFb=|%vSTe1 z6GBKyLLoFCSgIWsU8kp#=R(pTFsP6`sB`lb955(~gq$T>v!YQNvMX#uZRAeF35byT zE-nvU{Abu4A6ZgLd?%U5A~Py<;8fFx3Z$CC{f-QbmqlwFPabQ#rn&34Jc)FRTCcWN z>T01;s6 zv^kR8ncxSQx`2tu*%|fZt>-m*hlViKMrrhjxxvnurur&+&_~E>A{!3xi*@H*u9ai?J_|Yk zv)md-mypImXI9Z;EL()0ofw;Sl_N@C;);X}4HDR6Z6;Pv;sDD1x0x4H2?_dxmYZnq z@W+5#mYCBZh|sA{edr8*2BFHTF&SG3K8JwE>oV)H8|UKrTqu|LD&2!>66RPv?+Emu z^ElK~Hu^y_P4eUDl_H1&rM#r z5&~<^xfg?`Qp;VPRf-$HbX0Hj$dMxgJi!^egQ8%*9DU#GSca@K@F7XiW(CmP40{LJ z2U+&O<+Ca9Fe7z>IkaU+`$HZR>HXmInqNN8X8?DZfe(Y|((TyLs{2m3UmRsT)Nh65 zwUkQph2b?)!`gtbW8o*`h6{}$Oa$Wl$q(ryV6bqWCZ`itHbcXJy`I3|Dxyke1l08( zY5X--{1>y~9>1zKv`UujW+S}h?rExJ66}O&X7@aT9#-Rbk`j((5)UyTV^N9?iKQ*; zRB^_lE27)FQ9fr{*k>fJhh&?3)TtMkdRtnLR8I7u66@~t%B>NMX);4_F={d(m}E1w|i{Lu?{B-wM9 z!TE+cJ@kE6m9Jd84|-vkh#7@7r^G%;+z#}|HcPCyOf^;f5@IOEuJSce<1v=Bqws*n zLk5{?D~=ONHgG?sX4hjUzVdQf$RqEQZQngcd5XaUHIq--&7*!$W704%Pw?*(8_Zp- ze}2*BXQ;RHhC?cMEe~h%yvMpTz)a4})a$67!5`!N;6P8B1_lQ9LU9z*T7sQxcoIOb zEH9O>{f=1&R$-zTM4k$NfGQuHAtdimLIiI~&2)>x?zmaKe7DB57ZMW>+-?+cjI^hA zL@397laiXJ1dn>(lgV;(VA?d&ME67|-5cF!HZ2qd0RaJOCLTwQ_#SH4yl-SAZF2(YSQ7qr4P$)l zW@FPm)Eo8o|5<{<;kMH2%TrB|H>AC=mKmsA0D6jr4#A6!iHChDv9r~0%_K-JJCG%f zYR{P?p58Fqu06N%r{mwUV`Abb-s&FWHlIWJFY)@H?OPBm%=?$lGa|U(17S9_jxHSY zg)P_Dp8NZ`LmaXcNdG*>?9SbW>6qLLC1c}t^o-bdEorj-AdkG!#LbR_M*Q?*y3Xqd zAN|4n3O_Fcq9GbIoc`Oa)1XnuK|Rch<=GPX?#16MdE4|9WTL(0*}-)pk!>6Dg6Bq| zg&=e#K6N#>hZ5V2o}It*YiukcXJh}q(@P!`{iXRmF`|geCB<){=>A-U{1n7 zK_Je?Q-jn$rx(2KFPSIqbi*QX?F*;xSa6;mb=z)WefPz08Qo)Ts!xFmK_cfL4hW=a*|KeS=?{%<<+Iw$h43*b5iK`vc@lQM z;i;HU?hdJq4C;F8+B5Oel11exYw(S3$a%`#oQI8^?EcVQ6HVzdfn$lChJ-dw_%59uJ*g;3khTS3;NfANq{ za5doEc>HZFw|J&+P{$Rp4_Kahi4^oX(NcvEt6jUDbAoF6*SCl|x|qE~fh4i2+L

  • RBTK6Sy#L_-s*bCz%z(Th8Ad?MAglwc$y08v@Yg{5=Ys6Z zY-I+9Qrn`ZG*CPCV(-gK3NN<9dkEiTv&&Ppl$ zEM1IRxrHjSvY4=vOLJ%iw}k5#POq}jvngd$bsM|Nr%M;MJvu{E6wmUY&H}h;%4TJq z{}!p2NGqG3@9sAralf7S+sYO z+#L@2{#>|wqKKx)V6T>V5E(-7%rfEQd zN!V1V75zrD{az{;E49lQg7)ZV2KSp@J+GA4>&id^R5cRLKsKBJ@OR5ScyoRLRL=p9U z87oFf9tbKtRPcOFQplJG$?9t#E3^Ml%-J*n7T|h0ypL?Fr?sv`oV>M?P&^lK|F3+O z_c-OY$b)az@7#G6Z^fCdSKn8kW6_YaG3uC+R_ttDcE5$WyTT`fi=km*GM&eqq`tWt zisk27?g%wgf0bd$ro$?Gn(EewrImQGG?V!Fnv%I*PhlzT@ShfwvB`X)6xuy2zmZ

    M0ZutWrv23r6lJ^STUW$S+;A*K-lb+}FA(WVVVL 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�_r4^kVMabh3*&paH>VG(ek!jl-G>*Uk=JTk926LFkQ__PExUq@v&obEivy$;lbnla zOx0JyCyUF@ec}f*S1_9P$((K#jpGqDNX@su)rGY8F5%YN>72kpA=x#)A@8j0GQ-LbptV= zav~z9u1F7O%cA!7Ux61uDjG)k5s4AvP7bhR`^(2w9o+0Et5Zl^ z6~ts?laWG54@`N9iUK_r^|cLSqs2bHM>RJL*wDGg_lw+ezZQsIL<%1i%@!|-*d#@sJcsIAmwrfDhgy4GXi22E5%C7!)f8n$M5kUl z$?>u>U~}JR^Tj&c5c0uj!_RH0YgsWztF$36g4rlNNoKtrpou2s$<}V^7{&;024cga zn$^1VZU@^Ojc4(u0->}(^p>Rw3z4^ji4Q`kh%aEF)!cMpo?wiA;mHXEp6+$%U!tY- zwP(GN^Y47bn#r;xU&wThy#1W47aStEVWG4}Wc>a9MM8eGkPs!#JRJy>tp!dZ7(??` zP#^MXm3BTo9+yc!?6+1jO9})LNg&A5X{XXt;~$>ky6XHzIzdHX023*qvSRtPTyvpi z{i-n`uGX*W=F>>)5KUjsVfsi$f7b*Ds4bazAgDp98Tm$&?vw%u+zRi}q&8$;S6s8T z3(ng?0gu~9UG-S|L@k?6L17@4^@c-;T)0?ZIoXx?+Z}N^PCHT9QPo>JGC&z!4B2$8 z*;!3fGchqyR9yTM78cepi|-ME3>d=aTN?i=NyU*On-Px%v?`SE5~21I*ueZlh#G0> zfpmeiAJQSYn7Hu!Td4-R1ybSjF8^blZRs05K|o+I0IqP-@nV3&OFl}!KNkW)c=dK# zgN^5+VXo@VyH`yB-y$pNCm_h6?Z6+}f#^jRY;a93Tro2nxkJ_%;jJxS`x?_=Jxd96 z+jzY0^!XCeCvjj_Jwc!Bmh;H@vX&3!EXL9THOkHPPPmA5?omVUL-lUY*}sAaumz(m zi}`$(witn_b=_mL)*-77bL;6adoyosvn2^E4oi0U^Zz zHIi|u!-o@agLB>;ysi#-i`?kQWO@CiKm~;RZa&_OGL}|VQL?fowq3MF3chZmg!6+A zvjZkXQTY2i{a%hXI(QZNuAy-BzbWtD#`4|30^Xul^?vB+==eC74hZoQ8_-YX$>_wi zNMq09={O!`bnoJD&<~8^H|L)@D>0&UU>ey9K{-0*ceE*&R%MGGfLzgAKAcpOR{O8M zk%KZ9q0#b9F0ktzJ1}V0t4t*apR%fmB58t%E;{E|jTY!YcK3%%e1|Ch=#X#Y%2N3G z^Cz&Otynf3z0J&oXyGE?h^H4Dk)Orbu|yJ2%|omW(*qyLUKd)zB0o6k1TF_+zs%Km z!j#gFQlaDZMBz*u(w)0+O0vLkc9M$2J^}eBN_LPVU)L1dX$gm1YnxQ-ltS&4LhCR% zC(2BX%tsVJ?EBCXmh%^t4iQL8)yxPXV=V#OiWXMX-H0*B`9kO>reM#e)R8~W20@VtuwH}>)0)%Oc5KI?WV*k1>5 zZqQ0&&6LhQSDm?1m?jPpH`)U?d3D!&#`$WpTN>aut|t+N>%0V_H)v18qSKgaax`S< zdcE28brbOc{Hqi$!1WsK@I2O>VSw0Xa$LlltzZ-u`xUl-v-ENWBF={EB>%q|8b#po z``DP=zYJc7t~d9D#Ke}ZAOs!$iwt(#=GU81ud~{r=A*pGf1aX1&xS{*S!H&2RFvEH z{`PVUn8tDVS8I5f1a54W6)Ms;iB@CxY=YDDB{3?=G$c8wC&)GW=a~^Kz6$zS5)sd5 zHEzE|nNuc4w23--crX@XmM8_nue+D8+BdDQG`6i*+I6kFpEiD@Q_GQvSD~)zffGhI z5n6Lg_6(CL=AmG$(zYC@7;v zN*xW-IiI*PE~jDG2-UIgwxBC0v7*zd$myDW`g3Wp~ zdEua-pm^V*a*&j}#z5R>m0^U3pz(Z8C{v6LR2`gJ>{56IPpVYtQ$H5M*o)~LD&+i| zqK5ukHE&8hjatVW!lx)bk1>XGNn$^Dz)}GR(eE{$h=E=bp>!=()=foPKmBN#%PbvZ zo=)L(Ay-KwC zACe^T>j&O#Cf0Yy&L>LMVuk&5%irKzz%S#|h01mS4guEUoNlYuZ20E%lc5EBNoiZ3 zOO4L5Gd3CASbwb`>lgiyh%AT8Qig3Ui8qlm_)9}Q8TOOViF@FAt23|sk;Ng$nWtP@ z!3ztFvARbp0bj7I*6kD$(sFLygHuddJz2)hZJ{NLzMV2F#Z8E9|IciqvWGL z?OhZxutGeJ59FddZ+0tF?9nVvQE{UtA-`(LwQZyH)w8-=_k;5``@ByuTWgsq4DnY! zm|-TwS`N2PwdgK2QH^N0&%AN*yqLv$`VoKXu{jqsO=hjuTy|!+lq~yO9ky$#Y6CGH zPhLX>4#uBbeq>tCMd2L6XsN~+_>PfLVvB}uynK9ar$kZEfN8>|P6{WMMDQJ=3qNmF zH-FlQBlGE@>qCCAO8fyP0p#=3|UicdU7wO5$}Ur z+ueEZC>RsRQL@m5tjKlNWYcDxjPzRj=g&s4twWEPyzVXnTwgKo*E<&a_}qEU&@F2^vQXgeett2 zIxtuO560zWjz%JucwK1ExO;KQvYwk*`*c z48MJ2gBHDY3x$VIO&FB`8>t%uO5X$!?2f+#)BXlpT?1m_L&C%Jr>&Gq)&7-aX4647 zpNh4$H3|DC%f$yIz;X9qmOGH+9-o>zB+J;*U!t7P<$k7BpPvf^6wFJw1;c;cDnT3d zz8pNIMIZ(7FI{Ipn`3e3WrA2@zUkYy`_ra={F*Q=DUQE{?$2>F$rB5G;u06vjVEV^ zeEvXDoE(&A`V>kaE#(Q{)&V6wGedD8-QEuUYgN2dtrz)V3nwqWC<|h0RevZ-I5&y+ zSF;(OI%|MqU0*1BAtu;^{~@QuxycswpSXt!Qm($i!OD6Z;(XERhbwz${NcLPhxv8E zI_~-&o@9-p5$e1Kz<~|OV9%eo;db7EvZ%L`_&01q2X7~E@&b^@?J!wvTvnF6iwn!~ z@i8VAma>6COy71+O-)>HZ!h!<@L8~aJ5qv2K!}Kq9XiIm&kYO=i~^4OMu$&R8(#@W zT0DTxoy?b~b2*#nYbOTv?)RFLfdN*<#yh*azyvQDyzkic#?j_+X#u=rF>&!)`&EvA zi5emzB55qQJ#eP)Gw;~PaKPCs9f^AVGD7FI zBL;nW7iD+XRj3@PmP5}=L%$YMY#A_TgLYrD3@^?5gssiK-a{F?ad+_sEn%8^+g2mM z2@me+OELE9k%>5nVCPOO@Oxq*;b*Hkf3)}DQ<_wRmNb)Y$+uQ5%!|ov-cY41m7nD! zfg=UtIBrkk9}|Px=G7Ph#HPJW$Z#SAYmWienji*${(HK`0llHEoF1?xdigO<=vE|@ zyv-;ujS3kGs^XxV8yDb#Wv;njZgiyTfdzS9q97oU(bHErq|Y7~R8&L(8+sWT8EP7u z+>(+XfBvumZKHNq=f)gA3rhlUh^K&}(hHc)Gek%?28p=p zVMAQSOA;Fc@4#-gG5YH2s&yYY8J5|8DZuG)J#Scq*dd_F4@HwfHliD%Hcu4erVrh|h+bBn%kB0U59L`ExFgT|cgSHL-N`TkTC=BL8MPU23j z1j>y0%nwtVzyia8A5HcAA5~w99=&c<{b|5950j^6`?^Krk*J89+axgv`NG%MvZW>; zL++v!VNqV7NJ!}5A||(-eppF~^?zyY=L?jX2mcVAHVyHM;ns}$amrjjF#eFN?y~+r z99?BlmP-^S1O(}BB&55gyCkHgyAhCXkZuH#4gu-zl5Xkl_&~a)`!4s7Gdhej?7O?? z>{F++?|BLF#|)L42G6Ifz9NC)_wm$O~ebIV?tdmX^{gD_KdwjtBF3`a)2M zWE2#(06AQslqCc%LCEH2ZkTBBhtYR9Aou&_yuC0b^|lLQ1&Sy(Szt7wYfGy#q+GkK zZ_9Cl7&L*Vk9U_S`%r$5mzq-1)@_{~)^+jUzIDu3nWU{qWoJxFL6za0&Wv=mjBx0F zO1fy(nr<%hLcQ{(s!u!Rw7eW4J276beN)lKRDpy+TLk%A|D==%|IO`_28L%I8% zEGB8L7@d%M%+@E2Opcs1Y=XSm$>w{O6AyyAU!n)N{ww=EhLh|&RiZZ`gmm=u8@aJU zIfaFV3r^N`+Q!Dl`0pJ}TY{#x0u&2O2ELJ(X*Y99T3OKzC9&AE-h-iz=PhfGs6XU_ z8ya|WrDOY6Rx)0y4ES2$Gr)^{`h*W!qR*=0;Q?L28}UdTp`@N+FKs#K!426{_5tdu zuaA!mFCZvLAnYrkBy>v!kcz@W+IqW{KX?Y-onTIivRnE~I5+sZ`abH;5PIglZ z6ZQ{v4NOC1%scHR=G8mrC%AtVb=RHO_{TGTuDjWy*Rny|)WV1AoW4h|Y~yVA?NcfR zMFab3$JvxE@nQz8xDS<5Jk_6}du6kwen3S-|4b^m5wb-KY!-@=bAj$FxNK5>-GSBq znm+?q4gojl#^_7)yq2-~2L&YF8RIR36-riXHD~t5DuIfiSdKSPmCd>Xv#Ib%4MP;l zlhmLa6QP9A)~*j!h!IWSa1oHQrwhLK+9z$V-Z2w0xFAV%#`!8(OJza)&cYtGKZc~+ zuEB&o1)s-`I4&;E-P3c{hmD6Yy0NKA??+rISotd31$IW+1{a z7XB3$VdLav4Ac)gMn)b8 zD{vhkDBlBMA&sDh(=jts)6++QKrVLnws)2HAn}0ED?Y5@ z`^K5qqj2wNNbB2~e{X?#xxKV?wM^?IGZ&_@C1GA&FrRcjN!ePxgEB9OyAA6B|2hZQ1^GVW{cx?ln&xTzO32>|5b^G_B|qENe@+m`FM%C047ckD~m}6 z)T?B?q{+Wm=K-L;NhVl`p#18@k_P%qSh1itQhZ?EOm-%JtuV&tUcU zWm29bVULG!gEqby!zHlU!1$zX_(T>>FHIX270eGw%)MKV7?_w{3Op-86%{O?0sZ^; z??)At$pL*v3W}U^{bgJ38i4$kCBY$XSWM%eeSMsX7JF#A?<8Sh z8CllW!c~=XZ9^PvpJgF?f|B=!Pn?ra-4om?K9QR(q*^_}=^dZnYae6SKHL2X zuPpL?uqVgsWNRpwz-4G-1lOEIe)~~h`GU*kRQ{7}0CYq5c9P+90TWV(c8CXo7Wzhi z(tELGxP8ufUG_Xw8tS@;Y<;O?)N_mYEE>sK?=OJB?!T{K!oRzjdEFr8EA5y>ei0+{uhb$?*AZObSr?JU{1gJqnku~6C` z&4^Si=!Q6aR3>lxRI5RA41si}zjr!4J2L{CtEaaYye&fYD=gMKzFr(IdA5}Q07=Q1u;P(>e8-KqF1fqNZ6>|FIDX7D*m+@Wwr4{@d+e`s_gBw@ zFq|C7*h;Q@#{8-H5^!FBnSvC|e6w&eR4FNRXl~rurSru*9+NOo3Rni#^*{YY(Fw{l z*wFDMrMgaU!)bB?J$Tt)D{%M*?DTZNi9wkk;!c>wcHr_PQHA?mhpjh@*du**HHY)e z{Z3V^$L{%KTI6tm8>6!=p28pMF4Om?TxX_gxL`ZdXG3v_AeGE?i~2nMCr5AD6-PYg zzF4v`F#3#T|59J}aJ~vQwEJsX8WGR3gScLg9q3lTERHBClR&Ay?Qc}!h*;F9Iy`Y`9?yMN*nd`?YClS zB&pXIl@gDB$?tSk+9#@^feQv_y)yjwcW~FanVXD-k1rLlIJ(Su;MXS> z^h`)dzy=%|K$l+rGWeZ_!||`oJBq?_2rbMs0|#x1W?}6=BeXucOTLEw zl(M#A4jlA)8NpLp;c|K6d6T&s2NTvlsjI7=W>Qel%Ls5P8K0s&EN~ZD(U$M;U=3Lc zyK*bDMG&uF@7(H-ogY*iy!UB@B4S*m>U_|kq~$9ltz5zHCPDa=1>6s+!teT}fZ$V%`2V>1_n zqHr4W_h`yv81-U%EF-|9*y**02ER@O1Fu%!cN zku_JBer3lB-(|L`(O|1SsKeAxWY4qHSLSJ=M<*r;Jo~!@@IQJXXQwIgm}*fov&Q87 z7E5fuM%ko}rg0o-A|ij*IQ#c+11Q|K0SN(Zcz<;Sb#r^m=61Y{8a`PxW1W+m8y*p1 zJd(7*+DtMy2tz$L~$m3AZlpWX0y#W^JU*co9kE6I>OT(aicZ z_Sxp|`~~J;GOWvp@6WSmb8c{0Rt_fm-_V4&f1Ju`qgMRd8edrNi|!P<;M_-ao!Tgv zs%)4Z&YYBkanYu}w+gQ)H17Kvk~5@NW?burAeGF?wHl{r^Swlm8KEC5^H()HqU_|) z3HJ@h1&1caf$Hy*)jq_6SJLok+7VQ;1I#J^43r?ng>-n0Fp}SX#h>boM^D?uUStY3<{e^v3l!YRf8aY=zew0p{n03cNHPFA3Jkr2(Gzy~O2(6eGr4&x+gJ zO-GZ<#&;>mRLMk&3%f_zJNM9feDxST)Y}sqv(6G#zf5V&%n+dP*}R#Kk1s>c)zxVh zYTT&J%cefo! zl8M24_Fo81kAwfBboJ%ck1Cy@JgxjZP@9-p!rt~CP=acaOjhq_3GA6MUP+GMNAnX@GUQ~i>!aVY z8fN$Js9kXHnU+qe;mcp@$?xHF0J@}z== zikJoz%gwiMHTegg`t3mTd745J_~)@}x?IC{GLFRqPE(eGb1uBAeHD7nsob?!XrB!z?z^}* z|0PXWbE@ItO*qZJ=DR)$m2$$^_h3<_prqVes4>ZZrLL*zbg`@K=;TyXSorg&NCKxN zXQ~wCAvQK2j3g92n4Y8%x?tZO5h}sQE2tr{>p23g=M&Vs=d7ZqH*&nv^1j{z3%cIf zj&udb#g6zd2^N;5t^#s-EIzYJYX(gOn~_3XHT0|iUC-u%jCJxQJhBRPXDwW=)Cd!~ z?=t4b+}}H4NrZDJQM#3A&-^Na> z1Fm?MR9*VSEBooj(e12uvu@X&Cs&?TIVXqec(>n|r<41FO;Yi{f^&L;9rb(?NH@y( z_MjW~hBwOj52~ToM0xWgTG^-I{r)X{PV0aN@4cA@;g!3q?sFbyjZyi~mX-!VK3yQh z`SOL0I}N2^N>)bZHTCzfD_mUMe+w2_0Ouw;nk!Hu%fnTL1pIt05**nNdKAkt56o)R zu%$k~b>egUa5H#zr2>`YwBSK4d11`ts2f9b3#@*}^PtL3^!G5HW5NE%Oj-OV$zdXV zpZ%$}(GOZBH7o6D$fYPlTu!mtJogTd`zVz~@peozxrzq=l~QZ+6JO3!htp)1v>2$U zu$VWyd0~7HMR&8C(4SYFIWbAa7VXVPK3s?vPUlC`qhX=Hj&N=ktSF9yDph3c`^|7) z+Dc^^$m|e#h21a1-04`0uJ;SEu7D_BBRV^Y?1z`rgxk5*BQQ?Leps6ByI6LWUJtJzdoX2LPgzYZsGz_iLsDLqiiW1fc~2#M)KeO!PJ0Asa$ZWpVEjAK za(R1u?@kqY zAsbbKf&N(AfTDlLo&G6qM#vG+fjpKgOPc9q_nGK^q@7)9A5Rv(jLAYKH}p8#s{T5E zFEQa09y|-8`NGG*=5f*2Z}Do6AZxm8ZtnpR&EVtpJSSa11k>e|e5sY0aZ4ELzP4Qe zN3MH0l1`vYHKWOU>Qrb>Ur%DmobpLtTJ$26x`ah^N_sbxa(VM;F|bjOg<77xvH~Y^`@mb^y&V!jtmxWasB3T$C4cw3m4$b2OtUKnPPR9e(j9PJ-ypq=Ix6v49nZa@GM!HB)fUI6k73k%nOUXlU?3uoI1B0&;Bq}fd1*Go9t=aw8{xU0pP8iS6PYf=H~V? zNzL*p zw%{yM@kyJHO*Rg=nmmhhtZ$*lWaFayO6x*~_IuRL`n8O5Prv87m`|1Ez>HZ z04%boF7Eu+QJn zRj@F}Ex9Bl+`6PK@~k(7mEA~{#(&*U?4d-Drs1`R|0LU~zxQn4A&ij~1dJ3~tJgJ< zf#nZ!Rxl0$U0U3aAH7v3{oq0=OmjNf+!U|SZ+(r5nh(xP^c4#uhxd(x;_vBuA?Zj5x7pWqygH#VLIGL_w#d_*Nf*eSJ zO8vh&uI(0jDu{R>mZRTVNtTD^g5nTFs{X`AKwoEHhC}JVp*kU!ABqRpf}#mR5Xav#BmU4qp>BS1eLl zv2T4tMak>6z>0cb=vC!H()Tj%95B=B$pQN-ov+jj0=({3rvO7MpC#0~a!*IAYx`2+ z24xf#x50`+MUe_N&m3_PqI{=T$N^jQWlUmqHT%oJ_RBegwFkvu*-NQT{xxhXG5_gk zL$1Iy6{qtw!^2J14QSpr818oDyuF;+8oEgnNy?8-ypmz%VAp{SMXr-kxXhK@Ck+_P z;#6(kx+I+%A@5N+e#s9pb0j78+lq68k^Wxy6iXR0d@gDH#the6#$)W1O+`z(_ip9b zH2JwoYJsoJlyM}`tn<2`h{rAKE(ke)9e=rgg4J2+-*SXhE=@Eco*g*y$$AO@=*XE# z7}QU=L+kmJ7#0*YYSvr&=GP*2c>O)67|m)R{pT8Y;a$Os+b`ez|FlP*Vlv4GOe=Jm ziHJgY!YH6{m=T$iA_0{HI}T2>s}@O6NC>;noiotJ>!6bG1$7!4F4kHp*dLb0qW@$Q zQCA*uB>GDnvO61G(1jB)`!=V!b*D4#&O}d8a>Z^K}|=H^b0Nt?~xA zlhFM#`5x@{CfunjUxD^~zGis7dbsey+O#05Z_R(mwgk(st`QBj>wZxYoV&rYBm34w z6Ul^fdAJTIM^nnk*03iAMO~))>Svcd?n_gSNsNjBW@Y;qg0SkxYeaA}f!q87 zTx2r=yP1Ms?7k1~FCc!oUJE{-Gp&%2P&AJ~t;EBjQ=kmi(*upsYm}c&l~%QD*orNu z^1m?!mO1(G-Mu!qen^Tz@s;I3XA^Wgh@%+alw zcNLA;S7~Qz(}KA#ljFGQNBFwqJm)c>E6F9X%j&kqo;bWBWe zZ-s@kZf@LxHK!30LZmGpD;wVfu(Gt|A0l374$lE(MSqSH@zW&4LFf!BBl zliKcDrySW_3U(OmG~YsA@+&fC@All9NSesie_5G!?Fba6o;H ztEq~kDRP^C(M+53#q{-R4PV3H^@+DK!XEi|2Ju7K;_PdYL)BLaN1ggw5wp8Kz6c3s zr^OhH>XIK5wd}cm{X8WS+k+88OJL49fBAhbE}TL2&?xy45CC&^xDYtBWekv2rn#$` z)5q(zmxtPgry!V>ZV3LhaV9@J9o4-x%^fT2Oi7tCf1EFvuJSI{!P;wRsmVj^<|AA5 zJ7fGHl=si6as{xcekh45XZI=%Uq)>uGjH_fjFHg;+Fb>7m5VK5pz*6;n?Dx#E_IpC zpOgQx!*vR#5qcwjEY!X;@#@FDV-+!j7@k>|i5fmV54nh$R$r>F+}0{_vFgw_rpsK^ zOF^HrRE%iuUE<#?V{s3Gvh!^t-E{n{%==M(*F!oFjl|sa22nk~>c7q0vvt|sEj5u1 zYam7zh&{2Leaa1`iO1QzS$q3urvVdMt??Cr3?2RsV1Oa8%t>#+A~)%}@c0A4Fq`*{ zLqK4lj=(HHpaBg`&cF}}IA2GwD!5GAR4SU*(dZay`T=!HZ|;-!Dl%&X2h>HC=@~PT zXYwdE<_Jo=~`ExY& z=X*Tc$AZR_<2h&g2D+%HE|`1@X@PP^^ahRzLZkUsR>k|v0elj&Tk55RDh$S^HXE4b^kA_-$ zLO~G1qU(ZwBM~t^&;49q0$%_eed1y5-=0{XKR`0vseReR65f++1hV^}nI7T_4{-z! zFv|pC+3a5v*;Q3yOn3h(o2u%K8%|w0Gs3X1PQrUy+6O6R zaDz(bH8(daKA;eBBiCt1%jJpXsUSw8{D}DZ^DUptevp2J{S+GtJS>p1*LvXC{J#2;p2ortt@82y9%)D-k^;g!c)^QS!I1N~1$Rzxv>#8`J`gzfBqxz77^ z-M!<{f8D8*BxYj*D;ThUQsGB~ zn|j^ks=(NcFOS^Uy0*(GW*d<81vM}048tJUsaIAl0=cYg#*KY)Z!)&KVJ;zeQ97ws= z-8mL~G@7U>@yC6&KU(RRp zhk%LytNT}({|R2F;K5j#Y|+&RaWFF$2fO^^OHZa;xnNsr(4GlyE9kDiEwOZ1Lm*{P zD4#pH33*z=3DeEM`Q(Y^JwB2hbujclOHg4`R+6#Dhe&wQde z&<&Gz_pr_Ai=FZhl{S_t4tq%m=JfA={B7=B%wGy>+SLPn`WCl57A2qV!-5k-(R^m` zkZre#JpNs|!aQXi^jM*x?)5e0PVQr}no#BJaQGC_uJ29)YU^r@;X%9-ZAoR#B5%sA zDE~JAcF~^B3)dY%dGxXK1H=qfKmg)w+ z_yAzfrJ@QdQZ1NuJISO3ngwj^u&!tDGS1Z~0kr|x>LMZ|feL&Z%y~{1@Q6R^xSU!w zHtezyqxEK!mWXWT;^-<8(D4>YU#8#hV|<4)_kn%_=Kv43mnOLKVIMFGrzej3V+T2J zTO>w^Uw0qY8<(LgU=^51?i}@41n+*Pzg#>q55s59`Mg(kI+P_jqCQMD5FSdoHfz7+ z_tYeu6qfC9P59|HOlE7bh#`pXAmUv#gT?21Io`Ah3Da+= z(?&FC;GwIltHZ*=e)>H>?YP7MiLL`sm4I2&QCSAv=CAgt7>iG zSI@r7;AMP*U0Zns6Nc8E@h3e_84)<#lXl!*^5jF^t`BZYLBkF{iM9gI-BqWcb0 z=Bx9krQekTSUNbyGNt#u*M_P^l7T^a5}cCB0otppTi6vtF+ACfdW;N;!7S!;_M2-8 z-rp4c^4e~@X^@V!VHrq+y4O0|l!Bm!qwU?jE5gpbggSlJ+eQqT{Z*HGPI%9D5*2(( zhI*CUgeL}V8#=D9r=M3tR~dTV84}}0d3fQDVlo6r#-cme8#qI#AC7kw2CroshmB#P z@83>Le_{JZ!W_{i2NfEnW5ELOJpd$^TT7?CGSrenEf8J}u4py-^Dyj+oz#50(4Q1B zxon&6%73iC8svNgeSz*$2sjsIy@eUpN#qT`&kJIeS??^p#<23wR`MC>41?DM{$KPc z5`J8;paAv(v&ki-q$W!=SzfeJT3Q6Xy}dg=F+hCN)s@Purdcur#Df@wgnxiQA37{N z9AmFDt=+%E9+yksV2wiJXPqu5oGDqcuuP8wn_TgfI|!q?@gHE@IkowB9po9rPHqgd zsFM`VS6%wJF&}smfPsdjtj2I`s`V=~=8+a@^c)L{U;#gD`n;L&d_U5(GtTRmP_dP@ z5n~pk_=ffTQ%G3(jGf=ebNE)B`Ow>a@;5frX2bV|zD;s)dij7&UAl;?uY-6*s1VL# zW-^dMkK3SHl48;=gS)aLc-%_2n{cFBKI~p@`NXZ}rpOhmE}fvIcXPk`uHdKYW_uwI z+_PYSU_tx?8tSHx9&!k2+in&~jP4OsG}4db4SchfQdIhSGYEY{HoNs%GjqrTKtrU& zjut#kAOAKB&hubHO6+K#Ottzb(txKiE$zpEm$ZIify1cUkI1#iiJ_KNc8mSB;PJIH zG7PJ$*S5Tq3S~H4xpjV7Z5fbbgVC}pbx{bCB!)i{UhwIu11_VbA<(tJApb}yF2-t&MmS~H~5*8-R*bQZSZS|&?{*IuDZU& z*Y=HBe^M>Q+_H`tQ<`3Nue5hmjCHQYO4c5BSQsMVSQ*;{%P?qe^YCS=rJ&slV?<-E zRA69)D{e}L8cQ2iglt`R4>EO)_U@vY5g6XsoA|nj*PLX?kx4Nz~-0y8@qMSC||MR@T=`mX5Qmo)H@D=~uJP zm+{r2zap|%1+?=5_%*_0CbyF$BRsE`Ha@`qL}AA~ZE3@16bi`g5Q99&pV6jr(r6-7W#D)D;5smHfURqQeLNaPo)Ugyt5~JEGZLE+xq1v$wpO* z$>aZ1X#80NL)Nz6hH8l2%FIel4QJi&hTj|2H+({0ul;LT-ybS-%;B=4xEh`Hj-8JQ zj6xD6A0&gOm6?K8!tRZ39;1a>6*_Z@U?fBzE5^ciZ0Inq*z24LY4np!h7NiSl8SQL zFeN1NDG45FC8GRq@^S{+|6+TyqZ~hJNjQzw-rqQq3e;-8b}7?$@A~Tg$c0iCUfU@Z zKt?8in~ywPwV+1EkOL6do3(|5oUo4Sx`M(*7JXp_f9TC|LjkE!<5jw+zS<|IIVW7w z58`+h`exQtPDcq^>!YOu>6oUbCN}p|Ltv_o>>Q6V4>AW9J)kN?2dW~eTqEFRKn8uf zz5U6yTXjNM5eCzIBNZ;qzx&Jff=2CMsgymgC=QYl-x9lN&UZv50e*A+^TgX z5Nyq|3-LIE{SEMR`P`mJ@7;W5p0e$x&`n;q$2`W$e5bC!)Ay`%&Z3)~{amOVZ7EBwVm3+}6;@|gLLbs97@^G}EF?@PyB;Fo=+?p#-#(C_UwVObNcYPuW>a=#Xt<5idHx>f{v;j_{2?h+gQQo<1cq1!DWE(q^s6 zz51993l7+efKCz6K3OccFu_7nvR9H@t++xwSbHjmF)Md9SPd+lmt~&wj3He_SPo#jHILW`vzQ& zMOyZK_?pv5V!e-~PV78YGcgqj9e844+QTa_ZRcIHrA4}07c@=?(fm1cR-c4$5Ow@X zjb0xnCSBg%)%(lDVDZHx1r!kp*Fdl^m*1M&dn%ycL8KYI+)E(9YVyu|^z?aF^tUeFk*8_C1+8 zlB<5M8(z_PcSeD!D?8rb%m|vaAM`I*e28MAzT5aU%6$3_QtU%!w?Y*2q=DVrAJy%n zXkAJCxL1g7+yd51UXh3!)yDmg z;1%l;mz6aJ6pGqBK9q0Y`g@#jV_>SR$y7S+D9%5)GUemRss*YnjdJWveelfvoOWXaOV;lv4?f8g^y97JAB1G*%ejK$y_8ac zmX=C_bY_|pQp~vePy~VDzXOBbKTdICv?r7!rGF+jLP(%L*q7n?ebgKfg4$)4<;7|s zA6ic^vp$Mqg11LjeTpgHk@vl`Q`vk|l;NlSVIH0LBCj#4T9i{(0DYTeozF{GFNKcfthKp#T2TvM&`9wkYP=y)mW3b`!{8?r{hT*e= z#vHUznAPh<=2cY?Co<6-f+a!4JNafi|+OiU}VkccP^AA{I;{ZwnG=wjbUK*NE zE}L0aKi_*n2zb-nUM<;!o&(^zp+HpV+%pCDwn)PJRETpu8-4@LP-Gpd-s1DlOlw6o zmlH#ci8)8j)NsfI!TQH1a{YyKB%FGrY6i*H*^Jf`stUPjCoW9?I_GxyyA^t*IcPO) z2gD%^9K(dI=3O*@lmWDT(VL^8PL2dhE&l!4N2YCaYCBJHf#lnYkR1Z&(8^$0UCLB4 zB+Tp!wW*@DOO`OHaE?6z<*0R=k=HFb`A_!a89Cd>#>Pmv`4bw&({+`}# zf8C}X!?4;KZnaeNcopI+uQek;{{h};G)(Ny3QeCU3{`vK_|>oGaPrT+iCUt2#kI|k zrF3}!_sN{f>#WjcrjeC44*yj$KdK(w8jAEx&enMEnTvKNHS^W@!tw2|4?G*QqIPmB zLqu1}baqJpOM!UZfc%hvAjix3>EZU~{+_2PL%Ylz_>>PnP|Z`~VKY9!}SFd5xwCo3qdG zrMKf>tYTvI!#}IN<%fEn0AG2xVmDTqj!vd|(nw)SSjCD%tt4#g1rXzC(+M)?++ z5{N+q;5|`TI|tCix@2@|m(_obs~MURI$r?LH=zAusAP4$j_NG=PP;qk_zzTOx9a~% z9Rx;+mbMs?kSW}TN&4WDlUI@Zz}6&6sB}R;`MS+ zPi={3Ju6xl5eaRwFyM~!-@+^1FpNccf2F7kc~#c4rZG%YaL9Q=>X}5OvFbl!CZ213 z7AGo+eWL*u42rNmchZo8HeO$%kF)y05ec!8k&Kie~j!uC`q!yS(a#d;ASY|8* zt_fK~yE~g*4xsRP9o~wHmdV#z((nO9RIY)=&EWfz`#~!K4tGR9yl|f;N4v4}t|f7G zAPZL8UxRze;>)YdhI5f;1j0kNoSRqh(a?2NLeo=!CJD&|PAq)_rFIZ2cPJrFSG|{& z8%E#E;4fM;+z^DmDkd(_r=?s?F$zUIzm622w(4__I5>eU1?J5$n>madLdD>ce!i0> zg6&Z{&#r=}_T)OP_zvsSAo7XU** zTqCt&`uwp;ZC~FMku1{(xFCP)=r(AdTZjEnTe-EK8ve|@N&P{WjEc=YBc=1Tn`iEM zSD90EPI{EwZmAk3C!#FU$Ar1Q*4a~k3#mVe!@=tu=6^`FDNNfRR_@o0P%@qkjZZq35;aC{gnS0HtC1drKR{K5#kJku=5hL=8E4>hKvHb>)k-vmc8U)20Qe7(1r zF?AL?pr8O#-a4I>`_o||7l6m-z`GjoVm<;g>G;%CIbB_1(DuJ1c>qk*VJE{aXO737 zF)b|(BypA;kOEKZRGp0`J;OnZr4Ck{YfFZK-(#I!TV-e{LXE|k(9SU=@#f)X2Wolw z$Q&N7Ze`U(zSrp@rN+Z@YLV@o@-x|((qDnQk;Qpq;#|fsARZGxfVo z@{9+rNq7G&*+2t_?sFTy{SQ(GI}1n0l18&53#o0Pm>bXY;3S%B&ubIcVcY7b2Mg;> z_qn4Vd+~as1`2ES-e-xaY(60z`xmBK&hW4|uGZ%VAEoEtvYj_xa6Y(UUi?f=-SoQzKb3nEScOSe);@X$3<@8pc(JZPA3%V9CNLjZ{g@ zP@J|8y)M|o(flzMzCfAi;F(5|EPS)z`5pnaC3-Y4g#A2DID3CUmR|EUxt=76Vc%Kq z$lMq`E$wvn%R2Xsj6?%z`FO508rX>gfC&zZj(!Ka3c!-FTa2cgU)L?{+d$;0v3ORT zu{>KdS6X@>W`N-gmq_HrOj&QMr5Aekja)ogz{4>#S%ydQvzy(ws>dQTr3{+PX3uVA zo;o5PxAvw9S6CPX*7kevHra;|G*rKi#CFR37fjjg>ue#p0JqRVv5rUY*iZT2ei5;cx0c)svbP!-Gf-Bp{SZhLTP=R{HjZ? z?;)!>S9RXqR*e0Ttf+4~$6NNc>CveN(uD8v!DwGYXXLpji0pJ~BuB`xBV~?-!89St z7MusRg~bM*tC`LcEv>HQ|IkBYOAE)B4GBKC;};Lm%XS6YizwoS3-nvyI0``~=mMbw zKrL%{w$U3D92}R$V*VGERG@JI7$cc|Zy*Scj*hJO$irzov4DZs|F)w(#FbGFk%3^W z3SKb7!oagKn$E+$9}CEQb92?xKujkfCMSy&$)h#z*POiY*nP@n{a)*nf@#Aw;yAN~ z{Hi8?*=>5;wv+L2sNs~bwN(9H9Py0AeUHCXDBxcpvQ*&^mfyX_PjT)|9*|u7e2m;? zYV$(jZYF~=ocs|pg3~hzBZAG^H%2>HaI4A`+60qmFrJ)(b*4R!UGbE+n)BD;r<*zg zaQTn0Ay4Hl-FribfEjGpnU=L)=^b9L8Lkind}TNbFu_-rd*r@nz#pSic2^9yzO20k z<5&J*>+SKhp0e|G+qXk<-`9EyLR#+}`JT`VHc)|&K{OlZ1=$x31H#Qe33Rsem0w*B zW{o%r_vR`<4H`}5iiC!Ruk<)KS5Z|R9L6r{1%T(>g_RH?u)|9O6_9pWBCxFE8LZ8g zNuX|%n3~*4-N9X$csk$j_%iP{`S)x{XyljwwXgEq>|YShnR9+PoR%4r@w~m<088E` zSe;~c-wJ&*7EIwA>C3309CA=aS>l<<{fnA)qOo`5oKWNxdW}#Gyn8Q5oHnHL! zLnK`y{>{22WbA@EfeLco@NoSxlg_Tr^!RgQyA;dzZl65nd0_M<$*Q|FQq|lJ{LL}5 z%n}7Pclr&S-6!w8L!-qHTAn`Nhba+K<;@N#1eLw^9TJzmm1M|h<1{Pk@3<@uznW+? z`G+QAe_?5MuqKoNkG$>tKt73jWN}*O#C*90cH``e3nYNe^TP7a&=BzFvIsxjP;#e5 zlZ&Tv!=s|1SwOmiO)V^5A_IQ^{=K`@U|)0T2ebp12XnBjUp9Jhd$Smah?xZbxXG?n z37C1TINI~rhlJJ|pL&7BptFn0f`yFMw5SMVuza@c(OyE>+DINQV;9`Vy>)?K2IrRLRrQ&N{yR7GA zx5xf{w5Ln|jmKlhf%o?t6sP84(SqY-v{bNu$NFA89_k(g^-xjo(7w5aJnDSOi`Lw^ zH1j+$WaOqfOUYAPBt8^Y?~-1coKH?xa&_Q>K(%g9a|RLnlldZ`ok(wC_}~BJ=B^k3 zGu5O%wYp1(pE+1;E){K2qbI$QQxxmdpCCzd!uNf`Qp1f%X|c#r39of1_`*fG7}Lqq z&?VSj-#&C>oO}H$){u24qKIPFalQqg-Z5wlG7u_SCW&W6vA&v52{|ISx<;M#dte6T zB$=Et!BP{YEgOO_pZSUY&!F%RDa(dg{!74RYG9Lz1iy&@X~v5-6=q#Qh=pqO;B?-C zV4hFF0I64ety{?TQ=?=iT54?O^QPlJ2n6k*XKj2v+eHDN$GP<@=z55bRIMtg>Ns%< zh88w%E!ji(4T?>Illkk$7nmNP!7eN#`?N=r!TDLN|eC@{m zyt%@7XDj?hTI%dj-e{iKz6@io>ZGe|C`eu2rO5K5C0f-<=hrbPS$X33NmlgqKk(1E z*EJg5-b59##jKtio!`H@3}~5q9otd*Sb08jm36JMZlLOyT8Y%wOrZF{n5x75py2%3 z4gE`dxzUW@ldMZ#%6fA7u;<0tpTCiWRI0@deWAMW5ltsxKHABY2+eE~iYZkzQ#2FK7wPlu%JxZMN#cr^Lo6 z&FkNPDAPzG^;z~Ni`P^5=Og&rrODk`I$4%y4{e?XPi)%Lt3Ci+$*bEQ-sy}fC3#4{ z;(&5<@rV;IkW4!I;6ykpnvb3AvE5HewLKdOFWuH0?n4u-H{@4hjqIPnjq#r76UUd$ zp0UsE$vs@B2L?Tldo1D&S92e@#qu!gY2Ok`X?L+mrQ;cH3codS8Co(xot|Yl_B9x` zK;1)A)yE#XdPb8|%($jIoVDim{U`KxGJ{9(xjKGZAab3VD`dQE1Ww~)3uKJ! z0u}6wf4z1D7?+iRc6t2Z5}8yW`Dm%ZZQe=MGAb*JjAzZ8<7J(JF&Cg@Agb$AwW+M0 zL`B&&QU~a1xSQI#G&=G6C4Hu+x-+-ie6QoIMyV?3(fsW(+aY;^EN3nobGC!?U2$EM zf7Q0vze(nH_gzH0B(jXK2^k9*ks1vQq|?5?wiGv)q<#*2MDSa^voJLc@mwC7`rrh; zO9mMc)*!1`nq7Kp-IqxA$J#l)uD?e}`HF)&>^B#buZPOAeLi;?#``l0vnb@ilMoEF z`uq1>A3hf^D>IqnH}rG_vzo>=#*m!^Cq!SuPbN`ja=9{(658Z`QR+$-j$pP#w>mxl zSzMZMqK}!Qn>)jHU4&P@U%s~edXHZJB>iuXM!nj{#%8e04nW8wa4|0a68Hjc^-Txf zWbV(}uvqILOD766b^kyB_)9Q?NvvQWWvoC~1P(*0pq;HPihu@0pNtGaG%Zg&H47JM3A$L^FaL2oY*I33jvtu*HD0QKMW zc}Gw({QW(l3-W>5+N+wW8HC9E{FZ5LY6;Cp3k13cACSc5SHuXoOwv?}c+R{a2M z((7cFKEPm;4X}BFWBcBz4`(nMs8oUB>vfcl=A#UR)js{TN;JFdINa?j!s53B*T!OD zE0YgwsUnGHn69>vNMT_yMHLFZK>j)5vt@Owt7Tm9Hl%N36pk(acV&Txja`n*Yy}xu ztg|5!c)FNULPSRX+wm3lC1DBO_!#6Hn1b68YT0eg=LI}^sVpEDp>1#z*wWMFGx&a3 zWO~5+gFiybbQ=DF+@pUZJZn&=z)tP}B;uzazoFbN3$#OLod{&7`z00nVFgRfAmn7e z-A@?sCO`=Xa+)SAfhUU)93H?qN|elCl9DQB3Hh9!S@m@vu~XC1hJgr%DzhP65Qs*= z>tL961yW3a>lC0{AU?>>F^#b%>>C_W;D1D&1yGgW_w_-g8>CCRyITZ7q`shlbO?xa zcXuP*A&p3PUb<7dxpa5u|L~i4=AF@Tz>#t8bIy78S$nO|-cJZ{^XH|rMTQukyyC(~^Ifj@viVxC)O^^SF>O3;EwjP|+jl4FKXU2PUywIE*NHVDSq z^2TmxUogsY!9VF?ySdewh{!R7m4vYSu#+lzR^*pn2PpJ9HF*={ebhVeRY@h9*uTma7gw-J5=E){{&jP6i z-ttLna-8(x1A(rWgCFKMzavbg4^iF;Q8pEbdsIUuGh%E*f9sm(%RM<<((46C*`NAV zG1OvG<&_W66}l`7Rxf1Ua|;Z4t_gInm%?30VYo|lGK`E+>)#vaiA#z9CS)xhfX-`_ zbFql@gcEiS#51W^n_&I^{o8!5642qPmtBi*Vb3ZmDul0QzoR9{XNsnSLkS0Sg6@Sx zHlfIO@F9)Yf~d*ya4+kJ-O|n3q}U)j4ug}7Jej)Z0xDOG7epdP z)0I;9;Yc{XV}%wA`~1Sr{5cZRRa|a#PJdhcekx5k8?I9Mr*>$r5b~=!qc#mjW-dqF zE^SlEV1wSmSI>a>IClW>+Lp^>4Nj@0 z;s>kP8J+?*1{(_&gZ>S6ZnKx@=8s}ZD16sc!6f4sj?q*BFyZ%7;ofFwd^lS7g5e&o z99z&35`#z9fb%|*PakTFXtm7#;Ja>%#L;wX#9txBEcS3CSSYFov$m~ zeu>>rFtfKr&2;0EC`}E@59sGd_FcxwFMr%0ojvO|Kvu{!>_V$sBYnGg7YPbCti%YI zIHP3Hxk!7VRIxvM+#l98TrazUH;i=7c$RX}w`OPXH{n08z5z)g!d%MN*SBvNmq!_# zUHs`Icy&LKi>E@TOiSFfbaa*vYPUN>aOn2Bbhby*kpNtXoxPlOPfc4}-o-@#xG$(_ zX8zvVe3 zw%XiRt-&HHbTHc(A%!?z^3Z&x^HWlZZw~yaAFARt13%X8-oYw`)osa8XiN&$Z|+}> zUW=T{dQ1e@XiZ{@+QM+lH%Cg~h2#h0h@rsp-Hq4gC5uSt^%Qq_-|a+Y4>r70BKRcM zj8RQ$)we`F7xWJJK>OMs_X4=QKchTuhjG^k;9o3{eaEv;rB!Z2Vru95+uu3Bicc)+ zl6iwh$wgW8Rq|?Au@39|j-WX;{FnP@=~C6ep_H(^?6k+?L_70+sYr{ZPtIkc;muH0 z%KHn*WMV;)isL#7k6Ey}_0SaL`}qO9bEn}hF2S*g5$`VAMKjBI|_NuMsl6_RqewQoghP?Zg_ck2F>4h_9o6u z8rfqpU(h*{%=$I99bz}vMxuG`; z$;mo_FY{ZYPwok?&Go%E&UBVnN&V2TH$6<2jhxw&R26kT-GD_>W>=tcK}WcL+pVa zR217Q&reQOy(sADmT&t9M@P%TZf@`IfeTM_F`psBWs4A0!h4or{KEE`y=I=-Oetuc z1Obd37AdJb&{1h)kub|-z=dAO@n9A3Xbiw6v_D!*e#!lS{-r)=GB`FjCKSaiI8t1S zqK%R>y`GZ6v=D&SsH*Z!&CbnzlQq{UU0fyl+3Y{!6p@jTH5}@^ zUt~=!4{gj+%L)>fJzH)cQbq~!MbQfmHcI@46B#D|O*Jt+KHTd&&8f#T{tJwCeZyJd z=Ao@wALa7|T|R8h;I%Oc!SrH8Tf8X>)~SiFl)JgZ`zDOb+zk|hQZJ0VX*sFeTg8+8 zbIfPb4Z6_zI*lGNqzPR(YA8~@mCb$Gk_N1Y7524L3)No(^LUnHJJE*+=ZKqFNk2Jj z`Ids*K)97oQYQbK*95kFH%@+kJ~+1sb$QSvC_Xls^a+IJiU@@~iU*(ULY`l*TS7Fk zyhz_WM=dYjEQ-}%FFkYU-7!-g0}^= z^$!4MvEQ3yKQydcs8~3R*lPxj;8MIs7-=w=;qG#ucoO#MZoe#?hzA)4fCWH1UmfBp zVz)g~vS19%s>8eqFaTl%-I}hQT)pMgYl8vkiZQ+9VdESy<0d>D70e=%1#Y$dnX&>v zH(~gaKL+N1fjs%`!}%!aK|n#Vp9|tBsKD{@0+qr0sjr>@Kt~%V&`wZ~63E2VpY{+h z;FbuxD8KDOc*CvzU{zcJ{q<|CFy$Gi3`Z+Ewmz?yWhgj9Tt|iM3Et}g)gtOsMu=+O zW(b91IhProCRd#;`|&$J-;~T|F#akwhZaYdVDywy|2kV$=Ha;XcP`#nN*T=E{YX_0 z*w$j)G;iGu>}$Hmyt%%wRLnG#0Zsd)Hso3Na!J<}pE@I*-{-~-a!L;Cun5NP#F5_9 zMURl)W)V=-ogN!Jv9*=EEjXeTICkJy?Qi2G{%EgU==R`a!!kM${W3&wW%}c5HNzPW z^uLJUCI<%lObXLy1xu_i$|IJ>N@mcMnJ&p#4a-}2*CBKJ9h{1V+s&mJ@q}F=7eNnN z1(D}{6%+3QFPK>TCB+q)iP@We1VcKSmzN=bzf;aw!5@ zI1iKR-!?;r^Gz@B9c!8Tc|bBVz>I1H5c>{T;I= zc0u})mY+WfP;Gz&Pc~hm*$WJ~O2t!hY5Z7LR#r!?_m(f;1dVTieW3tk_XsHou)kl> zqF7j1Z{XoUH7c|rKMJ+htN2#_jTwzd4P=<&^=Bz#zJBuP}TCCqZA?0ej!mv^z^R*tl%`i=3h4?z+9swsP zf2?ZT>tYL^g|2c669+|JHAr`VYVdf*9WOW24T7y}-|6)aUc8J9#&b;m=lb}&=9Q=G zdo4eX_qmb9g8_B#5cg9rzS(F#3-1U^PL_a|#buP;@8=Dz+mE9UF07BBK2WJGH#L1h!0 z;Qi^^nY~=JF6WTo|2$PZix<#Q$O1vL2QJ~&)y9$j*PTLiNQP7w8}9dkMPdDsAPeGi z#~U+>Ntjn|VOtTce7qtBYTK-mqit|&6@zdEwe9(d6n15p*6`#zo@jE92!_4wMTeBb zjjO#0`>@wQ4^i~%f$`#}ZuA$=P5Q#yHhxUuQ5TFnz*?T%U7=`!AKgPXgN%|K486Rs z?TWe%UH`+~vDhGj`rJ~_BW`KRNo<<$ikfCax{kk`6K>yx_=yp>rB^Y3zdZUt`}8$} zDjOXalwMc#T-r4dN93u^ErK&rWVThB;o;SlvjLN>0mn`y+^BbAR@adPKy&T@M<#tU1hVn&F|8LF%B*m0V@j;{lNvq|SD>{m9?krpVo zKV}n|wb1jo_Kk>@n0A^w0$|=KNqhsW_PW)_=wFRUk?shOmX>$?Ah_0K9BDnOjs};e zL6vP+gC$r%`j5BscNz3Tp>POasdJsF3r*6Vy%1kA!Y6APb5?fpsK{)YNLFEr+v=0` zIle)&Cc=LzIz0n3y6r#faJUTlagLkuceRXJn9{hw{rkj*KEZF3U;j(Ew~9P z9L!6V*~AiZSc&TR9NrKEoN>p0t(H&lg*6^8r78jjS#EQ4ni-(7mR708_=2-XnnqJ`AJ*vkMq~yH7eln0r8@DMWW}3EAl5E z)%*y_ME*72y(B75`Vh?ccgcg2>dmR%p5=#JrO+o4E{c;*pk5@#_n`|J*v>N)4q){dm8c*D41WfG!OZ+E_1KVugZra2a?HfZ1?az<({IO z?iZH~x%#rQ#LfE`JLsgvpp8lUM7OUpFlul<{W(yvm~WlbK=u}&aG$XaQd+QCxQ34o zv|u9hVc)|jzha;lSh{-jbp0<_Oa{&G*k9b~!;W{)Ld4zcE)TTsqHKZ9yKAC+-{L_~ zm?fQXzp=Nv=YhGA>|8Q?_oO+tBROz0h8;~L8oQ^=e^ydI^{@DLtNiL+lh)t+6P8Ns zsF`s4dlbtfwFH-Yt9W1)tB1}(V$_YOmUMa8L6SgS@`!d^KiqUZd7f zWsQpW5B@#{{u9Elcx^jk9h1DS2CXEF#weq%UuJWvyZS9ox-YXGkGE*X%p5x}Wvzm` zrq)`QQI516o_@{^z0UsLz?`!@sYy6a6EfDyj?hHx+QW> zspNr>cFlpyimD#cJ9oLHmP;ME*sp-@5Bd#ucGiDklrUa7FL*-GuZ{se3~-$mu-!le*mCfSudrGX z1*{!F9|ulTm=aZ46%|~7IZ{s`Ui5~KQenh}_k#rn{TGxZYBPHc2cVY#7o+vbsyGa- zd`j1Bh5XDJk2LwS=)7D`e0^8! zG6h|vwIiUDjz6w_MrU-@tgKfWUeEgcN0Dy*=T*3pZhhPg? z;^0Szk8o4QHdRSXg}kXYvwwj(egNf+fvu0OIAVrEhEe$80WmyMzpXl1@{-tjM0d?d z9bR44RI$wVE$j_(LGA7Ly~(27ygV#&az(Hl!4M<|9G;PQu&X(Y2Hpl@kT3s6dk2Ua z%(|^zEB=rrlBgcNwnqOxlsJClDiCg?H_$O&+>RfHfMF!m)y){#Md4yxt zt+!@q)(cJ@vjTSH?sh03Ko6)%jwpZsoHB=#G0~dYzlD>@7KbnDBoh5IZ0z~CV-a(G z^eSt=TxrPpBr4J4Li_j6#l@vvUU%-Lz$nq}@3R)F%$u7@VJHl2%vsrmn07W;w!_x1 z)g6^Ac)zI7b{QF|GbDuC8qR(Es1J)90GE5TBL6Az=pvQVkF`@On-AGaHfTvfMI9+V zkA~+yQNM`Z%9;xwUnwRwVsJNW@E6uaGIvCw=p!zNGj29wTk#t(#y62wd*@#s3g~?U z?yk~*|GfwIQ;;18Y#X4O|DrBB>MXi?N*)BRP!$ywa2C9%eBinw4OBkhNy-OzUo=zH zLq{P1fCrBT@Z9CW4*-P`NSHi4J_0@u4Y>ONswE7N*ymb3gg`CTR~Zh75=V;-_WRR^ zX&H9aKe(Y}P{R~1byow1usdc`TOl?+HrkI021`W_FDu{c^nOqv{aDVlcfhAm`oh-C z^KW8M?bh(ZW}7+J`tK$f__3+Ya;VkX(~TIP6|{0wh2qJc%({2rZgS@qlNXUvD)?xD z{1^lYW)}W(2QeTX-4S};q?hZfKe^RA#S&a6eEAYR`_GK~;j}nYa3cvB8LU*`4q^%ss#A_jUe-(h zT++UC=r_IqPFVb-3>^n~diYp6kPE!=_t!70wYJ}v!gexm*HGb-(2NP*6Y_42%^x?~ zL7Ys9k-Kw+V$*8`|beiC%hkOk&iu*#=HAYxCaQKZI-cG^P@FAl`~aCzazTP4+D^{I`t zm1qj8WK0ep7DCPXg>cqJ^!(h?H8I8X<0H`hA6x_1#0o^YO_j!CFGY z=YsNmZ5yvUCiyTiOg!ekvEzhDT(5UMO~bAda#OR4B#bc`zgcoT$sW+qHa1zlM2RKki?Ko z>@yIRNgd4tit%&mFsQbMe{?n9Cdr0IYqNeert9dh;2(P$6KcqQG5*+c`Y&0d?EuGS{xR+iMnk0 zQu0P7C;z$LUt0c?EvG5f^dv19P(1DWMOR;Soz7yZY0R=dqI*PL!yJ!AL3MLn{am}R zHC%blg7x?s3UOM}9G31(7{ZO^r<6jhz?*FPUJ8|qEW`Y%U4Z5~imTm{!keUvpWl{f z_YqB>1&}WMqo)6BYLD&j;LT_FYwXPO$FubmhhcU6;v(q^xUzLcAEnO}^El6;V&it^ z=Q!qXK@d2vL4ahb(&xM8^mkTr@f1+=^(K+=W&}z{1AF&e+J*w#-*;C z;8Xjny~X?)L#51*&E@-Wz>^$IIL?L0$twmFh~Y4=sAoPqwGkty5lhj?=OnlLWw)c@ zSKt^vQrEXfWj`9tcB{=>N2@1Q<_ptk!I0|oUeH_p0QH+ul1FJNMTpS3xh2&^|H#0; zCO-bt++3eJ(+eM}Sq~auHvyQxeuRlRi6I^_^)wPTe~j&mY4|Ui#s9?rM@l6BABWW$ zTx3(Jr4rY#ass>F+src9t{D0kTB>7r-`>Lo$c8vsBZ4dIiy}aExvI0q_jhv=>gS*& zo)g8^(j%YUTWIq}(!uodvFJ~8_=NP@Co$V3-w8T~0l8j%vj*luhQ)CFELIKi5SR>% z0lwhCNvX>bsTgW7Qij-pR56t|;zbYwjN<81?J%&&U!-I3zEe+d@p#^M_td%>YcN|> z_nL4OcF6dau8JLs&9CAa%IJnGjU07iH$U3fXF`nza<1@P&QmcdAX6vd7j$-T@?+fW zqC_;&_s=U&h5O`AxS+Y%rXbK`S!#q6!9ST@?HwyhmC2i4wHIix7`7l`6CBBE6K@vT zJ;X~DhtXMi7R)5QNA*U+!g02l47=I$&}>;2BvFkX3HQKfrtyA*lj(O_5-wl7xxpLk zK_25-q`NhNbhm;GdRae-YGzqFi1HiaY-MxJ-$uWe^@xN0{?0y`as)@D)@6Nj-~)yE z^>dlNWI0bW)$bn>71e|4IH$1`SqK*?uWx%-2Z_7RIHKC)AT`e}Jnc^^VwIPJ&!C=@ z8r5yfGAolVZbGDb22fP*$_K4AzS8xbtxHfIPP1I2p%wJ*oL&%!lv)agG7rA}VHD%4 zhrw{~6dUWFt@J%qs<@Ml61gl(uUe4Qu{b_v(fqS*rFEJL7uhUBAQH+kMYaRL*+cTvI2rv#m0uQ{?~Iv&eYQtWhxnez&lkR?eiRr8Pxms^Dw+;% zb|0PH`_?UrrR@cvz#_6j1_G8G9_v~}ME{&R+i9#HK=c6ry>k03on(Yo2P6MU5vHtY zy1$Rz_9-Tpy5!3I2bahs=9qeoAzD$KGQ^})#*60C+ed8$if@XRxOSdQDBYJEblxSE zI;%ASN&V#~()=d^fB$ea=u|66ijCrc>)Of6b?{1kt);sCv$!1OSxMM^0a{RKtxh$_ zIsNA|i7Jz0DsIqJXs2^eVUlnJy*ftM@Dy`7LXl0 zzteVamS>0xba`*B%Q%xa>4{MV;8O0A`fZ|*!h22dWN|A zZEM(*jcvozE8tXlJJ34U8aH-chjksSZ8e6C!m1|#d{pwVE^~fkHVI6uE)1XWJ*6kK zMYw z$;_uNR5At0vR0tblJ?eFn4SrP`c0HV|Bd6GoL&Keub@LaoA6q;Jvme6N+^slTbx){ zAjplC12Z5@xD?eyzI=`@#1m~#(oo$PR0@Eqnq{dDUUSJpPL{8^c3(;YP>G=;lXQV> z-bVK_!82xT5Fh zTO^nkjnNh9=X5hc9x<$rt?E;wj>MN>JJ`ADErRFs^)oxrg&b(00s=EcF&IwD&>p6Q$i;SryV*HGe{Vzx;f=Z3rcO8}9TUqPh%HnqYNi++$OPI`gNPP|y+I z8-bFNggRM&O82?Ud;kCUVlHoG9q4R<`J6Doss7@7Eg>d!0x2NX5^RP6aO`5geJgo= zv@~u??71$>tgr!WoSjG5KLg)Oyqge`|KSdhxbPSvY;FmW?;wnsKkCE=GZsaF=5~G6 zln82$lc{?Hn{Ow4H+_dr$? zqs`W0EGbvr6)2|4G+iiS_V=0^GHpGwAXv2Bw%hMz@~tmh9ZUSrIeXWF+clT~eti9% zF{MAcxxk*fHvH)dvRT^0cZ1{+iQIwvC2F!Cq~6a8*wK^H7*0tH7sB=RXyQ$>z1xhMpXPO#3#Jz(bD}x=a`8D=~Gjjc4^8^dhn_Msbktz z%KcSJFFJxkQ}B@h$(qNPlJP86Go_!Cg*-%G!AV;5^5s}VQx2bNmNMT%Cd1$;mCXz< zs9{PJcRyo8MO<;FoM3d?De6hkUlct&l&N^*fipXX{*i2YE{WhdBKHp+Nv_8d__>kC z?*EBGaABQw=)K;{1OFokv={+Z6kKztWwOC0b9Qzvw<{?84Q5b~0Q{xtDSr?Odeu#z z0q6*}DSaR3C>SI;+O<4*(yJi7mQdT$Ith8LOu(A6Pp{i>7!MQ z7AiRot~F$0e&mjcq&d^UGG-mQT+rX=ZClk90^hk{*dkq{U9>f$rg7CnosI9gy0*Ho ze})=vro|Vkm`3wHI=|{;t#?7XNLiv7Ezt%PJse47?ENowU!Gxep2uvq=?AeHcK*Jm zOxIL-14oy2_IE_X55qnKh9lNrK3tK`q%^Qk{Yxix5m8vrv-a!VCcQCI_WSfmH?kkU zlC9&&bv$8%mUL%z~#*JEigvr1kApdVLE+GNTbcI;2Np|kY>*>7% z8ogCcp{`w=`k*r?1(_Aq(Y^h<{DYAem$oh8-=LaYY=N5tdZM4LrT|WB`k3AlFnu@k z-L|0P_^{4(Hv+4(qX8jM`M%CGjTdq|_}1_&CtslBP|{X6lD-cp@g)=+%34%qr478f zEXs@--*>IQ4Xf3*cWBtjmjA|b<#6s@I-%e7xVpd_MhpY`wx?N19PYF|F!gRF<(3V< z`YP+6zASu17PMgcHV9Bzgy?);=unAyr-O^76UeUjoAbo##c*3P^XDzQtEUT6?Vt#Q ztIUj+ciaEfv&U_2SKdUg{2t$Pz8EBDyWG^W9zZ#lnIo2cD&ihEJNMtMZYND|!s2s! zO$&JsJcB1up*{ddf5wIBpYdg*k*{5I*e>po^H@oW?&1BguieK(w!I+m9hcd1o8`<5+ zF6bfq_f3(mp$O@nVg_Q>)JiH= z4uu8Fd-a&=xo5Gn_+zKPG`ny)N;r#s+C(8?WMi=-f)`CyD}sA^BT8uAF zOu;OVUd>ws{+otiOsPohGFi`8CYS6tme5$|jOd7=b!qjhwFv?b_uEH=Ox=WN?120V zHMR4rbOVDCs!WftkyX!-vQNK709WGwy(VQPG}MW7N$!1Bii*n{%u9~FPmWM{;m?mB zpN&B{R5atxrdZT7k+_oC?mlHfK-t+}O$32+{{)$09`A|X{98(nFhT+g<}gvsjZ={R zb?9Bg8OaN<(!$c!YlT3chU-y~% zZ!lh_pD4?I5s33b^y_IGUb5QyI>$gw4J-<)r8Q2c&`FHbN$nf22Tn6W-U76OgD#pT zq6hoNz2m_?0v;*hC1H+TITMvjhO_W9)F-OxTneE}Z zwwZAcvsa8oF2@d|Gmk)TbwAPvr=%6#dmCJ1E#yh+rji^_Ok%meZ)hd<)vb1ya)yLp+Lq=MQY2Ak znKzmB;LIxi#6>@~%w$8l_P(k*yr{&Pc8{$7bn{LHF@;O(7(IXxoy9*3lMYGK< zF*_mltGW8^AEuN~gG^e}CiWj-=8H9X^Fa%lni@TdMVKCP)T9qh4gFfMM@h!+z!*UHR zY4s2Mfb#I;hia!l%?n~)`FFx3ny2a4ltu?^?D(tCLyjCSoonTKHs4^zZ#Wt4-+Jw| z1nY#EHNQq!9=}1FXDNRwE5D{X<z_#vWL6Z|>;9qx%2xxge zyNn_Pp|vKn?ZLq2x1dsy@725XyxT)MUSZwBwsKXMBos@2Rrfr0w1pq0P9R~n7@V;jJ%$-!aygtl!l5Qk{_4(38(I$qfcj5OcaTKFEk8ox+`3%&#mA zrvgDop~1**8t4X2m#txDg4%vjt%sN53W+#XOD`A_@bE$4oV+C}>6NW*lZp9Bs5vwD zpF#b868-pB?2otvr+U4JnZKS+`aH);HRC9W-iIou)EX3h{Ej)^VfO6tv~7j4+qBoc zGUOGe(Tn0ULO)dAB!|W2FRRc__XT>ygE?CzAH<&cHa7X)1ENIJrHPSQz#!VF_xn)J z2?jRvEiV9-=9B`tA2k|JcrhKKbL`Ow=g3$iy8gd?)H#%XuVi7tel4B|UHS=@sDa&F zxqwC7H`z}NGQTG^=69UI{~n_RVO(cC9F2Hzw!Fgr>d0LoaLP2E2s8zn7n_O7m%MZ|Cs!wAzpHf&vC8Aal673tIsd1-3B4#!>VXV~OwXiFY8;xfQcXO6mB{aSdx2J8lioe+tZ5Jq2bCA?h32B`@S>^vsxDp*R z5_~&w17&_>H(5FPhKncJ=SwX$;-~o%eX)qj3LGpkrXR(=&XKCy{{>zO_L_K^A}z6s zPu1z88FMlZTc~O1YWqUEkvk$E_tf$NaLiWIDazLch^U3~BAlb69*Za;x z(L=HlDO_jSq`D)rkeltgLp!wtu3d=VpQ5UZI#d4n{#WHgv#k3^WMm8AT|ib7i8oS> zx!$zVJ7l;j{Y+&kAsCRnWnAafKoS>sK+ESu+^4X#wBx;64x%Pb_gSJ` zyTVH|KAHVCeXMf}i90?mwIoW<6yF7hPX!a*r=)WZQOA#?{E?P}l#VWpy-r(t#g3de zDfADW^4o5g_?f>vtw+lYzAjc|uI^y`7fqG#M)E-Nl?b36yX=PyPmpnRLbkv3ZsNj2 z77*#`K0MT|HM>YdgI3xfg;FwP|HegVkDFY&T6#ViwLbRRfo65(Mv*ju;Jp;QuA7Jk z-(y~@FkFD~OXow41#TDj;E4e(@eA#{f0EbG_6tIMDSI)mucjesah$DQ4q`Uu6@Be0jU-bGUDJekiSRmr#z|nR0=uX9?76u!m+I4fAwQ%#n) ztAyi{m#5#KQzmlBhPGE5?S?jo?!lxf z&;g@ePIYx0h_d5g|BH;atTDNyoL5T~W&4cl;M2bkyD4!$+|pZN-d2r9ZvlN0-Td+*7Kz;(0_n0flYX;U>{6Z~d0wqamG zgk>bfzfR9+vAF#tk3Bw+d7t~q!S$w|WB<43-{%W{hWqpWS)sHOC7HN{BUrtar-ly? zurc;nDenh3CGXUJV6XR$7>)*cGyh9&OMRtEE$h>r)oIQEM)$}VrUXutuvr9qE} z$80Tg2vsUq|7Vs>nWEr5E=r%>DPHtAwRV|P{z>^k-6nrqkF4NdAQdsy8>v+B6K6Wt zQ257Co85HHVeKFoNQ{um7pC6*0^J7wS)j>A$Hcs#J72PkIX|}-l6AxD(VknJ%}cyD zbfG&@ziL&&JB3c|Iz7Nrb(sW`r7AjanVHn^%?|dfJyNJ-<5fcCQw{Vg%aClF0 z{`V`+s79=bW8I5=x3n9o>+e;#uk_A#A{4g0plrOyg*Usn%lwaS4|Jz?U4Dn6=0-)h zK`Qn=u6Kkfi#@y6&}tg2x>JikLeFU4G#*0GoGI%`bA3%4xQaaE&m;*bYI$9j!%JXK4=13xUe;wa0y~Ye?R6-ac(ra z*c0)`Wv)Dlfhf|(3wQ%GTMRl#A;neCfVvY$&+~FPnMQ<~kstJmltt0}DI-@13bPoD z=@Zk|sf$o7kB+wMeppNgw0C>r(G#dyNT{oKR@U6b;r((qhu6}x*lE>$c;KMy#K(E; ztS|T75-JTPN1ig4G>&F1@}Uh~15Z43GPB8%Ew(&F1vuaHoGm;D*z(Hy+qCtc9?Gof zBBORYqsLp2Y$lX9?N4n@d9Rw?KSOYMV)KDDw`HG|x&-fhYd9^qsZ2{JiGm3jX+6SSP(iK% zClhCXS_C2CUoH7yj?|7S9=8Tf(O1+o4is;e%xAR(dtl z=JI4|BAh1_Yw_xg7`4?A{S!7&P}e4UkLWzt1%b#DfA>B>Hk5jhCFv_e^(T9fziLnd2Jjs)PLW)ohC3^Qc&5ZVEX)twb^d=H|}RqWw)931;}d|I429A;K|1Y zPxm*fPsE&xLHiJajW0pI6rx_nq&zgAIfFU0WZeVdq6QU>e*0xX&>{EI8{W#_+1Xf9 z%SM{;)MLhqshcU1y)I=p7YUeL<3k}^N1Hikg04Zkx25*@n_jmlffN#b51E|EJHx|y z7d{9lN3(Nlg4{MXhVFMr^SQMsyxUt_XXDc34!aqaO^3W|==$~*gRgY*a%JTsIZK3Y zq0_J~nrEJY7_2ISv5QSrQ^rw){k<3~5*{vuGINgt_gl|Yf;2S|IO$qIif%V!tMurn z{0EOlVKK&@;C1I2=An#$x|OE+?9ap6nqEaBHel;`x=zgYrOC;Q%_2e}N~%~z>XJrA z1~HMET85ILNo;$`vyL8pS)%K{jsW)U8yIStwIO17Of2%i-rXNd*8%r+MbuImiLmec zM_d!UGTbgW4U7mqoj=_=?_4Cbleb0YXITx&2RXRLYjo#bN9qj)Bohp25RrN!*G(%& zThYC}z&3x#7Pxuc$2bB#YJ3%Qq0ua_>(}HIKjwAI>&mj!A-(AUV@P!QNieUiVxS!I zHn3HZoKx&EZio0e_-*ZT1j~Oa0gt`p3oRqS=Vv{@P*ClBkf3P^1s~%kn0~hYw`#<6 zyU#Lryfc7}sRoa!wvBE=Z-S1L9Ot5Q-YG7MYo2w?f;~C;F0prf=Q3{dU;}c!JkT4p zIGB*>*|u&DA=|V{W*%gIG~r!Hq|a$?=}qgnbJ69q8YSS3G0SiXVS3`-_eXqszkBI^ zSjEPnKkv|$Z|smbC%+>fmM{BF@01?jH%3G0VyL|ySOSHV(# zHhtnXXc)F2A@&92CM_*lVWe`f0CX3;3X$Obn6(NGW%g5Ejgm!+k{!*XhLHtDkhd)^ zy!+7_lVpVfd%s!N->J9wI_}S7f5}l#j=UhP{=Xj?7FFB}(w)IWcl_t@*lkcs6w> zZmR_l2J%c-wSCXpCWzXTBB5R1lGuFqRIv;>!P0|^i={-^;kuU>ZVqen+jMamyasw! z->|hn*j2QEas*#VGoHefX-QYgzc6o1%``>S4vEY75WISRON7)vT<^I3aB%hj(PC5d zC*Ys-pZb%wa*T0ujjv<&WVB_?9}*nAbLK(LX_v-{T{zv|=%m-X7pv#p5;Mx%SK2{q6bs2Bk?V{7sQ#L=;^B*`gKgN1XJ%fRq=&xu2xd(51yhW zyBsD`(x=LpWXPZ))&uD*b^V;?ci(QE+deLxyCcXPOeD(8EPZuu)3b-9LTaG>ex{#B>!#o)mJX+FYTU*9MYe!)=%f3m5%fYCt(min%|#1#W(|CL+})KN#!B_kzTBudWhG7jEls@ zCu$ARlZ>d3IqZs`q=+XR)ziLHyhT>7v|tiS0JC!U;%{0qwk#@klO<^iu(2||c@%E( z!5;#GMDd?JP|NPd-dZhmEP9FhlvPA`EqgRPlqvqU&=70%T%KX}e73j?7YGPDdK~0v z{g;7%a({aRbt`0L)EnEV;@?haioB$G+zP(7;&?@m$P(w~vDj4XpSe$xhONIn_rMFZ|9z5(xuQZIR-bbE<_+Zu$QK_7Zg%KzQ_Suw@Awes{&GB)buZ4smz`&_d;h#R}NkM?9 zhgf&gB)0e=se2x3)BAy*z;MF%xLl2?hApaKM&`ys;0iML2Fh>ynDDBWpBVPr+^V6G zU+jYNdj0Kl;MpOBZ{4Bhuej=$HFMVAuQ1aMjxtc@&STG>vj*dKdtPs{DY?%6abQ1v zI&x{XTzk5l*;-}0%TEn{ga|cNtI8`E1vg$1bx1{JZzO1*;=cXQeLVtHM1+fuW6fB$5Z|m5>GK25TCchadPek?4cf=fOB09GuQRUQM`1ER&cyRH;nMR=J zZRvj0L0`aU5~Eu1)S1|CpjqRgfSmFK;s1()Z;8H*Q|mw3#ZC9A)!8#WX5~6KhE}=XzT$m2eQg;Lj{J zN5g(d&VKxR!u94lsI)8+v;`!$0XJHoe<6%*XE``kqC$jA+=Xoe{}FERWAmE6WsD8^ zH5kA+6l@=SIEVUp7kzpv{Z=70(n^u+lH4nt`;rp|B>lk9&A+oLy));YZ5u_j)Dq(C zp;7{$a>PDWtB-bWV`R-kH!rq5De4CL9ga((hx^i4`Cm%A>t@6+7c`?=#O-(S3@izP z2g;uXFXy729jC|H*)sJ?g6^I-S2a`=HYP<))-is=EyDf@L%BU^Z$gB-#-E=yaacTA zBmQP!pm|7++#k0&`K7)FFKR-ujr@%|gUnkja;ak_8%erkj*pW6+&1_b6D{BMC&I`N zvPjq>DvpIrtax*cc^Cg5OJ5yS#rwB?kQO9GX^`&j1`!15?nb&h4~=wpcXQ}NBOu+~ zt#o(u?DzM+Gs_=4v%{Rd_l{3oaRB~jOt2nH5YsCc+$;A@kEso;+aEq5^c$Z@e!hnF zTIiojJmR&mrSc%=#qf7KQVfwIi60hHJ%zTN8NC2R31!1G82F5n``0q5u0*i% zZ$t=A+%Jl>NQjug!OEejvytdQ-JLe|X*|02(ovC8elO)}V;Y}0nQUgN+vHHk=*r?Pfey5Q~cxP zJCKbd8#34{u})Z6%Qx`3&``Sk{?;p5HsPxQj`i^#$yge!MW8rP>sg=mBU zylmy#r#s&(A0C~Wm|u~llpMAM(WkBM_l=>V3)vN;_&}m5IJ&_n}Qet@s>o(*sTo!h0=<451 zHalP=#|U^2?y+&3?jw|!BYAX`UY{&TU!O1*J@$Qe0II=2&hy=S+X2)g5efR;=EV(g zdH@dYrx(8p71q)A%XjNJ9YlUzG6xf_`JT7)+?N;WMnB~S#gk2YoGn*7NfD|pAK?!L z?&)=MlYey50hp~ znC57!;<5^Rn?a(G-Me}Dnhjd_CRi6Ea5Rq19f?`kHLyEilD2eJ*j*0f-tRwh46HcF zRoe8EprZOg(OaT3_>1;)-W<8S2Saz$9M8GMTv+fJ8Y{EgBP;dFo=|w=<TL--zE^Wp%pGVKW-WVUKwkMtT;AO`0bF!={lp?FV? zlSfnY>~G?C_7%cmno)x*#bKnE2V^AAq@HnD=9RbR4cMFsKR-mz9QcxFePH4CX7ai_ zIVlDQV{&_dc94D+B_eH$oQ~j=F&#y(!X| z2D}&|@f1uQ*Glu1x+57C6=exgsD~+I6T#A#A=2;aMSD*Yysjowv^_3L2%oz#CPp{l zMRIu9l_d)~vgB!3$SqV#d-&dN_86OXAoiFZnk2_45$3B(UtcnH@H>aTrr<|A7;r2n z+EXM2aZSzA-5;|56!@ln3+Wos??N#1A&Lu=(z%`7*+-RFbtNJA^ii><#WXAd*s|cW z@@_cMfFu-1X~l=4`Fztm0*?(@|G?n%wWnALYVDXgbGpSnl9uv-r~3e0;<3cJ0%|r1N_Ty5=%iBrTRl z3S^E6W5VOGxo5Q4cy0$c;(wq_1QcZ|WAKR?G3W@aLE7g$$JQ?`rl0Yux(9#HfCp&R zb)u#S#}5&x!K-S&Pz!Mz3h`y0?fX@#U$#}txgln!17}VbstZJoqzcKV zs?1-H8Bvw>Z9^_zEk;+wyHTUX(xw4NoQTUy0EVxuh9_+Vu|CeW1MeD@eQ}gOBY9CNdWd!IX~H_)DTBhE+x6-;F!d$TJqrMk;h~x{8CR(vnW+ZcD#R zUOitys9L6Qve*_P)8(-pqJ$AYkc0&r$KnK*NP6l^vX}4NI8yG}qhx>cFnpRA=QFWW zz!M#Ta*`P1V#toVz-nch?Q1I2tTzQ`s*b<_eMCW~&$by^Hz zIe(9lq|=A7|5=xRLBz!_qN;6;TaIyyvwth?Mf*u1C1%AH%jK$3Ve>^FW8zlp_(*!G z-U3JaFvX~%>hBfxtC(xoMW*K!YJNN)j`TiaD`}?r5{XV~$aq~FACkZJ|HhV*BxVO`s5Yei9BN2oc zm5-C)1Gm3ESG;FQC;txl&urp+ybA#rDm~rcZfh!Bq#Bp0cb8n1c};~#`a7?*zCNj8 z>~HP_FLHt%E7{r@d`>d3zbStt+XKp0?4H-=@BBgk$?cfs_;3L_IloPmK53_E=td8B*bDzfkqF~iNj%NG*g5cSc*5S{sv9u50Oh6hU4 zR>)2DO=3PP%%6KS(a)4G7ZygNS)^R;<-ipE)7I|R-)TBYT=iz}E=K#J$o><WtDl zYrmRNfy8^8@^W!pO8FNhfBFR6x!Typg$QQsL~te8=(?oSgDCysXPXL|sw`rF!X#;J zCysbR@BXQFqU{x8Czb=Q?K4`-mY#2| zt$e+qzr^Q?uRl_EJk|VmyQqxkH1zL}S<~?`*7+0da$Vf&9Q~j+6hCGpOsm3lnCLEE z&{nKuf^mcm2*NK^e0k?OeU|RA#BU7^%KZntsM-51B~~Ov^!Li`%IVm3+1g zU|_#2cm~LB{RZTDMiXh3LCrVw+M0l0?1b=}0EzFt;gv6bRPo$HaDsB<#6;J7`iG|0 zyoXwN&L>#ErgfFpqy-rM4E(N`ma>0Mjdp=mxH1kb+snirk3Kd_92X8MU4?KNyX;iT z=JbW@+7mU4hPs|#fiT4M?8FU(;W5Tpx{NDZ=DCX((AZdNZ2T*mH(}5UQq*Y(-_KuS zQ0x3E02YFhK;?#eDi<1-7YeN&=w&zhJ$Zo4%$)3c@zW>FQp>{Ktg1h2T&S|wuVqJp zfx;l!$T$&Q@=h|ZjFm7AAJFsBT=z@#ERP8viSd5t^~qJpikW^!C*3^cA#>+SlfGhjN8+Yf$%`G|8q zq<==uz!?|?+rND{lf!x+XH_vdubQyyl#rP%wfwtVyrg)i5>jM+$5bA1xin*7<`ftx zLMk$N^rg(68$n+=N=ozKieRGx{p$o@%pW12Fc}#MpI@4n0h3wL6=y;jvT1T*;g(); zJ^+Uh=n(C6+^rv-3+O>U_lnvHNEF2V8_|BH%+AR8p`?TbC?Erf=Sx6*#0^)&vqP_a z{iRFf_;%jzT~F8nx;BcYm0M$*pu|eAFkOZaOcz1E*Vp0FmzWeg|KJ!plAKqw$=gva z@y0o^-17GW=qTno5DMD%jMNcCefsWApMuk!NB;#k&+O^9l`byU+v@qFj|5P=feu?D z^F|ZjSKj3yILAAIuMQX5prBB6spM3)r?05I{XzPFhvvsK3~yF z0hxQzW0uYRk1UnWc1+m@m?Xuoi-Ybx6W{wT>f^uX%CZ>>F~ZNB^So<}TJ)u%P&gCC zHWn{ZH-@Uu7(jY(G=r+y{0D@+y+?Q6@<(Y>2;7accW6jocO$ zS?g;_r>YK=Ua{c{lwC~=nR?;VtgTHRUL5*GQniNG&8&Q}Q2EnmhE*ji-bxWaW(77_ z2rv3*yDqXN3R4427$IZ1yop8S@2A|kBc?y+S9Hd@H(v0NSL297J1y|UqnLlsJeYbb z&-tUI%DFEOK8)2@o9g3+b>}xr>RT+@?j3GqX16CkI&(xH z3KAhTkf|b${-LkKn20(^eP7Y9QdD%5a9o*2D9#8-FisJWw$;GbO>eD)ILRg zV%!Gg`KWMFxe)FDs~MiSCIDn}K}c+}Ycfi`l*e@{xNK(VJ*HJ?$9k0Ww;&<8ZEYXt@P5Gg z2VUAR8o?m~ka8zrC-%}0qd9yzxW)s)5pK82?>Q?93WoHiJP+$%6Xa>UcF){xWktL! z!Q~a;cUrz{bC&ESvKql!RqYmt0bkTU&*sgP4o0I$LR9kmRJmho;x0IBA)>c5VWP_)*&U#0UzMQGkC5kQW8W7* z0(WAEt-kp-Ek)~i$Fg+b?83lC+7>8HiWDj3q01X=t*p>aKpV{|35jif6PE2|3ZwOW zoMQ7YE+3RTZKV@HBWY7>?9h9Nue6Up6K>TkoU^m-&amrsbmdECpEfV8G+1-fSIllh zNz#&a?D98>5ls5CzEt%n;MVs+8zoiWcqcsQyWVNe`mRtNL&C=XzqnN;-!dl_%KqWD zz@%y6Q-fdi^QTmhsgaGk_-0PV>b}~4dL+H-r=Z`P4(#T42>r)85oLLw^qZ3uR0zGs zFlYB8DO6J8brd7HHmND3R3xbth*JV2?M(doMnM%}5>m3NGGuQ`lBC@5EoR@e?+v~` z!J_s|QEGJ)Ns{~hCo!hoWNpDI?UoBj;ignTI$L`;(Km}y^tJ)5+d_R|QzA)V{&26_ z!vgwYw(EO9C(i2C{d}`Kl0-PWvhsh?@M%lQ$)hD8a;6Yr1Gs+7&dye^=i3uLS*c;= zsL$plQG^7AZXqE&RG0fBdQx77{!QJjV-X}X>%@A-D=@g{d?ML+F2j1ZK$SLF3|uhi zxQ_n>lgr%$cZ?1a6h&vQ2FFoT3oM=TJ{J{@k(7Y%MKhk@&2UgzMG?DY>Fa$ z5T1v01sF_9k#34taZT@|IF5uKX~xg6eRb8>{2U`3q*t)rG7{=IHA#VwuidnLg%BX} z1e3d5PQ7&+cYn(l`|_ndn2WG0aZ^t}jEV`Bfz7{M;@K-prM)Kl7i|a2b&Qyj6wu`^ zhTnjZ7G6Kh57=#WdK%2bFq}s!L<$QTe!u&+=P~SqzWr2(@lTf!vlT8!d$)1=lMp|Gdn3e5 zooFi|sq>H7czff@cvn@K$Wuem?LwDyytrDbe}=O0L(#aX-p=b%I!j}?l;YOkB+rZm zSYjGQ$j(!oPqTp!(_S$ptM7L%K>Q~>8pAT`6Zre?rL32Rsq#-`m2ObUeO9F!-o;TO z=}Xfr&ueo~uRp?Cy^JgY3M8>#f~R_?__o1HhTaQ_!pW`DR1dJlJIKn}ZjYt>kWT z4ar)Yl`If&@Q6ax!^tOBwAShFVJXj71_+QG?#wc3@EbqQHe~iMxa3h~7pmq>sC?Qa zs3VVSuslY2kF3(7ef+udM&YR&t)Xn|&l(3@O1-~CC=pNzy6`~<5b|J(%^NJ#dxsU9 z%JeJRHZ=x7YT=jcNMSh(y1p?vz;H(nd%R>*8+(9L7N_VP&lZCi){b=)!*DUsNRSj$ zeoP!%(U@CxWZ~rx_}dxBV<`yt{H0EEz!+(v&R7#~%i}|72TXhLts14CXMCO=MJWYP zegl43ZD!hfk!O%@qur(pbfxzmNjY?;G|ZFIxNCalGRFTzR@9`u^W?XCP@nkHUqeJ; zb`TKFaw3933q_RYCTAv>_#N#U8r}Cu>=Xs``#>mH#%Q;yAff711iH?`Y`uTnh2PmHk(51vbKMPAe?l}$m3e#mp z4>&1%7|mhde|_5cP9%irX?z3%vh?E(S}BHk6lU|Q=cpCv&Qd3lN983|TB<#sg#{Qy z`tedpwTdFRsA$|;s90Z&C%$krAfVM%e`tfi5154I#N6ZeJLuDoX`lYLO-U3^!wW(wU_HzptuP<&!kZLmf z-;XsN{7{(KSvSz$h6f2keHwGl1AkFllW~Pov14dXf<)@K(D~e-0B8be^4;H5w0~Nd zce8$%067ZPTH|Y$9&Av0Gi9pg#98cQ7P*CIMGHTe7pW9XM}nQdVyhXW|6S|OJU)lB zA1`CR-NOT~2m@J^I#DT&nA zR#gy)LpkEvaJ)*L5h?s%iqy&8Qi#z-^b`CS5PmjzjFKoDX)2zXcmyVBH+`;kkMyss zDQFW?#^7@V($)Vo24jR@Q(K(&05S4)oxlmpMk9gT)XFv6y{ie~y@$OVyDNbOT{ohk zp`p=qP82`DZbMm1YZ!3VqhVzHS35vV{_*|Ap72}CL+XF>uPDszu?-Cz<>lq>mqXNm zXsHg^XAfl!K5ykOEv0fzF~PxA%w6hbWygmg)NP=rr^8asTwxCCb&5?iu+@;775R6N7EP%`5e=h2jfO6HVs^pMXT1UX?A%dmF` ziJ)5G(D}awPn*Qz_9r13#(W*n|b+Ep>aPCPA~CKctqQrrLQh-Y6xOP z*_-hib?=wYik7bGpHqsbAN7kv%(TM4G5 z^Qq<%ZE5U%U0rsM1j!p+dEq$8=2B->%BJ`c$2B|N;=fD#)5pwDE2m@MNjsv14fvdV zBa`kJ3{&SUZKZ71+FBU5@Hulnj2J!c3M9|-dlVF=gBu(=0_-aZXF%tT3yG~}|D)#J zA1~FXu^LMNl?2~M+BybyKx>0*LRm*A^3X51k2oguzpBRbv-jvvcI9H3tw3}kQZll9 zj~u{LND?zcuuih~>oVqJ~1R-4S=gmUiz=XlXDc z-xGgT$krQ7WQB4BV{ZR6RIv^oMXU%1j)&`T&Nv*a-Quv0!RU<*sjo%K#=Ptw8dXEN zONea(kbRPqrq`sWe{T*+|K{J1tRLKHPVSLC!t**vip=2qAQcZl1`@aS8VGT8uTQO- z508GyNE>bz&O>Uo3XIMVhK{v#yANibrroR;hE*-FW&UjNzh4+;Bm8l@P4A%aFyVrW zD1q(b^2XE%;n#^Tgd6YgX1yQIROxDMSsAxcs_>`<Csf0d<(2!d(p>> z9be;*SlY7ulds6mArMKAmEX+KdE}F+Ezl(~)*iPM>Yb?--^@?$Uv%Sjum6n*{Z9JN z@(d06@y)U^%zHNnU@ zmfKHA?pvXui8ed7{I&sISIeS5_-DFYNPNH7sjOg~|@^bWgq>YnLb= ztQ)d4=_8ohhbIwG-){HiIHo}xGk=6ovkPzrcxVsf#T)(0NkIo3{vNsD?c3-GW!!H+ zXRd>`5I#)E5e4++7B!E{W{vy+I>cv9iH26!^|j6ZQGFkpeP95Q9g~PXK$7q(ReaR= z_D+92Wvtxx&^uqLtmVE_5@<~UJlGhvq`gyw&CxVyKDw2cr}Or8r%ZD`dO24yFModR zVQqFw7#Ahs-Y}atJQqH60n7S4Ka8(|_Pb{C#hJ@C#2K^n0^<^nfIx^po@%1*T6JH+&;XP#EOww0J-2Q}<82_pC?AuhgEnQ*Kj z(w+;^XIe6(fnj3P_i=gZ{@w3J-r#TGrQ4ukcSI{z{BWm@R>&%9`E?pBIqR)Ss=1xn zkLZc-j%%T75~4#^V=LtqzNCUDR3^M4rvJD>;*zq*=MViqC16{u=_~+e44?sI;weyyq+G3lnE+B!8siXD!YVpWf~TYgin$(Kzq<6&5#f zdk_W`9v5&1Ulm4z6LtUUD2AN0J%5U6QA@)1@Zc(ryEq}~UQW4Jz~L~x z>))4)gfdIT+dm5Wg_C*V{Q@cQ$YtURiw2x)W4{;MdjD@HsC%^jR{J28W%0HT4LwjcUE+q85`c$_vG6$%aIk=q~&ZJUY2Y#M!~_+$Hrz4Q}~X@ zd02^b{p%VjQj2pAjTs0Fu7A|9aB$`Wz&MJF%Lc<$0mZVmdEqHB3myeKUcm+!cz%YZggkuiUx(z5eA&J2$b4OYL+{73u-mRxoA<+WV}7UiY)|n zI#wpK>8R^ahcG2G!sF1@J!$?)$>h6KPFu=^;XvHGVY9q2E{o^N&Vy7z{{uhb7Qv?ajB5s?6 z9V7fpjgggfDqL})PB0CkNK1LymIj?Nht^(3#L!MC&eQi77ANo-*e`TyAhLvLeq(j-TF(8&@~f1_DFqSsEWJD`(oP^9hooOq$$I%%d6md1;; zuq+MiYh+%D`NMQR3tFv2$be)}rvfsE8Px!W3n8^K2ZFbZ{tIk{H=g;$1;iQsGsW8A zX{M5;<)>QyJys(lsywBz*1Dcrr{`BVg zAN;&n5(pHlLkKNNdcm@*$((a|UpF`F*5^s!bq(z&ga&Ri($}F~z=!8&$EB5gi6T%I z!gZazcBt61q* zKv{hcIYM&0457CXs=>5(NAzyn5Z8jJ4@c7uU!XbV>fB!yBuwY?ZOZ?ZOw=?Xy>iq_ zL8ldWCwO?u13HcCL{8~gtxpa`9JDuy0t}$9~;Ml z1fI0>;E9BEtmu(uo8%-e)SHoKX<`PR4Kd1#A-cKUfz&0P9$ouZj{v&-`qMI!3U`2( zSjy_{vIMXI^7ErFDqI~mPO&;6wY9nZ;{o{m`fBq|Bhh3Cj2q))D=3;OR=^(E39$fe z9{w@UkJ*8XeVPZ4Nr15s@I4p{;v(4YxIh0F{eP1Mk#f6z@&)^Nm0$)DQ@;}dM(!}Z zm0#r~sKpJ$hT8Q>i61QyO;SbWV?0G8O-lnH!ON6laQF)#H)AErC@etewXJ9F6s5r& zwyWO<{{hwMILm8#a+!43!)2V=X7_7?qNn~2GgGIhzw4!F^$2?NgRJ5ug`Hc)S>g=QoKQbVfhX#HK5dTVN9fZ|NIJ&i?5CD?Uxo zot+peI!uxcx{X@qRMbxL)0r+$mwe0YN>;oW)q0mCYjeq=R6#r!T_i1UKJ&|ccDE_; zvE*{3#n_D1R8JV0j}hIJ>e72dCHpL6Soh$<1lNTlL~OLma`TY!L1rU!q6eR{-o3EV ziad3oM;(47bw{o9E=S(#Z@}oh<^|pXGzjJ4S2o#gkbg>9g8R zI;yr%u#HpVL{uEh!xpgzT|{5cK}pVfx!O-RSylH-j{`B3_h5=VAAT z1B?5JaEsI{O2q};6~+l@OmBgV6?E>18w1th?2T*dB9y1m&AFm5`YS*?c|Jvlo_7tr z^(222&gD+yxf!-nB$2q3)b`Gn==8mHFwsmKA1CCh>wPiR?Cg3Q<}cMpS|7*X1m=QzCSCFWmGLH^R*t+Ov_jg}FYTEnjx zU)(^AQY$*^MIIyxXe-a}D5~B|RgwKg*i-g7I1?VPP*N&~6m=jL7x zz?!x{hGb*AR-tsK(McD_<%HAR$oL{9S?TVyZ7k*o9ggAxoo2TxbGx6}-Knidr_XJY z&ytpOhbCDd3C{4r@q|DTX@7K67YIrJ~LxF3q%Y3fTf`9nn zIKM%&REv`}HTdmLP^Nz}dZl=%6XQnzFE9u{YUI)a(C0mXZfy@2Gs#^)oi`=gVb}WC zHM6#iA1$c^l=08^W#T%;aDPNIpu7d2wLb5rG+55c1{c^|p&)s@3(8539BvRk{{W!1 z0N7u15d!c(1Xch5nYB3Ka`OwhOSDL=ur=dmew0^wU>DvH>uOHgB2a<*hn{Gd%#yvI z(6c@Df@`BFK}wmsVA$YzWWj8cGe8t9(T4o}L59U8#-JJaWO2{%f@_w~W-Kev;7mu<|f6?2NyPA^)o zP0{kVtM|poC5D8BktX7DIU!CLMmi%Y%8o}>g%jCP)frlhx6!Ke$w{*<+5&niN83f; zT0jE6Ln86ytq{c1`we+&KF+9zHxGXR5p};c_c_gRpkpjJ5B=NkGXT@===*w>;hu?h zZAex8`p*ID$ytv?Zn{iC+ZpaesE4}906*gw^moY109n8Xy+dKUrMD&r?GMQ4krFoZ zTM*Zo^MBbd3yK}!6aC3~Pr7LiGPEc8zUBqh?Iq1n7ilWs!ESYPYIF5>gR$I5Fq&!M zXdw#P*FhhYFl1K<*dI+HLpnZ_fdw{FIdzVp*H(OK{!g8w{zGmWjt(V%v)DchXIf0^V0E zg7;lS|KU>;{3xUVpO84f<`)A8XRd0mNyiy8`kV01{e|Q(;IGZrl^Hl}Afi+u<#|f- zQ8ptQQu?dMs`|@I$KBN3u)y}7jOBa4{)5;=(>8lrFHQXiDFr*tQV(UXmb@r&*>P=2 z{v5&mBZ+*CRUg)44p*37uO!K($IlHP_S-``kP*A5!}`e`Mn>{fk#S%naR6G8x$9yL z4@VrrKmA1iLvHAA(ZIHNwK;+5y>*SbR}TU$M8B;BB^Y~~N-~Y3yw@wj*)2ZQCEcU4 zr-`0$0IDonVtTtN6QYgg^C3sBXxm>;kXzc9#}ePF#c&O49wBk_EGEv&!+X&5=r;`D~rVb8sNpD$7%8q>@IdkC42oz4z>YbDCnEbbi{=3$hdC*{=AdJfpOk(I0 zk`&}6f$4MNw+n~nx2PS$Wp<8VN3NLGSW14aF)mGgI=-bbK4va-=8x9{l$09R1*Rx{;Vi@XYHQn4v|>CUg}I71lmD z;D$alcjrFmZC}kzPS!L@R;yzylSzQ!MR|7LU82`sbXGKikehd=M2S>f z$qmpAvbUay^V8*TC&zDCPFJ#$*WR7QkKp(QVQhC@L&1kRCbFJ})y3I&)g}9=RZk$d zIe>`83HDwA~!GPfh?+c!(tUh)N#@#zUFR zq{)wBH+sv9jqQzrPDa=2A7L6vRrT=jAlwyO3h>?xzLLf_?Hn=PJq|k27l75N#4#(5 z)~iV+wX=kOyQzNgR@EGVG)a(#@K|~{iIDAV^G|Hb%VkTb%$Cj0GxnZUkmEY%O-W#U z_Ts8g%L!yatknp)evU#C!~SoV7lX3oaQT;}fkD6kh1U2y={gzt1FwG{N(`5Ky5<(s z%rV+Y_^FZN1md7kk?Evi3HRyi1OmZRwzAM4lk~MNCbOG`99a7;L9hkpGj9h>Q_9l0 zo6;(ub+Z!O(t{PRsQOA64N!)hEDj-m3Q!r<`g)V0U3$OA*Z$3wB59WF$KtJW?WI2u zXPH}Gd4oba6@kw`80C+)GW_tBO`Fh`c$}6JF0v^#6=Nx|Bf2e&VZ_?0`JuL7V&5+) z^@i)Sy4|?hV7SdQubG|lIiiCj{6^G%Z}UBjt}j{vYf|FhiowzL!1WFiQu3FdmPg57 zMno&;6@!9Jo$qXr#oGQL<&q+;QJt=PU3^a2)M$>adpaAqolBt@_e8MqCIq+_xsOTe z7oR`x=+wcDB+^Pgdn1lte&VJKD*Sfy%uxU>6U-EJ2y%Vzwxaj|1!w=J$N)E9k-d z^<-KyqOjg7Y9p1;j+W0)CGjL@39H647m{=oBY(L)HMRCOU+T16TM{wXs6D9@@QTf3 z0~5IK)OIYiezHs!npzvcux8_AVkO9>->i~%hV=Jifmk1@30?l%J+U*5HEG99y{610 zv3MINSbge~ROsS!-l#e4x@1p|>^8L?=!@LezV(;nke1EuPtO2Dg?$(TXon!GG11y6 zNc>bHYh%8Gj_siamoh7d6CKIw5-eFd^8qIjSd>OQx{OcT}N+g=Hr>OFk;A(na-K zN?c>y!w#y(0^stQxdBYVx53|<)skR0_0*;A4`_8yZ7)-xz^O&XJ*EVrIPHBS{9dH!ErdJOu-LRDP$Y@rBK-=`RUsPxO<>zU>%1RrL4%Lp`C z9bA7p?mX9dtfuCaVl@ZMlUscS3K&FEuG><-lL%RO^%xK;qVxp{S$ z1nr&GX6h(nZalue((t1DY~Z3c-h1|(?05bc<2a@q-U%^6z2Q{FW+kF+G6itPjx*^s zvD$bivCcMkzfR~X6eAQXil;~lS9ia#_lvj4YIY@=pR}kMT6Mm@(^jS2-N%)XYiXW& z;eI_Q%J?)2E0ym%dsbE%oe$0rE@8q0^s!d$EfMO~KhixVbAYm-2ToOVu!$BLD|hg~ ziu!JeY!eXn>xUQg0TDn(8WZRI0t#1$LhEz=x8qkZu#LkD>I4h7cga-;oYBV={P*U7 zRpP+quzP)Bx(_|PTny^ovj)Ag5&5sF#+TcYc#7FP2`<}Km<$^tkd_Sv?a)GeoC5)! z>fN*dmh)+GWGmxDT*#}FOmPv!VTWm=pub&L z;rx`8yL5OrsTK5N%=Y9!*FG`Uk23KT+l-#?t=3fd+bO=`mb%guaI7u8?1Ac6e2MslV^SLN1!IX$K3t)#dQSu0daDUp*3O}xzTZgg-z zi#8Wq!R-F{mej78@14aI5%ahD`kv_qvuOodq$#sv?c(wjxD&tX{%oyrZg@&$31N&{{3Ua1VmV4 z@Ln;9mz|toI-Dp1E)kYrO!O$amWP-*E0Y}4e?P$0a#p=5OJQKnzbOCa>C~ zAA;zX!rxxz*>j=4?^kkjl+;-d!#kVn$j#W`V)l4OlTTs!dHJa2+~2L+zML;sB-@Dj&Qs;S?-g zmQMXdaW#-Q4h9+z;(F^&A91=q1WlTg;c{KuRL*Z@5wFA<)Wl_G4$wkUfhb9_EHX%p zScHo1QpL!4qT2|Mq0TS3Kn~zm*as+OH2eXIM%A4EIJ~nEMP?g(vp($$rUx{-fJ({9 zY9`)ba$D5Tzg=Ae4z=uzOWFiU)Dd@?D@_S&BL1- z$g19y<6tI|`h@88K1-Bs2LdAyBFwhee^|o)qi!vkV`$SDQy4$l@7CcwCX~JE3F4^j zcRdr-DfoGj+NsmXn^QF|kK&0H~nS)v)WP~;ARd*t;r~cgmvAklw00(7Up3NMxhp#+8|F_^-q$mxEQ-Um1 z-mR1IKaS4ZJq}3cHYJM`VS)D9*d3U3-}@aTMoUh#TIxFnF=O;>DN2BP+gd;k9mVO) z+1_yKUj=I|@N{lBH>U8GbCvBOZ2PLS~ z?a9N;1LnVD0%FIHQ>FE-Pzo>Z$xs_!UE1dQm{H@sW6jNuEUFJ#MhZM!IX-aOqAzq( zVA|*mAWPKpG3ukAjj5}CU?_8{Xc|<#r*e?Hm2w0SQ+UL_v%Y5X(2!4NhgZ_y?BnCExO7L|k^z@*-}-c{Z=*_8_z=)N zgZKBPnsr)$o1UKNcIqZlLS#&!_;2S|sAxDC&NH7fB?fTNLHnL&U+_@xFOz5$W`dB{ z9AAC5#J(d)&LC}e5_hk4+gNQzP?K1^-;GauJjcczC0ck{X}1>&&fw0SI&8l0>@td0 zSuT$niZt31EP*{=S~en1t;b1GZ`sW&!yFp`fQ!jzWnZ{BGGEK%=jC%x+;y|Kx(ng_ zngH#gXz|JFN>$}xm)9Fki&>q7k7JxtQ$Z$p+N9q_k}rZyp6g`3hX9~1`^NXmpBJuR zg1Akixm~AyifX1t(u-;TUKx?|t*R%RUS8(dy?>>s^IVgZzHHR(EFQo2mWJ#r42H^) zgi@5XR(VH~64C-MA23P2TCo5!AmoZPXsnY^oVHYyY&_-k;wjIPj>R^fudkX#aMpkK zon_N(d0v_i-1Hk2vm~636}frS;ne2-r?bTu(@`(#)XAC zGejxN$G;EOblZpxNB1FZH_rj~6#uONF=#@d=Nuhv~mh1#Dwld zTFtWk+XP|-KBx%Y-fi*xgMavw1 zq4nRp9n!hw@tebn(ti6Fh??=rV^K`RW?Nivtpupu0Z*{By!jn8H5m*NiAFGNbk?kItlxR#w(MQ$46CA zV$xd-(AQwajo+Vn`ofUh4H|TeWz@DSDBh(OG1~O|{QY}#>Bz#`Vrj#;av7?L*<01f z0CLY1>Hl7MtutT(0FVP2fI$>naOr^wMO8)TT7E+pznj6kqlm*Yx2G_q88*6(E5)4X zzmD*`FZk1{>?SzWL!ds z0Ro>G&vb%3#CCVa^o6EZbL!J{RF>HS6_uW})Q~kuAR7y~xUAblFv*3)k^@!Lyk|Z> z!we=K`{XPxIw@4NCAVm+L*Mxg4qgDzMArjcQ68=TH*Z(Cr$3v&e>Txq#uBCOOK3Cl zu>Ih~MblwF0sO=W;eR0e*G+Mb!*PDC2D2k(;kxsvH`0+Wbpx`S?fj=UP84S%jx>K_ z?4C3HHYArf4N=p*)!G0|7FQ_s?>C>iIQSJn$C&~^1pJL=_at#esHZL=*7yIwft6f8 zjebOEgWUQI=+~2L%sa6~YkK6Nj{xPb01bLCR1Pv|2g6G=kch9Mo-5Q0YiGUaA6^|NZmlr{S*I`ByR76Co|#7c7H*0v4T%76!)8zL;}^}{ z5$Pg?El#PE7?ElV`%80MS`hkAs@Q3%%K`t${*S;#R!Pp9R!76&8MVAjSvp4r6#218 zz%YYf9368+;%DLBp-GPjXm?|{(P(%FEz#xLXU7?61a7Wz}>h?CWQFL+@aB?LDMGprZez(-LK zp(-3&P4G?HWPtN<8IbO#7kY}F)AvF6U5dhNygkS5Y*Dqc8@y5ftX$-}T4-fv6lJBG z`5?06^$pu*TJh*IZ=uwE?{P|I-^nC6WG$ooT)jCC)si8B@2ncj`QCh0??)-CpU_A+ z6J@*V00wB`QCi0;u@O8^n^twZWZ-n+YVm>Km`SG_<25!?j&ce}V*i%fSIdP{l@ru~ zg&85epOCPM)mVk{Ps%QoVq)-C-k%S#?e%H2sob{+ej33DLG`T~vyqZgCmmt?@WE

    KelfWhcf6=N+0+!2S))j7EZ0O*iL#U5#z$Etn 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�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 literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/images/thumb/sphx_glr_read_satimg_thumb.png b/doc/source/io_examples/images/thumb/sphx_glr_read_satimg_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..1ec7c1c3c0f48ec409231fe0a8958e088800fe3c GIT binary patch literal 70802 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 literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/images/thumb/sphx_glr_read_vector_thumb.png b/doc/source/io_examples/images/thumb/sphx_glr_read_vector_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..af3c71a4bcdc24711b745817a3996bf2f1bc5a49 GIT binary patch literal 53375 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 X^NaJ8=cb-+CmfuZgVC*#f(!ovY#MoA literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/index.rst b/doc/source/io_examples/index.rst new file mode 100644 index 000000000..2cc6d6f7b --- /dev/null +++ b/doc/source/io_examples/index.rst @@ -0,0 +1,96 @@ +:orphan: + +Input/output +============ + +Examples about opening, creating, loading or saving raster and vector data. + + + +.. raw:: html + +

    + + +.. toctree:: + :hidden: + + /io_examples/read_satimg + /io_examples/read_raster + /io_examples/read_vector + + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-gallery + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download all examples in Python source code: io_examples_python.zip ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download all examples in Jupyter notebooks: io_examples_jupyter.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/io_examples_jupyter.zip b/doc/source/io_examples/io_examples_jupyter.zip new file mode 100644 index 0000000000000000000000000000000000000000..cda2f7f0ad581369714b9c0bc68946fae0c939cb GIT binary patch literal 8208 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 literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/read_raster.ipynb b/doc/source/io_examples/read_raster.ipynb new file mode 100644 index 000000000..bee1f43ae --- /dev/null +++ b/doc/source/io_examples/read_raster.ipynb @@ -0,0 +1,144 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Opening a raster from file\n\nThis example demonstrates the instantiation of a :class:`~geoutils.Raster` from file.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import geoutils as gu" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We open an example raster. The data is, by default, unloaded.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img = gu.Raster(gu.examples.get_path(\"everest_landsat_b4\"))\nimg" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can print more info on the raster.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(img.info())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`.\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img.show(cmap=\"Greys_r\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Opening can be performed with several parameters, for instance choosing a single band with `index` and re-sampling with `downsample`\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img = gu.Raster(gu.examples.get_path(\"everest_landsat_rgb\"), indexes=2, downsample=4)\nimg" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The data is unloaded by default, even if when specifying a band or re-sampling.\nWe can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`).\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img.load()\nimg" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/doc/source/io_examples/read_raster.py b/doc/source/io_examples/read_raster.py new file mode 100644 index 000000000..cb9cd1abc --- /dev/null +++ b/doc/source/io_examples/read_raster.py @@ -0,0 +1,32 @@ +""" +Opening a raster from file +========================== + +This example demonstrates the instantiation of a :class:`~geoutils.Raster` from file. +""" + +import geoutils as gu + +# %% +# We open an example raster. The data is, by default, unloaded. +img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +img + +# %% +# We can print more info on the raster. +print(img.info()) + +# %% +# The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. +img.show(cmap="Greys_r") + +# %% +# Opening can be performed with several parameters, for instance choosing a single band with `index` and re-sampling with `downsample` +img = gu.Raster(gu.examples.get_path("everest_landsat_rgb"), indexes=2, downsample=4) +img + +# %% +# The data is unloaded by default, even if when specifying a band or re-sampling. +# We can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`). +img.load() +img diff --git a/doc/source/io_examples/read_raster.py.md5 b/doc/source/io_examples/read_raster.py.md5 new file mode 100644 index 000000000..f94094c68 --- /dev/null +++ b/doc/source/io_examples/read_raster.py.md5 @@ -0,0 +1 @@ +97953828ecec7cf0e5d6d5348df67cfa \ No newline at end of file diff --git a/doc/source/io_examples/read_raster.rst b/doc/source/io_examples/read_raster.rst new file mode 100644 index 000000000..4baa314cf --- /dev/null +++ b/doc/source/io_examples/read_raster.rst @@ -0,0 +1,217 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "io_examples/read_raster.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_io_examples_read_raster.py: + + +Opening a raster from file +========================== + +This example demonstrates the instantiation of a :class:`~geoutils.Raster` from file. + +.. GENERATED FROM PYTHON SOURCE LINES 7-10 + +.. code-block:: default + + + import geoutils as gu + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 11-12 + +We open an example raster. The data is, by default, unloaded. + +.. GENERATED FROM PYTHON SOURCE LINES 12-15 + +.. code-block:: default + + img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) + img + + + + + + +.. raw:: html + +
    +
    Raster(
    +      data=not_loaded
    +      transform=| 30.00, 0.00, 478000.00|
    +                | 0.00,-30.00, 3108140.00|
    +                | 0.00, 0.00, 1.00|
    +      crs=EPSG:32645)
    +
    +
    +
    + +.. GENERATED FROM PYTHON SOURCE LINES 16-17 + +We can print more info on the raster. + +.. GENERATED FROM PYTHON SOURCE LINES 17-19 + +.. code-block:: default + + print(img.info()) + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Driver: GTiff + Opened from file: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/LE71400412000304SGS00_B4.tif + Filename: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/LE71400412000304SGS00_B4.tif + Loaded? False + Modified since load? False + Size: 800, 655 + Number of bands: 1 + Data types: ('uint8',) + Coordinate System: ['EPSG:32645'] + NoData Value: None + Pixel Size: 30.0, 30.0 + Upper Left Corner: 478000.0, 3088490.0 + Lower Right Corner: 502000.0, 3108140.0 + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 20-21 + +The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. + +.. GENERATED FROM PYTHON SOURCE LINES 21-23 + +.. code-block:: default + + img.show(cmap="Greys_r") + + + + +.. image-sg:: /io_examples/images/sphx_glr_read_raster_001.png + :alt: read raster + :srcset: /io_examples/images/sphx_glr_read_raster_001.png + :class: sphx-glr-single-img + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 24-25 + +Opening can be performed with several parameters, for instance choosing a single band with `index` and re-sampling with `downsample` + +.. GENERATED FROM PYTHON SOURCE LINES 25-28 + +.. code-block:: default + + img = gu.Raster(gu.examples.get_path("everest_landsat_rgb"), indexes=2, downsample=4) + img + + + + + + +.. raw:: html + +
    +
    Raster(
    +      data=not_loaded
    +      transform=| 120.00, 0.00, 478000.00|
    +                | 0.00,-120.00, 3108140.00|
    +                | 0.00, 0.00, 1.00|
    +      crs=EPSG:32645)
    +
    +
    +
    + +.. GENERATED FROM PYTHON SOURCE LINES 29-31 + +The data is unloaded by default, even if when specifying a band or re-sampling. +We can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`). + +.. GENERATED FROM PYTHON SOURCE LINES 31-33 + +.. code-block:: default + + img.load() + img + + + + + +.. raw:: html + +
    +
    Raster(
    +      data=[[[255 255 255 ... 255 255 255]
    +             [255 255 255 ... 255 255 255]
    +             [255 255 255 ... 255 255 255]
    +             ...
    +             [ 38  66  92 ... 114 147 157]
    +             [ 86  54  29 ... 122 142 171]
    +             [ 94  35  35 ... 150 151 223]]]
    +      transform=| 120.00, 0.00, 478000.00|
    +                | 0.00,-120.00, 3108140.00|
    +                | 0.00, 0.00, 1.00|
    +      crs=EPSG:32645)
    +
    +
    +
    + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.104 seconds) + + +.. _sphx_glr_download_io_examples_read_raster.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: read_raster.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: read_raster.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/read_raster_codeobj.pickle b/doc/source/io_examples/read_raster_codeobj.pickle new file mode 100644 index 0000000000000000000000000000000000000000..2acd7678b8c6d6c88616dd7aa038c0f9c1fb0243 GIT binary patch literal 1481 zcmbtUO-sW-5EX2-C00@D!K_za{!rol}j=#<6 zW;ba*TB7YGlj+QRZ)P6(?0)}rkMvJEUd$NwIE|xJN#`Z;af1CP>T&g@?#5~)k7*dF zi5h)C+=)<#l{^`vl;Z?~F!lw5%uRq}!6{2!2on9Tn>~~~%ZjEci4(45&z3?+)2YW$ znyR&QAjZoDqaNjIEr5-j=0v0~uWVbvpop`DMh0tg-q2X^5rI)0fV#iy4u1Y8^`Z-^rHZ_grab3L59xC2~{7<~?E{T85#b4yN+GFif z@0;#jiQ31aR5dYGSPm-;u8%l^Rb~Ah{RS(l2?3AI3<#=&7_%4` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_io_examples_read_satimg.py: + + +SatelliteImage class basics +=========================== + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`. + +.. GENERATED FROM PYTHON SOURCE LINES 7-11 + +.. code-block:: default + + import matplotlib.pyplot as plt + + import geoutils as gu + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 12-13 + +Example raster: + +.. GENERATED FROM PYTHON SOURCE LINES 13-15 + +.. code-block:: default + + img = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 16-17 + +Info: + +.. GENERATED FROM PYTHON SOURCE LINES 17-20 + +.. code-block:: default + + print(img) + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + not_loaded + + + + +.. GENERATED FROM PYTHON SOURCE LINES 21-22 + +A plot: + +.. GENERATED FROM PYTHON SOURCE LINES 22-24 + +.. code-block:: default + + img.show(cmap="Greys_r") + plt.show() + + + +.. image-sg:: /io_examples/images/sphx_glr_read_satimg_001.png + :alt: read satimg + :srcset: /io_examples/images/sphx_glr_read_satimg_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.086 seconds) + + +.. _sphx_glr_download_io_examples_read_satimg.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: read_satimg.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: read_satimg.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/read_satimg_codeobj.pickle b/doc/source/io_examples/read_satimg_codeobj.pickle new file mode 100644 index 0000000000000000000000000000000000000000..35b5271c83f9d6c0b81265750790a969e758e9de GIT binary patch literal 991 zcma)*!AiqG5Qc?TO^Gciig*=->LD-S#erQZWFS*SPz277WU@)W z(=W7#DriIjG0 ze6(SmauzXVw;I*xX)Q!Y=Aug5DOl?7RiooAopdDb^r%m-$|x|SSO?`8Zn&o0fq`18 zn@-gp?RFBfe)*fsX4;>tyYx_gdN@r;dOoYN)&L;6wCyP0UL3D!ldQTms#cH%bXS6 IY&6lqKaRP15dZ)H literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/read_vector.ipynb b/doc/source/io_examples/read_vector.ipynb new file mode 100644 index 000000000..425f762b5 --- /dev/null +++ b/doc/source/io_examples/read_vector.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Loading and understanding Vectors\n\nThis is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n\nimport geoutils as gu" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Example raster:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "glaciers = gu.Vector(gu.examples.get_path(\"everest_rgi_outlines\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Info:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(glaciers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A plot:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "for _, glacier in glaciers.ds.iterrows():\n plt.plot(*glacier.geometry.exterior.xy)\nplt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/doc/source/io_examples/read_vector.py b/doc/source/io_examples/read_vector.py new file mode 100644 index 000000000..a3282839e --- /dev/null +++ b/doc/source/io_examples/read_vector.py @@ -0,0 +1,25 @@ +""" +Loading and understanding Vectors +================================= + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. +""" + +import matplotlib.pyplot as plt + +import geoutils as gu + +# %% +# Example raster: +glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + +# %% +# Info: +print(glaciers) + + +# %% +# A plot: +for _, glacier in glaciers.ds.iterrows(): + plt.plot(*glacier.geometry.exterior.xy) +plt.show() diff --git a/doc/source/io_examples/read_vector.py.md5 b/doc/source/io_examples/read_vector.py.md5 new file mode 100644 index 000000000..e49c8bd26 --- /dev/null +++ b/doc/source/io_examples/read_vector.py.md5 @@ -0,0 +1 @@ +610752bf652a95a4e6fda70357a5314d \ No newline at end of file diff --git a/doc/source/io_examples/read_vector.rst b/doc/source/io_examples/read_vector.rst new file mode 100644 index 000000000..f9d9cb713 --- /dev/null +++ b/doc/source/io_examples/read_vector.rst @@ -0,0 +1,150 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "io_examples/read_vector.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_io_examples_read_vector.py: + + +Loading and understanding Vectors +================================= + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. + +.. GENERATED FROM PYTHON SOURCE LINES 7-12 + +.. code-block:: default + + + import matplotlib.pyplot as plt + + import geoutils as gu + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 13-14 + +Example raster: + +.. GENERATED FROM PYTHON SOURCE LINES 14-16 + +.. code-block:: default + + glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 17-18 + +Info: + +.. GENERATED FROM PYTHON SOURCE LINES 18-21 + +.. code-block:: default + + print(glaciers) + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Filename: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/15_rgi60_glacier_outlines.gpkg + Coordinate System: EPSG:4326 + Number of features: 86 + Extent: [86.70393205700003, 27.847778695000045, 87.11409458600008, 28.14549759700003] + Attributes: ['RGIId', 'GLIMSId', 'BgnDate', 'EndDate', 'CenLon', 'CenLat', 'O1Region', 'O2Region', 'Area', 'Zmin', 'Zmax', 'Zmed', 'Slope', 'Aspect', 'Lmax', 'Status', 'Connect', 'Form', 'TermType', 'Surging', 'Linkages', 'Name', 'geometry'] + RGIId ... geometry + 0 RGI60-15.03410 ... POLYGON ((86.92842 27.92877, 86.92866 27.92930... + 1 RGI60-15.03411 ... POLYGON ((86.94077 27.92375, 86.94059 27.92295... + 2 RGI60-15.03412 ... POLYGON ((86.85344 27.94752, 86.85340 27.94752... + 3 RGI60-15.03413 ... POLYGON ((86.84776 27.94845, 86.84776 27.94849... + 4 RGI60-15.03414 ... POLYGON ((86.84567 27.96906, 86.84567 27.96910... + .. ... ... ... + 81 RGI60-15.10070 ... POLYGON ((86.94112 28.11455, 86.94129 28.11452... + 82 RGI60-15.10075 ... POLYGON ((86.95861 28.04669, 86.95847 28.04701... + 83 RGI60-15.10077 ... POLYGON ((86.96454 28.07843, 86.96445 28.07814... + 84 RGI60-15.10078 ... POLYGON ((86.96302 28.04915, 86.96304 28.04920... + 85 RGI60-15.10079 ... POLYGON ((86.97074 28.09496, 86.97039 28.09488... + + [86 rows x 23 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 22-23 + +A plot: + +.. GENERATED FROM PYTHON SOURCE LINES 23-26 + +.. code-block:: default + + for _, glacier in glaciers.ds.iterrows(): + plt.plot(*glacier.geometry.exterior.xy) + plt.show() + + + +.. image-sg:: /io_examples/images/sphx_glr_read_vector_001.png + :alt: read vector + :srcset: /io_examples/images/sphx_glr_read_vector_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.110 seconds) + + +.. _sphx_glr_download_io_examples_read_vector.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: read_vector.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: read_vector.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/read_vector_codeobj.pickle b/doc/source/io_examples/read_vector_codeobj.pickle new file mode 100644 index 0000000000000000000000000000000000000000..62eb2e1f0b7f54709df4a5256a9c2d4931fbb6fb GIT binary patch literal 2764 zcmbVO&ui2`6jrR;ZBuo(c8iFDAV_=3p&)n@%K8hjtv^r@$~v2!?u^bRVP;}?5uwK} zL=cPzFJ8U+FZiGNCdno{)RW1#Wb;L`5ufiR`^jf`Sn$yZC-DX9%Oq}$fD>ue)|A5~YT zw0EkUZXZ&CnB26iP^X&QvIOgrZyT6)tz2g&m(jiwdB*~yb8P1MI9|&Q7w$l~6s+dK zp(u^+0DTup^pL|gE8j!M)?tT)$R8k~{2oBgfyz-{Efud!x91nr5f0fhR-7ST-D0(MQGJikDduR!l!fqc-PUzA7BiFd+qk$!^|~AwseuRiSWh}T-Es25~u0hHz4az`~ikO4o`M-O&3b@H0AX> z+X_{+o-I(5dthR^l<&Yus4?G=i#!ht5pX4!+E%E_Wwt;~ZjfVeyqHhHa_}`?!fC-m z-3_aHZ$%z<>cjfM*Gg2?jt#Y1qNeUsp{^M+>B9YeSjuZ2M1J~xpahn;J#_HMD7}!v zAg4V-GGu{-3&Ra3>IvFi8_kx0VTwoYYd8>KOmXiw@__0J%-%>+mX80ASsqku&cZPI zkIRc*18Nx#`C^szwQR-gx;m>S?c-yfrXK~{8ngh9(I{kCEmQVFBuU9U--Mrr39#ZE zs*IR5sHmJ8Q4^gRk=ma+R`@WV`Io`GkQsdz`>zy=icOGxv(0PU@09sCLXrCy@m~(; literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/searchindex.bak b/doc/source/io_examples/searchindex.bak new file mode 100644 index 000000000..38a7775ad --- /dev/null +++ b/doc/source/io_examples/searchindex.bak @@ -0,0 +1,3 @@ +'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) +'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) +'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/io_examples/searchindex.dat b/doc/source/io_examples/searchindex.dat new file mode 100644 index 0000000000000000000000000000000000000000..a179f4911b395951a6ae047c77b42ea62fd8164a GIT binary patch literal 84326 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
    GCE8D}oUtbusQw-}ba4 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 literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/searchindex.dir b/doc/source/io_examples/searchindex.dir new file mode 100644 index 000000000..38a7775ad --- /dev/null +++ b/doc/source/io_examples/searchindex.dir @@ -0,0 +1,3 @@ +'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) +'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) +'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/io_examples/sg_execution_times.rst b/doc/source/io_examples/sg_execution_times.rst new file mode 100644 index 000000000..a4b8c23f7 --- /dev/null +++ b/doc/source/io_examples/sg_execution_times.rst @@ -0,0 +1,16 @@ + +:orphan: + +.. _sphx_glr_io_examples_sg_execution_times: + +Computation times +================= +**00:00.104** total execution time for **io_examples** files: + ++-----------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_io_examples_read_raster.py` (``read_raster.py``) | 00:00.104 | 0.0 MB | ++-----------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_io_examples_read_satimg.py` (``read_satimg.py``) | 00:00.000 | 0.0 MB | ++-----------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_io_examples_read_vector.py` (``read_vector.py``) | 00:00.000 | 0.0 MB | ++-----------------------------------------------------------------+-----------+--------+ diff --git a/doc/source/mask_class.md b/doc/source/mask_class.md index 40c62e2e1..4af6e8c67 100644 --- a/doc/source/mask_class.md +++ b/doc/source/mask_class.md @@ -147,9 +147,29 @@ raster[mask_outlines] See {ref}`py-ops-indexing` for more details. +(mask-class-poly-overloaded)= + ## Polygonize (overloaded from {class}`~geoutils.Raster`) -Some methods +{class}`Masks` have simplified class methods overloaded from {class}`Rasters` when one or several attributes of the methods +become implicit in the case of {class}`bool` data. + +The {func}`~geoutils.Mask.polygonize` function is one of those, implicitly applying to the `True` values of the mask as target pixels. It outputs a +{class}`~geoutils.Vector` of the input mask. + +```{code-cell} ipython3 +# Polygonize mask +mask.polygonize() +``` + +(mask-class-prox-overloaded)= ## Proximity (overloaded from {class}`~geoutils.Raster`) +The {func}`~geoutils.Mask.proximity` function is another overloaded method of {class}`~geoutils.Raster` implicitly applying to the `True` values of the mask as +target pixels. It outputs a {class}`~geoutils.Raster` of the distances to the input mask. + +```{code-cell} ipython3 +# Proximity to mask +mask.proximity() +``` diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 47af00aeb..5d2845147 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -1,3 +1,8 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (quick-start)= # Quick start @@ -11,18 +16,35 @@ For more details, refer to the {ref}`core-index`, {ref}`rasters-index` or {ref}` In GeoUtils, geospatial handling is object-based and revolves around {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. These link to either **on-disk** or **in-memory** datasets, opened by instantiating the class. -```{literalinclude} code/index_example.py -:lines: 1-9 -:language: python +```{code-cell} ipython3 +import geoutils as gu + +# Examples files: infrared band of Landsat and glacier outlines +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") + +# Open files +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) ``` A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as `.data`, a {class}`~pyproj.crs.CRS` as `.crs`, an {class}`~affine.Affine` as `.transform`, and a {class}`float` or {class}`int` as `.nodata`. When a file exists on disk, {class}`~geoutils.Raster` is -linked to a {class}`rasterio.DatasetReader` object for loading the metadata, and the array at the appropriate time. +linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata, and the array at the appropriate time. + +```{code-cell} ipython3 +# The opened raster +rast +``` A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as `.ds`, for which most methods are wrapped directly into {class}`~geoutils.Vector`. +```{code-cell} ipython3 +# The opened vector +vect +``` + All other attributes are derivatives of those main attributes, or of the filename on disk. Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update with geospatial operations on themselves. @@ -32,9 +54,15 @@ Geospatial operations are based on class methods, such as {func}`geoutils.Raster passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a **reference to match** during the operation. A **reference {class}`~geoutils.Vector`** enforces a matching of `.bounds` and/or `.crs`, while a **reference {class}`~geoutils.Raster`** can also enforce a matching of `.res`, depending on the nature of the operation. -```{literalinclude} code/index_example.py -:lines: 16-20 -:language: python + +```{code-cell} ipython3 +# Crop raster to vector's extent +rast.crop(vect) +``` + +```{code-cell} ipython3 +# Compute proximity to vector on raster's grid +rast_proximity_to_vec = vect.proximity(rast) ``` All methods can be also be passed any number of georeferencing arguments such as `.shape` or `.res`, and will logically deduce others from the input, much @@ -83,17 +111,18 @@ All {class}`~geoutils.Raster` objects support Python arithmetic ({func}`+`, {func}`%`) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. For other {class}`~geoutils.Raster`, the georeferencing must match, while only the shape for other {class}`~numpy.ndarray`. -```{literalinclude} code/index_example.py -:lines: 22-23 -:language: python +```{code-cell} ipython3 +# Add 1 to the raster array +rast += 1 ``` Additionally, the class {class}`~geoutils.Raster` possesses a NumPy masked-array interface that allows to apply to it any [NumPy universal function](https://numpy.org/doc/stable/reference/ufuncs.html) and most other NumPy array functions, while logically casting `dtypes` and respecting `.nodata` values. -```{literalinclude} code/index_example.py -:lines: 25-27 -:language: python +```{code-cell} ipython3 +# Apply a normalization to the raster +import numpy as np +rast = (rast - np.min(rast)) / (np.max(rast) - np.min(rast)) ``` ## Casting to {class}`~geoutils.Mask`, indexing and overload @@ -101,25 +130,24 @@ most other NumPy array functions, while logically casting `dtypes` and respectin All {class}`~geoutils.Raster` classes also support Python logical comparison operators ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, {func}`<`), or more complex NumPy logical functions. Those operations automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. - -```{literalinclude} code/index_example.py -:lines: 29-30 -:language: python +```{code-cell} ipython3 +# Get mask of an AOI: infrared index above 0.7, at least 200 m from glaciers +mask_aoi = np.logical_and(rast > 0.7, rast_proximity_to_vec > 200) ``` Masks can then be used for indexing a {class}`~geoutils.Raster`, which returns a {class}`~numpy.ma.MaskedArray` of indexed values. -```{literalinclude} code/index_example.py -:lines: 32-33 -:language: python +```{code-cell} ipython3 +# Index raster with mask to extract a 1-D array +values_aoi = rast[mask_aoi] ``` Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Using {func}`~geoutils.Raster.polygonize` with a {class}`~geoutils.Mask` is straightforward, for instance, to retrieve a {class}`~geoutils.Vector` of the area-of-interest: -```{literalinclude} code/index_example.py -:lines: 35-36 -:language: python +```{code-cell} ipython3 +# Polygonize areas where mask is True +vect_aoi = mask_aoi.polygonize() ``` ## Plotting and saving geospatial data @@ -130,15 +158,13 @@ The plotting function was renamed {func}`~geoutils.Raster.show` everywhere, for For saving, {func}`~geoutils.Raster.save` is used. -```{literalinclude} code/index_example.py -:lines: 38-47 -:language: python -``` - -```{eval-rst} -.. plot:: code/index_example.py - :caption: High infrared outside of glaciers at Mount Everest (perennial snowfields) - :width: 90% +```{code-cell} ipython3 +# Plot result +import matplotlib.pyplot as plt +fig = plt.figure() +ax = plt.gca() +rast.show(ax=ax, cmap='Reds', cb_title='Normalized infrared') +vect_aoi.ds.plot(ax=ax, fc='none', ec='k', lw=0.5) ``` ```{admonition} Wrap-up @@ -161,14 +187,15 @@ Otherwise, for more **hands-on** examples, explore GeoUtils' gallery of examples In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses metadata recognized from the filename or auxiliary files. -```{literalinclude} code/index_example.py -:lines: 11-14 -:language: python +```{code-cell} ipython3 +# Name of the image we used +import os +print(os.path.basename(filename_rast)) ``` -```{eval-rst} -.. program-output:: $PYTHON code/index_example.py - :shell: +```{code-cell} ipython3 +# Open while parsing metadata +rast = gu.SatelliteImage(filename_rast, silent=False) ``` There are many possible subclass to derive from a {class}`~geoutils.Raster`. Here's an **overview of current {class}`~geoutils.Raster` class inheritance**, which extends into diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index c39d53913..45ff209dd 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -22,7 +22,7 @@ A {class}`~geoutils.Raster` contains **four main attributes**: For more details on {class}`~geoutils.Raster` class composition, see {ref}`core-composition`. -A {class}`~geoutils.Raster` also contains many derivative attributes, with naming generally consistent with that of a {class}`rasterio.DatasetReader`. +A {class}`~geoutils.Raster` also contains many derivative attributes, with naming generally consistent with that of a {class}`rasterio.io.DatasetReader`. A first category includes georeferencing attributes directly derived from {attr}`~geoutils.Raster.transform`, namely: {attr}`~geoutils.Raster.shape`, {attr}`~geoutils.Raster.height`, {attr}`~geoutils.Raster.width`, {attr}`~geoutils.Raster.res`, {attr}`~geoutils.Raster.bounds`. @@ -31,7 +31,7 @@ A second category concerns the attributes derived from the raster array shape an {attr}`~geoutils.Raster.dtypes`. The two former refer to the number of bands loaded in a {class}`~geoutils.Raster`, and their indexes. ```{important} -The {attr}`~geoutils.Raster.indexes` of {class}`rasterio.DatasetReader` start from 1 and not 0, be careful when instantiating or loading from a +The {attr}`~geoutils.Raster.indexes` of {class}`rasterio.io.DatasetReader` start from 1 and not 0, be careful when instantiating or loading from a multi-band raster! ``` @@ -51,7 +51,8 @@ The complete list of {class}`~geoutils.Raster` attributes with description is av ## Open and save -A {class}`~geoutils.Raster` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path` or a {class}`rasterio.DatasetReader`. +A {class}`~geoutils.Raster` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path`, a {class}`rasterio.io.DatasetReader` or a +{class}`rasterio.io.MemoryFile`. ```{code-cell} ipython3 @@ -235,8 +236,7 @@ Resampling methods are listed in **[the dedicated section of Rasterio's API](htt Cropping a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.crop` function, which enforces new {attr}`~geoutils.Raster.bounds`. ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils. -Vector` as argument. +As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument. See {ref}`core-match-ref` for more details. ``` @@ -259,7 +259,8 @@ Polygonizing a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Ra By default, values equal to `1` are used. ```{note} -For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly targets `True` values and thus does not require target pixels. +For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly targets `True` values and thus does not require target pixels. See +{ref}`mask-class-poly-overloaded`. ``` ```{code-cell} ipython3 @@ -275,7 +276,8 @@ Computing proximity from a {class}`~geoutils.Raster` is done through by the {fun target pixels in the {class}`~geoutils.Raster`. ```{note} -For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.proximity` implicitly targets `True` values and thus does not require target pixels. +For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.proximity` implicitly targets `True` values and thus does not require target pixels. See +{ref}`mask-class-prox-overloaded`. ``` ```{code-cell} ipython3 @@ -321,7 +323,7 @@ A {class}`~geoutils.Raster` can be exported to different formats, to facilitate Those include exporting to: - a {class}`xarray.Dataset` with {class}`~geoutils.Raster.to_xarray`, -- a {class}`rasterio.DatasetReader` with {class}`~geoutils.Raster.to_rio_dataset`, +- a {class}`rasterio.io.DatasetReader` with {class}`~geoutils.Raster.to_rio_dataset`, - a {class}`numpy.ndarray`, {class}`geopandas.GeoDataFrame` or {class}`geoutils.Vector` as a point cloud with {class}`~geoutils.Raster.to_points`. ```{code-cell} ipython3 diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md index 0f58d439a..4aa791ff0 100644 --- a/doc/source/satimg_class.md +++ b/doc/source/satimg_class.md @@ -1,35 +1,54 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (satimg-class)= -# The geo-image (`SatelliteImage`) +# The geo-image ({class}`~geoutils.SatelliteImage`) + +Below, a summary of the {class}`~geoutils.SatelliteImage` object and its methods. ## Object definition -## Datetime parsing +A {class}`~geoutils.SatelliteImage` is a subclass of {class}`~geoutils.Raster` that contains all its main attributes, and retains additional ones: +- a {class}`numpy.datetime64` as {class}`~geoutils.SatelliteImage.datetime`, and +- several {class}`strings` for`~geoutils.SatelliteImage.satellite`, {class}`~geoutils.SatelliteImage.sensor`, {class}`~geoutils.SatelliteImage. version`, {class}`~geoutils.SatelliteImage.tile_name` and {class}`~geoutils.SatelliteImage.product`. + +A {class}`~geoutils.SatelliteImage` also inherits the same derivative attributes as a {class}`~geoutils.Raster`. -Example with a Landsat image: +## Metadata parsing -```{literalinclude} code/satimg-basics_open_file.py -:lines: 2- -``` +The {class}`~geoutils.SatelliteImage` attempts to parse metadata from the filename. -When reading your file, SatImg will try to load metadata information from the filename. -For the above filename, this will be printed in the console: +Right now are supported: + - Landsat, + - Sentinel-2, + - SPOT, + - ASTER, + - ArcticDEM and REMA, + - ALOS, + - SRTM, + - TanDEM-X, and + - NASADEM. -```{eval-rst} -.. program-output:: $PYTHON -c "exec(open('code/satimg-basics_open_file.py').read())" - :shell: -``` +The {class}`~geoutils.SatelliteImage.datetime` is always parsed or deduced. -Currently supporting the nomenclatures used for: Landsat, Sentinel-2, ArcticDEM, REMA, ASTER L1A, ASTER GDEM, NASADEM, TanDEM-X, SRTM and SPOT-5 +For tiled products such as SRTM, the tile naming is also retrieved, which can be converted to geographic extent with {func}`geoutils.georaster.satimg.parse_tile_attr_from_name`. -More to come... +```{code-cell} ipython3 +import geoutils as gu -```{eval-rst} -.. minigallery:: geoutils.SatelliteImage - :add-heading: - :heading-level: - +# Initiate a geo-image from an ASTER image +geoimg = gu.SatelliteImage(gu.examples.get_path("exploradores_aster_dem"), silent=False) ``` -## Tile parsing +```{code-cell} ipython3 +# Initiate a geo-image from a Landsat 7 image +geoimg2 = gu.SatelliteImage(gu.examples.get_path("everest_landsat_b4"), silent=False) +``` -## Instrument metadata parsing +```{important} +The {class}`~geoutils.SatelliteImage` class is still in development, and we hope to further refine it in the future using metadata classes able to parse +auxiliary files metadata (such as [here](https://github.com/jlandmann/glaciersat/blob/master/glaciersat/core/imagery.py)). +``` diff --git a/examples/README.rst b/examples/README.rst deleted file mode 100644 index da293984f..000000000 --- a/examples/README.rst +++ /dev/null @@ -1,4 +0,0 @@ -Examples gallery -================ -Here are examples that showcase the functionality of ``geoutils``. -All scripts can be downloaded and modified offline. diff --git a/examples/analysis/README.rst b/examples/analysis/README.rst new file mode 100644 index 000000000..000bd8622 --- /dev/null +++ b/examples/analysis/README.rst @@ -0,0 +1,5 @@ +Analysis +======== + +Examples about numerical operations on rasters, computing proximity and buffer without overlap. + diff --git a/examples/handling/README.rst b/examples/handling/README.rst new file mode 100644 index 000000000..c8e11e62e --- /dev/null +++ b/examples/handling/README.rst @@ -0,0 +1,4 @@ +Handling +======== + +Examples about warping, reprojecting, resampling cropping, as well as rasterizing and polygonizing of georeferenced data. diff --git a/examples/io/README.rst b/examples/io/README.rst new file mode 100644 index 000000000..f4b389036 --- /dev/null +++ b/examples/io/README.rst @@ -0,0 +1,4 @@ +Input/output +============ + +Examples about opening, creating, loading or saving raster and vector data. diff --git a/examples/io/read_raster.py b/examples/io/read_raster.py new file mode 100644 index 000000000..fc4eb2890 --- /dev/null +++ b/examples/io/read_raster.py @@ -0,0 +1,32 @@ +""" +Opening a raster from file +========================== + +This example demonstrates the instantiation of a :class:`~geoutils.Raster` from file. +""" + +import geoutils as gu + +# %% +# We open an example raster. The data is, by default, unloaded. +img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +img + +# %% +# We can print more info on the raster. +print(img.info()) + +# %% +# The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. +img.show(cmap="Greys_r") + +# %% +# Opening can be performed with several parameters, for instance choosing a single band with ``index`` and re-sampling with ``downsample``. +img = gu.Raster(gu.examples.get_path("everest_landsat_rgb"), indexes=2, downsample=4) +img + +# %% +# The data is unloaded by default, even if when specifying a band or re-sampling. +# We can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`). +img.load() +img diff --git a/examples/io/read_satimg.py b/examples/io/read_satimg.py new file mode 100644 index 000000000..9c4d4afd2 --- /dev/null +++ b/examples/io/read_satimg.py @@ -0,0 +1,23 @@ +""" +SatelliteImage class basics +=========================== + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`. +""" +import matplotlib.pyplot as plt + +import geoutils as gu + +# %% +# Example raster: +img = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) + +# %% +# Info: +print(img) + + +# %% +# A plot: +img.show(cmap="Greys_r") +plt.show() diff --git a/examples/io/read_vector.py b/examples/io/read_vector.py new file mode 100644 index 000000000..a3282839e --- /dev/null +++ b/examples/io/read_vector.py @@ -0,0 +1,25 @@ +""" +Loading and understanding Vectors +================================= + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. +""" + +import matplotlib.pyplot as plt + +import geoutils as gu + +# %% +# Example raster: +glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + +# %% +# Info: +print(glaciers) + + +# %% +# A plot: +for _, glacier in glaciers.ds.iterrows(): + plt.plot(*glacier.geometry.exterior.xy) +plt.show() diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 69cff1ce7..d587d6e92 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -487,10 +487,12 @@ def indexes_on_disk(self) -> None | tuple[int, ...]: @property def indexes(self) -> tuple[int, ...]: """Return the indexes of bands loaded in memory if they are, otherwise on disk.""" - if self._indexes_loaded is not None: - if isinstance(self._indexes_loaded, int): - return (self._indexes_loaded,) - return tuple(self._indexes_loaded) + if self._indexes is not None and not self.is_loaded: + return self._indexes + # if self._indexes_loaded is not None: + # if isinstance(self._indexes_loaded, int): + # return (self._indexes_loaded, ) + # return tuple(self._indexes_loaded) if self.is_loaded: return tuple(range(1, self.count + 1)) return self.indexes_on_disk # type: ignore From 016a8d64c9c5f70399665b9608d7dcc481f6cc96 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Feb 2023 21:54:54 -0800 Subject: [PATCH 036/184] Incremental commit on gallery examples --- .../analysis_examples_jupyter.zip | Bin 22 -> 0 bytes .../analysis_examples_python.zip | Bin 22 -> 0 bytes doc/source/analysis_examples/index.rst | 38 --- doc/source/analysis_examples/searchindex.bak | 3 - doc/source/analysis_examples/searchindex.dat | Bin 84326 -> 0 bytes doc/source/analysis_examples/searchindex.dir | 3 - .../handling_examples_jupyter.zip | Bin 7010 -> 0 bytes .../handling_examples_python.zip | Bin 1580 -> 0 bytes .../images/sphx_glr_read_raster_001.png | Bin 213447 -> 0 bytes .../images/sphx_glr_read_satimg_001.png | Bin 151024 -> 0 bytes .../images/sphx_glr_read_vector_001.png | Bin 100827 -> 0 bytes .../thumb/sphx_glr_read_raster_thumb.png | Bin 92341 -> 0 bytes .../thumb/sphx_glr_read_satimg_thumb.png | Bin 70802 -> 0 bytes .../thumb/sphx_glr_read_vector_thumb.png | Bin 53375 -> 0 bytes doc/source/handling_examples/index.rst | 37 --- .../handling_examples/read_raster.ipynb | 108 --------- doc/source/handling_examples/read_raster.py | 24 -- .../handling_examples/read_raster.py.md5 | 1 - doc/source/handling_examples/read_raster.rst | 131 ----------- .../read_raster_codeobj.pickle | Bin 975 -> 0 bytes .../handling_examples/read_satimg.ipynb | 108 --------- doc/source/handling_examples/read_satimg.py | 23 -- .../handling_examples/read_satimg.py.md5 | 1 - doc/source/handling_examples/read_satimg.rst | 130 ----------- .../read_satimg_codeobj.pickle | Bin 991 -> 0 bytes .../handling_examples/read_vector.ipynb | 108 --------- doc/source/handling_examples/read_vector.py | 25 -- .../handling_examples/read_vector.py.md5 | 1 - doc/source/handling_examples/read_vector.rst | 150 ------------ .../read_vector_codeobj.pickle | Bin 2764 -> 0 bytes doc/source/handling_examples/searchindex.bak | 3 - doc/source/handling_examples/searchindex.dat | Bin 84326 -> 0 bytes doc/source/handling_examples/searchindex.dir | 3 - .../handling_examples/sg_execution_times.rst | 16 -- .../images/sphx_glr_read_raster_001.png | Bin 213447 -> 0 bytes .../images/sphx_glr_read_satimg_001.png | Bin 151024 -> 0 bytes .../images/sphx_glr_read_vector_001.png | Bin 100827 -> 0 bytes .../thumb/sphx_glr_read_raster_thumb.png | Bin 92341 -> 0 bytes .../thumb/sphx_glr_read_satimg_thumb.png | Bin 70802 -> 0 bytes .../thumb/sphx_glr_read_vector_thumb.png | Bin 53375 -> 0 bytes doc/source/io_examples/index.rst | 96 -------- .../io_examples/io_examples_jupyter.zip | Bin 8208 -> 0 bytes doc/source/io_examples/io_examples_python.zip | Bin 2205 -> 0 bytes doc/source/io_examples/read_raster.ipynb | 144 ------------ doc/source/io_examples/read_raster.py | 32 --- doc/source/io_examples/read_raster.py.md5 | 1 - doc/source/io_examples/read_raster.rst | 217 ------------------ .../io_examples/read_raster_codeobj.pickle | Bin 1481 -> 0 bytes doc/source/io_examples/read_satimg.ipynb | 108 --------- doc/source/io_examples/read_satimg.py | 23 -- doc/source/io_examples/read_satimg.py.md5 | 1 - doc/source/io_examples/read_satimg.rst | 130 ----------- .../io_examples/read_satimg_codeobj.pickle | Bin 991 -> 0 bytes doc/source/io_examples/read_vector.ipynb | 108 --------- doc/source/io_examples/read_vector.py | 25 -- doc/source/io_examples/read_vector.py.md5 | 1 - doc/source/io_examples/read_vector.rst | 150 ------------ .../io_examples/read_vector_codeobj.pickle | Bin 2764 -> 0 bytes doc/source/io_examples/searchindex.bak | 3 - doc/source/io_examples/searchindex.dat | Bin 84326 -> 0 bytes doc/source/io_examples/searchindex.dir | 3 - doc/source/io_examples/sg_execution_times.rst | 16 -- examples/io/from_array.py | 56 +++++ examples/io/read_raster.py | 28 +-- examples/io/read_vector.py | 23 +- geoutils/georaster/raster.py | 4 +- 66 files changed, 83 insertions(+), 1999 deletions(-) delete mode 100644 doc/source/analysis_examples/analysis_examples_jupyter.zip delete mode 100644 doc/source/analysis_examples/analysis_examples_python.zip delete mode 100644 doc/source/analysis_examples/index.rst delete mode 100644 doc/source/analysis_examples/searchindex.bak delete mode 100644 doc/source/analysis_examples/searchindex.dat delete mode 100644 doc/source/analysis_examples/searchindex.dir delete mode 100644 doc/source/handling_examples/handling_examples_jupyter.zip delete mode 100644 doc/source/handling_examples/handling_examples_python.zip delete mode 100644 doc/source/handling_examples/images/sphx_glr_read_raster_001.png delete mode 100644 doc/source/handling_examples/images/sphx_glr_read_satimg_001.png delete mode 100644 doc/source/handling_examples/images/sphx_glr_read_vector_001.png delete mode 100644 doc/source/handling_examples/images/thumb/sphx_glr_read_raster_thumb.png delete mode 100644 doc/source/handling_examples/images/thumb/sphx_glr_read_satimg_thumb.png delete mode 100644 doc/source/handling_examples/images/thumb/sphx_glr_read_vector_thumb.png delete mode 100644 doc/source/handling_examples/index.rst delete mode 100644 doc/source/handling_examples/read_raster.ipynb delete mode 100644 doc/source/handling_examples/read_raster.py delete mode 100644 doc/source/handling_examples/read_raster.py.md5 delete mode 100644 doc/source/handling_examples/read_raster.rst delete mode 100644 doc/source/handling_examples/read_raster_codeobj.pickle delete mode 100644 doc/source/handling_examples/read_satimg.ipynb delete mode 100644 doc/source/handling_examples/read_satimg.py delete mode 100644 doc/source/handling_examples/read_satimg.py.md5 delete mode 100644 doc/source/handling_examples/read_satimg.rst delete mode 100644 doc/source/handling_examples/read_satimg_codeobj.pickle delete mode 100644 doc/source/handling_examples/read_vector.ipynb delete mode 100644 doc/source/handling_examples/read_vector.py delete mode 100644 doc/source/handling_examples/read_vector.py.md5 delete mode 100644 doc/source/handling_examples/read_vector.rst delete mode 100644 doc/source/handling_examples/read_vector_codeobj.pickle delete mode 100644 doc/source/handling_examples/searchindex.bak delete mode 100644 doc/source/handling_examples/searchindex.dat delete mode 100644 doc/source/handling_examples/searchindex.dir delete mode 100644 doc/source/handling_examples/sg_execution_times.rst delete mode 100644 doc/source/io_examples/images/sphx_glr_read_raster_001.png delete mode 100644 doc/source/io_examples/images/sphx_glr_read_satimg_001.png delete mode 100644 doc/source/io_examples/images/sphx_glr_read_vector_001.png delete mode 100644 doc/source/io_examples/images/thumb/sphx_glr_read_raster_thumb.png delete mode 100644 doc/source/io_examples/images/thumb/sphx_glr_read_satimg_thumb.png delete mode 100644 doc/source/io_examples/images/thumb/sphx_glr_read_vector_thumb.png delete mode 100644 doc/source/io_examples/index.rst delete mode 100644 doc/source/io_examples/io_examples_jupyter.zip delete mode 100644 doc/source/io_examples/io_examples_python.zip delete mode 100644 doc/source/io_examples/read_raster.ipynb delete mode 100644 doc/source/io_examples/read_raster.py delete mode 100644 doc/source/io_examples/read_raster.py.md5 delete mode 100644 doc/source/io_examples/read_raster.rst delete mode 100644 doc/source/io_examples/read_raster_codeobj.pickle delete mode 100644 doc/source/io_examples/read_satimg.ipynb delete mode 100644 doc/source/io_examples/read_satimg.py delete mode 100644 doc/source/io_examples/read_satimg.py.md5 delete mode 100644 doc/source/io_examples/read_satimg.rst delete mode 100644 doc/source/io_examples/read_satimg_codeobj.pickle delete mode 100644 doc/source/io_examples/read_vector.ipynb delete mode 100644 doc/source/io_examples/read_vector.py delete mode 100644 doc/source/io_examples/read_vector.py.md5 delete mode 100644 doc/source/io_examples/read_vector.rst delete mode 100644 doc/source/io_examples/read_vector_codeobj.pickle delete mode 100644 doc/source/io_examples/searchindex.bak delete mode 100644 doc/source/io_examples/searchindex.dat delete mode 100644 doc/source/io_examples/searchindex.dir delete mode 100644 doc/source/io_examples/sg_execution_times.rst create mode 100644 examples/io/from_array.py diff --git a/doc/source/analysis_examples/analysis_examples_jupyter.zip b/doc/source/analysis_examples/analysis_examples_jupyter.zip deleted file mode 100644 index 15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22 NcmWIWW@Tf*000g10H*)| diff --git a/doc/source/analysis_examples/analysis_examples_python.zip b/doc/source/analysis_examples/analysis_examples_python.zip deleted file mode 100644 index 15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22 NcmWIWW@Tf*000g10H*)| diff --git a/doc/source/analysis_examples/index.rst b/doc/source/analysis_examples/index.rst deleted file mode 100644 index 9c2be7621..000000000 --- a/doc/source/analysis_examples/index.rst +++ /dev/null @@ -1,38 +0,0 @@ -:orphan: - -Analysis -======== - -Examples about numerical operations on rasters, computing proximity and buffer without overlap. - - - - -.. raw:: html - -
    - - -.. raw:: html - -
    - - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-gallery - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download all examples in Python source code: analysis_examples_python.zip ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download all examples in Jupyter notebooks: analysis_examples_jupyter.zip ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/analysis_examples/searchindex.bak b/doc/source/analysis_examples/searchindex.bak deleted file mode 100644 index 38a7775ad..000000000 --- a/doc/source/analysis_examples/searchindex.bak +++ /dev/null @@ -1,3 +0,0 @@ -'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) -'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) -'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/analysis_examples/searchindex.dat b/doc/source/analysis_examples/searchindex.dat deleted file mode 100644 index a179f4911b395951a6ae047c77b42ea62fd8164a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84326 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
    GCE8D}oUtbusQw-}ba4 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 diff --git a/doc/source/analysis_examples/searchindex.dir b/doc/source/analysis_examples/searchindex.dir deleted file mode 100644 index 38a7775ad..000000000 --- a/doc/source/analysis_examples/searchindex.dir +++ /dev/null @@ -1,3 +0,0 @@ -'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) -'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) -'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/handling_examples/handling_examples_jupyter.zip b/doc/source/handling_examples/handling_examples_jupyter.zip deleted file mode 100644 index 2711715392bf5fbf7a6302e1f134af6af24c15b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7010 zcmeHMPiqu06t7lAdJ}r`6e7Y_bX4laLJuB9OGOqHFK!vLGuhc-CJ9NV-BEfd9_mfO zn`gg<_!+$UaeT@A-L;Eds^Xv7c6Miyyu9S)<-K2i)8Ae`v%2Iy_Ya>u{qp|c@ZI^P zC3jtbGEGSwYGN$a0aLjczFI*DN0f7eH_$V9K*$xjk1q~wt|(ODNX8Ujt*YpRMww+& zgptgIg$5$yysl2EB{8uC%B$X7f0A>e42_|9M7W`4Z~L{J%&f|+AIeT$Xk?}%H-=(( zerT6dVilK`vms)FGeL3L*^d7$yR4K?PbDS#MJ)G3GoAK$o6#JlcpxsJ2a?20B#4L@ zWg@29Sh#g1kGy5gKn%o_F*6AMdzvLvcPxCwJA2qh8xZTn*TIYCukE)SGr9P?n>7)K|*b)h#mI^nC?W_(WZ=MUoh}5VOlzsb)aA^n6j*f$xz~-P)&RMupo5#uzZ6H7(e1VhlQg>lNjAZ4Dm(Cqr;+D!krQv6-{Fp>gPTj<`!WvXES zc(#?wlavZ;LJo=FwBNZ0F) zmHqw{e0lkG`QZnEF7B$~3xp`=Jso_h5mI|QG)^-QzC0!t@E)^tD+R|#9%7&&F)aFp zz-3x#2VjalF%N8?B%yWy1^`SLX{i*AJ0OGbu@6-pkZFSFv5nRNnGVQQ$mVDuvjsfN z=IaY*z5irrcltqm=4AqhpkLFi5ML3 z3Nohfoga}gI$F4vp{{koD=;-h&DoqExdUb1p*aUE3 z1X5$q^wxlRawNQ?X&e#NyHZL3rzqhls&il&p)rHpHpx3a=RfCjPMuy|p8rD1zo~++ zj|cAVyVq0w?P>8@LZ}h{49Lohx9Op__)##d5q|@y%!{`{S%3TNIoD(v{yxF)+l%h$ E8wTNBGynhq diff --git a/doc/source/handling_examples/handling_examples_python.zip b/doc/source/handling_examples/handling_examples_python.zip deleted file mode 100644 index 3857f7d7b3ea25ec013f56fd9616ff553e959bc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1580 zcmd5+&x+JA7@u`q4>^d3eSnY3LMzgfr@|aOh%Vz{&}G3>H`8X)K$?Ukv$H*VQ1Jmg z3nD&%c=PC41mDAV@Jrk2GK0Hw8Zd3zFUj{W`Q`V`rsLa3qtKqcJ3agH=EIk_2cuEw zcX7@#TIQBI&unU%D2m8)O-rFTP*uWGmCQPiVwic+syQ;*)d)GQgadqID|qEW=~V(0 z%4J2$r7FD8ib~-d&Th4L}_!-aLU$C#4#ZIm=@IxcEzCec&N8kgHB zA((NukdWC1VxNBg{{9}(3VlEfL!u8?<{L|GfHzfQ3GQcEJj-chl^wRl5Cd=B@*7?e-s(3w0)c2)zafwH94kx)f<+tzNk}$p~NzJPcHgAM^%DeqA$f z8!QK?D-glUW{a-}w~ofw*VwlB;^*`0AZ=FM*>ux(o9$P&U-2rnwgdOKZMQGol`S9l Vw!;;iO%Lyc^)Wu*@IOBcqrWz_L9{ErBPM`c* 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

    0cVW@Agb5@4j6N}zxwCc7+X&? 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 @rH^(5Spw{h2V$pQBG?g$Al$(`p)X( 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 literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/images/sphx_glr_read_satimg_001.png b/doc/source/io_examples/images/sphx_glr_read_satimg_001.png new file mode 100644 index 0000000000000000000000000000000000000000..f805df8c33f1640dd836439824e362ebd8666f9a GIT binary patch literal 151024 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%0D+tSA>d2NQSEl zKeJjJAmH70Bb6s4y41s}AcB!=j8TJMVbz?Pe(I}bX?7-yMdH;bVlhMY4|v7#QMvLD zuu^iKq(>dR`Pd9HYVT|ia#KHk%H;Iv>bkl+JAc-9tfQr|8y!`uwDC$Y@PVfd> z&r4mflsiPF{Na>)eCl&6^V>hB@;UnoG7N;FsjYxg>|-xd>ST&KX+xpGMWac85aJ3# zU-0S(<=eyf`C3&kTx#Jl0NC(cDvsaSKqMpMsnAZt%1%9~&F1 z0b@nFc_iB1u_zA}pQ?W7tQEc0s8UiQ^s9nvMLHBPXU-_GcHs_^oZ_4(dV^y9b?Op5NLOntn|6uj##eb7{)lOFQXIAHZbrKJ__F$ z04wav-k&a3Aqv`0SBmMKduWo5tbmAU`PWX zGV2#Cz=2)j<}#LIF(zPdwa-j&>c{04*p}H?VgZQsSNJjFc_$)2WJ8)Hqk>MfJo`}- zx|PD8njx?rha`!IMRule#GNh5w#Q{468ZWKgG_DGxz|LUMU0VB$j&1N$j^AS^ntk-{>xZdUlE-}KOh#8@8XNrj5AZmVqev9TSS@x^ zGhgy=Z|!G`6&hChvMKCk?zKr+sE98|%BnY@n>RzQa!4-1y030WQ1ziddj=2t+SBiS z{`d$K$pE|{o*>~yM(I1QN?a1icsgOaGM{SWI6HJjvEci0Ki;hZ1slhJIs)e;L0J1q@ursVG z>kMy<_R;Tl$jyGOaBk}(FP(FwmH@7iLEnV7F&$>^Fskrn-J6 z=2hWLc?Bwzz?3`svgelgKNi`H39wkbmqGZbpSoN(d8PU!Tv-jX*hJ*YAGq4;3l zMxUCetYhO3lWp{4+z`W!u?P?8ZJ$nEz2U~T@PnOfI}9GVC$738$^Kuz$FzOB62W^ zP^rSUk4%8KnTvIq#lMDWFVg7_%&&m9@{ur`o)9*9SLhtBl*}h<icbL z>P+$~sVi%&eD+d=IrL<<#yph{Ajq=-+HK?#<%sv`)mp`0X&BG07K)O-}o6kAT5FKBw;FF-q~2!9 zgoMr9@eQe?wQ0RT=bRUwBpf2QxszhIwvVDZZuC`+do$aT{qTqKi^WZezCT7_x`0Dl z{Pb&DXxHJx);RQ}o8h1(+z3d*!GPF*z92iB@3hH90TWTy#5ZrP@-&n8#%OPk(5$sr zS9{zOlB(QzIq~mie4p@xb=h#jxxStrHyZbx{I@lJ2erTpt+ChFqFkQzv;!K9?Dc#S;?A|OPa&m zjKSn2H?TLx^0UVK#9z2ed^t&2RQ|mIYZX?zi~_$$=R=ISkn6UmyZ?E{Qw|qu-X%Og=eo;UV$ToiC(E=BtZO$F4P9|jhBmlZ)X zoWE2K~cqj$KwOx zIW?TwH*gk$L|6T_ikEY9-;d%asNgpU!iYgJA{vo@^IVf(lNz_MISX^v7sk({-ut8B z!e9uwPnAV>EQ9{j)}y9x#*qhjzbE76GZMW+aKD;|2EV3Vu}gpeaAWY?^^et* zC29^h75OCt@`BwLNH|bAb#4lhY_8^a`@>NEj<)DtR>`LAI)63KQ<6DM-9&WRE;CC+ zw7?bx_$Wmbq98zP-~hAFd|*$LcR9GVRFFE}qR#%z5^t>}+s4)}YLvU`q1VMfrmow_ zX6yujl*7&)aBb~5eTFkmLzA{Q^jn3E8f>I!T?6hus;TasU{~LnA|BqC?3Ih!u{hee zBnoSmsq5z6M$p!QaKJ0Xg=;7T%I~eNPxw4ZkG*( zSvn+hX9v=KXoE*~YHg*;_(10#Ir3_}>8OM`gq#1#79a&*4G$5*mWbW#(mHx7q$9%F zj#_S4ZPq1W#%JoC}Iz+*RD$s3*9BK=%oXK%!p=z>TX!rJgJm*BB_GsBse- zK}q&11bRUyA$kGeJBh!$?w4TObm7qI)|S1w>ZwkfQpEyZQ~kc8@z!UZ@SfdYoC-}8 z3QdaR>NgF@>IcfH??~RA?CTx4CnV^gWh}dw_O~Z6jk{KCls1min;ZBUUYp=>m9ImW zG3??|S`)!l@{uv;LWhf|=F_5?g(G}_B-ZdF2IDpajedS~BzDH=1|-kB{BcwhP+ydl z82Dhb^TvDuMwI?nMn(pr2cXhWFK6DXxO?9W3XovqqWZmB0Zv&;L&B|X&FfC;HNLtu zO8p|-ZYjnnAzr+|y+PYC;+!7k?x|xhg1x3+mvIAj8swPj7y|Feb{WuDVs{tML?BD#d?t81t-L}9e|NU8rg_F}?|KOwDRlnR7L~J@*kH{7q zMi`lnVJwH>f)6|p#6Nh}Nsc2N0a;mD-Vnlzjk5Qk)xZV-XqZQ&+D~XG1oodCozni_ zETqm?Qp>@9BoDX#Udp0P*kHzk)-o$LznI#-)QTrV5mJrkKpz5?!?6&)P+41hMv@mE z4>i$r-uBjk<$q<%w=82_i1+vYVaH7azSU|N^N9Bl4;ebY9nCCLL#x)TA2lk~hK)9Q z-44sLj`6V%#X{f)0=|gOx2I@Hi>^`Ha{NSe&X`c3MZuf23t0wb>_+;nYW_9OSF{*) zD=X7a996&JdT^GTNhRM%Y~+l-aD;1dJ+T#4{AhnxSH6(?r8$|UO8#w3f_A$6kkoi_ zt39P=W2O%LrGXNqw{e>_`0#SVTSKgh2=)BWszeR?nb8Is7--R;abSQ0jjF$>rJ6`JX(cDH0 z6eQsfl(ZfAK7WsDu|1uya|s;T{y43W`qM{e2_>Y5c&CP9_@^Js+55t3?vU)-f*-Pu z+rP5Q*7(QON}Tvm*ln#xi@b9I-;jF}^ty0?z*QGWT$+*NQI9g|Oiptd-0O|mdd*ZfNy`s!)I&$IGItKp4NDgYMgqY zqRn=bVe7$1JX14WS-OXivQMrh37&Z0cU*jK3 zdj+?Jnzuut$-K8O zYF>S-Q84XUtGkZRLBlw+oW1x-yhKW{(c5ooe_0MlwX$O!53_$=4G$USh zOVLc}u~;SGh0Q;A(0kvT%u_U8XP7DwQ57IJg6(?09tE~X`<2@y8b!wNxhwW&h?~5q z=+elT&BdZOl@<&_h?Hjz@C~I2S7)Uu{=9#bDK1rwN2^_^@T;R>R8lo<4cl^T?UaC_ zJZr@FsD`0fE3+rppH(c6jrKqFoKP@e39ZrjrXx1n=h$bN8b?Bm5TT1%nCMTMNBR&@ zu-P#Idw1w?O*ClY!CG>k> zZ-$-RR&j1FE>-A%LKUYu+~m1wZnqNG3i*4t6$Jo14sj`duaki~P)P9xbQpz9@;ff-GE=bof%NI)Ic-?Q2lzPa38?zJpT&n8Xe6-VM`NLzz zH(b*x(kiln!aiS|(35Ja-_rC;T|6DwGA8dd?bfrm=Qy)lD!=HNGdsg;Y7-p6^x*Y@ z&ydPcRKI<{YQylkwI}~+^|cC1kL_Z653>r&>)_O!;Vx0G4f3W#s4Ys#duyiDhqm#F4v#wnH(wt zlQ=qwyAb_&nAQ&LG?{?H2Z3L_pqik>f}u#?7I6;e?KcNjW9h^WH4(01IS=VxYe}=H zu-o0M<0#M9vNIXZ%g5oB0}s`-izw)Ji5U2}#BELg+Epw61;);^P_*K&BF|e+hbR=x zf=Ng!xnn4@H|48Mfe=?cS38tXV3%FQ0f93T80<;-ZBC6I&+Q=D^;@1g1?aLlI_B3@ z2PE82|4e!%Op{n8ka8s8^$B%-%96Kf#7$K})*o0m=)%Oo5XVG9q$Yfc_`nl;=Dc&PDOR6Pq8 zmZlG?wm1YwQ&Kbj>D@baWMx@NHN4P1$Wud-HwUu%!VkWCtkx$S_KiKe;|h8ri|?27 zj3Cmk^124iMU!$UrJh7bQ}sbe5#~S`cH}tJK1&o^FV{~Mx|=b)mN1qWkI;|6S_b11 z7NS%mNQ~;;teLT~3i5H0o8$TB$26s;Nb~;n=W5A6IaptX?mr#Rsio4Fa`Fx83=VbVykT-ussX+8>%MBF2$sTfipP= z;~8|)e9m&^4RzD6XNpfj`mZ;S`}NJ`0%tAuKOl4hD!2jSmHV)}Eh)UdFuSv)$*Uc? zup`9Jogi0BjHDCJ1nWXApfZf5STh%HHSa9PMwr$bfHR*izD(-w>Rh*y6+lvq3#Oj0 ziQ`!PWnHLmR{ZgXdwP{S+vVp>Z~xG0-X`j4Y`f!J&#@AO!}Ubj)QA+@_SgF#+O11kmG{?BOfoC^(KE&cBc5wk}op(i0S<~O$JW@O6c^isI*7^LYqiz zr&EdNS5Ix+e^~t@_Jhoxu)9~W02P}Jk{2#b)XV@wA$dI6*LoF+{tdDvXQXf0N-7pg_Klg!|jR#OC<~#_Y@0|J_tzd>tI%U1f!J>!d4oM#yKk)Hp zXj_D@N?wz$(2+d*H_-~~Bi^jq_|3P@=h5}5MuZCXOVys$4HyON1ZHV)H>Ud9;}+R; zoGp7f))BENx}YenJ3<1zfs7~dwjlcs_J-D{mDAOiqjMUs{3l5XUt$|HN2 zoUHf2IU)?I>a*XV2n@!UcD~l)ttC+a41Pe~6Sr|zwxFknARcH9)LK~SqWSxfi;9KL;(l)4)H)^2ZR7g2qSwcGbfcm zCrW>S8s zpZ`hXJq@(U< zT?fVnkrJRHBGUu_j`9ToMa6|&5_mI=;>Z`^t z!gYfT0tQp)qZyI%9U++n%3|sab0R<_U|Z-xiPin2{<`7GX)DdD4_Wdy5&KdKL#>h@ zHlBymtkK*VO4GAsHPyerozQg+Drm5uvO!}(F0o4y+2u68#%p{H`jF6dJik1XUs=N% zgA58N(ZJP2#EZ6?dsow6Yq1zAp$-2HXbnG;WLCl|%D?@EkEP;ZlH&9(E(O;ufV2{ZNCvs@Ocnk?iEbuF zl6y==0O+jc0-3f`2 z?)RwamFhj#{VhfF_M^p&(jB<5M?43QjQ8E#@Yf;ES%k69xKR7r8s{F!Jk~}+Nsb-N zdWR^op{D1GT$q^lQybp4BbHf3jyp1KcLnZv{hXSs>I|NViFkl2jBj^eD%=ogo8}|T zQ!qOX#*l##Ye&O$LSB^9cIMDOMwz*P-u}%t1BheLEd`16K<@?~PUMpirBnQ_K zQ7+ya<^>=*R9Sz(B8-<#gXKB4kYTilEdqRlgOTJFn><73JQGwEq^cw6DQcoOg}GIA zDR_A}x8;|Upy(LbX?`y&D@`L2X7pz(u^Su+XU8xFxft_>K{qdC45{K7M2c18Q*&9B zAGoj3dV)!=z=^w<0U7LvWO&{zVy^4c83ZhI{xgxy4{t5Ow}A6Z1&?q8<8U`xM#y?l z0f|2(lcCT~Jh$@~LUUO3QLcz;BXYMpNp3~u5v<2bDL69l*kPd!HbYfC*A!cfj*0Pu zMQ%lU8EJ5_?Egcr0{&ist~rs&8)@f*;+sd1QBU9zXirhG05^fWXbMw0{ls*v0P7n= zwUf?gulksK=9kH0%Pmte-7`DKktOto#NDwIt!40xvDeX|%ulmXvF@V&ytTYojiYmZ zJlZd{-s!-$iClcMdwcA_Rgo(c3ykulnb{HHh;RX~6F$;h&z&lsaD1CtK#r)|H)9MK zNaDqEMS`|N$${K~rV*-5{k zB6vR@R0D(5l470IJMS$03jIiL#>rU;1>MdWE}2ocA}GkodJ{aJ1Ri7!BzP_$Pi&ik zf3ElJNA9(>0%bvdf>_zIxNiZ=#39?|)blTF&5*w4?^{BVygm{1!LLib`E9E1PG0PQ z!nq8s?Bsz~OJwpxJJXMK>RAs{+pJ`x0$qAr`Z@bF%|AhkOAcYOyO+??gD%(%3AV#d z9_|nle8*2OO9SW_n?F{gUqfd!BKpUvK(Lr~H+9Q@k?s&vl5FHFd$2zRJrj5Jue=?l zm;EeK#<5KkViE zw{fkPTc%HfkIU6!@js7_H%9%S6*=0=l;L>r`+jwnJLgECi}IV{p&{@z3B%U_6=L@` z!D0|=aHVZv0_Fh1;A3MOVo*R8;35#`A0(l8D2dk*zSV$nRI2bEC|uAiAkLd4sAK#m z+e}vWzda}nBYSzlYLSJVgzlWk4Uggshwrg2(&&hDH zVq}XahL>Zd6q$>%FCJpK0Bo#ST?|~KHBCU$K$KctP`>w86*RUM2 zRn`qsRdd?6yympVOU**O?bg?yla7zo>p%;ECId{V@*;cBkU~^U40b9w==dE7da2RX z@;Cg?$@80P@j7G!dXMTXZ~>i&6)j|~BSym4-PR#_L7L+lTaI8|s9mAW2G;fe%eO0I#4}3V+Qot#H?Sj$z9{8Fot65fr2LbR>cR}Nl(9Pjo(qNApKWym zdYfC}2O+L1OmKmAiQ=)sySk%}B>V4arI&+}0~UlA`wQMk%iD=5FH|!4dtCQXG#5_h zmTLRm;Mt=+*|4ZC{==Xe`d}D1&y~Li8 zTnaw>xO;zchTa@@k0`Ics8aP&qLy3aF@^3v9f!eHidv{k9^PIY3>I>gKehc>yhBeb z?#1~BTbKy(!(nIJl*uZ?*kA7-NSt+XLDSC$JQlN^BzuN|#zBN4;<{BLFdv)6pfDus zS>AhyK$7ZlW!>sk(osyv)PNQbQ0^bS9hdZ)Rrf{qHVofun>Ttp@}_loCkiU|TcLTt z(O{8bme#5ZAtu(ZG!8>2H0ZH^4@pu%I+4ra{r>w_V~;4*44510TahIJCJJ$q(EO25 zW`y)$%PmrZ1U=erh%_#vIm1W|C@r(&-)-8^8o=KqI)<#6g_OnBP5k#|6CKQ2#VflU zvJPbHrx1Cy>1G_%x;7JZA63>~YW#?t8l?ItK_^!E)#lBJ!{3*y-O}$=LxyFxvUQv+ z4BzK5|0j*%V9QLOPtnvvZ_^^i@UWAjJLvx*DBEeMi~hJ{!PX}|N2eF|-i%?Csnrr? zCDa9!ipV;Ju#%GzUJP(H_zfItWYIWzm$;Lan-Bp@3Z=N&plcq!m;J(wXD4 z4|M=t;0VT9j16#S_C#+2f&w8HYJ-yUVA~hnV&gNtSD$YmrQKV)n>#Kpj?K}q;j-9@ zULd@&)@MUsU9k+}S>qOKOETMi2fQk6A1m0EeA z;KwLaa^V*LiAQOaHax>He(3W~YAgM}O%C?SgsmGn^aHyhCoQ-)*|g?|XvT8v0riLY z6;Ni%33!GvSQ+c4=&GCo9Y0&PJhR+iD-^e7GRd`bVyab5Ip$pp%_iZsXmoIllV;E_ zwDlnsM1$3rmb=%=lyIJbd!zH$2zw6a6FU;M4*U)?CH(_P_27BGJt+l&4z0OmX?(+l zdvvS?M#Wf?0b`3`%kx(i{Iwk^dvYH^^abERzBsL}-h2KNosynjMM^jkNK<{+m3DydX$=?nO&j?{f;5cIwnfYVLPXy3A_SJz5OaQmRl~ zj%g-l=CAl^D>ed%Ji4+Nu1T6)(sPEGl+4@W;&YtJQoza8xwyp2_}Br?3##kgVS zY!8I3?lKQ_RR*pS%Gn!7l!35dpo%2>eew0F2dWbO#^?$iIPqRD534(&l+vf4Gpf2H zH-zULu;~B>9p%LqU6%15R%Xnvw3CPf8_}US=k+c)n?m0k{obhq{a8hH7{@iL4KbkscEtAAOJ~2?6_WKzzXfg1OU2*f5<6u; z^XK1~7faQ_rsGKIq&klUGg@BKH^h^cpy z+cdb-BIZ07qd1h$U5sorgY+8kE?~)OSARdrmr2yTg=X#>=C5G)ER+R^5ZI-~r=b@o z_sUidaWD?o+g--C>RTgM726!pAQ9#lS**zP7$B9<)HHG(ZBd-$-p$@!ZGfrqK*E%d z86F6pQEVubS>3+R*z!Ry+9*y!?)@4$QhAYF|9}EIIajdNTKa&`dUXTdEQe1d9NeKzgyM z+ums&rzkl|MtW3+wXDlJ=SQY@Y?0kN)L|J{onQ9vD%sqaccfwA_&Pr^Rz?^q4c@Eq zZ$e-Ya$&%)o(kQzvSJT>-ecpU?ntL2CRsSPwLWUS^D0ZdQ5+{wb*;wt8sbtrujZ3f zgveJ`c$1wGE0)N6YY(f{jg4bMR2{m>tuW~wzvBP!eW6T^R>?KFW2sU1@^Q2b^>djq zx!HXMbb{*{+I8TS-S)Y!7Mc-o89hBIs#zh=sF-{pjW#&e8Rr?qt>{{)c*h#gcEBNz z)bPAefgHpc!_a~?-hh#@Oj+xhOGf>nKS<0$2MtmE&hBd&vagVtNuULQRj|V$rhyn( z$ZQ0I7!Jh51>y^WZwhCk>KYM=i7WP66`|d54n?u^${yOwZRKtY;CA^!&YKEIxk_ou*R^{xU zq=P}VwYB+!s!DmU41^JsjE&;~6Ef*fPPUY*ueCpWX({vRMCDqtX$It0PM{gq8rZvC$&?O?fC29Ua*jh=6p$*I98z%O8P3^_mm&2Q^U~zR1C%Jjo>Q~lR znA_Pg!-9@+EQd4XG5umJzIpVhc2*j35BtF)lUd1x9HiR8eM=mf%&LtlW(OTzAm=i@)=34yM(B}JZS z_>PD?f}K+W*|U|yQ+&`K0`w)pTu`j!FRZd=dwO8AWaEAWC?bo2UG=}1QsC<7>%pgE zxh*$3`%1RS>ASCDnv8cGYeDu3v`1OrKsN30&6a7H5w>=;&^ zAa}*(SV3>2|2v$b%;9i6gDeQDg#B^>JPJ<>ZW^#KQEb1)CvYgFX|C~(WjOnVkAPr! zD8#+Q1^bxD_?ir{NEV9|5$;N#`-38VLh>wqPql$UGjK~%SV7UeEbJB?W z5~yz$2{FHrrx{-4$o+nS<=XWEDC!h2oZt*3!bzO|ue-$|SjtAc3hh6#*a*YxqG#D% zIu64$MwVteG#lP&(HruNPB+x{Uv5$J3%r0=dFK|Nwx2AU9eh-SUde!n1OyD>rD>)1 zj;@awt#RwNn#qOSFuQWRhR#GRxj(n6>tw1`Dh=ANuTyiVcTj@$;omXCV{ZX;b#vW8 zJ;of*;8I5QCu;-Ir=!gcBI6+Ol%girExvJJkQJ*$e6TZioCR30!=*x^8NYverf_w< z=V#w)pW#Cb(cnsOq2y=#Zt7=MUaoc@fQS+6Jc_!>VwB%&>qR_r&n%mMxJlk1TZ<(g zoAZi1?Cjrb{ql^`o1%tG7>-fNW~?|JQ4VkvD!dX;tthwBGI}{m%Vz4I;oWoMtW}O& z8b3hPP%7cAOx2$_o^_U3Xdz!G!76M{=fyc0IL)^()EwoLwSipnS!)^SyOGGY@fgQO!!!zUWFqSaajeL%Kt~CR`_3^WXoQ+0W*UtbG`Ap! z@lR-k1Gmn(eRFXY8x@V!3p~H?bIV7?r+<(Bb}GU#d9SUw!+yx*)Xps!7OU(dv{(PQ z@DI%#c{*MHB;U;hxUs)OuJIdgO8;)t4TMa4_~gU*=Idcrj5|&UV8zxcn^u~Q;bM7- zEGk!-%w;)(xW9$ox%>Vbv)8V_n($J_*JqnALP9#onteSnl|x_f(c{PW-~PeaK=5k8 z7T7Zz#%Nb;Z+}AQSqipk#2=4s(^>DCDK0__q zuPcubGMmQlpJL5Okq>Hwu-qvl!--3C|6L6yI~dW%G0K& z3;=SVs9G0(Vaz9S~3(ro9 z!ax_`$b9c_;jUG@F{%XigR4%{>?>zso59PV!`lf_@0tW#K2A7d-3Qo!6xz#S$dDkx zR;+>LD`^B@SGF!kneBi6m2|*{D#PDjQ zzc*#iel+wSLOe|{`-5vIa~Ota!TY<>aG?ptEk>LpFkT{DAn5su{G9B!ov2`xc!Pcu zP%Q>}Z?F_cx8!#s-tRV~EKm~{bpO7tV1N}|w&Q(=pbA(dwg-|-gi;KP#Tl$_{qiRf ze=)>AL~IWU#plnT(HbECSS_#rpB_DzO@t&TlYgx4@4P#a25J!p4U6oOS-Am;;e#Sg zfht3O%?rsj8S*MzZUP5^6tsv(gr+vG*3iM~L6~rgSRsJ4+G}s2@dRT$LHkEL^D~nk z{N$u={Dq7Y6c}=5Lk)E9cC;d#W&f!b3*8dW)saw~<@TohiJ-E4OCXf_6>`OY1&QsqCo107lNu9j(gDL+pCn;GBf%otnxv z+cFcdyU1yQ?-Z;hK>{^eEbJ5Z;raK8r+RQOVPhI6wZ}K$2kxest!Bp!0C zS<%y5T)e1WTx3`H_~iz4jzGJ}s!7r(qBS5|9mql;x1VqMfF=wpq0sWfdF5p&vh+~( zApR@7wP353z$a^K?| z3|}P}vlXBgK?|I(+GrXh{_+^yYK^}M90KJddlfzY^J)vMgy%%VZ?($MPToZ`**|TcfHh-Ils^tD>Qn*9FVZ$ zRzHq;0RKLLrb)V+IWwGX(YkVgy?VlXdD_yhWejmfR2=(1`8k z4+YH!l}mCRIn(zf-?g!YrYz^o`VAY-qE14PimOg`4&aHxFL<@venxbUfy7$LU%rm* zL(w~Am#k&^WZ=5bc>_UD#)*@WX%E)WnR*htO7dm$AcB z)4DIebXp8&!OD&l9FXO0vR)oV@TL+Jq+wL;a5A^WYnY$P4D`4i_= zQeC{5g7j=pJQeN`P~lo-rC-cpF>oRA)5S9&NFaT|cpt9JViX-(>{4QyvUEP=631O^ zE{E0@^B#S6!U04s3e!_danl@I-t{)67@GRrwRWZJ;1yoUGv9ef?gNsDf(YXW5{-$Nt0250g!Wc z?GH_#4pCbb@7Wh(GgY^R!#W&6O|u0Tq@$KYN@jv{7K-0j#kmdl6xzF8YYx0{xK%h( z>_I_w_O?#n7aHncuc)sbq`GmMHw$sBp5`-yw5+V*>jHOi^7`Fxc;tJG#>qm+$wZ(d z@`d=ZPk#zaW<0&T>Rl9F$4=&m8b=OF#@V}zp6;F$8=LWno0*f38@`E~RV#m`r}$LD zfoqZNXQ=k}TVzvHNvMRDL%}93m3s$I+V$C(6j6q)OstoacYG9nUvWoPT;3$B@Z;uW zko9Cpz?PS{oYx@eJO~s75ml=oo59Lf^si*S)ofwh!hT$b;-@l3>GIO!9GGF1%EhZT8NquvPHk~R4>Jk(&AcT+Vm4cGH%k|UUwPb z27j(WEePg%Rn`d)XG9&D$AF!{OZB>_h^SV88bYr)ESt36OlRYj?D3j!c84_8+6Pb;>4 z+}(TZk&E`6J2&p)AX5vN(8KtYa92QK_vg=_+c6A+%t&;>L$bHWc{{Ro@Rxt=>|97q z+vC#J!aOwPf|C>k_?&Jm=#eomZC#bdXszjd5WWsr7%zHvta_AA zg(yET6kzG`DSCImlH<(Yl({2An93b`v5E1@4T$TZrpmh7xo0J4^>^9w(p2d|%;H2e zqxNoztU;-=Jh;&Dpkxf`tK~um=QN025eZ2v&c2vcn7t|4c&V5J3)5 ziup@X)&Gk%01yEMfy7V7_o;>vH{vsZuGG?O3#^8a0I(FY6rfh30fT}=@Wkcx{A{F2 z{ryFxGLn1u3h_pN=1HYAMlj@dFeNq9V}8Qgc#zuA3De9#6@gqtBciWF%p@n-f?V@v6#yWuyEH`mbKNFCdL$n@GZ3AF21kneZTyD2u7J8TQuUW{UR zUs533fc^s`H{F(;%j>+Z1N|Y3CIqMFt;RMr2F5rNwpe9)diwN7hf_(-WEX(h(&Pg{ zO4onMPTh`esHgp?$7%@K0cqjq2vM)y5-Ofk(LbM>)DIFr$J!1T&-b|>U1jT2iX zEh*&ks1+RF*}SMN-ij`~d5M->9GF#MPe@E)r-wjPV#^E5x74PO}nCFmF1OFiVkxez!D_YWP~r}Yy{rnP?F0|EaM zFPU(yr3Lr{+pFIQ{{nm!c-92+GTC#OtpnSw(?Kzc%b!5^a? zcmm<>8f!MRK8!lEA5sKo=K_`EN0b^Hu!7-!+e7ihp3H9R_ioOa&njjnX{Ybwp2FG*b`i8MiYlrZgHpS2F3h{oAFPD_!)b6VfwpbFq+Ol* zT2*y_D$Seg#RJ>6c`Xxs^cIgNJ4|f*B&bL=C+oGS-jHbIm>D@#j>dkHD_C=I$(HI3 z`RB^6zfsGBften^E>n3QT*y6Gxsp^)0{RDKLKOb8b9>I3yAR346%J16^6aLOMh&^7 zm1FD84YkrAC47D(mP@-i&?4lfqWDA@>)ff_1C?mEN55uSY|rXghWX$Ifbz1Vg3U6_&h|I(?JFHQ-@?=KIu4!32E_UII8*@) zAyRZZYv4+xwQ=wKiP?WkQ$N~1$eWO#ttT=a^2o8Z_p3FFtGp3x?+AM zOXC6e<5gxyuk+=6wvxE;;5}VZ^RD?G7zyyjgD#|>r4M=%=RT={k4W03ZGHl67FVd= z1Qnb#k>S8p8F0;WDjJh~!Q4=^+0+bS>&vE%%UUPCAhlm81^P>Wf}ykJn`=M>n59NOzkCU6_@c z)K)0^eA12c`%jO#vUx5J@C*>>1!|B4fjLha3r{m}$K+;01PA+M#oH@;(9y61krRJ# z^x*|*-xpemLAM_{onSQYdB=OvVoeZORlpHw2c8ra8Ecia+KVAzCC`ZF4$hP#jTg?O z`;-(Fabnf=YYL(V0K}qY1z>^?j$z2b(J_E$8+ASg^v+*z6=u3_Zj*DOq9SA|=<_8W zv_K$|C!mGw7rW-dh*UH1ETo~Al%&F`23QAB8Tm`L-NJ(!$o^yK-S4Kli--b0<03NMlju?XK_Z27ryX?=niFEo^1=li@Fd{{wo4Vgg&;OjTE|ucR015Z#abKzTm(%s{a%RWA{I*+j73W(}LJg)t zS2s5tSy)JnBTG=N;kT5PIq|-Ns-A8$lh{S^*~Hp2J};@Dv&Ri_93aEsRkYA zU`{r=p{`w=HPGUYj^Qm7dMx!li|z*o);&60nQhA1?^drW*u)l?ww_8zNXT~Z-6nh< z`N?{*%lV@ zl|CwT9)Irj4_4pbD8k+G{{B#9%K|o9jSQ9VmN}k)15!aj0s0)kz3s2Aa>6_L(hcj? z9=y!52^To2A60B9{)jh*p-;_rn8 zj^UaKy~wzWu;*=FtE4)EsTKl5+%F;onsYC}I`0H3zKv@g9y;DTef0kj^&QYy_wWBV zA)}#$BqcP+N>WBhrHGOh65^&vDJvuyY1ktqBH1gO%1Fab%BGA+WQ35+{;zk>_kVup zIGyJ_hjM>D?{QtPbtO01syi>AvCY}>8Iy^L&E>}I73oSqA`r&|6~jS55sTnsb#F?m zhM}qbvC##}5jVxF*8!RVcq5}4j#2p!$FagBg(PR+;_)(WpkHWT@V-&FEnLN6ey>|A z^Q7+X2K|o+4gD4wi^3LwGU#yN=%k1;i_QG`-ePJ*^%ZVbls-fQNycB2SO7wPfN{W- zdCl6aN(trCV}OQ8r(E*}w*&3%dX)`@pUF^0 zfBp>T3nD2*OW*;><#EwKdo8H> zVuRvUX|I0o@WZ&tU~$aw{9l?qsjf9O-pyj<^<?P7usBS-xSu6S3SIP~7b-{6= z!T~!BxcW)ckE=)yx9%z9ff<+NVWZb`BSMtMqS3HgpDw`W8Q)wu9&vMT$pScoULrA_ zg#0oDmEJP8seRm_iAIJf-QWhm?^S}&@$aU~)WvK{=bBlAthRohai^76o_}QszMPT~ zgSRIY#=Wg&l^eh4<}cc|{R;+CCV+6z*Wr-jvqZvNpGeIMUlE!9$phr1;X^Tu>K8tC z#Qfx9*zOu=5K$3GQ~?D*51%Sk_Gs2&1?37UKv$H{9O`Pg%ILqZa}tY?nB#j6oQNIGz@xymw@J9doEQ`#XS4@z0?X`ga+wO zr||~A=T$f?5@98K_*OQ=dR7I@=`9!x{`-+4iVTg~seW>p`ZuL)!AgD_rNG8vpAcl|( zQbIUHQ6fUqhMf<8A`VK^pEt@z-Zjha3p;b^Ohe8S-2lz42D>)k=Llemv6LkUg(1m) z)NTDYpF}?I>~e+9Sflq3;Bv1R3@YTU207*nB!IXb15&0KLD1x(o8CVDyHz8Q+5s1>kAD7>+uf4$ew)CDM?6X+~5 zd(UrS&a>aQP+jg$x}~t&i^E}83_sWz^u$akl@FUnZhmwdm_9emg*epk!9xz=p%8Xw zY^(xs1wl8D??}Y0&&qh*MFFl)2+>&?dr6T(B6F>!AX_BM6@g2mUtUb5Ij-%z=kJvG zJdH9jd+YBkiH)xYb=q7^+#Ta2_FB`UL}8LEi%iAWAsYdoCOfh z#nr?kwi~nVvVK6z7cwp3a9}f!@5-J04*Lt;ereX*Gg)c(6u|E!@_oqWSh>=jL+XKP z;}>i`onV@OcG^stBZxQPD4kjk617OMju)H1GJSS-;GT{Hw2Hc&xL>eqb6mDLso|8D zTiol*N;!Urj)q_MI-u$4-@i>sJ|AL!$ivuk^f!b|FxJBd3X8ay_dd?Eqgi20hV=5( z->&kpdY#!*6$m0>LS9s&g&G54v!Lv=ANPxi*@IG|qrtELG z)ZxN~s>j|b7-+}&;qe8 z`eN(r zUI@a|%}9QxY%T82eJuye0kI8zWXc18SQIADihs(;%@4&BIS?g-O98?x2iH5DJ1JzI zb=iDN7GWXYC)j{cbX^)TuG%R-$>jPgH1-{?Ix_Y5-b!8yIoOD~gG`QN$0hW+$fk-+ zsz0vKb;2K#J$V1`M=j3vg=unFRiqh6zE;W86ZV-C=Mp@bEoAp*HbOOV&AHk{Bh{U<3Js+6iMIMuYUJ2s?b2m6@JSI(P`!Ocj)N)r7ZLN#Co#Zxt5b)*0Q{ z=k=>=+--mN5@`3n8zf7hFCO!UkG>jdAMwcr%*JK zn_eT6GOOiM2Ppz^Py)m8QD^8-p9d#)TE!uUuLFb^M(zP#E;ne&P>v6tevZxvCvWY5 z?aKJnwh<6;+gp_Q`*AGM#KAkm%zTj%kz+8O2&i{`{_KlJ9?D$8P2jc$(n^x6(e{(+ znH0dA2d?$ep^w9;8&ZQP9_I84C2Vd0S@@bsb%_B)&|K7x?X(FaIPJE;#@jp ze&*k#Cz2pbj_`cM+GS`zKx{d#R?z*vJgcgDP_ZMEOuQudRID;;(`&Y=EZr`Z%IePP zof%MB#kh0+dQ_tEaqm?GcObNpRMFWt7aYl;1kha&*YJ!n_M|-dn{06NW~O)TP>a<* zly%?)dWq8$v$gRXlbJ=hmAqb4?!G1SUyfcbiI^52$g6Pv=|9mx6*4Z6)@(>N$W-Nw zgMjbOknkIr+ffy{zx^8YGz%C2;8%Ey(1ig-aaQ=e2Zj<7$Po}gwzkANFl zQvMD1qz(VGQuZOno@>`n~76!bMY> zs`S+kvua)z5GH}MfNwx4?&E3awx%ZjQDOaqFmFMhi-UR3OesqAZ^cjo0BPUg0xA4c z%qQ0qW@b}*duhwcrK0xnhrIALoG?$RF!cZ&h@!(N3pV~^q7iIQB999QI7kC-0{Z;y znJC(MScgu~uy94f`e2|MRnHJuWvA|`P&rq*KFc{y!yQsPOgw;M6TVlhO*oOrL_g#> zsjS<%o(W15^vMu-LbdRfT%jO8$mAG2K&fLmdk9H_3Km+Y?qA_Wyc?KR=KpH;yShIy z$V65W#IlU#CY&ox>Nj`xPZ~BlVB{3>IwUZD&#BsSSTq9F5_{pKDqU>LCG~}4`VcyV zJrl;k2oL0X(zp@W2$&H1Kt&Xse8J>Zz3^4z-Jd}hL|V!AJvZO;CDyH@ZW2ovKsk+t z@>HxyPdMbV#<&L_3XHPA$Nxxc%7h#j05X46YqM_!k>3`5h7T9z43IrkuQ*u992iXT zFdJ#jW9l2|95_2$)<4;^`i=_LU5=}r z)41z zVg0#Ky?p?N5mRSpeMt{FF_-bno%u{CdQd{6CIdt1fpI60hvIN>Ll_@sM;t%R+;P4| zYDlT!?yzqaYDr>kL5Gcf$6v7(KLq8Xi|ng#+?(W*#51B-L;eEIt}}ek^8G(nc>Yjw z+=z2Zb0hISkkcQCS-U964+wy;B{1S9WStctg3O1`>FlHB(!NP`&dW0{=OmjsA9-~o zeT2*BLM%&t=mY7SpVxWRafm1s7G?|Z5A-M~j=(6;Zr*>nlMZ9&&}wT7(W7++YIL~_xENkK>~3^db5GD0+_WDl z`X}|k!GqHokjrZnXnf%Za52Bp#MOk~`V)R5^&c6uij7?*Nf`F(7qoKa$_AEp!QP!` zi;e!c?en4>rr$RjsTMRiyMT5(0JJgm4!}xrKK~$dYe-m}iV&hAFzuHY;Yn@J&H>^j z>7;Ng0q|P`bru<)gh33I5@Rk-awW@ZO;a})4_-+ELF$w_*_o#xmHydyZTy=wQq#?g zPP4DN!k4;KYDn>MkM-+8qA+kNB0GhC4NNdjl^+8An3PnOQ*R<4JN~fvq8~6RzySa* z=%Z7kp4YAW;-30}dBn>-AK0Y;UDllzX-A7o)O_^2Fqs{dFMsgyY71M8 zxx-@yZcv=GmkGzSn3QtAIi~vUe7v`PRm`%5B=^xB>{K zeu%@uAuv#{-QZHnaar)s;H85Vhfm8N9s6$-7HOa-5e!y)2}le;AgEqUeTw0%re6<$ zGa_vky*9FQQ&)mIx_vo#ZPZt4RO~4C?A#=W!_-{1MrcFueXtA*VG@ra} zwHNL?eZ%VSSw@SsjA=UM@w#m>Yqe8FIP_^g(C(b?`1JfHR1pw_fFU3@3iv<&#S%U3 zNIZwXZoRRFyzfZ5Yuk01zB@Cv-Lf;%u8B`pj#g-nXHgxqKI9EwT8yrBx9tw7>c`e4 zVMfo+6^l-e8r`_c<9sl-=U(h+JHo1ULy>%D`{G>k!eHm<_I4UJK^%3d<*JXfqhAiX-@m_oku<&WKGXd+3>;J!hxAW3^IN)F{$fEUuP z{Wx2ty_!oFXYTS@Y8v)5XuJW*DAA4J z{74iDhwcACT&*(rDwJSmcqL?#IJ3=f%PpYM02e_@o-lG8TG0=WWx-eTspSlxa283_ z7Q3LatoWI|#W*x8)I&2;s#9#Thw;Jlx!nNmXwB1zQq7!T+U|S46%3S;RXN~5i7XR_JlreAj$@w6@jtS8hJOC!cr9~Xx4D9x z_vA66143_xFUX+C%rZ+(T0V|LL?5fJj`$MBq4QQ7%a87q6@>(m9!!U36{@(W3#PH% zfM7sS5CRy`FOE~Py!FaA{Dv1^ySKu04?rQod0b_uicm#M>utrX{Yq`pk^XZb#Wx zLw#wHzE5nQcIG*}gFv@wekd6!to5l#CuwhgDEt$xGZ<5pLMXD(c?2pfJKg;q;+NGk zJ}$15`e4j5+6D;2gvN@mE3YTz{Wl!uiR}5WJwsh3E|_h;;x@VY-;C* zPZ?ydxHGTDHXVF9`B}I@D;@z!G-9Qk3=ii}LofU<(1i)$7_8|p$53GBTo=OB*n(3@ z(YDOpaPQo=eX$Cv`rOr>aydS)bkmviTr&?w_oQg-XT{Bi=gX{bbi)4nb?DBSb|Z%$ zXbTbo9HiIY;Fs!i`1fkBL7v#Lw0iYwLdk5&MjeD}5iBu|3<;IJ11i1v8Il8}6Fwq) zOC00Ng*mWaiH!>1gtCV8%s6mixTHmkn%X;6@W}LK8B$nJ{ zd0rN-GN?1uzK_<|`Fz8#Zz#GFm>3r~H``lNR2;~|0fG%EM72&z5S}0w+s7Oq)_;%P$Xh)vQu_O$MZ&=k^Z7Q2 zjvI23>uhRPBnhkHWG6434ghZ-6i?GSq#q|9UO>+ zLH7F3+h{{EWdINW9?g#I!C)x(Nj#q zM7Wl>|7i9_@r^Q%Lh#alm`fwnhQw}y>2|6++wF-fpGd};A9)izBBanFu{+1C3TpTY zjRWiNqvwEV*hes1H+?LnP+ed>>FA8I?m~uJ_#N6E$_Jf&Q-6>lKuVt@d^8l?O4!rD z8*sm1I3N}N7Hc3V(8|26oc^HLV;L;NrzviuogjTtBEbdCTR*{oe4gBLF83~ zkFUpYI+Zr^HCUa%8pOSVz$+8%WD<|U%UbTgzq|c4$K|es2E?MxJQDA&#h z8mqG$@l3_rw8(R=r#VI|np;>BKsTCkJNa2)!&w~42z5hGi0CbdQ6GrKvRP2tXSv+< zH1^EYU9eeVxZhHUt{;62epBLlle*k_8}$UrB>Hu9Svb0$1}1F@uGn_QmkHWv9Et!) zlrhV&ZNwWU3zMjZ!8nXmWza&3x!1i8>+t&VIYP{V@_tBwW9JVtN=TU+$gp zAq6*iG|=DXpRnE=hD1Jz$g7wz@M&eP7yZoXVZMqDEES#Q(oK3k5s`i85J%Sas@`H` zh&iN&dKHKQ7-ZrK*zoZ!Pmyw7Yu*C`%!v0xoa__m+sL?4RG?~}V2E&Pp%X`;gFB_G zs|$Aug4tP^xx2)~#7+qLs2xQh1SAf~QGpf~%^!7-3P#-x*pf1r%saC*6*aa8Ib?fz5=V)qa{IM+5Ulo)xd@0oXTL%^%oH6iFma5xpQF zWe)IBU?#k4sGh)BBqDDs$G=8u8?A@?ajs@>FBaMH$Xb`F0|$Ou?t;!V$6-Www6kEu z{AK}89LLdi4|p6@j^Pe;UIR*xm67gSS*)1)OL_dpAG^9l+=u)pQj42=_gp=kC*N8p z^T!pcA4F$qf8!WUI-FGoVSafehYDKGUuSW-Q*MiVyYs*#^6#h3m;`2mf)Or0imX;W z78ef0#@Y}GR+m$nx(HWJh_9~r*)p)|N{rxIeE{lMpA~*FcHH}msjq=N14aktL&PAi z@_{Hg>KyEfyKtbQV8RZ75RChFO=)`)71zFMC8_9O4q1!#4Zf>`wm%9i3Jgf52kDZb zC?L616b{jc)SnMybTWC|?O0;b+QV?wJ@f4Pd8D!^vhL&r@!-ulvhy@^4HSk$rG#!c zP(hX0hW-Ruudb~R*W)+PWO{g9AYnln^9BI-BaxTzpkm3x8=!_blHF}BWgeV(lP=rh z9g-tYXSJc>jQezvP*ZrYn^$$~qQ|g);C!lDNH~}fKvDU!P z0s-AKhr7PA{jtp`S-(;HvfM+N0i~^sbc8tv;Q}ot3LsK(gA;q;bBz)bzS5ltsZi#W zzsOfuV8MJJu0hmF`LUFDykDpi@ad6y4O!8b_$u-pv}PH zbsBD&T^o#&f^YJh{T~41IiH9cZArXtKmy$0Rjh;8k3MtLj`YjjPKL;L@T#DWNFcVI@?golz(53mXW8O_SU>9L zVDU|IHXI?8!FC;dBH77H?C;N6O?0OzH`6s+HMFqM?>m`R6paYyy%*fdAgx6fCR(4T z{0H%HkiLWhu=nZ#LO3ys3y7PgX|!ab$tNwpRRKmIV>aAVgG@wVVd!BWr23jET3CTd zqSbmL;n7@~n)+#~s|^=BO7fiJeFx(1gw5}zZG|NU6L$Ke2JQE?m!J6+_H9l}==Gp< zq}rPSTaq01zSS&eBl<&onR9n_njw3_#(=NDJO+6kQ0RdKgRbP)W=>}B0M#qGRjxl% zr_REyBA)zD8hn(2Z50EG9@K1Lbz8Uf^R9S5iuxh!!e9TAp9p}%eTtJEz2gx~DN`3J zzWH%iwBr8FYwGr0*uH?m#QNJ0{aSUwnRM!MyZn^uJ96v)*TPLM&;AP=1$2hsY9KSn z$NBRkcG>a=4HtME@IU&`-R(?WTdE&e?*942qky?Hb}lmZ^+NZo{2$k{2$5BbF*64H zXfwcnW_}?@AqvvVkK0|YKm=r({8~P8+WiEw8#~@tJS-3Mr5iK&b4!A4dYhKMpyydD zPYNSGrB6!-%W@U~Zh#K#?ga@=+qP;1o+vW_%H_JH~cd_se>LoSR5`+X^Fqtfc)g zg8;;hi1Q)P=k3VE=qIR)LA;aW4O5)S9A7BIu9uFSgUILCO-|Zl=(jLoRQN9L3u$!B z;G)?OH>`OR9f*{2hLy3Q;Sn5T5V?Fjy;_yyMT?cno~AI$?U7fpRZ4FyBHqV57;(d33z;5O0%5%n#g~x9=5g$y_vvzoM>+V<0LrW zM6=r;s1U$udj5#Sn0%tMYqa`G`rdn{0hcAi?kr%Irb%N5V{T<(!H^ zr!v{=ts=t>4Gh-b2*Jw+=l>%PMlyo)LC3{R;;Bzmo8)DIfb&$F)18vKr)33x1mKD8Xi`HpARxPIOle? zgd*PE(@b&$Gg%9^fdFctwHqpjC6oB#Krf+!EEy27A^sm=DM!pww%kHOu8#}FvmHH#MC=%=-meaR zmb1|L4xU=t>zm`$-JFjJb2(O>P&Z+Pk}`7s{uGP8*6_}`;s@SPprieB%zhr7g$_U< zHynK7r)3CrA>F0NmLszl93o!vz>d}*(oUwECf0R4(U z0`!_V%1B20`6;K%9R?v?enV>%|7tp(*k)FLL4}7Of3?riXGi`c*Z(1SDVMmnY$1vn z#Fg-%eI+BYJ$A-8(V|w~F@u+dn-pK8m>)LoR3$60uTpzMu~c^FXp5fqRlY#wcrXxn zE{#QP@WUe85qB7wY>S)s<#jhTP32xX*&21u z_9DUxlU4cV7Z%WB9AS8(dZc>&qk0RP4$AG|blvoj7@GnE&DX~0PLOPcdJbA>VhVzy zf(2YK2EijPiVk3A>KxoDGG8$Q!BJ7t@=SeaPYP+XUU(f$)n_e2qC~jn{UcA~MNB0k zH#ll!wE@8f?1lm2WO#ah))~7k9TcLi`1krzJ_0&T%%jl?gAB-czA-}JT=`>R79LoN zk%8Y0!4*-hNqnNGrTKKNG(7Eq1P6d?&TufeK0`N2TV}=a)qw6+xiqEyq9g*hP$Ghgd8h0Z7G;&T-pRR6hCPF=ePW-d#LO%z z;KD0i%oNjA@4K^sxuB1WpdWYj-KP}_ymEaeU)CN6Kb%g)Jr0fkHKgFHp4LQDmz120 z1B~E06sWRn0~WUg9I9oRpPv^f-49R;&CT8mA9^b|jgP!AnbL;T7 zQ6b{Yp{3~PE||8oaTX`bxiQ4wKTfPB&Fw@L0urc1LdLO0AMFelye4 zj5ng;m^M@Vm};Q&W5(shGSI!40!TMh3Km>(*UA&<2`Xfv>a{q*9`8w!M(i3;l%Nkv z){j*zXxp7PY^Q&6hfvq6d(J-pPlYOLgUeK z^RNt#Tn;_LYK6HOVuR?|kD9H>nE8g3qY$faXV|Drf=S8B_Z0p7LTjkQ*(qO@j@GXq zV|^%^#AOqw5me!~<9)`^;_#VQKEwwUCUn|huekRr#rIb_f=VHeL51zyF>^DIp~*uy z8i=wo46FDt`Si4{0^{pm-yU9BbL*_}zKj6b0G3Bi$O9r##8BDeR0KI|@5;HNt(hb3 zbqBQ+PS_*}ja8x`B~A79fA!`8+QhWQi|5>r5=sq`B*InQ4f|K6{{fG}YV0)u^>+ zx77sWd(@dwje)toy?wBB$J@HW2lU1$Oxzkk-9@S~%Qgsm0F|lj)lH{IjydjG8mt;J z#Z@1b5RuE27>R+6pitmQ*biY>`$>9QkAM;T?d~DyOff;b{cy*aBNhcvMtrUsE8uqU z@g1@cry6tn-^srR;2)?NPFfO{;B^5vGWkdFaK>-_U6AeMGy1_B|JOzS)jEZafBn7T z^)3gn(;!|@D+72V9A@CHMKcQPokKI7!#{q!CNf{+g#zQ^gr6%66|0X#uidp?E@zCj zV7Jw!7l13Zl24w;NuYWDMp@5S+6ww)v;_sCEOO}WaPuV2NAe9pj!^26%1w=<@!00z zouBu0hPHLb69h}Y2yb4Mnrh^!NV_>VfM7dBeh1O``7ytLx|@IX+OP2+4$ z^S0M~Jq^vwaxG^ zb19(7fpv{S*7|Dp25;_C5{Z`>VdRMpGF26WVbMGz!WF}vJ)kN3V&9}r3Wgq+_@5MeHXKMl#1^he1KsGgN=bM{}ylOm22OwdQX8 z+o`}%naeBNMFFa-?B>%QhMI@)Un1uew%N7S6<#&8p6dds?L*ss-<^e>Q&cKuFaTY% zub2rUtB}8m9Sh3zf%w7a0B_(qnJ68~(%e;h zqc3!aNj@$$Jhb&40HLfl7L=3leuc|XrytcPx!Zc~%1xy6C^4o+HxxzUwnf{7k_*B9 zhzA3tOJ>XhQh-Qo=H*L^tHIlDJgb~jq%Fv^N|_CEbEDRScB2>0>5+V8hLR&n>ar)c zL=A$|6BAzd^*wc$ zkiUSJ{GH2c0a>{Ch#)Y9J3pf-Z$0@yFJ=A>xEr8Xx?*y-Xismlo#GnY6PgIAK;P#M zs{(Sjz*f}j0@5M(Q16slQ0njYoAL22R!ASSr*fm~PIx1YJaxD$u`f}F%40jhX4P%() z{eatL7l6E@Fee^&s4zvPTTYDe7Td78(p(CwFwEFB%+T6sHv7eHEf`2LxQb8^7|*&; z7#9~8kvM_WSzv&0ci>h5h=BT;Kwba&!``ps6xZbn`Kqxrz^IMP`~=pHhzH@Dsk+z} zF!c~h^|(Oe=_haKpQAlsR@D2bsGN>xf<7H5(4XpK^0u#+^KGOij!_I#wJ|N8disM! zUdXQ<&*@tg<(eK+>w$i{uR-rWn`9`;VxoWRTs6n9z0>xWBsaC^TQ-fPeaz4n%4&}p zFJP!jf-wb?g(%NJD`Br91&esl0e8FXQJuk=_C|4i|_@&|F}E`ubnMMFMr4%RUZj&Mq&V zH)%(_D0{`t6l6aCD2i&qopM7SAh^Ya3&43vZ)f*uk z1Bu!`_B)`hW)%AyB`yg75E{W7O-M8VSAj_c+L9i-M9k1r;Vu&2P7OaWw|A*7UxIR; zx%G@^h=<9wLujs$SmNzHngT%=L~7Vgq-9ny1C%AWskcw%luXhS=ViL}%|pc4hwFy3 z#3)O+GtF(->eR=GO;j=>BRWqvJ#5Q!$(1{J_oLz4a^~`Wh}!dyr?*&Pm-9$abl8q_ zJoNj8@*n#RD-a70$^cOpj0hRBe;IMvNiQ>vWBRI`p%w7?HID_=-7rladl=Z87@D+l z#0x+=1})G{uDautXt3n|%HB2K5458C-T7_<6kB6Md(|z%S~|Z>0CEUEgd5<~P~|_X zHnCMA=_Nb+6bOhP_69L_5_3dQPp(Np3jjH`JU=d14?dJwl^Y;=?)rhvn*Lh@t58HH zs3NJ*w~A-~%Cs&m5Y*G$ZbNZ*-&x%;+ri-*hYTZB@g(;jIpS#cL7J_>zY4n;q%0@O zUX8>f!U9)&`7U4Cjda=8EXd1+sh9{A`cso*JTY8{?H2$ktZpb$R{V2>lC0khA9ziO zNDyZ}jmNb3x9@BR6fWH?=u=%$zV+{)sz0F$b8LiL0DDbx%O=K`yIzIf`up*&{B1F} zNnf5=G6RHgF1HX-{O57c9FObRC1d2$RCZnO@h5`l2eb+P&r480^gQWJC?A&kEv9p` zPLxB4`!MC*-OjI9iCxgD;Az(t{)jqABM}+&8W>yuq`fP&s4)M9v~#fvz_hhl+Ln%p zT(@lDuz*g)hh}f2^zihbvm@#5k;;6>zdk*zSe+ja8Exg1ToxF7u(6*`r_!@b?^xju z>m*UhDGHCtXp5`!UxdLHmI~jPw3TVP>6*dw7EJ6LuT5Imd;`dS@S*cEjJO7DA(H~f zpPu0xI|Mfyf_W)6w3mjbu_wtWgB?3y0w>%Txfm;FchkQLRerv?No^sw@3C;0fuw^V zqJ<#Tz3pLPZUcuWKE`pehqTmjgBIJ{o8+MrK}QJ)5k?jmI=NAdKI31VP&bhKFi^_? zu285}vJpas3<>48k}_E>Ryf6Rf1&2Aga8W%Btm$wM?sv@SJQhP5_AUvMr4F!;CHH@ z?xA8-ib;n1u6;jn-Y0^hTd>kJj@Wzz)~{180~it8z>=qy)Pcm`%QiQ-akRJ6xS;wc zNgQyFAjFA~a+ov=IR%C8i%|gd-hUb+${_3W!GTf&6Ha*jma8Y%);7zR>ilPvgG6wk z>}r5&auac;Ezgf&Xqs4R50GS~tTtHKOWqGH8u;%%N2e*wK{%_$H)4ylgcNu@d(Z}1 z29w0F@oRRb?O({*&`^!|PeDdc!Ff_t_=1NfG_+nr zr2Cf0#83sAF}>0nD{#%cYw+!f)ndx$_(M4rronp@>)EsToBXhfgv@K0_li6`nLMzs zmZ@5xaiS1i3z-1(dkpBtc)Gk>MBR);#^8s6!LXi|TqN*D}@sEk6YyGl$k8k;`?UZtTM$?Z6Zr!78 zb*;DFugRv=0mp~SJXAZ`CD!>eF_D8=gtrxEUd}q&k8xF{&ArlX0Al|x^h9VcynnxH z`Mb?B!`46LD>03Yfx>r<(i$@d_O!+#v?mPf!^Q3c50crsu0&i2kdhoeTi-B|NJUxAdiX*edF2nV!d;g|+q=f)$ z;+lIu4(M*5axtn4E2rM5d5|hH)1Kq)qJU?7IkjEi%ToGhu7k{D_Z3$*!y26MAl1Rl z!5YfQ=Q}g36SJ2!KOCOnr29Z~qn5ThKj?nRgYhepn+S7_nuq4!?5IEhc!CcFf%S9C zUH4p(EB!!_5~TV|G;}-4mbrllrm$@bMsNNJyF+{q#j<12c`g z)50~oryLp;{nzGx5@xi@-43tXRL*!<+S!tYcDj*5uMZTz^-R(MJDkycKsj|S&|i&EYX=#$|2$eyhOr4 zYZq?ZxRC>=fE-Fnyx7(ER#X1$U@R#2?Db=FMv`>bCA3LK4@SHw@4sF>-XhkqCTUH_ zQ1rQD2+3?eiw=vZdsM$s)p$az*2N{=)W4~!SZ#J*Cmzc5hC+;ZGfMqXI`WwG(BJG@9h( zjE2nw*(o0q3-rYkj3Q=cRn!X_i|YF)k(tWUUvaI{e~riPj^q2r>NZ>{HHsMj{+*2L z1?5YweSm2Is?Y?11DLMc!T+IeXHtBJrPrub)~$aL&4{emF(+i1qZ;vloz62z+v*UvljzcOsr!b3_+x+~ z@>Fk08;BF$23-&9~TvEL&~W$tJ8 zru0KlzL8Yo&}6OL>Z*|+O{HNn)O+xD`PHc8Tcg4+8$MpoLC1#nC``3R2{HP&x#HJ} z>x(8$>Wpz$J*lCFOwL$QB=z@5n5mu08v^%>WkyIwRCN&br(I4xqr(aK(htx7fAPZ* z6lv;MiEpHO9eylWa0LQG+))oLlL`ezcy}v{h{&2k6#`rLWSMVOf379)Hy9M4mqIh& z*u<@OBc@tjOY{&&(Wi^|cKDZjw>_-bB>c`Ks{<{Nz07LL8>968yA5m|D!O`kmw97X z%>PL(e_+x9-s^jC&k6TtfDMq-LM!x*J(eeT2E$6wVFJwn3j;i<_+7H;>V(G#fWjFo zu*Q0Odsmk~;1--4@cAHmozbiQq@a**5kNQOkfMyWoU9xfn3|htEh=LN(rR;DXFf_A z!Z+FcvaFeTVzvuYuz}6##_UhBfQUW%>jk&(7exQO*w4Q(Z*iZT758V>Qf7>;lPm8> zzgY&_gu9CDSeUF3PyB-p9Qls${7jQ{n2+F~+EYjk#mRKcW5xt7{0CUjV>_wbW9@*k#0`P1vCWQ=9R z{+V-n=SQ+4PHq8TUM||9nfnTeuHt5qfCH^GM53Ne|G^duyd8BdEiG#pE|ngSrD)K9&QCr6n!SoA zc3;x|)W6ytx8xhtBIjM@7n@HlJ+Lq89iKSxXQ5e!HYly5jd>GrWVE4RDJC%doCmz| z&XduYQF<4hAME0fso1@*y;h>u%|V=p(9+3A$2p`SENSAQoc`Yp(FfvjrVrh22=?aR`)FFniN>806_E}SU zU{VD11tS#fG5|yOm_VO{w_$2@LRl1=$(Q#swnlE3IED2K5e)qv%; zf}lPt4A^)>cv9S~Yp_c}2{Y5`twQ`0Y0ge0vrBBk{1~5gdfq59-~u>O4*f}jK@`W1 z>0~o8bJ6LNW$H2Y9ia*12i7qDZcDx(w%uuMN`3s6z^GLPuf+bI(s-SZeg^X3p9*m{S+9zJych*$DYv>G{dE(SYEJ{C?oTwhA~6Y2oe z2Sko+D0&xA(>a9$g?f~ukGFVb>~(M`uS;L*_L>)j<^I!d4c(oSrBtioj%O7R@;Kbyze@&ompxfYex0SeU zgVK8c0~NA7dJ~pZDc-`BbBoE(>wxLunggAKCX`1|x1ft{VLI2G`$oy8RSekpBy>^Z zap>-y_RF!>tJTf1NdelarBK&S{YyT^g|q!=CT$=jv}KosKJrdb!>)nRZ@q^40%9b* z>y4W?ZwQv&`334@=%Q$^hsw2T>norIM;B?NaldXY=Mz+75yy)B%#&>FFta- zICx3GEVN;ehJwEb0^oLk!qTVBExFO`E?JW{u!e>sswG4siXi<7$SpgJ)N*xPL{4V8oSDyv4C+`c2b6wK)&*N*u z2;4-}kyRv?4-4WbH{Dr4@%FxmdluLP84u81@U=L>@PE?=JG_dzuHXe7&tdqhzVzzR zZyhd|mMMSBE4|UPaJq(DoeT@3er0k9%y_br_x&;{hGG|9#L8y*hgqE}SazWB@jMv| zuxgrCHS^rmK&mp(o47YM_2tlm({EFIi(c^y&}pm{)_S3azpfQVoKZH$m#(_?D{peQ zW_U*BmfKPnHcjI?S;0v(g*812h~lB4$RoI;@9W*IMDaVBD0Y(x`yD3)fd`2lzxoRU z<<|1xtu@%jvn7KEP3e}%_zas)rRYlsQA6qkplrE%U=g6Ti_n^J^HO6-GCNFFc=egO?#3{EMr&5}-1oG}8L`tWl;+Qo?jxM`j)xKkK_TacSxNBgb^dNv0c-Fl43~>~`tA_KBfYNTwq>l0#hh<2Kp& z&fyfnYqa!*<9!9XTD~^gDO)1tPUc=IF;=r#>2+vwEw%Hyi3x?68xbf4`52LOf^bs_ zyMh9plo{VVp19xnPL_ZZ?i`n2i2Wn#6lh@`Rw1Ogsvi;n&7)bLG9nK*#fG#6^jbms zh_bR$Ak%_TgE3$KtGbk4CgaX0CY>=RO7w0ev4)G0t;sXKk5qTIC+eI)nvFV7PaedR zi=A0Zo$0^Db-en&j5*fb3FM6p7m?I8+6p!SU6PbD&SLB)r@s3;VdXt96@^I63H15u zn(rW_dKM{D_)K7`AxCjZa(@So()_1qgr3ujchE{ONXuc zFHXKGG^a*E4vV4S*DKNO;()ctn%ISDTLvY$Xn`Kod9CD8})92R6HJ#1B-}~Y5W6t&+em&`) z>PcQ5hrt5W?n9G@)<|;#UIlmzfo8(E#2i?)0{0NC)lZ9xW_@TBX}w9uiqpuAmvXma z11uIKj#y@oUUr;g(vvgMFW>IaxKJBoc_nP1M4M7|EQD@w3!j%j^GFy$Nq`#)f)4^h_f@(qZv;9qa*tS0SuNRx;H_4Y(4)=gD*ZDG>_wIt{tGb4)Syrk`gnOV#*d{|f_k*wW zFPchj!dfMo=0(KI3jsGa;=I>*QS!|e+vWTJ0@9%_C1kI-q@=x3{gy#xM5v%x0|LHrbj+JA5JX`TfUncz_ekzruLv~o=+3v^M;(eF-^sT`g8rS`9~Lqg%xhT= zL~KfuzaP#m%p9lpN@EwJY>m)~r1;%o%;>!!^&VPhc}mIga8}!i;1#|b9`a$p8Y4+D zfVe(q69I&==Jt4wXKIReS0;C$~6uMF|C z8yi<%MS>o#Ok-GvZ(u2G}6v_hT@;DgHpD$b7h9(zb*VgO+yu3}6(hGP|2 z4YvFJNtxJwgzJ7C#9fzoeQtm;K#OAlMGblyU|1wd#Kh!@ncpwD>O~8Qe;#H<;@Q6iWg2ZMIJf%TP-JtFO(O=D5K)G zONx%cQetXt!Ax;8-pcXB&Q#UE?&|T@O4FR`!Dijd`75`v4Nc9#w=WK#7)Tl*K>e6z zMtodA*uh?le6J)U53#Tae1sF9#DsjS32lcI5@C|&uOfsNYVCWMmlh_IZ_OuTsA$N8 zVykmchx4BQxh72T8&kjf2M0JWvL{TI1$TXYleN4Mwe4Y{?$~{&?#*|bb26Vj^Vw#L zhLq!_#xAJ(?gxaF1rb@+JTgw4CUR2V;&-id?|m8jdFw_E#=I&=M4*n~@_9i}~!Pmd0z zY^Zig=ES-FQvDN{@pLt{pF4DPb$OWnV=?(~oZl+Ic+L zBoD3P+(avld&KsIaLWsD*rqn7MW3NE#)?RsS=$u8D3>nugR9yHoAmZ^0a_%WL8Ss( zP5uhL8DjPJSGN=Zf|9W;5S=de{9U+p4e?Z9k70{pL3!6@eopbZ6B}Kjfe1jY-mQNp zw-PDtQ@7=+OGzBrtc>U%zF-Z*bA!k(sVJz8rl%!|%MH%SNqfV!(~Lij>u7Hl#DhqK zVjC;R36*0-e|CEMiToJ6MOav{mNw|#TV;}|J0iaN`@^l9H^2MYRg9(Ok6+bd2>pKf z)=23qm4J1yD@=19YBS}rrWY44n9qL35os+|cgI_ksql*7T0QM^TWNMq{PLtvx^3C^ zZs7y-WsZ=bIjzCXo@yMIPX``9c<~>znK5vxwa?>9VgG?XUoqH+ky6fcJ8gC_{ zpo`plf#gCHsAF(Rgm$Nkmys_XD@L&jj77npd2A0dRJDa1{0fLPV;c%sZq_VGY1`9h z|H@m95oZFnObrzj;q7aBYlYg*DrJX+j88)5(Y4CmZdtp$Q@|Q`q*$Ogogpu}OmWWg z7sE%&j*0M{KLm)Fll1~Hsem^iSuaY~dLAwGHnlUj0Gv#P**}%)7GUrM+iZ{fz`nSh zyt0Cjv;Ip~1??1iN}i}A0Mz0Z#|WQ$Yer`nXxt6Xt4u7N`a69J;aNcmu7E1C-9{9$ zI-OZ5d3ki1$I(+^LM_Is;mG(lpJ|-ZlE1cNSCU@V1Kt?xklL${$BUS(;dCHVi~C7P z0VO)7GeU!gTC8#39m^y0d}(8&)elow zW#T7564W|!sG-+`S;Hq%y6KkHY4+E=``oSQ_h0?#Y<^U(AtJQ_H)_9UnP>AK2cXV_ zQ!0n<)e5Jz%B#iYg$BZLiF5nSh5$%!R)Hk|()sl*G=E3+)9P}Z``Qs@g>p|qEcMXH zBdP=(X?FFV3cb-SEaNxf)D$Ke5UN8J6&1T3)wl)GsOmu}xt~`w|6@p|tg&~Z&`^@% zBum(YSYYvbCy?uRZ;hYHowh@zJCU}GehTjv_Y0-u?N`>DLg{+QMF7?|u(v^O z%5R9S7xA{3_DxV8mt*j^kw_(qs_ z=(K4Rq4mPiPC2|Jw4a&oVut2{x@K~m9X#J|ePrH98X4Cv{GDg}98B%Dq zmLk(9AaSrpY!B8pszEI8r!{p{30k>b4+q~o5nYoZwOw3m_e!qjO?$>p{1vA?DjH3d ztB#)dlA^`W4^1M@1PFG-_*|1}8I4jhO=%$cYh~fF86Qt0{q^s9rO*e0u+HWsw-+4Sjm1 z+of3oc5$K8B-4;$+~zH#rR@lG2UJw=*^Er#%+jNpMxAt>|J?M_Vpn8_bhvygsQu)_ zb>I5Ji~1NIf99t5IjOW_{ucH8*@mLv<1n5;V{s#jL;$c@=eL=VW#MN5t0 z&J8W*-0VS>5+4SWqIW9zbQta{q^z z-|R3AIfep~0n|A(u`Gc!6htiDujcccG4WzAYhJl$siH#T@}aQf0u)6*Q>H(9eoDQ^ z4`0}~dUVo>>cM|D0jYiB;_j#vJ{>_^$a%gNPj=Dee4V)EYj9P-)lK~qEcgjtrJF{#;Gu%0lXlEkhcUhUl*l}9%HQw%U)?9K{Mq)`epd5!H&S&?(pfM8 zmBjfn0Bu2MhBt)@+fb-r?kwOEw7rC(fvyX361accXMTveBS#j=4+^az$*`z>aI-V-cRB|L zC}b_S>9u{2AU+~H+RE6Z)Trn5SM{J|iRqH>CE;}DYNvAJnX;sp=BgL>gSy4m_%}5H z4^lba12^7}WA&zz^0WC?5VxSB$UQF`>lQQ{9i`E!vQB_z=qfj^A;KG5^EQv4E~sv! zW&CDXKIhej`Gme*YsUGhqgRNYS$Bv(s>D@&vi(Nif^2 zR-`t&Tv}3&5Zl||my!Ddgaqv~ZUSY{9WWERZvxT-5*aS3#R}s^dMFf7UID5EcZTB= z;OTutD|M=J{7k9(e}sK^IG26@{s$jrHH>VsqL3saWK>4O-jZEO6p5rVqar(HrcyGq zr6OeS3Rz`Gp^#)`{LZWU_&)#so};7ZuI`&Y*YzH+*ZDd}8=ac5C37tDYX}7}SS=O3 zsX<&MtS=}gQCwn@2i6hMPnDkw5v1hiF0 zo@Y2(UG=H|BN+-AP0h$2xJR8n{p@g6S7#^Tl#9tvS0Wd8s_W8SH)i#!a~o8*mxs3< zs1&52UDvkK1Aq~zaiXX_*a4QT1X>(0;JDr+hxo zDV|CRpBxat;OA&R1o14?iTY*7%cUq&@QJXIL*(ft*;#mCf}LJzrywJ zMuscZ63YAVdBf|dbtBY1OwOaoj%h4I6Uxc)w#~8V*t}5ghGVF_Ox;Q2tkV9% zU<{)Y!iGPPgX;Uh9&AOB>>!Q!aNyIVI}CpGO}G9LZuxr2b10AvwJDmqnV~H{rXMHC zYLo2>6$gTXn@t2FE4KJ79!_7jJ9ghgP}GH@^=e{zS5#RD#JkArBZ6L`=1sEl%x(~e ziUp27h?!{U7Q|of2LkAX2s?Z*06a+FQQ3i*Po&2a)EO)-lnb~9kFn^IMv_o zAU%_=bAt}Qv3?uIlsIpQV4cNknB>Nben!Iws1FeYaF6BBN!FQav!1pBj7B1s#Szce zqj#FXOr+Q z>;_niLnhzg>5oA5g0aurL)8h}Mz};!Y$D$R3YA!sdlDh(IDUl%4y$U1{>_L8fdjthJJSLl9y2SCT-~@$GJy>>abBf>iE-RvE_VBE zbW9JZ6LePfk{M3CJE+C^`1jSgzVJU{DO(ivkd)+f23lya^(YyNy@AO9vblf%Y^e!B zNH|#JM1J|%LC?+snZ}wZHIL7d4^sQNdAm5uM7e z4a6h;ai29Ke%LrBB^;*wfgxD4Tzd?8Cs6E~w)K^^E0;g%_H1vZD(zR?j?$|?9g>(0 zF?{#NYYyCQi{R`y_iLfvygV|#Q&s?1tKixb2YwBV$UAJ8Jw|d948|&^UQj`Ip9&qqnlZ`8-D@0 zrkUY8tQx7{Ct`5XzP95XTfU%pu#Nd;sGuN!oL8`9m)5p{&^s>umEO0`baJ55dgbfoX^Cd7Kr`z@5im)p)a`keMeeK zck+Viyr)GDztExxv!-zrgJcMUr=tD&`avemOmVJuf5*zGjP$QX`pJSZ2ZRJ0&mMBV zl4?8z^1}ylsUMoTO@Gz4R=Kua;Qf5r#X|eUiHecg+jWG&{{f0{KhBe3YEZJ&Wp+*O zaBq4dj0*(9Hk_TfgAskxP1{Brj#TC>-uLEU%7ERnFO2Ok@Hr6 zIV~~B?7GbRc~jkpD|q^0JX1^LpCIO3e`2~9I+US>zj}C8q3SgXprO;2pu9v4B{jsf zf&mqJZ;E4%Y?KlcPP^5t_eo0&(H}-=WNJV(M;~XpfQQ6=j3yUhibZtaJx)IzcGLxi z(jV|6vzck%W9cq<^axC2^b@gLimdh;_;tun%zNcU@b^_(=3C@mgD)u*A3pQ2vZg`x zhC>X125#AH&I0Woz2Q9l*?<`38AQb+`DXbis9Rx&2q=Uy!Ktm348W=e? z0mv(hVd-hBx^I1FP2g&dV-e)!V z*76@SN=-gD|MF&3%!sLW+;AvqOQ)__sn5qlr%!dhlkva)N2Lu!!5Ujzqe4b_CRED^ zrsA)#NHkEO3nG1DsdTyri{vLFvBSEEjPL71^j2H%2Q#N~VuKJk$I2>U%KR?@n6M>} zjje!D4-tVtlUv&Hh`I)4FN$Y^#!T6zlT~qGD|>j|Gx_`Gaz95Kgl1OBy5s3K8B09|AAiuu9vz|@P zWZSV+6>5_#QKKLFG^`!>_ z3FqY#clr$hkWJYZJl^29okIndXt>M<34drf7|$%|bBN`it2n@VH;dKaQAVV+-&e3` zAX9<;QRsh8wpb8L&iRR$zP?RfizIVQjY-MK1cJL@9ZE0v#s+6}ddHcZQ&M_lZrpXT z{Jh{VvRG>#=bK+K&2FLG-fuu@{q{jl&PMOjpBZC1n~XN5vEEAazCz9k2?^0pL%JEh z9rT-Oj*q=&X5C7!z*b%Q2KRaUMbC0mIn_q}` zGS)Fl(xwvYXJw!i*RuI8`bW8b{C*jJOBm*9Kq|k_sg(yl$@{mUAOXV7gBShwf%|>_ z+e7nbkHsyhxyyLUJeFEhzC$Q}z8y}oOW!i~`RcO}ll|=fBF@}NL@w#)>vJt1m!}Y| z#tL1!SO0>#03$Ni!n)w91$jC!4kFqGYiOk>d6k36szAvzbVuZ2h4T8G@)d_YYjZwp zZK#&IORpA4(*HZ_VBxa9Ej%V#IY7ai@&%suc(M;KWC)P{UXKg%uTa@7K3IJ`)fpB| zu}M%Qf-gbBa8v(iEuFlS1G+@kW(;VPm`vT9elz-psg*+Y7QEZk2+nfwNPrJp&A=Wg z5)BVs(jU2YRh&$1AC#tSYaZX3)Zv=mWxeXofBVsopk9p!=Z@wFp*_c~hpL6u_};Fg z`3d+nF>|?ub%pWn&0Rpg@P|LU71Nm0%;z&9ucQl{`Ek*Q3kVet_1=uKJC4&;ldZpZ z=7iF%APl!u1SZk2z{2zj^S|Nm-=Opl537i^ynJGQfS4dmOhg^~VlUNG`5C#sd(%>b zw0>Bq*50jQ&)BuEj&a_r^i!#2M=9e|dG5`5KTO+#=j7A9D&}ABmCBfyH>9}z`hWm^ z%JTPF?}*b|$ds@|qtyOhw~>Uj3J4b9vJDUk?K>I`;2(JXFZTAR6!qS3i^eal&ldDR zTUNa#npJmhZtjVz3}-0JwWMD7$s9O}&kC`;vjafx>q^@}mqdKv&cWy;X?QgNoWZ4? zcV*fVeC&o*{e7g4yaEZVew zhF+=tGxvw}$j(KiLQ8*a)=!(P!cOD>B@AZmb0?Oc*m%5#`e0^!nQL*L?$vwX*-j7*?wjCV5DNC4^b! zO9|z7;BZAGGM5hKdH-LU+QWqb2+Xi%fx#ee-F0=oE2KDN7r%CO;GxHYo6wd9qRgb< zy582%W1FY>`p&Lwh*DX%Vp|p}Ik~DSOGDR7=^VV=b2?zk5{`;MU(u!;(nsAG{`tvX z3eIqZxk}uL;L~6eLSGnF$jEkN+sanl3 zd#po5pXw^Si-6t`zPcE70*WRobixA|?hP1%!!HFa>JI_w2TkGx&azQz|G~e4jKxX_ zq~IrVWXrQuZzG=jy|p1DzNFw98r-+Ou!JR|?m&Eq-C2<98&d`_Peq(Hdh$?i5s{z! z7aT??i3!>}MEP(F!kI_JQ*Jk-q)Od~R138ALheS{#^}uzq_0i{sTchkykQbf?d$o2 zQRT&1UqjZTJj&x#ERM4jbqsz~IS2i+;)BMoM6M2(2{;H&p4brYf5SY{Wrb>M_VzkS zmlsJqA^W?}oiu%STLGhdOxm7K#Vx(k&(kC4gX@8SHn*lob$Rju7cR!algH!9pXE5Xbz_!N)HAU=YS$ zJ>OqhNB}VUz_j6l|XfP0wneWy^b8VA^dw0j5X z-p4nFue|RK__Eb-lPMH`5I|@w`}5U9*irHHtvGLZ)9@pY>$+@br3?~Ky{f#wBhq(3#8y3 zB{*EWT5aYfA-*q1eeuB3ixzkm{ReAueMH8JstN;W3RWVg?vkHj~}j4NfI^iL;sE~K$?l#`pcZYa7 z*T2ooC#)X5PGQ}G$D)r^)!ic@Zj>^W1nuua*}UfbhVj3h zPp>q1PPy+&VqS(ag%0pp3&pVn@aJD7_u!J6pa^Zn6|)Jpup}&eBJX7U8b` z>?B=sHU`~XL;uA^$dfF#Cq8OpBQi1G!W-{$>7UhUpY6c5f4t8ooqzKafFw2t5hUo@ zcCBA<-B>invUqOv(4S+fwP$X)2n-ZEC)`_Ze{!?mS~_IRJ{e|~uu#SjBXA@9m=0k= z_?zWm#qg}B2G?U4iom`)eH4v6iuwD7%&Wa6b5HHcaVu`gK~sVy9$b98Oo`&M?{4Fr z`qj;D!vdPA0q4$m3F!Z=zGFtMDt*_ea=SseXu@+xnshdN9q0@$O_6=GYK2NtyD`#& zHkbm3lj7WOud>`sa3>M6PeKKVN>nt4jtF^!gBR_|pDuFn+l4taHF`Q=-oT8Y=zvM+ z(bu>>-dRg(TJ?cg9CCo|GH-c?&FDb`Rlc0aMyK3k@|>XvBe}C_t5_(9qpL5SfZ^8q z^SNs`R<2lDxqr@&Qe?VAV*qL#uK(&cy1V8018PLwi+pdLkX%m+)aUNQ;1Fe?{x@(` z{!5O+A_m-s-$ob_D2@!hOWbHPfZ^d7?=ehIcQvkcGpM@?b+ip!dIg^Aie)Y_T&ija zJq0=azwFB`N{fTk*FI*PlBMCqRsHQK0ukYFglP$Gc#NrG{Uzd-BCpi3iE)~P!@+CZ zwW{_V5A3V|$V~=tJwr`Rcuz5>zBLrR(+ne1f#`|sRfF=a}3HWpWVv`URW2Z*_ zVnxC+ylbQEQ!t6}H%8eH6PsET#n=~w(=*Ttn{(%m3UQHr?-@EdV4i3moJmK5Qr2ht z!hnx5MqNN~X6JK!cyyGOgowaR()qi67{9#z{cH~f$@{B`H#OMPK7It$N4O^V!`ozp z-fEHZ?VqBYhAX^q1Hr73h-)H7@C2|2=mSt-x!Cm8I)#2e9iQcP0y+gELqtmqwXIFH z5xDN6OwvZx+YR=Ie0-FVcXqGYKLPq$U zjCP~6;u%>Q=*qXqPRIQtAU0BBM=x}2`)wC{&H-`0qh3c?0SxcMFpVP=lj0v!Oxsd6`#rGc)Y z&>_%Iapd5^$NLa~McBaK)pBP(!q=!4WiB$9_%Md#XRUks(D7$cPmf%+bbL%*=j*uX z>4oO{!mY8H%PRRA7SFIr6Xs7E@p9$nbS<_U|-;HdZT%`H7C#CROBsgy*d zTm5M`6|Nz0cmR*9WTF2WEX{La4?lSS%ranJgw3@n{mr^dq*=f?6GIzo!V8g!!=QE7 zBt6SOJ-o*LziDm|ikMb6lL)grB0|3M;_9c1LqtYfAga?9V%%6mxxqAHRC)G}jy2?) zSgTtUXGnx?C`9V@*ObY9FOZ4|OLW4;;I1;jb5E`D#}c-McF;RSISR}j zQP_rbDx~saFoH82=q;{dR1t~t?#E);{OP2QoS2vVata2#d&H546de0v&e2HFrPZ&z z$70|6XRdkhvDm!v#P=Q1M~^CJFl=gfgf^tt>O+63?#|mm$DDntTN$#>7>7Ql6?#R= z+`oM)jY6e2YcG!@Y7*_|)YgP$eL|EPA8G#YDhfFQAVBSS^D40niP47OArNFGT~M;Z z=MiTQ0rfw;@yBfGM(RzYAnnusrk7P)?)OrwP@9YLVc3kb>DMZV7&wL`6IuwGFWg{o zg2USejk7}cOQ;W7=mIwf*cdD3ARZMbo3a_Qaqqz zMj!WY^3AP9Q^gzp+^%`9`|tAd;H6hj9g`~_cqpQ%SpEVlrLey*j5S4~j{>>{dCA?u z#fVG9Uw7Gx$pTXt;_}gWh?3#E4(E&M?mQ~o+}l*N6?V2#V6&`YI7sZTY8{ z=gMb4{6(y0_Fehxx)EcF`QU-`Mdq_TZQg%m`rc-3kqENCSr1InU4%ehk`q)C5)( zF$H|~)ZW+HAcn!kA|w8gOj%uC{fM#H1v_*rRp%~gdnDYKBQJHd?_i?cC8;l#ls<|4 zB5W<5bKMw(q441lBzI!C14_p`@%(7=HH&t|*ag3!SvadhS*+h(W4q0~Hk*D*uJ)#Q|| zim&g?S#)Y@C&A=Nx6;0cA^q}n)VaYYFT8HL91OI9&hJ_$8(#>K#~{l>xdPk;)gSOj7&}FOW2O(J z#4I@!+HW&%nuoOSjoch5P2KtVef22hR2jt1G>p!;8(pg&;Ml&MNFN!R_o80;AW#ST z#E436vZfFbYGE#U11}?hkhJ;eLSSo8`gZxQ$rXy+8j0_SLg^NRfjeJkk#xpSb z#RCB9;|SxTbhO;pKQ}Mlwj7v_^91a!3Qm&En&IjrI;N#~@Sjm{NX6Z2;dGC(U+%DM zMlxLOd!jP1@_4LA??7`%PN_cjl0nr%?ia!NGdOB5`MhE&WTVZ;Sq2@CSVaU-tmU}IyW z?rv>qp)ig1%3WRn`a?uiAV&e!3Hl$vk?3^6et=6HzzN_Eg~R&u&oj~z@lp`AvM67iAbjJ2ca3pKIlSImx4_Vztem^CP}?L)QQJ4 zah}DVNZqWGIzefc)JRE3NwYlW%Kvq7SU7Ot`LUfetsSrc;$LrtOh@HYQqElWf#Qx~ zUdzLS++y`@X-VfwB2)ItUprP%4_69&ag&S@KbfE~;}eQGsHDIJBk0xuzTl3qB5SnD zxq}ZQMiK_dY+DXpFobaZaz6#%FPi+jpTFTQ#Xub}H(JiOwiA(ED%lawTJOczopEb< zDHW+nQ13zi!4v>s_e}s@Q1gjc-iMyBRLtysF^250o{1|513*$if&M{LAow>>5>Oa) zG%}UADh-~A4m_7(Pm&XH9^$Uw+c>RXK>s;6qQTCC;8~xEkv|K@5Nu8c;k-E6-7MLM zLB?&+w2ig7m8IY7Ja4z{dxl!0THWHuKi^pZrUWnoz5=8wL}2Blv?GO1lKhUTZ8C~1 zD$$RJg%rVfg?&y4tC$llO`JIrt^jI-Ny+Ja`cF6GXZMKn#xrDQNYO z-2ziV0wo*NqCQP-a@3=8Ckkx&^AUR|5nS&`%%%oo2T<4`MEwpTs9dub%Pjb4y&dMH ziIgb?n`?l20y-eQ4Z>U4JlLmV#C_UA514>&~}k$ z7-vuwTve9c))5nFB*Rso_v?_ZYIM) zmsZgrhECWWpaCiLE$I7!N*L7Qo8O!pEEaI3VG;z4o3gmX^^qdj<*5A|gVHT?=xKxm z)qU$g9Bv2cE9t7~XL}f~qVXdH8sph%H)JY9&q1f!uA>|ZvKR0)kXDnofzQZ-$jKzKAq&VwWrR1FBom?FM zV{JEWh4uyC0=EnrTa0eS50ucjF{NsjL78M%cUffK$uR@DP{)H~LfHLMUWZWgrJw$3 zO>w)0nuBlRdr8Ue-MeXh0x{X>N9C28Y!QANK z)iDl81nF}XJ*Hxx8Xn};sq;S(a7s$2bfbNV;VMUE^1w7(P}KM9630YSK>nv*(WkGG zK9IAGUKGd%Yyo2FUz+d66`uY(3CAVe%#qtwbEX>UhLiKhMDC$(LxdtoEsX+BMdr^s z+3R8(x8~${j2*~KlIsY(6(VRRM9RPO=!#&OxSzK5QRkiQi;ff|3JQ+BVf6XppX=X` zCd3~MQ(!gJ3#p~HI(>_zIk{g-OI+CYby<%SQWTIu19RzySMTp5v~UmxN0jDrYn_*! z2phbjF^k8>`O!D*C^#sDGW7)`Z%c-tR7X2Y1myKEyj^HG^G%=d!+g-h5Ay&7Jwk2E z#tQb|v7v~rNi~x$^QL{N3J3lm%Rcvfmjo1380-PSMYqx8prx(-4ugkG1!McUc)j9^ z7OG?&|NX5Ag(63;`Js=>^D&1kVgx2u}e6O+uDPcNjVi`2WM!?x}x4r{kTkfpo^cZo8zV;H+;}Wcxb%#?9O+P#x`W zaoTT(0g|mYePZUQkm;|b>u_iM$QAeK)?k>O-3>?Mln?dndh`cNW?Y?I#UskbWP}9| z`_{RAl^cFn_E2WT^ZCnXjl&UT_Ti`U>&Wyd^-$sc;!Y2$RMaTev#(^BzYEG&|KZ)1 z;;hWhys|3fj{p&H{B#3)trw*k>kJRv~gj*Cfx{@x5B*#$8bm?UOUn2SQi#+1o_q27$I=IjNe?wG^e#*^bvktRPEb4ZQ6 zyuAyPfB}VyiU39YYdM#^lq9M?5eiQZt|yLVCDCUhH@2rujUV@^K-h*cCt2J+-C$hpWd2ZxTcIy7VAH@#oi@fgosh{q0N~RutMU&qfb;GZrIx=bS z6~;7iS+rYezrpm7aHZ)jfenit5!tb6Wa00QP>?P_VSz)-j@4%pJ16|JQ=fM{p({F8 zisj?`Fn$kUd+}S1Phk4~{ldH7r(yOBKn~k+QZ^M{(>uWttKZ~sS!<^LVJ6{TUK1z9%)fR!nY46iek}&d!(Va7wSM z92u1xE_rgoKVJULmo=%e`0=`9RuePbbdh)Y<9IFB2W?e zw-2d#>HTuze~n#98@b~oT<|ZB5VG|g8;qdx)30Z%k~BxgP(NY*Iz)IfV~UG}P51?T z{cLr8RxQp1APZq<3%^8QKj{B(yc`6QGbVwl|n z1cSEcvhI*YBZHrO|0g!jG`ExqorZ;skv=%?eL8t@n{~z?lj+IHXjZ?KP*fG)U6>KG z0%8D`pUt^fdSCP4{m!9dBKjX|;Gebt=50h&*C*jwy!U2y5$-o%`fZvXPvn0Ke?HMz z;u~oOf53ceDe8uWT=R&#=9oaxhAk^d(Pll|5nWFm8M0H1sdK_O{`DnF;U6UwynZyTwM=yme+EqmS2yl$ zCk|CpfUDA++bEQGE1KRoTKpTg_?}6Dst$hDSLUH<{zGUu2eLUeH3S$jYx~~T#CN>L z&Au^VtSajhY_`~pqjouxh7TBn8wKRaMNYnT?}H|H!%0XgXA4G5^?I!PuRY|lGGQ~B zmnjl3_(Dz((Ffv`;=0TW67Qm!%#LQYQ`9dz#v=Je3JlT>?c1wbw*IA=b>#yjStnN6Hgi% zTPE!}5EkpqM_-QWngCXcj5rP#w~v+rm#+S;kkCSixd;w4{Ei*Gb}hh!CM9)~inId8>%i`PBl@r@>SaVQWym!#3yvN9K zMCxXT5TW6lUI;l8Qa5&4{+WhkoT@|#`H#~q;zTf+0@aNWSW)PU{(bH^=b0#4wK0+E zyrW~Rk8%6h#F(F5*e5RTD)PMOwHFgKOJUYsBIbA3YVw-w@WFYcm^%4=`3eB`Y9VYB zyA#HkO1^~cZga-KZQW@OQiDB&8U()@G*=avaY3U6ToMhV|9?zUT+VRBvxScg*zm90 zC7FSl9nsL*^ARzR7#V(`4+{&!fT_MTy_YJ_;0_4EYqi{N+;bTgR4TWYN>NRKrzD)T zeg^3aC3o8nWTxP$qt$p8qKgl=^!|s(@#R=G&5ZG}7{oi4{Aanq0Y(L3(j$H(mQzp6 z_7!_n60DUWeBr6BItXYG$a_bsS zUzW#Rg5>6EHb&nQ9TvKmU%Ls{7KUR*5I>N|=&9(izpM8IG~2d-?tvXVxrTz25Pe_R zS=y>{c44zjpmw>2#4tQWDg-m-mMmEXBzfx3AKP1s_4K}BV8{8%Sx<~-p0SHy2JIcH zbSXo-5G>|Zo38~K>w3k!ee|D+|KnizPMmf?Rb|CM7JtE$EIt^gJHL>ITGj#lS9 zUzk_U4AisCd3rjXfMM;T*JItS5vpfGtTTb3Aa{_C+mMzpSeq2E4y{Q}O4_!{Q;FA5 zgLv}SuRrxJB!PyG>#o92^f_Pb&_(9zIg6ze(Wk#Iy@uM^YcjiJu2mrqbt*Zaa_-NI z8-f897Su90Hh^+rdLtDC@5IxGo0e{}YTDaX4M`%eK(q(ZCGuZD=*9gEe2(ZZSyj^V zuaSGEKB=nc(9-S@yeF$01jpL1p8Dzcoc528jX`3In`{Wi=`XDFwmP3PrRjj&e(m<> z2y7;(sOl9}Bumzox^IV;#Q)QtpgRT;7u8StiR?~CtY+Kel{IGD{${FZP+uxEJpdmD z;-o-(1&Fe-x(i>8S=42-ukLuw^!TC1pG?@_7&T&(U^x9QmeC@jb9gUf^T&#NyV4<+ z&Xav~U@uz+CMfVLZRXPo$~Mlt%>HOCr0&?N2}G5mW<^pl-j1RLRg~LU4Q6^#y<)6O z4p2omWix>31Ex>kqD#69TnKd~3MoK>CkG`S2h%%t}CAf^(!~`{RhfcFjE;I5rsL7-8*^6 z(NMi}Op6|`v9ldwqo=R;&_O;e5;UNOAf^RCt8#HK?E3Ti{-NFV4llc=9%2?ci%Big zl8#AnPR%zptIBah&P-4aSUrf?j66HS!;tXoUQ3v8A1N6<^T{?3Auv{BDX3wvgap|c zJXo%~D1jCHgm%oL@~jZdC=oG$J%@*iP#1jX2(8PQm=mxXuzj!zaq)Yo18Ee(^rY)z zYX3LWb*H!Y|55$_l_xmg@x8%jA$F@8VjD16Gcgq=5o_VC>to|h7GORhBl(|#0(9I+{b6r@BKs6k@W5hFe3!RqO8RrGTN_7* zhs}jM+xt&2dtA8^vOT%6^y48-YTr`Xj&VKvR+jzYclJw>D>2MMcY+x|{`S2t^9%nd z$XUJ>4?ABdZlM%5>N_hHH#*?!#(+X3qQ5zdbzDH}%*_QonZPmAF?WOA`t_0@lB2SO z+sDws#GOc`T1@BDIjFv7)yasf;d^pZ3-c=b`J#A|M1RP7a@l%Vl;ZB3s@ao9Ut-)| zyl8zTa@NDcBW{y;uGsyZLR&WqZDl7HhuyUAp{u)p-QK>TSlq0(`x~EM_1=84It~BU zePI?4zw)OZqql|?-T&3 zi;Ih6eSg&1>Dn~~G?SRd@2CssQ-B~7V;%F74(E(XO%07(v7n1?A{0lrd=_tYzr1J_ zvZOAvy-Y7eoe7N;Zb#C>mn+-hoLtX|ui`pCVMMrsVAT>DcGTauii`I%Ffd>?3_n;} zLY;%(6v+qoM7);n?E@5GM8|NQk*!@)!Erx*SbVpri;=+*9c zT%F3RBN+XB+?$C(9(0Ed_W7rO*jdZwo)b5NA_zMsVBUkF*`qAdk&DzHBw$Jxc!*LI zBwV1sRRKl|tG`Nv=GK6%MLkKKaaDeOxy=&KhBdz^szM^$ccqAbU0 zc)^J2EdsG&)q@Z0orD_ z(Q7e|#H?0Ec=vMr8ULvopNQ&Xa4+PWEJ`FYleMe&oa61J-o9VB9Wh zD6j7aiElZB%TJlKJ4aO}HH|^oY|=K52BaczLc1O;r1x4{o;tOS=ONwN&VWHfn)#)O zPzWCd>>ja34pdDMuKIvGAiyD}gXMDy00I^F46=$5fJj(R`t(qso;R$;v z*abD~FX?XFMneWuO{gkV@QY_YHDA0SC1t*;eG^OwC2u&nU)neCsfZg3FOo^Qf;rpq zm$gEmz!eF7ZvL8Y%>E~f&-V89!2;|3r%yrPS;2!4993&cVz5vU{+$8lhf*JWYd6aO zMeN<(P}}ncN$gKvI|0{4WTU{~)GO&jvu;(kO`Tx(f;=fF(5HEWW5j*2TlV8yXX>1j>nB+6 zPp6=si&WaO9sdU!TTR>v+KmGSN*j+!rcI|*`_HN=O22>H@)x+GCV6vikLYFjB*=jX z%~&8|b*&-Ee!b-`PmV@s)7t)!?0CQRW42>XV-w zUaZ?6nKpFZ=iBF=*}K8_X1DiLD$fYHZXi5gZF(TEvm~LZtcHk5Ib@09XU+ zD@7alC_dmv2qJ8T(~X`EzJZ0#8Sh`K)ce_v1;o^#t{hOyL1$(TDRiP4(yT1$;shg2$7P~Fn6!OgzL@>RC||V2ZbvR zq?4mz){VoVf_s`vs*YSN-YNI)Z_Kh>OrFhMMxQNxU&#X*Yatbeou@LM`ER0BCcSfH z{v8$goWaJ_Up zSEuU;4=~z2J_nC&8hXoIeth+y;(JUHkh;OgbVt)7oHO)@LEWd}@mtnw&sbXQN;g7O zjxmASLX)+Zu+)F-;>Nk5agcG3{to*GJ8Re`cF^v-@}!tz(Bn%6eshVp0-y?yDt$aI_Ms5vwqOOA6-J1w`D)!skgr!ZeaUhOE&FmOcCjtlq*7oAG%>9;+8?J$N2stN&X!S);Ej=dPhUPcEXJTK|5AiyO zVI``9xQmGp2L;<<$Ad|6>WxRkGL=*RE$B*cCgTTTg#)VqW*c=5xl>^xB# z45#~ufhola@Q7evVE_u;N)n-(N8WyL1gydNaXV6%Y#(g7fB$3x`HJRS|M<5xb+2Z4tv}Jy&gMn zaUQ&-9R#o5xLQU-v-B|S(GJ+z|0Zu+{B_pzM$hK`z{ge|&qZEve>-5WiMRrEQ+MAB zk_<(BXDml4Hu`e2cYB>}GDsTAbQEbw`xuk1oka0MqkU#!?pm7bXHIhSwfjXyUoz$S zm%0^i;{1JrVL71Nnf-CL8!ooz9?H#7zZ$VR@k=YQdBP+RcK}ajpNxO zN{$)>e%x&O>133GQ$tA^&Kq3bTrlb*&Fp7k6Qz85U1C#YGOBP-V(2J#BmK8oB*G1 z_IBG0*dOiWK1`z2PQNjH@K{o^W7wm|w7#R`>Dk6(IvG>E9IAs}Yl5;6b@*>5ico) zD;<0*fXi6TyIo5!+C+LeX^fvQ9{#YY(Ew$K8mM6K%&4v=qoO0dMs<7H!vh1;;J0DE z$9YayuV*@|c!Snfc*JvNA$srJ()EuoY%?>=vys`_Fs79nwcTX{lN-OC!rk~i7p_NY z9vR-_$t29qe^R~nW;1=?JrbD&n$p$&^XJb)H-~^JNPq(y?ENuTWKb)r(JW(=Ib{F9 z^9$-H$=!EDVHW!B+qZ~2#N{qk(r=pwmY8Np!cg;N+i8^CL`)!hF}3yjg{j6EF<_x! zIYghwL@N~U<*yS8CF1LYvxKY+p-XUjQ5$28b`=ss*7eO*n@OZ=(t8RtRi8iV(o{#m zCq=$sZg1qNV%;N+NK)YqtC!GkYpm)@PdxGYAAxE@FIDp4%Eme8zBnFf8p|luN(GZ zd%n-2v|d>;Rxts~H>9#05aD~BYi?t z!m0ui}(K3ir@I`p<-8tq8?#s z^kRqnyn#7J9!MdVxVTaoX>4;noY6H&DQCFhHYq4z;?*73$Wc>_OaO5N8;W*5$sW1$ z3XI~P!M-;KgBkaA+{Z)?kKn2$7-%>D+-2c+`8pX^H^TJzQNBMH7Z+9nkRQS{4jx^w zoy)!7e6FC!=~BiR*$)vUm|LPjK%0Y07KM~Gz-iJ=J(x7Tdw9uhdTOc~aiakC0aHRU zwP&*_S`;1cU(rN*20r)B1m2WWzFM_3-FFKHHFFPM`!fh3E31lbW~a+&J)_0z%e26l zutUHp`ZZ2tLHmd{xBK1m6TS;uRY96y8MHxe#EHt=3{vl}uhWcil(ZDX{m7x`Ui~P= zR`r-R)fld=JE^+ny`35;vzHZO{6@}yO->j4Y7^+S>)2L9UC33@aDL_dNUB=on;(2! zJ3e#Oi8?4Tvx(sduM_*XQ?UG*IsB-(O~8x@JHf}y%Fc#!Ey31A3lSS1AGL6kGasEj z%%(zu+F|2KNW`xV{I#U0y7*Ko?gZhfR$HrnJOOk8p0l2$FN4v4K&LpsYsW-+N1oZ`_a8}lg@rdEq=%tBNfgXML@+>n z21DzQLBQz^v-t}qaRSAPWf_ZsT?DV`QcEcxr-0}kL$*4yzdJKcs7o*H9eB+TkeIMp zA^gq(O)wap%`KRK28n9F>}_0q$j*^1Uuppgh&9GEb^HfUoH5AM7_kD-!Hn!!NCA3r zd~YlPkn-T1qb|o?ySyxqdJ4ou&=W^rU%7l4_%irn;0yB*K`Kwbb~#8_XMI3V41PK( z!Fu3_M~Ik(dr_1P|80}dO|ZSaE7mceXVjRbN+~yy6`QQ7vA2?Q+9bN6gGv2_GoO3# z1ba53aN7dIE49?!hcbSct|_^H=Ahu6rieLcc(N(u&dmkZc}uKF_5;PiOJ*bEq2aT( z$$hYma{7)i8H+u5Y#TV3 zn8}Mr2w{(IDExMXBfP3AzHu$*#S1!JpX}ycfWL`HUR`~2)c4n;#Y3Nwt;YZ+|H4CMpS5D;F0Rh~q|Yx-;^$JWnoyFjllsJ&Oje30`8k-D(Tml$ zsE3mH+bL91eI$3rWd*`j6DRpz^~8{)@6)Bc?2mT$j@lkuS!Zs^K&0Mu zOTH}2`?&=;O~8T@^dT;ZW7k8;65vmvQ6cmM=#58(Hp3UR02>L0fr0P?*3t&K!s93W z>VYd^k=_&R{Llp!RGgmIP#7u%^J>O$0!Eqs0LS#y6@yYj1DM*kicr zrv^Za`dPm2?d0TCU&wsnHb$6Z6adj75U{bfCgIjie8g}owcgTQI^l#cv9l9e8owz_ z9BO!q!4aE{-=!r|c=tR<5#k z?c_<{AT~EwrImDoN2A7-%)322%%YfTi9^V@*QGWTaVQE^}4#JXPhrw zcu^Bets-0y!8rcc9O?r=ZX3m*3xH+%bdcT-`^_&oxA)ViZCFHOZz^{yQ_4nQzSUi_9zrL(qtH_2T&9yda1fR``}=2X!bAp7?c@^ zilf^V{J7mO+RK)xNVaRQau)eu7m;V|-}_EF33I$XQ?Sd;%jhrzPc>?j{c79T-&qM%q+XmV-Q`C#-{Ncn(q8stA4^E#|C9hSau4#3~Nlo~l z5dvt9DeV2Fc9iG8+)9L$qD{evn%%2VK^hV$OE=x21Kvb`w)%b8}G4&e>cvs*PUgM?xMfJcM*( zlZ}+tO_=O1i3H#3L~)717f~o&+MQ$Z${*8UXuZ&(Br06dCb?ra103T&gD23f0lDwR zfrO#5*&9F{$dH2*Utw>HYkWq~whHa%T`DEekDL_>aY@ldmo-ER$e zZ5Cc_$Y4C8FIelrlAF#Y&O-NY7$ywBl?8djk8_1=);9{!w5~L!YuQF-y1Fdhdb~F+>Y_g|}=F%)PLHT{2?9B<50=%XPE;p)V+(LRg zS;#CaRmv+=MAQafEg;0Nmu$dFB~FY#ob$Fg5Gbv_-`^4Npy{jqiB3cW(*;rj%$9@^ z0cfYdaDZH1T}8hib;C5>5bYCTtO||~0Hr%V4(HDwhQ;&0K|RjI%A)gK_R3f`*7y*mK!wSmq%?@ajDuF+4W}iTIQ3- z&LE`${KwVHbXLt5{CF5LV^v^CGmGGE@D6QZSBUO1$^f`hFjf=Tk+e_GY6R47?{Av0 zneAkq-eI)gC1qH?XJ8`ON8ZqmW%z-SJi$ ztt}UGH@Tgtv;*ncj+m$m2TF9HY{g6jSjI5yp3sBDu!`NvTL4=R{`x~Mg}xi}%>joH z{R`m@j>vVAoX?sf&RWoXcC3f{A*Ps}aMFBbf#9!qB@z-6?_eT?gR-E28Bnb+^g8c= zr`xX;R9aWSjGl1qKr0MHg~oKpR^tM`D?dN)n!bi66C^k^C|XQ?Pe}H!8Dx)Xa}N~> zb{l`G!jeP!%16f?o@cM!`>B=x@nm1l$-eATHc!pLu8!F&&F6&Do;+2(!4Q)!STY=x zH8wqaB~vB5sFOcyg054OORB=@@4m(OF0Io!ev^XB^%G9cA{i8)ESUQd+dNDt8sSKx zC_G@-pYXD&!7J1Df>7VW$>eoowsLcvaCPC^iPLfo5=o1~3&7OSwV@vbS>6*d)Wi-T zQkm*TN1xubH_0?2YKn&%8X6nnQu4yGEEt1OFj*o~a_7rCAFnwQR0uCGb9FW;>cKPR z8Yw-^IoBbRr?Q?~eVCY7edX;O4`38x>VhO6m?f%rg3yM7x~XzXTbruKtNd+L^1ut9 z{(rr&o=FXLVnTf14_3b=Nv#{}ormeC&$*9`E>L6--u9{m#O$osN!v2L<9wPD>y9mOVWJ+HFqLh0 zPv?5l81~CDrPc|5J$uHJef1}0jXEo;CR61Ac=Q{pO0Ao8i*-0Lohkp#6vw8lMrmUKDR& zz1`pW0Hr>_Mw~6c*5lqDmR0V04ugZe*Pv{$8MO)-ZXQE#iIL}ztHUdw_G@zVG92D-s!%Y@%q$ z%1%ZZiIl=4L`D)aBT80OLMjOfMP-yk5!s5&q{!Z(sH}vD|9R{C`+a}^&+#0eqvPpG zyx#Zgy07cJ&T*y6eEaLOFkeOadK;4A@H*d3Qt9#;+NJmoyUw;Yh`_IO{nQ$lUEH=mi>_w&sLt2VMQa@#0qi*^a>xo$&K`Z&VxmF+=EWk zOYA-&jKSCYihbvyjFfU1c@YmSEQ$WHv%!l4`Syw8N!Gd?ifu0z{|_+>~M5IJklxca=|V95DyBW;Di zq>k7BiTX|S{Kq9^H2&_$K|!bab$wE``(z_03fLCeyfa=aIG4#=8(J_0XE^s~{?M2? zk)k3H?PF{yI&Q^3(9H27qc_(y(|fkCH-c1sX{js6E~6KExb9rW=hjx7#kcGKS!1Ah zqEjJ?o$~T>jw^q|sE#6V%c=u~WUiyN}ORfsnPB6JK$)cSytLtYjCID>B&TZESiw-r~1#M#QQ;V(OV zGN(nKIblUIv`^?}0DNWN4HoPS@~X;nU7Et|$j}UlAIVU#v9r^XlaJ8)B)#^=d+VN4 zv?dN}vSuF|>|RvPJjr8Oony$%yL>cIrSrV(I=$tSaU)r&0_ zuw7DBWq||?Rpn)FZjIsn|IVMu=a`;8moh+g&rN)hdpzDmY}Kny=2A#VR~1R58$8Mb z=6b-1nT54%c+UVekt#0CqWyjfUyp( zQAT+=ISqQ??Vu(Rt8C%yW+{DedobG!x3Y7l?LFTY5_ifWexrB)a03JLaM`2};RaQO z8?0wi{=M+`DKAX?maB&3v8PmSZGef&^piv2j(i z9a8-(gO}^ep8nYv%unvR#wJuZ+1V~L{BD=)uDzaa$CT6JJ3iljVv#jfr=N&i9?qu|MMD>XY7r8V7qk9ugLS zpodGMiQd#OB!Z_dyx*MTc%xR5xX9>pugN6_YhqgLWi}6Av0+y< zzI7!Yg%VK=_)$-<_9)q1p5l;($ZFIX*s5*xU=ppo;bv$4!Rt&b&jYfqO~Z!v{d;{> zV)9ziJ^91{=MjKfSbLq+*hm$n6n%;3nv!RrQzCL)H0!V{9M*g#QO;q*rQjLdpI*;y zNRu(oxqWP}Txd9Jxj^@0+pjt8V!%lk`A2=G=9L|CP1i)dihk5T{5tnAhrPgAwG(4NC*qWj(q?fgDtXRzOngcA^#nCE73wi8wkJ>N+diBKpr*wv11NF ztP+V&);x5m@l4pu{9{rMUvYhqKyj=FfyiE+9wAZFjDjHfB5uICTCh!o!6wY4#a&v= zJi>XCSzcb=)UbR`y(F1Bm1JIZ|GR3SH+VQPg48M&N}%n#(bJ8PN1i~b9YBA#}K+645K*HUtAyA zu^ov@=l@v!ll}QWN=Ol0m)`DU*%PcABi`SU>lll^5lA3DD8vewJMyz~s{eB6ERmS$ zm{L3VjZs~$)n@vJn2pNHs}*S33L9buhhk& z@9I&Spmu7*q)QHReaV&YPMyfcj^J1U7_Rfa{HW&0(m*-DFOcmrury@d*!yNICGSg~ zbFzc&YBTr6M63zXq>|H*yVOE7E(|9U?Ap-qO`R{ZvlWv($uw5j4PO7#U^$P#k+KVL z8Y%3)zT(7s?k6v50T%S+vh;C=S*Eq;T%sl^W^RrO1rnG2MwR)LWaFky6xJXbWx zCm|_y`1Qp3NWZ~cTSgMvgw_^YmdWO$hpeour~p!7dJH%#VIQ1hnu(E$J=1V|v}w?H zc#*Cqaw`;Rh~+WU*VbMO--`e_u+Pbf#R9IfYx+p%d$A3{9>*Wl_uE-Dfkm5q8?8u4 z^nXK2PycC9#(<@clE_EEgac7{^{QHXj_b)!u(406gq>FS z))f7aOhsvv?OS2I_3<~P5EUG(>rKTt;1~*N7vnlu(t`(u%_07I#9VJ3cCaNpeWz^8 zuZoe1q@%~^opEhqBghP#6v2qVtzst4X_aib!Kz1QBh+X>-O=}Fmy}$`oCO9pE=Ool zYVF=RKF`jE0S#fQQMH}@j<6cyCn&@QXb2fi2W~fFVtAaIYBu!)DDBIi@eK{>!p&Ch zTB8YKIVNbm(3?T=LQ1mvp%d%2s{Hwyg7t+uynGQ-1!pOE>Y8@U(SEhHR}2kileX0} z+($ny8?=hwH_w4`IvI#Ed)Jv<)1kzXQ@V6aW;0haK5Y!h|1sY)F_IQy#KLQP&!@pK z@BEm})R?WA>aPbZ{n*Q&F8GUDDae!2KFHMbu!U74jB+B&L{vfJQ) z4}}q;mEp0epi@xeO7$R+Iv~HA;VqseaV|(Tpyu}z@H~WFiupN|b?G4-OtX4E{L)%{ zYi#Scv(hi_M9C^VPdsBjvv6E|BLZ`1<55;>Mw7Wh#0bIV;3i6_$uSd?8=UNv9JP#0 z#}Z@bKN((E)@*GTvRF6Vb@1RpSP!h;Jvu!P1nTYldBpL63qhzifN_Xf^VhEd*mi=r z2A5E`A04vzz>`6LSkG8f)fkacT+9b{2QE_RNwJPvC2pP{0AwF$1vc;Nv~ZljbQ2FO z>$+XdkS5-Vhzj>JKW(#fYngq(ohL}hp4qNTz}GcT8Czy*P`neZOi7;BTx9Tu>0bUCkNICIqoqfUA? z{>WbKD}@fDhyCxAM&93-(tdVk9KH!KVJch0DK&a}ex&uqT!9Trx9`_|0u^RGQye(Z z1T8?*19KvVb1Ap08fXVVah}hJ7{|@32S!Rt&BK$!oWldFVm5 z_Ic}r+zvY2qTWK!d&VbaU_s|Oz4@tt^va2MZzqZ>T>#kRg9z-7WP#gom>|AmyIv#MwT0uUG<5O)OX3sb{E8+(;|p3a zssY+wrW$@8o=b1~%P;_ei@ei-6JHW9M*S9B>Th|T7&MUR^X)9V6s_8`b?WZW?NxQ) zU=~8z3fg2ywutWgJ7dOIei~0-<}QEOb97NmI_K)FB{=14`IWDt)dL~o&b!UKGnd4Y zkd-W{G9A}=!q)c7Wx;X%s^X%1LSKK5jqSrv366n?pbNMlm^I*JM54#!OD35KHyH31 zdIAL~RzUTDqzc7=ZZX}RutnBgK*>dDoI0@kY!u7wmSq4cR0M`QJ(d-+as%sn(X&0U&054@1JsYII@0&tb3W~mCbxHpg5VA zW_Tev6LP}+ixqgKA^ryq4p{RR{>wj%f|9Wl0=Ed)0}vL)77r`qK;D3j_oh}ev|kpn zKlvpLsuQ0wsI$xPWNew%y@~I3eid*k4}HXE93#k21l{DEbX1n+1}iAMd(N*%XA)or zDC)TljN?Gq#6lPb=R|nI!$W-S02dp`e9V^y@Q zOqjFDfqrgi`>f_MYDrT@)65#Rj&=eEeMW}w+wb3>E;6McCzTO3sj0m^6vH1`tvlb~ zf~~!=%$+jN6b;ZFwB?xf%S7y9Jh4-2leB_^*go^lf55|RuPP?w*c!I+hiV8B>lP?X^5my~VnIa5 z$<~}>l&R$~p_=FW*H6{!XIJRn2Nc{APy$`}SwMefE#IxO5%Me41`KDII-zWkqsc2f zZM-SvH2eaj<6zb}qyd9dGJXKu1V0386mVd1Ayte_Kgi5v?p^4aC3!$ZagV3RZn#FR ztr`^br>lAe@OSm}Se!N^nWt3tq0NK-k zOEfd%`5lgKHa0d>ejmT=jS+qBqwps*X>K)&r_P1HK=_lTU`>MO43L+0-I&+yeWV`PBBsw`34FD3jRHcV(9B-!6%e6mT7%vrwRQY2c zHmPzX+y3i&dhURNw`Bjr%{`@EG+)Dqzuf)N@l5`}8Je=r!?xRyrsb=WWIx)h8L}+O zX%!!Fz61|Z(aJsQh`dOGJ`4$&!MF#TpNO0ak;!Mco$z*TZ1xSGao@$f>e8)4?>^I| zh*#$*@~%Doh8)$g7lzwws&^qn8w95S3s2iSfS4NWE+H;tV^{joRLB-gU*0fRewCL# zzsHx^Df-31wJ)!03Qru(inhoiYU=VSnMz!E=;qL&Vfz6(pp-j?8uLOgW|){Idwv-Q zgBIX<1**jT76*VQa4@sYz@`|XxUjVfX|Q{P5fAq^D%53AgJ3HLj=jzD(qHPe05+df zpUYqvuAX2oN>fsDHs8K3#W@XpCG2xF&CKFyFGr~}UwI?lPV6QL?*_{$SPX>kb8>Vf zCOfZ6W_A)fe%bJ*P--8$C&<_UO99{Typrq1V4mLTOm4LvdWVN$N=L4jMlL_jqxE5d zB@FC~@SnxmVSn2CamlE&5|;-&Vg;94O8@*P&wJb}wOzF&4=ZRr5gIT*Gv_@<-dlk_ ziVYlKJb=M4`%qWr-#Bqd^ywe(vkf^rJFoAntuLYPS7>+9K1b1m79Fig*^3E!!avCzi#i7*Ai&XQC>*yYvj9=-){qY_X>63?6W%{*#YO<$23TWz#Lli z?Vt-pc1~0+@*fRvCTuRVj!zKbIT5v`$a_+VNW>6YU`e6xr4b*6-wU4leLemAZh_M` zcSK*ajlC%g9ikfAJ#_uFw(OFwG>2K}&*e0Wsc-;|u#wHv+!<9v3y=ktw<%5b_}1P) z+jLcQT_?tQP`d$}6FMl6ncn&L=Ufrju!%o%P+Rp68({>rjtyB1dk8&w7jcK4Su1$r z#6G)1(2<+$tH01wsQ;vCZi&Lcdvee??v!WB;7rZPG-=9_GeB`u5mn(Zi@=aO_x+S= zSV>7qYj;<<^_TTFHyA#`*dMMDu#_XNRX}Gz|HGb&E5#c-j@5TlFYBLqg1Pg1Zz=Jb zj$YV;ywC*w!KtA-vU~iO9}K_{m5``V(QnppTZ5|(8%4O7TMTUoOCW z7>k+*%^9{=zRNxm=grD5DG?8sVVR?te%WoC*mB`!0yFYlKbhQZ{eQI#+;V^xu2oux z-3$^vEKcoU$4O~_I5l5%H94Im_k2{zMXD^;;E0c2jd_PeT6ZvZeJ885wbX*1%4~-o zqAemiFfAkHhHV>h%bEJ3ZO5Ju9)&nQ`L6vz;j3r_-`i5qlxo7U|7t95Zrs)QbYSG4 zSI@IbuKZmf4Lw0!@LmD=y1H<^Og>KUz~kObIi|H|Pd&M_iubjf5i^%ZJBag6Q_ZWS z`l6*IVAX{E-qiVZp~0~nP)cpFiR{zH&KCv=cy#RPu(v_MLFMV6^mZOhm>a!zra%5< z_GuYzR4d?tp=|xddh%Q3%}_hRuBjavC|O>vF2*E!AEIhB5pPfjNw!Mney#Iq*WH6Q z4znIvRlcuwanU#IPV};9T`Ko=!*4D`!t z@{LTGr}9Tasf~^FzxV>=%^HT2!3l*jHZJa_BOxRJs03p}%5qYw#b?C9c0DuFf^2_IUeN9Kl-2hqVyo~+#hN&Qt1tTu`yt7R6 z#LY9dg*Th%x930X2`R8~SWt&?PqH4{F3o1zJ!dB-Wry4q3Cf0#S9;`5rlpFAnWkP7 za_(^>0=B_e1SU6x4D0E|bImAUWRZLdmLM*LgZB#SCk9t(KAtkJ6x)|~v03Uu_?G@I z3kzXLIUjS63b*r1aD)N(yFGRR^DPJ?goKGz#mDzU&w+;pf;XGo-c>KQxHzK9Qra5{ zq!R`ip`jxY#_}}M+!2q56kX1s**B&NDsqXNJ%>#LN*~-@u=ycjknpr}egy?r-QS0o zbv`k?Z8QWEEw0k`fzQ>$k7yTton$%F-6TdIgl9#p^^?AI_IKxx-*>zuWGx_XBBsGA zi~S^hs8nRUa>vfmAK+zeUZ+CJo_oOrXiK3o4^efHJDJpozhtnl9L}7)m_+bgfI|_R z5n5^jQ7h6>ntr<(CV^y!AO zTwE9~>|x1vP3`#c7gZU%Ta;B2P1=vZG!>bXQ|9Tp{dRSlF~=;sc(L(97r%vm2OQCM z-*vhMHpD+(A2#zKN{i*$P64W>4lHA?T#(;xv4c7sesIT*gNq+7US{XDab(#&+XA$4 zkU(bNoREYv*9KERUTUoINC4Gy9@}GSj~3%g=XprL+Xs6&VL2!;@XROsF0mvdc^G*t z(0AN2`078keSh&Djy2W?t3!tO@G;O% z;4fdHSo~R7=yjbGH>V-tJc{+M95d?g-NwThLOR8HpXNTdhJj+W>5YY)|2;DgY#e#>;lqa~)PW9*EG%p?Pj0$xUSlsE zx4E<#dM$8?OvDbfUZUKXr5h6Q?ZUqQyWH;0x5XyLw$m`HbyYNHvn%(83Mu%>s+JyK zE?Utl32`Rxx_N%{^`nEalKPs5a~;F`@9+NP{7U!NnsVAVF^WmiJC&sU*DHFgvkBA3 zyrC6&cNd^Ys=`H!*THx-vE!&6au4H)g3vxp=Vv^3sCp`Ok#|Z^vXmG)Bi`i$NWd0bl*#XzqSHo?>(7#~b)015vFt??)8TP06zdTE{$z+x{D}J_5fO{S&W( zD+M<%UNC7#S>ClN=Hi3cq!H!kOh?bdbN3B*UB)D(@DRN*@LY;y44Dl z7~)A1#0PU`$YROB1n7?Boq zanvLFOxCsxkL|6prkYpE`D}*x%<+^z+$in5Z>#^Lj|mp?-t#P(0f?IR>Dy+N%Z`#K zw}qb{C{KJUFS>Q|LcYq8O9jU}XSC7@Z~R(-euUu(3x3sKvVs*07_l{!&$r`Aq4^P}baWgv8u^1OY(%F*Wh zd2ZmOZ5t>#I-n4;MSLpO4OB@;hALL;eEOd!;M<|a_R$CGl#_O3E4n4|j{SL5mW+dY z`)X^p6s=vHhrI+I(ATr^gM~;408)VWeW=(&fj3#5MlRI=Fgq_|43L$$UYPccRgJZQ zjRFK0uMq6w-4{rYvot|s?o{W>Sq^8lq9=uE8GwfQ%Z{5$?h5f#GyA!_vjc%wom{6H z2>bT9Ufn(x3NaSINhd3L{5vvmf^67}bRaj_17pWht-+hykQzjjBW{-K$NQsl%!GZLTlfCpGy8Kp@rI|dX9-gg{jb)6` za)i&!%_yo;bb?mS<5)XCXoQ>fItzjH>Gn@~&N0PB_gvR=bFzUWa&cK~SIo50o^aOGCQ&+t;c{p0mY>xvWZ zNBiyWHjYT`*~5$iqK7N_4fwo}2P)5^&3L`9ghT59K1TFl$2M~v!u<&tjd+6#4|9J0 zbL3i9yy!V;ntL4;^N*aQ;HMDkfB*6gCvmO=D)jFsBQ%6?>!H)A;J}udIJtuoN}@lp zIC%zUEM9Z0H&G|y-Nl711A(Ua8fkrZhJ^!fsRV#buyMShMAa9*nd18GBk?n41SmkF z%)u(fph_Bc$Uz9B3G*M>SYxvyA|mpo2g(?XM_Bso6k&C6Dxf;r8;y&jEP2%VxOhyy z#WsIbK91>iMy+oTuC7slGY6rKSUIkZ7i9f!#zbqA>fez?D!HNzu#Pc{8Bx++F&#P!rlFEuH}O4 zNmk6JCY~%B!pZg5hDeT;}V6O%bANGr& z3Y(qP#bpkHnSLr(Pl4niq{cc!P8jzx^*59SGa4_Y zqrJq`9=%wNitqr821@XQ`LWU^NAm)AI&#$LRp->7W@Vu-?FW|wAO1UxdW2nx#Q|mk zB;T*Ksd97uyq!w)`0GXPPB&V*47=#rL+D!fV@f1?lk@UR;&}hgETLwR%r5Cqvq=tI z5n8Ajxt#9R@1vTm--bCPZkiK*u~zMuOZY8F+R&b3bS1JS;{1x#89)T&1t3!~G>D{N z!sNZV3lU!uSAsK>=>6<`mRa@E3oh6u%yT({QS6}!52_RV+=gfD3(PsRk`$51L?}MM ziqZJro@oFe7P zq@``RrYA}CFNtTmbSmj02Lv+o&lUgn!$~H8d1+y?d))88s*BxQ-_AU`t+XXTzXz2V ztWoUwxp%{FB|M?_)Dx=-^eQ2moY&Sa%)2 zxAe&O7R>WlnxO%zskHXuJGLiCs|9#B?R1%4U4CjRJ#IO0;Ha+x)mcufhMt{79hpJSb@YU7g?oiVUouT6BoJWE9&=R#RL7B;ggm- z=?rF;JLK85mOq?*vlS7V;6_CNyRNW%0XrMbwxAWx?jzk8qWqHL$cWPPmhl{e_-r z%0bhycBd8`X0ljtFX)m!rrB^}a6Mm+G_9wTz~fJ+m6sQ%5%iG!?MP|N>@s0hgF>v$ z@1rqracSGeLU!n}u?59vqK)iBlxd;9xb|3F!sx-Z=zq4q(3Z&s@C2`Mq*$V71hB$> z{3O&+lnId=&h37Y&i}+?Do?+k;SP|zLYx%z6O~s(!cqA1^%_8SCT3=r-VNSdivTn&*u&?d zL9_-!Ch|4y%D3I^)Fq#0zC;}wP+cBb4u=*3YMg=i-bIlfv;}pg^6>{3-;Xb@Et#v7 zAP!y!UCyr~aWb#I&tT$!p1AZ{*2N!=X~s{yC(7w3$rsT$b!ud6;@Z-D>lh@sju zo1cn;l*UEsrAdKhwq@}1{_~xAVo66?TpC|i0q8U_;=eR9)dosR)|_V=<#2*_Z7HbS zXap#%nK2Fl_xIo3xgn1?eZj`|8n*~HSjG>`jvtRccr#L6?xufFhaB8I(1cXMTJr1q z7A6Iyuy6JvHff`-`8v{zG4Zz8mQ%Liim(s2ku0;-C=5} zj@aV5@1|e@NL>7;OBj+b7sg+YiLW?)R99Dr1({GgNgFP&zs4pWs73)X|Ca%A_4&F7 zewRd_ky#G@giL(Aa6P$7an}2h{|gcJHK)X)wUjS6%8TUno%$})-VaF^SySC8I$i*$ z(x=P~EDmR!oJgKBF`A%ZQO2Th;Sm2~HfBW#1SXpz{R}x9ae{P(49GQ|q#B8l4>T++ z;z%X{lmikBd8s`y(|+GR3{y=n&QGr}Vu1ip8X=tr2c2jV_91*0o=!I}O?{4QU%baw zG(CO5IHsc?G(M{5_0&WC(E+!WEd3H@sZzI}#O#@kWEJ>P$23JzND4=HpX0#!y3YzT zp~?>zJw8+J)Sdj^UAR%;?^o@Us>Wk1OMNU8#BytHDyB3XP|4!Y@g;G7M5RMP$B?q5 zTl!0P4hxzHa!`qVR@P!|Rv+KEH$8Nw(}(Q-E~FZ|fFlG<0>^D>6?zHnNdDsfSu%)| zqSY~#OSr3ZA7e^3&wJV?G!xM4&$rZ}asSzK{#VHFc`q;YMiQDISRE^&w4sN52R7_E z7bNGP-u8QF+Z}2P5_BoPc}3%0r%_A23lt{}{iCFA_Ft zrZP?!iN#Kd?=cQ}*|L~$=65?bFotma);MWtC}}4hN8G-FD`Ex|W@W$%1hQMQ)iZg8 zpPpqRK~sOJjIJ{hOl#5;GE)$yD?M0wI9U`u1trQy9NjjQOG+n`%v zHzL}!phVuc`kjLmoIfU-nhZ$n>Fr5>`5n1`FNkVa72h53HKG=yNV}DT|Bqz=+!NlS z25t$6xk9${zCRo--%K~EO3**Xhf4q{L@5)VmELbjv`3Iulmr%kNJ+>z3x3a4~%xMZAD?qrG=Hui~ z-)t+cU;@t<<$eo_g#A1W?=kt^T% zE7gYzjE=m4nM!<@$0a(uZ__>3E%WJv0t`VW_FTQyxk_<*6+q9vIaOM9-Nckv)$A<{i>pM04xM;@|15Y< zTraAu#m!-J2>xtm&u_WKeu1S;J&VKgHdxxK0;rqJbh-HCBnSEtv66`piM|&}f5rR; z<^5&ubSoP|HXmi23A!!UF z7>H=&Z~e8t0I0=-*Ct6h{L2KA#)dY6A>Q-yTH`p=Dai>=h8i+Y(IF-JPU%4QM7-zN z{oOGt6Qcoruong{R8vn)40#n!emmsP4Y^Ti%D`XwBk6;6)l(Z7>8xJF8iJYCH*+51 zi$`>GNaVvvRDJ4NS-4LuX++>us^PcqIA0-)gxv0!vg?fp6FA**zREE2|F01wpCbA-|!M?*TemrdQ9Z6M-amxAI30% zCV*f;_$^tQ$s+O*LViI{K>B+y%s`ySi5(aRx&Q>72qqz|Ma1+*cm?D0z&`}?N=-b^ zNOX0`b*vY`40r`7%C^u2Thmus8bu~&BGwuN^7YD>#Q3_n*eN^^4-J||U+ebRJ%v0@ z@asXlBSr{_OwPptE8& z?pq0+1b39QrT`ToaK7Efdp%4zjC1W3VmyO@8{MC4;A@Tc7v&YrD&fhQ{usX3V=hjQ z$q37FG_mM(@c+IrIU?X50@Rk&&HKSr>+mN)Fp+C@tzRtexxb*l!E6re3=mT>6|o(@ zKbim*2*ldKE`HPTXhd%Q7R!Zm4hR!HU^bxJ4%PAoIRLXYT1cow(6b87<{xXyUSlr* zmvZBSU9G9NApM7_b|Kw&BbEEMv|3+odeO4RY4jbeTh7{nyow>~0jhP!YO}JU@s_KS z(O2`=dF~G9Z63;5`dhj@JNKh^=|{{ACste(=ly-IFYRD*3jX(VznKej)196dNO^=# zUQZB=jS9T-6Yu;N-_7+dFZh@CUAR=oewEB}R8kQoEC0E@7!^zW z0iUaBD@iwYaY2BFHsT^yeMOleyQ1^Os4>!^_p`DNnFcnqgH2ED_dh<;O{Q{mae=M8 zCbX6*kt*B&=v@27b!OTI8;;Z}XhWvmQPCAYW91UPlGg6`$kUqNT34KhFI8(`s++@>bsdaCc= zz7bm+!c7X>C^3L<1@Jv5p+1@3;&X9vc?Spyz&{48U>*FGnD6lU&qZ6#Z*cPvQ1#;l z&lVdD?5e%hWkM0mJ$Xbp6uC=L%f^=<$C#N3>jZ36-RjCm^TGYTDe7$a2UHdUT5(5M zBbej!Gf-X$TvCZZW~B6Ll&H%^{~3TRL|fqEda!9KuBCoDO%`w%reYL!Jd>Ayz0~|m z$~LJn!BK2ke^r8@>zOl|ArXVQ|GiiSftCDZ*kRgG;ub~#ROhU2JqY`XVU?d$4*Eh# zG)bKa_nx^GnwEhPldMfYk<+f4>#Wh&jN_{)I!1 zy~|6p(D6LjX#X%=OBjhXsGXA$F1|FXGe=doEJI5!g(mZ*?=m=npsZQ^D(DXXW1OO? zu*YW1ejByeXjv?CWf8jcE4TnL+PZZ%1*qSsINXGNMwnnZCshcyr@CbI_@E$h{X$U9 z>LlX^zELKtmh5`Xry#xZN5WzLjok~hTPYA%bNJ#qg^=*ce_13C2C>%cVV^L60)WE` z2>W(WGgsR@gU}wNKo-9V8iX?KALhwo?rtGYNbd#-CH>>#kxLI;j7N)8lyr=5m}T!nmBb`5pyzB-lPc#W2mM)sOZ;9tD zy&N;H1AdCoP1a8z&=s9E1KIRTj`U|;y8C6SnVhMal|J2Bk7NHLH3p&1U&gQ+Fj0U1 z_`ThWonj}jb%AN`-weNHukL({|0r`jrIS3~Go|S-CBF0Es=o*^N&DSB zrPG0>yVf(Vt|KQB192_&d2$l4f5a!9tb#2vE#($h9`0L!x;XB5bFSuH4s!QAiWZ(Q zUchsQ1g8F2NwmRW^zG3vJ+j^pHKz%B&@dgw#Y0l*HV50NedWLCn`6)w4RpI?=PKs1lJDfF=m ztb;L8;~&DCg)P)t0hMq-l7O$l^78$rYgs{1CNT~;P7v3_^QM_z9xnxC&(IRqC^dy^ z_1To!K|7IG3C>38fMC77sf+R%{Z$8M;Zig+G|Fp72Xu+)nh3e6+ zX{=VQp#uQ@FiuZPWgQs-pzD8|uBxcGO?AXo0T~)!{-DV`-MUT&eCmRYXmSWq5E23? zhidlRZ?bp&{rwo>u!9hf3r8Dkynl9zDDy-xshcQr(E9e354_sbyyXI)*xQV(o*`S8 z$X2YgP1aNkU#LFTZ|lSpaXGf+tK5IBJN*v+*xIEdI>xh&{yA38@KuW3^E>u!wk$XE z3NrP#cljcv3_%V}pa){%tj7=!CoY(64w50gV&rAllc?!lD3OO25EF9ggT6XbzEBaW2j-FZG&kpq>GoCQo7=3($i)H`+c`h@c5jI0$)fz!t{quztJI%~Zpaz`{HRwO4RIZ4XyF~e=YM;fI*g$c&3k+0mc0en*W`pm1qoci^*RYiPdH<)OIy%;Wkdp}(DSo~)_m9ScBoK}KLSw5c@ zo!i}79v`u;2-DE6t=o!HB@?-iMq3RvPEue*i#oBTz;(3VNxIwi-nX>?)U-+aT4Rf4 zK3117xuT7*+Z7cN&TkX$-D|Xc12#?sp_%u!nEHxn9^%8?gcFPIxSFpZQHF&zxajUj zhg|G%s<5zvY8(19jD3wb>hi}t6G_7A#+{vB}oif8es3wX6L=L1xeII1&p z@pv7m)J&TJnqZFlshQ^;p_QV?K2UssZxl3e1Xhs+7B-|M(ZN}KZ5Mc6?olMs2$&zCI!wpdmI#Hid*bo|vTQk@?GhSOaBTW7g6wWh=i@kG#- z{eG_-{tpeDk$Rp>VB~{EhfAzV>as$AUC% zkw(o_Slg}9rk6MKq>&M z3o=izd|_(>nk805F7w6E8t}#)Ql6wGPX(5)L9em|{kP&;(;UH9EBG3NGTv6L)ySFr*r)kx} zPd7c|5YHPByDwnuy`%IFnEbKd|`>t4)_u)qWrVp zZt*(UD?sB0sW>D6B-FlIw#`$Jwb<(iHyFu3i)Srxya}@Q&mb6o5WF4f15-r486O|@ zj4rpS$PXtF_JWw)fpx% z|L$GlfgjJxmCjE4cSyqDXE)7+Y=->3RTD|pQe!%2mZvoKck_$#fP?VQ^6S8`5!8p0 zoVUZb1u2V*fu{{=Di52O?(b;qp$fuBb+u;5p4peyJbZyAUfmK)0LiczTZip2{BraF9otVa?t<)~= zaT47Xc4&hqyxk7OD@4^G)^yX&{qTKE0WaiKV`3$GXmF^2Gn37r<)25E6+OilDz_~j zxE3at8(;rRXztNq7BV;MG~5co--hxLAP|I~TQYYBC4zK{>jD*O|8BkjCou`p^9C?RbjAir;Sl)69jRtxj!Z2oqmKvHK zmOZ~%Q4Re7?rsE#4RHWuB6uk)RDgRhewB&SBV!Vt{+H(kxaA3k16Ydv;xX6)fYg+@ z=K?SX1cpVRFn)0_XnlL{fd!4y>?`i7fU!Sn{96(0N7r_b%@2NfWE+W#|JDM62+2$o zd#cA@j~@3XW^J4>%#4r@3V;=|prFla0qvmH?z|k|MY;);W%}x^1lF|OF7 zL~5`S8*;A^`QlYrSuc$MRMdIA??hv>gQtx)_2ELU=}w}pB9v$(KVi3rmmbw`XKEp| zf=!er`^MwRnK-$6hS*DPNUj z@&?TVi$oY8LsC!)d`1Oa%st)+`yh_QJ8fUIGhyS3Gw@~F$Lhsg9DtCv0uNEY$&LPO zD}(HxU#B6TYr5sO_aG_({=rMVkNV^WWJSulS%1>Fy&~%XQc04-x1!>a-W}@@;xA1z z9N}PNLxc44`nQuDvG2rW-5T`6 zM#*=6_v)W*`XUR==Jb)H7yMF3BOX$9J(IUvp$9viKC-J~c^6H@8ph7mPBD-w>hvci zCBY~8Y+c`Z*j+=51a=lo?4QYao@EPp-*vdmO52B2L2luXiXpA`xmUu z3gQaRB3=HCix6KEAQkIH+*2^8qQ_4}3Jfl6>d*NKgk_nuyg1?p(d^(EmkT^W<$ZMy zqK6+gerDUn1Qs3O(B!7HSeLoPA@>3fK^Mf1_4f7(`H9>o0FH?mXd<9QP+xUkr2tzR zlCQfssJbMK$UZ{5cguoY)9M#!01$1oe`G|s8(wU7_sk&2yzIa+yNH-us`;;9U)#S~ zh@ShdyS>U`*~g0)yVPD?pB`L*7#zO(#5)O#Y_wU6Ok@X#SfP8$K1XCcuonj)017>j z9W*x5$F(DdL3JUj8JrH%(x8NcVQ^Ctccu2(dmz*hPd@w}AgGY0^O?!sbAGCSe|-XP zS_A%EWCem?cpki?SfY|9=}murzsKSBZ+HWIf4@kIcJol;;WB z0q@)Tu!q+pUn{G~2WdGRY?lS=A2&YO0Mv)!u7Vz0IR>U89Ul15F!Fsbfa9W4!W|k! zQX*CS_E%1hRx9cwcUAHZh|bMso|yPQERhv*qNs-R!%?RbIlD0H-8+ZkjqeWEFR0YF zo;6CEi3>#^(5%q>c6ZRmBOBx#Kph{C*m`jUuGxqgHyEory@Ll=-{0)`Na>bOPOH7# zyUOSTThKUzcu71*xg%;XqZ7)8sf(aJSeL_rSaJ}|892|!k85eElUW>OxG}EHebrXo ze!YZ%2DtWC>IOKZ2v0{B0g(Ch4)=#$m>B*!Es~Z6of?%=ndfm4#A&gU*|KNm**P7* zc3rFdYONb_Am4$*|BoGxw)W#sd0cf{(cOYBfbI+J1@@G8OqgC50E*ts!_rp&{EJ9( zUS+$nY@8naA}e4Fvd!uxioVPFPw}R9gMe69Hz&r&?*nBOW|q(25u*RqxYodIc_-Z| z*9UeLHx-i|i$GO^uwt#?uHVh?#G((d-&vy;z(45Te3eDZ9Zpjz?M?&4l?Fv0FTxVR zlS%LZ-BaMQgfNSCoM>K5Zgv-*tFx&cE_oqp3fjh)i+ zvMO&EGuNayFY?6LecYj&fqH|er3NSAMXe{YJW-O|9HRaM@&kTSpKV36f&V zc2LQWZ!z`(bSB7vht=${xH&Ci!Tes)DG2B`izZn)3b3vjg(6bCdP zD4D3*c%o#qe@Voi+>)r;->htqw*@_~vO*aasT?`|k;aPAl{ z!~eqawi|V?olgHMezNY?Pc;XAbO_Lq;>**@jHB#uvi1F0G%ATXYe%Do!;EFhcdx>i z1LT#89x%!Qfw4z~U2)AUbBE5;KAEC{9bkvPGSB*a9ds zY--ujj~a&DA&ZUq#?kMZUzz5XGO(xilHf_9dVxpJ4xZUL{Xv}qZv$*-h`R|2mgOtc z%>1!ugE1w$)9ZH(xGuu@+w^%lCZ%h4mBJ|8UCM-j}LD!_Oa+xKGwF5%@EH_ z!XHOJjLwN`=-pE-qjR-ZRQF=v%HB+17uoUQ_K~dCO7A;R_V3@kdEbpvZO^|`e|;|U(F0`sQd`H98BWjU+z)iP-c1kxJzsB=`{A#C+l1P z#f=1)k*uscaxe8$_rc2b>a#ukk@+!HpiXGzlzVp&rwjI-5U2r>THcXzdk!+-H{hic zF(bxp610+*4eTpQ^pH?Y9aX?f#npECTY7uV%>i!0Lu6|h02~<=hqzpV zya`}MjaPUrgF@P1jji-YFB|>)cjfn=;j_`UKhn3X`{>qlE%4lc((2cvbuD{ra~4kw zcQZ97*G-r#VEg-)6RnRcv|PZnt8Y`~9k2f739bMdc95zot=Wi6c!5W%WawFm#+8&6 z9zxC_H(MEFz@k4iawc*m;%GbHTwx)~4&u76s~oHps>y7%Wp~v!KCBi~4$!G#(}BQqO4zK)EKG2JLO2x#Vjyw|VW@&+G*G*- z1O&4S%{y%8t>ogf;lv%D7kn|?0M{;rwDb0Om zJ@S-?boE}`vUXa?c8aS`uSm55elyfa8?B{|SU>kMsjCb{1) zy@lO!2$+RsNSXlO43p{9>*jqF(fyM8509i{j)OJ#0N@lf3Lw3M$_#N3EC0n`1}0*6 zcGtG0J*t|-$;6%rkp*>anxg|2dR5P7iUs4nQpS%C>SBWvZqU-K#ah#ND?|9=iUk|U zPyi#7L63^6*?)M~^ntkexD~4YPA1&`L@lGXxPLP>#r?yh=HT{>46!R*!IXy)dX#r< zJNaMw98P`~L+_;TdgP?a=t6F8^USG2x}xJQFIR?HhLSc1z0K?}17uAI1!Rn+)Q?VF z7eD}sP4etQ_UqT%&uZ8cHS3_cygA|ha;&wU@sDP z333^$Y!i%LZV-5%ID2+Ifiqv=Phze7;P8HqVtAQBEW?ZEhW;Ivjnqt7NW$wHEePjc zU?#W+h`+G%CInm<}|*++RKXrTu^Ua$9tJBBIQw1wvs#B!$>v-paf~*xRfOO zTEYGORgbish-W-|Xy2ISvQnb=!0Hu}a%b<77ne^ZN9Sirm)9FGyrPl8Uj%1q{N?}T zDhM(UK3RAyY+mb!bdEZ#3&`RfM%s7~Sfz-q+NAaMaJ=oSPP#dd%e$VZpBnw~V?%)c zFjks1o0(oqu0}FQ$B9>+AvZayalBmP<`O+|u&=Cb<^v!v@k z1GjE$eH2e1>uWQkk&N1(>1yZ4NVrY(re@@09<%+rh-^M7 zG&tCh68LDpVT2?wUJS3mBFbjCzS3TDFj+>JkmOz8MsL-q8DR>(+DDFI*^b%_7rII@ zelJSb*`3}y|DVfI7S&L|-MJPvi7H8lI@SReLA!&mhY^7~XxnQMwf>4^=eJ!a3cT^o zpw7X$0L|iRK09gE<1x5vI^5ylMpBk4t%+t+ffpKVC?2W#(|fsre}V}~vJc*3La62) zz{#U&yVqUKmE5dy`HdCTBXmGjx=o?l=Oj|N;6p_6#11?nOm8L@7J~a%i6sEm!Xv&B zLFf&*eYw$MBclmD-y@^y%6PSCyStfU_g-&vhY-$v?oVs!b$paWGhcz19vdA~k@NVL zt@c%K{ZNB#1U4OLuPfU#m9|?tyM0hDSLJ$zjW;^;u=i&{PeF#~mS1wkS76h+BfB*BJnC>T z_KZiZ#AN@texeuf#R}gGJ}aVr1EScB*w$(y$|BP5m$2{OG0T`cK3M5uMTCs2`qZ5q zVTA9|`2#g&-3|C=hNG+im&ePP`=y{egTa1RTzJ>6=5Mz=Une}s%P71pCLX7fCsiQV z{ng^j*by5O=W~Xu2cw95G>L{~&x+>b=wr@?`g%1)K0{}Q0#6v__zo(=Hj+?6M@E)7 znOBlU@$@`?ziWEdrDZ47@#45%#a>lWjndl6I^(KAb8Wl)gXX<-0kp;D{)VkH32FaS z31r@M0R7I??(9sqW+ewUF_=>!Nry~V1BMd{pY!Lb;<>ksCg;00S=F9iZT=K5J_PBD zYmFB6vD$)K)B@UVs1VZ`NL=lv*oo|9lW|4gDH7ZHD6u( zhe8vd_C$!}xqg!3j*x~bNiIyp;3O-M7pU1J2E+4{2VQ zX6EJJhGY|jPp8=WlrIlLW;b))#RrL$4YlE{kBg9I^Aistz~_{&cu`1JO_txxlZvfV zH#A+}b8cR>w2GPvofFz2=$S%y`LfIxZe!CUAo$_ve^hhzp=9k znT@l#vEIo&Db0gWy-5jEv5HElK9Qk9vMnJwV~WH=K?Ac0cEf~m4r4@EhSjJ?B``|A z$>_R7i4ohzwYz7X!*zkAU6fF%fyN0_>2o(CpK!Id9pHd2do(wc-*_n(gL5Unx*ZN$5{hofJKYZtXzo-ID=m-YF986AS+i@1YORsz;g zB`V#ydOtAvG^OGMtDs~eb(`MTQ|~#Rw`-XmJI1z3tKl@PX{_1rKfWvYkYhVQ6tNcxeH@0FRAQg~O`d0(`8?f!?R3&YllIGK zou##}7)|wAm&ekznxC1@e*sN3KESWpBCOb{Js>&-*##&GiBVK4)(rPI&-5f5@f%|~ zt&S^~T8<;^*f70O+A0L(vto;>R>o;Yq$8v}{O6axouBF{mXqz6%;=DKG$R09lN}0I z$crsI33-JnT7ZT5)D8;|+*>G70P=bpq$8gCmhn4wQ(1LwabJWsP|Gn@^!yT=Uom~% z=v4N-ZZqo!>Q>Pas)!(=;8Rfc%0q?9u+v0<1+pj5{Krzx7rh@9e~Zrru?YW@2nS3M zyB;7jhzCmrm&@mzDx`gUc|u#afC$87)PaMUZ3IVlEe}I_L!h$rRUL zqgG0*{X$sZ9*_0NlU+XCT1@T$7N5UgBzw+@wNC(5dQ{KU9sq zy6oc(j+X90QrTS|6=*clZi>7`DIlZ|_-&A#Yz~y^K3~s=51=7MzcWxp@AvT+F_I3; z?z1WM$kIp~gg_2A5y-dM=#M{=_nKk+!B-;^w!g7h@|W-C}qJ;+_ zDM{`C-UUjVd`q}`@a1qvIF;(4SrS^864AqdWgu%zq7^=(zC7&)|UvK?; zu0YV|7KlxvJOSZ_NeP3Ttn(xbIEU%6y)X16kGL%+w1%;-bZ@&WFp?(Ki9YE4+h+KBL8J* zvB|IgFzb%&G&1o72h*tm1OKmI~jN znXIoS3pLM7FQa~(rD|-<9q1!08vDUX?drE@>dJnQ?*27V8gyK`|A~x!w+>y4 zY8kah#gK$?1z;vGBr=g22(z6zMrHh)Brz!iCL&7#comm2>|9B~B=^y=Kxh z4`1uyXy(6X&jl|8QxDT?#uo0`AtpYaAicQ%3(PJ*9;Wx2*MwZcDaFMFh^4OOL9LTxU8H z%F^ic@>6wdtF&J-L>~S=X759!Zck*64ApKb!haMtR!H*v*R1mHdX0T9+*hrw1Dz6* z->(w8o=W>q-c1uO#45Mu#m5adU%ndRM^+azmtoFZQcCilMGuk&Wc-1lk_bg~wYq*g(SF-aSQ-9E6G;F_gIHaIL1!YL|VTd(Al#;19 zQHGfb#I)_Wck)i(oi{2!+z%4Pf~g+gb683+6*+KVdZ4(x z8&&zzR864}Ax@2aYk3>%(-_&eez@Wn8XAfSAo;}C*vN)(nsnZ`EW6Lh;+zLTI5e5m zeodAi1nqnk+2&nh1njPYNMD}5efSvMuNQY>ug^=McjRPd z-q}s%_4#+NrA^dM)l284B!YjgY3)ySmB6hO3<`yjFaXX-mc%eT?#J4ZBVZd)lMGP3DYqX`Lb#`; z;IPI25&&Wddj#^7Ptj?0HBD&^#dPOz=%)WThOoIBTSL=fM*Hk z;Dg>29H``PLFfaBYG`CvSThUN=}ou~IzN~iTjS@H_(o}v*oh&VKQbm%%4UNoZrB%CFtxsMXBs!npAUIINu z5OGX;q|$_l(IHO6JBO_ni&SHBrg#SN z{^CqDi$n0Q=dY9-T$E`dKtG*w_E8HSsL04jdD$)DG~VypQ+j4kOUmUu$EJPgXKpBP zAVh}JE_rZ+Fo50TUI^8*K5vq){GKq^n}#_y`0Vl9D4Ua&%854N8{QOWBEvnx;kMA_b$yfx7|cD+hHBt1M@FD0mNh)b#P?wYmIG-n?wr>@y& zy=mUPqLHbq0n zoT?RVJ;M8}>6WA@fHM_O)KAsi*#x2lKQf8w!HY^B??b`1Y=x#sa3Y*k=#AxJfI|@@ zC2n%uKsgD{g-SZYGb7+h9r?Dqk|!S(t7U}9J0I0abQ652tkTZCm;Z_Mh?aEe^g`;5 zvHEQbB6MFb*$SB;&(O z^Up_!$-cripxDurzZ zk@i72Ge}|R@_|d?fvz3Rs;?q=^!Ai z6MsSly@V&6po1&lZyd(DKxMdBeTGRtqZ`*RN!#Y?0~`Z)C)QrDdANfW-+g0LIR7*R zQHt7!q}KF_CSQsi4Bq$T(avu!W$HO^2@RcDGFb}~!KOh#J?FlOt-Fd@hTFh)&0el` zCES+VsGdIykk5!Z(XHdumK+kZX5Y6pa*R@f)&}T-z5Mjwx%1h2SfN#BjZ}y1vByC? z1-z4fr_DTYYrb~eaXK4o}g;R$NV^uhIhuA$ov6H|7 z1_ODQZ;&riyh5*ku>d6d&({ennX)4-o$A^POXW?UnRIQtse7iJsg7062Z>9v*&J}1 z(J^{1rzm9;5wmx1Je1}{=3j+nV*sHP1Q~}RK4FcP_f8X~(j!0Vu8hK1e zg)K7oc<}L~Km4s3g5RQ=pn7V42w>-s=owySLO+7jbLs>EiD*$=UHA){VXss2&%x)R zn;TkATq#v39sFe9xN2))9W{_97|`%PV2bLv8JDlKX?eww2!wFfkcK(@q_=LJNK_{R z7ZQL}IqlRm-XtT0jtPL%W-#IXyN_$O{b53E!*Am%E9O@i-*DXFwnf}0CVu?&nnP+j z)`%v$HPVIUbk# zPMj@eJH!qEO`-pPEwFLqb-Da>gQ}q9#Z`M<$|~wOmDWhUZ(4QO<~HSRqFN(Ir>&~# z(eR)bp&NHz5f57#T-;~3Z1@AO9R%VeaL3KFizPcdn=}VO^@7DE;UFnfurfvyCLEo_ zYBYEF?;PNV+jlC{oJc+>&LPrnOU$M?m9Yt9@4lR*^CnK)RU8iFzN<0nt{>+}yTI0h5T4_dgzy zeP<`aYH)HBlR{KA$@xI%7M!Rul*GvR~{tZip+9&24_xa zyF9*qXrLpVTX#k3@8hZMiTP^_sqXaqP6@p|RiNUS0_5d?@s}M;8so1tz8|N^-;?@# zJ>z|Z2#VkXVOM&68rmGr_ zhS;q2J-Tj7XxwCwHQ7dW8Uba(Y&9^YKu&_HOCad>*pTs|VPYkp8<+)*C`b{A6# zg4gh-4{!S`3rhNJXUJ~0K!V2uor5Zw7`4czQ43I+F!z9Gcob+$k^R4zhD4K9APJCl zQ%#-vg>~y~%%lL&@D+qx0zFQh0B#=lzNW|iIkMRSeD{3l6DTIMSyg)H8!}}foyh(^DC84L)t2J(H1Y&= z>Bt^N;1Q`~!(xnhITBv}2azD_p(w=eN2J=O9X#I=N`RpS``hajjkl$k=%5S4dgZ^L zTHlHH`>`2QrSN6!>cmrk+?<7wdqTbnM^s7YBT6*hO)M|ZRAAvI-8}?6!h;8OP|0nr z^PUCphF-nbCq-L9V2H)N^5NKnO!SxBX zF2VyY6}tZ-QsCW~EqcuR`&0KOfAijRuux)h>bQikeh3QN z&^`Wi|0TDBI6ojEK*a!Yppoqfzat`^Bz>?~3+2cyFc?7NupP&vYR>5$5E--z zLo!S^xPNh!|7eOJ1@8lpmz`{Zbb(b<%@^pj7@k8o?Jhq5i`gTi^pxE#gT2S{wm}g? zJa|x99NRQH?@l72OD;|F(mKD?`S7VrDJ`a=G;PzZ*%Ib0(*pza=5BE|2VJKnTt&L& z6e&c32krzL7e zNAaA+w%;5;eTkQka6JD8JcuKf9%EUge(HK^bMR^sJAl07L6bi%HwbQki=|(Eu&5yQ zF>wCKV=NNAWtxIurr@z*f`Vkvqsi+A8Qry!^$U-`H<2I;_$ADz`O;M_*KkKY7CVtwMYB0Eo#RMdY66x-aG{{mL`#q*^iP!r1Ckaqk~DfF&c3W5Lu{iP zOmutr(2xuGuQe}^Lq`Jwh&^0@&VxbgCKeUx+?uSinrg)Qgg+f#vyDvY)KbU?!%2!> z7Yv?AKFU6Gnp-`1Te;Qb7PW$4LALO0(}GX8*6_x`lPB#zqeUcx!1zdu265z7-qVWI4w$RLhm8eXG#_0tHAOI?A-_E zD~XZ*wYv9=J~{I<4Nj>hY2cU!an9_%o`Sgw^5ez9HHX(ys+AA^?6n}75D+**lL5qn zm{1pf6Nr*jlR^8AVHjjPiSNWZNc0|rJ=G2sL>i5x(ShjFZr= zYw9U)lmz6yuQ8pwTb=7hWaXBUL^zA?O%!BC9@b2G0h|GX2B>>*8llz$lYuWSZ~n4l z;9whU5_Uy6Z)>azh^J#bw&Xs>jc?ZuiD`wz`^e8fQ>r&v$1`p*Jy#p2r6=dZn{6t(7QDlBF3DE@?~XXr@HU#& zv-TbEQId3;6p$_agcRKi23?7fM2uMwDgYME8U!W>lylz(SI+c6%6H=;oS&ZwE>At!IsdwQ|W*D{N6&<->)d z9(?2w!$4AZ;V;AuNUr9(wHSpN3+E0?kH@8%gS}jPk20xPxdE2klhNh%74i4L_W=6< zI{G&gBMt@wGl^0GrdW)0Bhs+#z;+eN1OHvIRYY(=_$Q*_s`=z~I;92^^MJiD-TRD3 zN2kz`v%bl$2#CPZD|I#|VY$NvqZQS^dr_FeISc@lJiV~~5NUhJFciDyPcVuzzgM_h zfA@QY!GnhXX?f;E#CSihR1=|;OJbxbwBTs9K$bZCq~k?TwpcJu%$O%b2~RQ;4TeJucQTwvoA#^n zhf%JU+PEED=Yo=zz>8cT>{X*yf^~bx2zxj z{F=nA;~13$&=Umr%UBAm)OamdQG34)rX3ldIqntJz6?CE)?NSy?sGsjtbdMAnNcANAedos~FpX(IfYa)Co+Px!zp| zW)~t_M%k{4>X~Z^niai=XXxC6?FI#g3&oWKlS}Z-NmSW(yI!j>rkmC|<#IKl(4gwQ zbH+d5rgvcH+F@$>v4#tKNZmY4TuRX|^GV}^Ub?5Bs0xMoZmBc%S_76x%nJ4=4zi99 zrEh<|92yt{i3$uA9CQKoMWt_xBU5czMU!!`m^Y(gUXl;+iHhXgu8o%s*V1`(wV@#a zrE!>hK23IK5Y$};eiL&LssK;Ovq1)U2$wb>H-`*`M&>*2-DP|Jc8c|F8p7(rcg~yP z3covZ0TMo}LH~80FpDLkH+z%%se}EhX`Gf4G{Fv%LGiuKK6ww*qU|CNHUx)NyXS3W z{H;C53sU%8PZo5;=yQH{$lBf>(waz@u7#!J!e76pBOcmpbKk58TOg4hU^n62--NuK zFb$DtE)E`#{Ga0AcUkjANh3&8gsTL4iH2=Aw!rX&L!T*84N4%0&B&vDyBFvX?o8BW zqvfNeHUL{(ey9oEwe&x&8Rty2!Z|Oj+t6W^PmVKykdV>Cw{B_pG4SE>{C#7W(Wc30 z0&bbaa;Bo2kA!q1wc~k_B?BmQ;MNFIk^mN|Ch#M`WEk)Rtm1%&NU-G>IcI#JTzE0+ zs~v1$dVe4zu_4~Wb`PV3>7z>{!H>$8qc+zSzY?#{38U?vkg3U_JH5MayX%MSukJfh zVO+jWQKEEz%7d@E2pYxJj>Q=6McCNbhHH9U%<-OA7)JyLnZOf;wUe50qG~iDTsi61 z^_?95rO_lT?Vwp@czF#*R=m$dJvu8b>b0DozM$cqn9h!o^R5Fz0M>z6_~B&uLv!7Z zxSINUD{fObN3d#=_XP_aaDiP*82$koJ8&77UZKNUaumxH{voeZI!-)H@C{H%1wxMR zyQJ9v60>nu53w=&Dj+eh9;sP?X8Dx;Hd{hs3!L?wd%!Q_v{tTXB^zH4&PUJMYFTwRCu(=^#P%r zTSQN`@-j^b=R0bj0Z) zUx~8~0bdI7YE@8^%dIK;r>EjR9s5-}rZrGReLmylQh_QeW(U`V? ziJPxU?L;8~%@vv!hEL~wkeTHxDQb$Y#< zeZD5UVCm1UROUjSiC26(*Yvw)KAem^%kB`pwQ&!13{@qJf=L=mLE^a6aoSx5=I0d~19#G)us51aS;+aIAH#rVb*?01XhtWHYdxd{#4n{O}d zL9Pr*0y%h4#6~e*O&+1ZM;T1=n-|jpcRs$25|dd5t#Rp>5nSv?WW2I3N(-skp517f zzRzM(K-k#nc#3_(@ia}a=#0<<5HqQSTu}RZ<&$fqs9%)I(Yf^8o8Q4sz<#>)n z2k0*TOHnk#_>g=F018KNJv&7{FB$eQ7?QD75}m8psWcjcK7&M04(lv9WByYl0jX}j zc34Kn4{IrwEXdpyt)womb4ot?pyYcSJ;FHg@o9*ESLul#gPzD+2Hfnru` z-1@Nj=&lXLxEHZ`0a>mZ9OS`&8cI(RH_|?z4O#kS%F%&wPoajYQ#3WF1j=J#V(=sKQu*t>7iMP-KF;SCWhNO3zyu}#|M1-W(;35*P=*E}5@u03%t?X=*e`OyWwu#+5J9Z#Ir30Fg?S*w3eG73?4|8?y;Kx*=6pVRSUUeRW9`}YT6 zsO{?NBJuEAT3Q0TcM~B7vrsGVBlskdKX#9f=kk%Hz_{VI(RP_<<8G^Y>z}W`hEQTR@3>VJbdJE)`nQ9ghxhDxpfmVOW4FiU z0TZ1!U$g7y)12wMZ(L-xDqa&Npn2rvRSBug4u~$%eO2?-MP>WdfQ$9A!215nIRMAI zM@A+gA%Pm-62?H?3ipENpAT$HKlwO18ndvXgM$Rn2^$~WZ1_ZZxA@GdDJuHPm3^_b zLu&hE!?A-}KW6&I#%|AX<}kd_vACkQIjn=5GSQLE_U62RHy(LMlgn)S6l+mGHZL9j zygW(&HD-*=7I&JPn<4wJqd&TrD(mc(IqH`1`}VSofP9QLY3k5RdGEWJ|Ce8Jd;Yu6 zhl1aeRCk8k>(@blR5x%2U_I3x7)6)faf0BSNk5s553gg0s3wN!iwjliKL6AN_ z8^%G0HUz;XR#qBkvI4`xK!ce_L0x?#J`LU9^hUY9^S^Pmr>Ca_qS+m|-d?6gBlvsb zT{W8O?vHQgb6j{ScXbjHo<8LjdjoS6Jc*!#;?vUTz!~V4xrhK+skP5-EU<2SvN%$4 z@@kp2p6Hb$XO~Z@s-Bcw6X?8m&z_T1m+AHOPxesyLDV^7aHrL+&Gko-vsy5 zW!dbk>G>-y#kK3-}3kkzZkS?m^FKfsJc0-w59gE*-Bvd|ib8>{aL1g9Hs*o2kf zGJYH_o02L~V0}D?OXGMJ)q`7m2s znkmvj&@b0;y;olrTEmi(k~G*51xmEifY02FWIlfztPMZOdu-Za2I*UF-M_lclY?&h z*O|j}g?vEP?a?QBXAPaLIoSqnl<2WYME3^)-^E4& zjlv;K$9g61?OOs|sTmG$3ALu9<)M?s6^|iNfg?i zM^Q5|5fkkP4u;ne2vAexs6z8dDE@F_0`FJDzYYup-c!4x-;WZ?tuLQP6i4<&Q4gqk zzVU$^K|CPrZmTGkZ>Z!0%rp9utovgHm1VWr^0h_O3?G#=rfZV~(3zVa-@16N<-3*K zbwjmO%_pRzo)BQUf4(3+UD>>%CsS`{R(<Xlb=GC#=>30CvV0}+|~tfQlYL@bw=Z^4XA#wjdd z5EVfG{`C6b@^U$gM_g8{jrAZm&@p-~MHE-wIW>M+6l9L1pN>y#!t}|vCuh?h zs7kV|!-e9Kh9z{2?;om*sLtYL(a-?Sxv zl=8@;+niDV)Ax%Hhh|cx*DoCy{IWiRx?8+G+yGGkkXQlSu1h`@L+Y6%WZ)kGDARfA z;W)kQX5Pyje#LZdhqQF0Z=8o@@y0i34?!gZv4VuOZa)jX@6*=Pa@-LP`O+L&IpYlB zrsZwDk_1sj!^Jp&d1X>SjTR{ zaw*`=L~9F0Oi=K??>Ol69p41hu;##(TBmUl7_+uE|2Yj{5ViVTe%m=8k{_{~kJ-O* zb*)u?d|sYLzy)^x`)bcw6Fe*FMeYk$B_Vp5fNT0pP|>uhLnrm8$JtZ<+q>ik-G7Ex#AgYeY+!dU_i7 z4`3idSL6FaThhUdXJD?s3U^Fuc6PYz8vwC%=0PTh5Y7U}G2cFrD%GQoFR{F2Nl1I7U|KGjijq@HA%yd3DC=txnbbL4@%$X{x0?AXvj)NeS zrV+m99`GPECN#&XtBR*aWh>S*!uFvFv0%_A8P_dBrB zALB0)zZRP70@4}ocLf+@i#^!c*=N4wcMKt?^`n}R5o#$SbY8jh2_?E`%E?fe?{Ej! zJp*Rd)!j`RC}cgCWdi~NUiJ5rbNs;f@=XX&BpFHiuE-2-?Xjb`CiUidh=qE=_@%EdMYNNi!w27mr{WKE3(M!-K%nY zO{vlN__(@(K_E0TQ0fxy5lIXr?gSR#+Qqc+ZL8Cz-rU#LwtCeLeIEb*mS5tpn=oz^ z6lew5n^-^ly>x5SFlc`#9?a6#ryd0!L*AA9uH|p1!Tvc*RAn|hv zaB^x&U0kc=LQXKzM?C7g!OFZz~%ftOJ>1V-lQ@c64t%$Y7d!JaS&j85)|dBj1A+-cic^( zYJA!h?X-nvS6wyTL%S4Yrfj;3!2(w)*vMdlFF$Hl|5avAPFmCHq3K$3;gwIzNDAU<{wrk3HGk9HE)(|UfNG1eWK^rlPn=0? zS8vE}X>YO78P|}$8n-wuHg#&2+rmv~@Qc&XjO#P2Hm>Id1xhPN!v_=i?r&!h z-kZqpyi)j@O}?#;JR^h`=QPHRjUfw&kV}QIP3!CqOe&C>vh2O7|02}q=7w$T7x_KK z+hJHZc?PObz8gp1iirp`S!%3Wze}K}H?ifxBQY8d*#n%Z41t1_rM=dDs{*Pp#^N&t zRAFA@w~pLIw`&B%#7K4)E@KjyG-#t5Y5n?aR_vt2>|ePJSJwUVIJA);2QZR#067oU z!|o}84~0D4NlCQpZ|^+w1Fwyq*IE%eYg=*s5IlI~1H}3ZKy>`cE9%74sVw(=v)%!v z&&4mkM_StdeYpQN+*59GW!Bw-O3|j5O>4xVXG^M&Gvk0jSc56LU;$akK`O!QiDiLX z_qmEp%x0#hHVW;x%kSg$oAaQJ?v}_{@cZQ^IGIp>|59Y2R92dw*^|OTHdrf3iVp!8 zfhs{xgfrSQzolRrO$XXp`n2d0K{8U*VEX764WAz+-ot)2Nxbow{O5cnGJlkUm&cns zJL@`Wfx>(ta#uYY;CESD1W7Y*0F(3>c?f7AACGX|=yk9*G- zKYh9iUgJ_~X&D*w;rig-ymNsz$7SO>1DeJaME0C+nUC@Q-#z(BLV|(|QM;G2y$VRs zzXshH6cX!DlP0>S^ir4h#VhRTGj`8KCC0?KLsc^M&6w$F`=RD$QAS1VgXAv=Fs}dn zS%CPe;&rlYtT*#XAAL4mtSMBZEO9y0ADgUimO&B5{WHq0P!TN;vA?Ji$|-j|ZF^8O z)wiI;)l>6cdVm^bN4kIyrXKR0K8T1IhN~G$DZFy7EAwwbyd{Mx*gp~#6-~g9 z3U2H_D=$nkpxSVp1mC%)zboqNHas>z+acilzJMs8*sx=Y#d?EV>Lqel@7&uYy!k9{ zQk+4^Cf|S`7&8`0xPu_4(C&lahvUUFmM=^WZTj)+RrK}UJ?DbQ+KyqZp@`hTdwy%{ zF6DqESS%sCCB;D;jLp0+{ew3#Ukzlm)3Nvv`Aq(5W|QT8T`ql7U9TwC>8M)@Duxi5 zM@L6L!;6O_h%|HCklEkkBPJ1F}d)aM!<{l9WRGd zif4bX;K?vgve38<@4rN zor#_(+*Y&}J2an#|8`p?0I|#olCQN4AY^|w}NopMD z86L5|^Tq7O^H4dyvXUFq<9()DH$MsY%%rBL`50HR4>umq2D}1KEjGWLakr~i_d>QaI`&KM?1b%T07cl49Mw&TSJB~$8YYqy9237W-Z|&8s-5K zt*WI7I$m|DR44hL;j0=?5;q)eGv~_~-@J0I4b+^MjyG_Q-FdCPhYf9i|igp)MzV}LfG|kxGgRx=m75PdEOcc&O(FQ@8+>KV20xmGatJfECl%}?J0(g3iVK}$?Z>94*anti_v$7jLq%^LKNlRQ??mHD~!M{IqRhY^hkY%T<)$>(!1E z3!7_raD&6X#di&=B8^+N&d&3EbwcS2`&wir3?9(><(wDQs-b?pQ z-V_R;GR!w?p7jU1BDDcBkZ-mHR_Qp7xE|qwMk2cg&m)N>Y&6Xk)0~OFp~=~f4s{O3 zv6YZ)T?E}b&EosDp=Y?XEqx~L(e3weoS_DAcM{;E{yNIBMX3qJ$J<3_O{q)%X z*z@T4FXh*wrvF{`AlY}ZZM6zfzx%qmVLe@|dvL?B8)xRD{w{ro^mE?kps9s5u6}+YI5addGgDQPx5)OrA9#`6OHBDQu@)=gT=+LcWJ?6 z-KEXxR6Xfsn!z)Kw!7^p1Fr}@U=XrupaG3V6H~tA0Cp)moo>GN z4*h?*l+|0W_IpFkb%tIBkg;RzS2?>|9kNx z*uqWUB%kVxwgKWHT&UV_+F0v*C{Rfor<{3<02@X%$iT5)Jm}hIq7i6a;+g1kZ*%pf zBNy@_i_C;0R-3mqL6nQV*3{fw!F8faY+)`L86dMXDRFXn7zE&`ARMLUb`NH>;h3*RrKl&z&ns(yqLI027I& z7Q0L8c8@Uz+AuQ?Qg`yt=!!1*3wCz(Sm!-|vnwT*9D7bFr%zp1$N3{eJ6UY^?jzT) zAEcZVk*N<(%@&gu(qz@}J$U8fl0a+Fxq6|{o77CQLaTLu-SoLzeeXys*g!`Q;av6J zn)}sBuN2idRL`g+>i-UDsFuikKt1I)voQHvjW;bUXOesT`=Z!~#>ssn32K^}w~%>_ zbdi&Kks=0#5Hy)0G5g+0-j1Q;3>z<{>j}3Q8TVux5s4mB(v)&XF{t!0PRE)av74WB z%UXg>2qGE{cHy%%wYQfU!|@c{W04wjJc&FaTyQ0r%MCgW08uUbm6&`_A(*)BUT+ zfc#7ddoGbJOwAkrEO1S{!2WE^`~sCLtLfdGmeFc#Wk4RzCB#1)6wLz&CfNm@K%H`< zf1rap#WF=Xf=1+oZ^E6ACw6SyeS+pu@NoQEizgn~w|QxB?!l^yUj!H(GroDZ0~Z4h z&-Y%?(x8nKd~o5@?*4NbIc8_xaXwt|QY)M1!ggpML^4~-P08IJI=!2^TcWZs=5?^dlX(<=Z z)PUPK{}3UBlZ&yguFh?%0*6?`yGPE>&X;wAW)?pRfY4sEev4O=WwqbB&9zd~#}D1` z^yJ1w1PgOZnK4^S-=UA?MHr0`Qv&5WAGa9FoDSB@YJ^`g%Q{*1VLj4yLGAb26taG2 zup8;>pb0;E*{)=i&8GL+k>-_WPuVOUYhB8M1xxVZef#z8o6B7=AI9vxTo0+zh`Utw zba2(ai@=OuD7@I_uJ2v6G{f35FS60=tp!~L*E^ZzF~|dXan<|o?%>z-~7Y#CL+K2yA7 zLiBM=3?i@5m*fMX&hOw{e^t*V+dz5gPd?ek^zx+}^vsn+=)&5p)Nv}~igDYZpTKEd z_2<1u;o={;7qBQ1>74|)Q{30NTxr^B0kMb&P!c99)A{p2uog4pAZjKQA{tr|Eemxe z41zFjT6P*U9%A=c-Oc3x(Dd`m$5#jZ4}2>=T6zxS`lB}q-xc0^jk{xhc$Y{^O@&hx zbe+nn12_}FGAY?x$kI_JRBNX80lPpp2IaiL1!L6zpVYHZ&ZetNlP+k-;`>(;G{=&u{|7`FusiQX3WT%>PA(YSrTP?SEVKP1BH^>ML& z?>}EC@HL7T#+>EQzcYuY(J>OcaGBs!|9WIFXTAc-apuOaH;ao6heZ*#X;^C!deDzp zAFyAcE^@=4@xRM)M4zhd@q!p0{?*Pj?hGB=_IGBR*rhE%VPN1?_7SJ@5~+{WV-Pm5 zLnc3!d?rIxpHhet-Itj%kYibeIBZZa{`8sDhil7>rK=G+vNjY4W1j><9ml%;EH2)? zckf=gs&Ti%D*0Q{*4=6!GO9}GHZT9KKDJIvExm_R#^$g|o|2i*n$S16bz!n6U1k^$ za89nKkn6(p@4}63aEVa72;lbOS9gv*Y*QS5-q4u`aF`$NjWF@#6s!1g|GBzF6blH{ zBwY^O0nqHw{X_RtBmSzl_YTrLnkq%*ZdgTF@Qy!8m&2Wb=6lP+cY?t>0<&ED6>)d~=v+{H$9#OmnfbRf-tLf>v)itf1JebRD z?YI^z=4^u~D|R_x$Pe#t_Bbf=;rT2JE35zL9S1$ccx4`W{)#V5iQvxAWW9@~x;uro z0&vt%Z*It;%d*7JbsQrcz>Ye_l?;;=dQ@+cQq}vrR7D5#D>UfB6&8a?xF80Pj@g!R z!*ykNoSCL}88x!}e!dpR61GLTjyT|67G7}kH6ueCQ0No#6?JYF|>1x zg9*m`;&<5#5^{H^+i#1kJ>}!Co}y=-E5C%e*0f{;HwJ;Lj0*-IErRFZpW?OiXWIiF zGEn#8#od6;`RqR+mw+u0vB`<}H4Ix}Bb7V@8U)gsU7HTo@n^}xUiGUOzzcAIYQUH{ z-|(&0;TI*h#qOAwLCU3vFPg=F9=6mL80mT}a&=k$4{vo{ws=g$_qv6YXQO`^EY^3^ zCOm&miq1%DH>!%JL#~$7;B&B-Ukdex-C0rT*bzf!!M8>vx^TO>~`FjzNl5VEr*>3T&zn@`Y>^2sK?;4qO)_s+FB@+uq;u$9>iY6DDt)FV+dAq zd_PKz!%JqJ>hU}y9KD}>IUArUyTy0l_p>v(kdge3T-YhnfYB}QlX)avAloKzz^YxO z7!_}hQfJ-Fa07S^UVkVZxG|c*H3SI67w&pA;c6ZO*i1z8hz0=$;g27nPYv`CA$o{N z08Pq)xT9&I!WP4`B;qtQs-daLYer%GijM@e1$!!%I?^Q77i9zG{$Ewk?mE{NW>RU~0;rH#dHraUF{~Kb<6YosN{fj#j{!dk999 zA)*dvN8u};>Z?`H1?*>xur33Jnia3Aix@F!CLqqxCWIph@dGuKtdYA z0K?|d(NPWIZgEDEUt#gf#-(JP0jTsh6RYeCAL~JC47din8UqqiN=x_CX~rRO8$vJR zt@5Q2C9jZIxpe)Lxnhykr;$W`hLY(F?_bva+v>LY^1T=j@ia3#QVF%)36MC148)zI zm7)E3(=fV=U%h)5@ISO5pxO)8)(U!hoG{ByS2Kvgr$91VA_+ z67{Wj|8o7dsK>|0H-6{W+(8`{V3~v_cWY?lXu-nQ+^pP0v5LHfzsr7z_RGk~(;sxP zxF%*v$Rp7l+Sm{c)#75Y?h2@|+2;Gma&VBwl!;#;Q@?;7&1#Ur3qXB^NxMFHGbwud z`U(PuoA~NVLY^znMh#ZHVD9hTijxdz;)RPBsh-)4p40w4JuMEZ4-(KXhf5D#=fJMfv~3~R}6>98DvM2yAjoJ6cDKTu9J9UbB?!uN^-66G<0LhZRF z57$q=?9-hpVvo;i2UwuHha9%KXqA&&8VS=aux176iBr_Q9I09-jvlQ!SEn5Q@L@c% z!{m0V2AO|o&ERY%ET_k)kjcx5H!0`BlKCqoEPe%BdR^M{!ZtgKqY55NQey*eMe*0d z_u3w9=y!2|y<&9b%b(xh8;V}6*y`=kW7JNczDV${N6`rU)8f&V-m_BNp%8@;Sc=WO;Cv>RrLm!*@XKy|bxXdDt=&@!$c;TW9OVoPael_c=({i!`kCzO$2k zuRmneuj@RQrC-;zC@meR#~Nl`eioWCf@UMSIMbkLJvL}r*J&O+tymGC7G)qN7oh&W znFBb44LuKB{dUa4VkZgxDlDp42mK!$ll9=eUUhZd!s~ytbJx|CU-RU-ri*E_A{-zv z6(UPnSQxJIronUT`nBLKNsapqfq9_)W@uPiTU7^o7sXx(3|-6`rB+s#-~QF54Dsqz zBUd+=g?7!(&dLRDZ<&1dO!VXJ%>kGL%0*XK<-Ibk7Z54H1@OuKj{PTOuO2_eqTzeM zgv@W@`?Y>ZdRy>m#>+<(;WvQef~jtQ6jW?>TM1wVFsKAmJVHVqFM8w%|*&} zc3X1}$$vTw1uNcgEL}SupN9IT3bcK8_Ihkt`%9mIH_QY z$B9A8C!p$mdmdLAC>OAnGRaW7L0mur$lToa!TpiM$7fXigP9~bj7FHWzgKgtsrr4q z;tU)R$Z(^X0z9zT^xO_3swd2| z2n;m2_-D}F#CdhVZ5OCXQrh<~aXjYy#Ntfo4H5@UzVyqVb-Oc-D9oQ*d7Mk{=za{H z(ZV+%d;QlQe1w3@@IIpglxKskn_Ee-AEKb}aR>?uJ`Gy+LAI6WThIWm6kz4im6tBfnyL-d1y)4nh_`iqylLQdMHC+ScFIni5BdojQixZhJ zLz0jqYiMr;lP-Q>hM@y^wG0yEs15d0PhV9RvJGYP7U5a>37lm zibhCtm>>-bZ8oxVA6SovCK2>rvl`%@gNi9BDTJ+sngdY6lho9;nCmfpz#>Ne97M%= z{+Wv#9(;Kdj})P|Uskt%Q79@9v{^i$&xc(1;)V+)2}0otaoVEcuYo<_Cc}b=EmC($ z_f7#bCV`l7Q#n%EW(9t_)JP zRo!(%^IcCj1)L8!V>KQpw*G;|`PUb*1wtPH>!b3=$@!_biTEvviW>ha4kL`+7)X&X z$iE{mHTC3u1IiP?7q$BEq^Mfcw;)g!SP6kk7qM7AeKS!$)wHi24yN2(lKo2xOn`Ku z_!3}OlG;lA(c9Cto6dlZi_KRO7JlhA;+G_u z&SVM3@XG3-gF^y$C(Iq@b7O57BZejgZv{?Zs;sL!G1XLCdrP=oZUIj)kqhD9M@SV3 zu;0ACz@--hnqQJ(Qqn~TR*RN>aC48(`Im*BQ^Fm_cOXh4wtTg3V2)tBzyQaBj)sO0IFt{2 zWMu|zJ}4JhZ8lnduA8B)Ft2BV&ry7RFr|G#e-nIss9Q!_Jb5xx(%13pm%C682I zmEFQTo=PvTzpvya5fax5{9HZer1X|A?i8EyjYNMstb_<>#BqiMYWxMi2N(0@tlLdq^Au*jtB>(W?C9SHWm68y|rxV+;6zp##Ox1c-&O;kdxp%Yw{= zz>a96Aa%e6$LjzAU6UJVvfB;AX!ohYR6$-zOiqX`A}pcnL{bE%x0#_pF>~D5%G;@y ze5)A7?ey<3#C?J|W*NnzM1&8jAdu{?S3SqC;<6VS5Bh#or>Ha+3q{1%sfOQW&qg}o zgCliF9#aM%@4t()w#k|7Q)~Eux(Be@hCS$d(1|HBq#%JxiMlWE9K-r8vR*lubruob z>DGP;=)K1wlYtK8-Ut^{)Xtv0Njl}B^2Nsk^adF}iwqN|+|VF{4GH3Uy@8#`0>lMW z54*YOx88?~t>+KjJac{W?Cf=6Hi*XZi>*KZw4AXbhjs%2G@O&c?_)02xV7h@vOnuR z?|wsjEb?f9D;C2X2%7-!5$`Zbdzp|sfxAVRu9$&a+IWkF6U3!}pkOuDE72tBNM|Ip zft}d3RVtD$62U9if4kr&H2?ap;QwpyyW_F$|9>yqMoPA#L6S{qh*Ff1N@Zp5C>c>C zB$AaPD=SIaBO@exB(t)*$(Bl)i6YMPt?&IizjOXN=kIgAkNfexdvqJu^|{{T^?I%s z8MQ<5-bo1qQsu>pO3?|#R|3s- zIEd>8_4;;SdVxYqOK)Uopz?iCHj8aVxo5|K*nT!TWtpy946;bAwL)GR+wzsSfB%%z zG?oF%by5XmyQrwpx{c4eoz(eecN5Zb1z=)4d|8k7q+98J0|N(06$$a{aINAf1$h%$ z4bx4Uo2uKR)ccnPp=D>~fi8DcBQGsGUk62nb>t9LxyTDkw3`Pg(0fQo7PHJ0nn zdOvVcLoEPWh-gA_?_h*fKgq+5ZHu1tyTUxJ@0yV4e=W^b%~DCQSPj}@e6&*^%05`F zv0_^(tzZ?`FTLVBb-Qi=7CuYGwZc$Rwoj0WDE0FnP_ky3)rUE7rTx_*8ADVjH~*n| zV!m`~p5j5~N1+aCzW&`uZow!`{}sp5pVFmSTD6}S<22UtiLl%@JD7G&1_|Bd(*MGW zVT3{%DMa^Q+!ygBHaRVgX#_TfDHZWPRRzvL@QajQvVE=4qvy2xM5U6ZV zKaWo6NtrFqo|4A`%yke%K!^r|G*-7F598rI90!lBeER;v-^of)7ZLMB@HSkqZ_&@9 zj>6FlAI@q-54~&oC`yOCCO|1~@`jwBDYSieLLX9JfPr!N$O(_3;KjFdXVv%Q{63Ro zG{Wz2Ep_Hz2q|XB5#Z$DsFK9gaCDC8p{mN{IE4EU5q#zKI#8Fyy; zZVrM@5Vb5c(L9+g=|$>mHuP}Q9qu!(y{>x0t-$i#?-%Ok()AId0+9;0e_ic1#4Mcw z%!-RL<%OCZaSBJg1VT*MZq*BF>uhEBIeryRrRMsHjO66QfY+%RyKB%(o&Nj10d-jw0!QFxSMi>9oQpZa46K?Y3aMeZ`^h)Z~?!oNGZ2(~2VW zLDncO2zAzxI-{mpbc`)y!rX87-$i_uLQ0itat7g8!j4`5243SYQgVlk=B9)R*`TqX zTbwOrpb&KYE-~DasgKKL%=Ou^YSN%C0E-Ns?Z#A2)Jb;rLNACim<$?5oWsPIlkOa4 z&u1(@7CAcCmOn1@mMcBm>b=sVDo*t7-B(WhY#XqdlbW9MKF>P4UhP#cpO?nK@wBNKq@ z6$Nu&(}8i)RZ@m#o(!+7x2rK0%YJRkxT>i}Ia0Nry){jG&7q^hd|PWco-gVkpN7t) z@ap%5hJb>E)(-o(YqrKvIvpMeXMvJXNndqqpk$mBK5N`Q&<=uC8hX zOE(n4QTM>n8j9}9FcAK&+GO-mlEq0D6di$22`YjeE%)q>c!(xu4zH!wSIN8Q7@rtK zJFl$4C0Xc`IZXYgBU$4#9Q%;!yf)_vugCRLfyN(P8rkBp{5sZFRtQ_7w+WxVzqDIOrBN3FsHj(Xh+o%s?;hMURrWpHx zN&pOwuw7Joese3fZrh3<4$={u!M-Q5fWT^i5K)T^wLM2|dKg$XPKV)=c>iL2RuH1W z{~vs*`R;D$1a}h|F)ojOa_3`=Id9eBJlAA9TPCM&(IlhQR%y>@6!Ki|o}JxIL7lXK zdFQmG93jcHoN#RKGTMc{qNoptOy8^V8zN&b$7T7|^w!e$P3Q10o>STBo)2dC!st^Q zuWNF4Hs%d{adpz~sgvH~@IFgvYHAud!u@*#RHX2Dkq`>1jH!NKI)z67F$a3? znPJKu0pAP>HLfcO$<;dh*zF%H?F@M*>nog65xk}BdG)>OJ5LMX1 zH5tvF#M{?s30vtI7zDMqYd{JEpo4gh#KX^@KR*cqOL-(P6!nC7yZq=zz9Tg`Vl07r zR5ZOlyfOv5-WykyqzaByL~!erBZVW^F-IpIy0j1t3yjOCO+NFiN*=?D@50TLUWO_j z+FNfwKN`Com_aeL?o7>fn4nZT?gDHe#W;!(GF=xB37BP-pB4&5vm4;H6h5C9WFg9n z<*0&k_@~n15JWsHlx~-1@%p)ZgPPd^X&dU#Z!qs$-N7DF9%#ox=@|xBvTJ^|-3u*? zSsJ+MG2R{nxCMe8>#v^<{PK9l$5=OSENRyBw+no}XeY8Q9nP3w4!~xjnplnl+6qs<)u`zX%iBN4# zjVOR4EKUGgDku9Y97s8BbNWU;r0o!XKZHjWby4>MHuV5av#B^~7~nEb;?1kN1bZ!`(^ie&(fu_tEJbQnw`FN-UKERH+z}<3B%V) z;s>uyRj$} zB&KmMJ#Cj_B)gL4_K)FZQ$yP4&aJ8x331R0}4<4X;dn0^(Lo$Hc-hVp|RGMynPAgK%nZ zuWD=J4T!mDX?X}}nuyyZEx{JH-_uGB@E^tzglCQq4cx*lM1JsR9&X(_4tg4G>~U9f zob6<)W7^{F+!`?vvp&Lt}jZlR3?D06A-lH|hSAYmoSqs_#dIE)=IkQ7a z9|C+(AO+Cef^OK|T#bqDG7=&sbK8Mkg<3QkD77#@e|N35BSl#2oLBPL5UuZtk}%JJ z7V3SVg~;JXz;J>&_hCBSzH!Ac?ER9Ol8vf;)TE)BrqFs&up>cx&1Aj#={W{_=Iew9?B1 z@m|%w{5uOLprqUpQU|<;1NY?iy|D5zg_=d{Lz^Cs$)c=um1Q`|$fp8|D<1X&?1-+! z-v!J9Fg7t%dK(W>SDVp`POpdxS#|LJ}2KX1)5f zgx8ER4eMjz4u{|So2EW1CHSxks~~50j%b#V;ER9+TJ1cxENj}2m(sSpuIMSVx2}ZG z0-l-E#ymHN7n3eu@9N^=oS5k_fa9{_Q)3kPwkLtin{2R~fx+^a*JIo)a#u!Y$c+qX zkJTR`LBZqf+pxhlrU3FA;C%sEd7To|XQoheFzApNPs8e?6_~D(DtL!!zjyik!oIHf zjXIatja&E3s^<6IsJ`wu`Al`|evFdgIZH@$$ybo!0pbN=50qEXCLn6)Pwjkw|A9E} z_E}^=PDu!N2W&BMBDO`C464#=>A40uV9+nt&(!j8J~^SKgoes1CT(c-L2jrmqGbIn zbRr|!9v7)-Q+a4yZCh~)M*>LUk9HA1-U5{~qPE)h0~ZA`4v^^DjX|}Vxt-}hUVZ(o zsr_YKlhKQ!k;|g4%Y2(T$vx9gyl4^LuI0p2G;>SCr3Q= zJ6}p3y%rFRZK%Fmw^BD6{UgR(C8|)HM7Qc(Py1zt%obGNHu6P0;D=zLBu*i0Y5&v* zyt+gN1%4g93eirZ((=J&MwK+y&0oVdX#S!LQjVRFY|+fB@0xVAQ)Ti@TajdLtnQW7 zXSXwCqtWp6FVg5Mt;G2Q)&5OL9#B#03bR7AiYxfeGjv7-Ltv@5r_IOJK@K?_E;vb& zGBd-gGjnpz=*j>(y8iPUX&DM0qiL&FSMaCgsJB(~V*=f;kvLZW4(wPhjxYM?KHfdG zf!-T$i&7gq&JyH<{34v6=hhVv5#?XR6bQKIcJJ+jixonpQ_AC*{WK$B&1l z0${CwU}DnE^U)TOjj=b0R46!w#oF(*&DIDl`0r3&Hf0aJf7dh%PKNjd1HO|R`8dWn z!QOfy@7d!TN(K`{0psRb2(t^e`P{w@_v~pH2ms>(1tQTY1y}dS1a}@OHxU;K-vhn# zgX=Fq;K|dVRzW}w!I(zk++~~ps5S9XJ4bsn>5S7Xxo^|7kDnXdcc%Hgk*-yLwp}-r zt#com-EIS94{{yaK@d**wISAQi)8_hJO812UH?r}nmxL5ZhsS1vCpmYK84Io#cA{9 zxQj&A2uhjvNxg}Pz+>6LNPvRF+$t&b!Gq%ya#usG0%h{{3cHs=4+0kU<4!URca2Gy zoz%Q(@b&d|U_1_3 z6Xz1lmP~tjO92d!_VhPtB3% z3lQg$##@xF3RAZTfGc(av){4oajzjOGDLM61fNVj?TYX8mG_Fmt-f(9Y^yg+`nb9E z$=03gvYfV8bEXTTnIlo`c*RTyEpC$uf$zTLcGu&tF*@idqfjtR8jL5|)~HO;G>m;8V3qMt86(5`84nKVyu4rvVZzcuf*Df0R=UMAu4RfcFQli zbsu%uI39&j?Ou4bKei$+wY{`roK`PH!1rs#5^GVi_US%8xnJwB7)h+JA%reaLcr0& zxT!%+&`MW2-94K2T4_?0jg5V)vkh9|`^SeoJAPU{)+_rPr~(D};epiF_3OK5vS}$E zGO26$>=xtv8Adhi#GY=5ulE^KDq+yeyr|VIbvidGLUpI_cVihY?_Jji#V5K=#ka+% zB|s-})OvTQJXjK&m*1uA=-(PEW}~u9oX!4F+#MBWry4N%I4jYk@2D!(9Py91xabTd zTh0vG7;F(pmt4@>9m-fAUTsXqIb(Lj1C{BnAm50O0-!#By?> z!Ay?f!rzt`fI}i92AZu`555!QD&ncC2S83#71eTFg`)$JnRt+dA5Biae`4*pH(36g=oa`5EM&o|2A!u-q2tx_Nd*O3##`K{qk8+m zjM+0t_$o590!2q@-y{+QE?h;Pe)RHGiMbvkSAe|`#V8aG*4C-rrGlat*(`F#w5|L& zR}_U>SXd;UlFqt#GY(W&BUk^y$L*=tcVN^AemP2$t((_lIL?r^Wn^`O1*Y=U=QLQW z1LKBX=fdGgwP&Z*)wj=lXX8_T;Tr#ly2(#sMplH$`s;X;#js4=gi!8k^|Sg-z0+Fk z`2?+pnS7ZDd|^{fWc;9iIXBz1?hEpf-$n{(mi?J_@u}J;g4IVZONx!W2y~*dUJhHK z(y{Z%8Pn*yC?&|Zs%BfV#s(U>pI=O680%~BO2KM=DpV}!vwdtax=Yw8?N#l&^;cA~ zUP}lGS*7l}3mrN#OGw-X`ZG|U<={5)FbnQQNRlv*?<3fjS1thQnkozp4Mn;BE|dhX z{4PYc7v4K=ODLL%L<)Xg4~$rn-mYh#D|%Wt=OR688-yTu6p{BntRqs_0eLc=e(z>Q zfOLeEw)wa}d3Mbq-Ti{@65`^XK#4~}JH)`UB6Qh!u~SYOV}_!fp!5b*KTBl`>FyY0R*%GmiYbK(JSOna6oPADtuenw;DPkEh#oT5b_QbA=2BIHZ=7 z2IY;Mek2ySpF?yh!2KttZ{c_cWIZjl$CS4zBn|FR-Jf{ZzB$}^*)o+ zaZ9>I&Pf_F=O&WJIzn|b1qzL6(>(>W=g(%>OioSHDD){0T;{Fk=?$6{4T8yw4m}PT zw?XuYU6)Js#+dV39$!}Jq;JfwD>Q2qmr6#h;P)ruMM&_p!W|IsMy zdfIl&hNo?n;^Epk$-KS%tSkLw%*CIptQGQZP}>vL2xlQl6Y1`bzLSjmJ=^oN;Jb>- z>fH!}_;Tfwwglz4eKoET+o9JRar|!O2=tpzgYpus0SXiKSTE0KFpG(U!i>s|#d~C= z4@b*uK@KTfR2 zBq0O{GO&{#c~}}D!JQ!C4d~?nT){x}@<*?bfB^0_4>&4p)~SAtpTED30oT6BE^d#4 zZ<~Bq!jOs#$eU2U_`ZEC`4w2NvEzWn`%Ai5W0j(?XWyx-Q-WxI55jWlrFGUenoE$o zsnc@Z3DiNr?O$962l!7{SK=q|)J%t~HS9Mt)UVHG-NP!U^PKHvCkB22xx+dk_VeQY zoMu^p3X%kcYYL|-%4OWI*TxD*c%pAsgywZ#TS}h17~np9qy7mGW}9PH7(h?Ty(etK zZ}5?+r$tLrMgFlyI<%lzhX3P8$GK~z$?ccSH-M!*E_SJ?7LS6rf8Fb=C)CeMI0Z~A z#GgyvP*kTq;4@1r^vX4CZ?mYrJruV(&?QUZh(KzUxX2Z>aQ)Jyzvzk9K|^3B!uewE z(EG-^-qd!f#c%S_&5~)0rKh&WiPYF8M##@xcbhCqaTxpjIf}Hl=ITbi5 zMt4NemPyV^l}W|D#qp5;8t=6s8!>RUis7v~-0uJ^p0u%6^H%TlUdJlycgFC;_vwGE zGyBn))xv3gMcMEL4UJsbRwG^ZS~Dq;ju33TurWq-Tv&0a5pUMzM&H~kE4v=>6H1@D zVABIKHCL(X$MjZDy~NnNRaSPE!W97Mf%`!lUV{+LYO@qL)UYv&?lBsXOW`-ENr<_PgdEXsz>{V z2Ba26Bhf$1&Vzm?$0ZQ3%8_X$Cy5Y-h{V@C96Q%ea+$I`=SV%a&4z6?Nklu?;91j< zatWQlv12~D)(5b&0Zx#lj32McWJ;6e^yTLMD&F(%#eoeQpz?Vs zn?39a=Gj_LULQEP0eT?D%b2r)_m0SBlHEs$)|U7qjPp-vL>E)TUY{27WPLLxbcH#j z;^_Lt#$N-Aue}A`Q#u6}4yNDvYHis@3qk;CrbMW-k|`+YV0GKR7?a0$3?SKqep6sN zvv!b(U%mDgYfrFc4vkAnOX*PbSM7D4zS%EdA0w`?&18{AfD8Fn{FJ*;9g0ThlP+gMd%I_P1)4S`av$tT?8l3tC0y63K}!`I4h7A z7i=LZW3Xa-fgH}3&n!L&Gw+d!#im{f4HwUr93OJ>LPv+yjSpgwjsl0D1LJ!F$Xyb_I8VOS#) zV!3ONmb}+Ubj;=m=BDeX0&6Acc8u#AWq$@mRLGUaS6k-%Q!F2vXss|>y_3K)HEDz& z22hy}1nQ_MHWpzpfpggIo?AGzu-EfSe!5JtF&@f1(2?a6y>5i{{=%2+2wN zezo_8wAAGu!H$ND`ob3%As+Sq)H;X?J5Td=r2M%XZBkhzya{vC?&nU$e)$??)58ntolAACQo2=&vQwwgr zk*XsIL8!p7A-&BjZbCs_`Xip~DA- zN76iK+l{l2pVYa%O?1(B%(y2Cuanq-McE!GJlNhcI7#t{R*@Wi0jmHGi|z9GuEf1! zT`^+Ykelqwy8sanaqd9yhNSrIbtqm5H9L3MPxbchB<4*V9VV z(XC=$&tK@1G)N31lsZM8N6sLoch|=X*AS;yk>lu-f_&&~8dDIIPe_>$ zL%1jPOM*)~x;u%z`jJ56_^e8c^hY(_+*S>fhBw2eIL4kt?75X6d80KnHx5LxP3Jg$*TMB!r;a0u#eWzeG`y?$z)|H;qGMib>+ZdOd@B=;(S-|2 zD@8uOyK?O9C5$@_MB&doc=PNo#a+AMA;Z{U;z$8e4wn0PG>0{_I^}sx=Q(@At?36%GSqFtpJxIF-lOTvWGRi>nU}Y!%sf?IVrCkR)?xR zu|e=r$grvlhi}?Qdx%jvf~jnv@!P3G(nc_Fp}I1iK192174sm+2dTU5d%`Z9_NMDH z0EYjcK-YJQwy^a>&8`!sfb6+zA|fJ4r>q7?0R=1JUxcsGw1el3Uys9Rs(SzJ07tWE zZhvD5fzXbiJSu_RyQ^GKZ13!+#>o?Wy5Z<19h=W?8R_ZO+zaL0hLBW}4#vzZ@s1qy zOp%FeO=GHojevH1<3PlXL>mrP?68gDKCXKjiaI}_QoRcsF%tKNtEmADp#nsvYJXz5 zz9+UCS#2pPJdu^g`_7-e0c5e_7>%?BZfh8WssXcL!#udQZY4FMa}UFl9pIYJ*M&&T zs-6_d6~6yiX#SC_4t}`VzGS3Wbdwveb&v9R`Sn84*mT?OuOmq;CGzz$LXjz>8aFQI z>zT^aOFzHTNU}~ZUc{6xtgZ%uTuq8j+w0rn_e||kboAL5TZFs=q6V9_PdzjylR80pe7y;Qh4N)X zC$T=S+Ew{*OyI`JEcufc?Oy?@9b@&#yL1 zYi}x=#ivcC+Yowyp58an>qAn*m2=4{DU4!AUyOkQg`U=oYPcz?+J)%jAUh*Mxb}7; z_JW{l$JdNURR(8{hK-t3AUG0K-6)=F5oBXqSLMh?Fza+5mr3_IsFj zr^(^9#rmv&mJtwB>gW3m-7!5(A5l85e_i>j%ix|Wf#>5vuyERpg?LQkqm^4(PUsYA z+uxZCbS>$O!?0g6^Z~OOaEzn-K*NcwDd@h8BTFEMu&dp272@}^&nMbM*Xn#@yGtr^ zr~Buiq*>iaLKEm+TQ7%-wU? z0NZa9K^~2IcxdSGOw}g~5T8(7V)z6)9N(?xzqs=64>{`dmxR|=Gu^^x~}E$pjeX9p05_kVM=W@NjjS^oDdY8tQ4{F+C}%v75m5-DL#1ecXH>h z&-MG@;q!6sTKwM9a!*UkzXWm_F2V$lU`{0R#f=?){Um&dxlhiP1Z&-^a|XR zx{JdrQ(1h?4#}`yLon%w0JPwED2$oXa&jV@3{Z=_ zFGi<)qE6g?e?xp-1kcVZnE0-wPW92-s_*wN&*#ouh=TyQz*G5Pov1DoDn2BvRqIrB zRsk}@qSF@jfdK?IT-2fboJag?5vC9EMV8|z8FCC(U1g`3N`h$p9)KHUvXnU5P1IwC zY^w_u$$;R9KF|0{!;?}26{$*$!hDv+g=y?bE@Zk z+G5bWFObQvWV$iN+w=GzLD|`qd;QI=E19^C#h*H7pc4P)L^#KEt$t~=^geTn@j56k z4#Dt8#H#-RZh^RGWZfC3gwwT4T}O$RpwiSb()|M= zwhIe?rOVO_y{u2^tJ(eUbzpSg40JbRdrN2nO zWw5BEU}LT0j=Ssd#~!TedS*+=X}PTUB4eBNe%e_H$4R%lswcd~beu*> z29xZ}AzH0`(-Z?$MqOWkUs&BluQl$zG!IOennE0|2v0=w!j~z;&rgOPA(|$(cg(jX znpKD*iCHWOZ_|z=2@f!aE16LxP-26pMYN7V#4t_F)2L2tO8+?Vi>wx`U73<3=J`;X z`#ZIpKd!}cgFkHsF)t33&G>P3%v1;N&ET|C^}ru%&L7N39i|ozXq4%_#^pM3_?J7f zaS2It#mQ*^sqS3)mwVUuEok>Xe6c!aRoe0!qNmRB&`j5Htky1re=0nBRWTzm+=8f{ z=w0k`KLV%7dT!YOTtyih7+35BKNz<##P=4aKpeD0okkKKG#0Kyl@CoELLK{) zpT)kJ*W0_f#z*$YHl^&P79Ux!<2=E$4^bZ#&wm`h4TTMA!NqUQi*#Vmoiq0#nwQ9? z@B?ug4rDI<&J-!S^)lG*`^T58B!UAN4=fsq}a`O^L6W&@ItA-yC4jsMFRM2XtciEvPF5SXbpgGiWAPEI|~bP3qDJRSY{m14@-ee2)yhz9fdLKVzDk?lO1{n>kSs4 z6Zs$qB>)aQ@$st+G7}!z02fV=Lr{Y#zW^i;v5&iSyd0WE6gcSw0|R`G>|F>nl8Oq%S!@t8_-5F?20U0GazrXS zOWjND5O_Z*w^+Mm3G{*uO5nwxnBQ7}ThdZy{#(xS>sx-I2q+4G0rtfe-S7iXakFtU z$Sq_|{&;_q6SY{)bhF%Ls7E=;aD}O!C6{82A(u!AVMpDLu4udD!6KFs48O6Whyo7{ zz#z@~#jl?~i&EI$TK*^=QFFMFu&QqrZanOP7lg1QsV}cLF~QSRW3V)1F!IU`QE4>^ zL9!2LbAVWKpaZ_$F?!HKT+Vsw2~`={1w$6lOiNPOpw^P2s}yznRd2vyp7VjkICc;f zwKy({q4I^_9i=vyMpIn(BfFMM0>^cLuJF~i+B}3vIMgKDzq(tGGOc%MaduiCu{?p0 zr@EKytaXGBXobhy-d>SRJsll!B(qGy@5Xl9i0%IID#-Vw+!hA8>#d>DriZDU{1UhN z8b@9e9bfl99qup`refgjTv*^2tH4p0hyTlCp`$4SWSEeXGyS|2EJhxsxcsefAMM0{ zw<1SRzl0e4I}ri_Iis%>&AL8ogVB$D@G*yX07b@3Def{@drFMmt6+R2ArUxmr*gh` zy?|dv`Fb>QLlT*IW94}1k{db8&=X8O+;YHai1~9de4(TfN5VtueqkI_WH1@X9T+SR zpoSvuu?6^!3Nvb}J8(+Kfp#J@yUBYtfiW8JMDfnr>M&KTjwy-1L%KW2&qtZiU|rsd zJ&UzJyk$D-V1@wudu36zAyi-cjntfO?%kr>0nI~IP$E$t&0Niig!dXoontpM3+{^fk> zQhILfFsN(9OYsavjhqq{Oh=wqZc8W79}- z?X>ybQRUjR$Dvv4#SR>jd{D}j$!zc-#ye4U?Ez^xT^VLs|ShwF7(2p(~xcFoz*P&7!jJ-Zq{{Ey~Aglg0 zDhQ(r`z+F1SJ&>c4070)*zO#0spTV7tl7%rVGD{27bG=LR?)9cF zuL5Z)TsMB4Mj0kE+wB0CYt5OH2fcgeU-ecZ`qs0gq{P}r7c~B|)`tjv!05rhSAg?n zN=G~O~4HEJ`g3`t)c#-j0d`1vwI%o2CuloJR zjq^@2LSx}-t+g}+Y9<+62}k$kYDOyjniph*{UGXS2|!O% z$}16nn2?dtG*`Ap)(|GQ?zvg_rK|9c=w|&W4-FV44~!0z4i-=~Nn%WEAPfhs=w2kz zb7pke;e%vDwN!&FP$R6PX3VY;Crgk6!Q=QBhTGg7CHKqKKjc8(b~C=0=NW zZuT%@s~OEKwIRDn5h;z>P0Q^Mt4x@{mJU*A;PhojVOoQr0duk|I{p*@l@IXYcI!+^ zPnU#vC=kU`+?j!X9nKETk}2kdDC;s{q1+vHiCW$)1JS64KdHY>@B3Sm zA2t*y0L1Cg{r<8S#E6hXexw;&t;lzil)Q=#w~Rgu8a67lD1jOKTSK$|1B(jja_DdW zXZXqTf9W<{&dsnY2Ex!jF(TrBlXF?v*vj6pJ z$us$H-@g2MO#ah_S=xw$?kD}oLr(Okt-5so>CXzwTd3i9$fea12-3 z9zuw)u;Jg&^kEcvU*s`;T7C5V8KyG>2cISxeIxJ?AXanCDa1a(?CC zC&`BR0?-{9b2lJ)0%4e0vGGEN!he3v@(cKHzvJJwmORkEFJ$>EsF$BDe)mqis{eXH h|No2sUws;jat5Eb<>j*}=Th)LB?Yyk8S;jn{{w$1vK9aU literal 0 HcmV?d00001 diff --git a/doc/source/io_examples/images/sphx_glr_read_vector_001.png b/doc/source/io_examples/images/sphx_glr_read_vector_001.png new file mode 100644 index 0000000000000000000000000000000000000000..aba17aa651005c6824b4fd809235efad0491496a GIT binary patch literal 100827 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

  • RBTK6Sy#L_-s*bCz%z(Th8Ad?MAglwc$y08v@Yg{5=Ys6Z zY-I+9Qrn`ZG*CPCV(-gK3NN<9dkEiTv&&Ppl$ zEM1IRxrHjSvY4=vOLJ%iw}k5#POq}jvngd$bsM|Nr%M;MJvu{E6wmUY&H}h;%4TJq z{}!p2NGqG3@9sAralf7S+sYO z+#L@2{#>|wqKKx)V6T>V5E(-7%rfEQd zN!V1V75zrD{az{;E49lQg7)ZV2KSp@J+GA4>&id^R5cRLKsKBJ@OR5ScyoRLRL=p9U z87oFf9tbKtRPcOFQplJG$?9t#E3^Ml%-J*n7T|h0ypL?Fr?sv`oV>M?P&^lK|F3+O z_c-OY$b)az@7#G6Z^fCdSKn8kW6_YaG3uC+R_ttDcE5$WyTT`fi=km*GM&eqq`tWt zisk27?g%wgf0bd$ro$?Gn(EewrImQGG?V!Fnv%I*PhlzT@ShfwvB`X)6xuy2zmZ

    M0ZutWrv23r6lJ^STUW$S+;A*K-lb+}FA(WVVVL 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�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

    KelfWhcf6=N+0+!2S))j7EZ0O*iL#U5#z$Etn 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�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 diff --git a/doc/source/handling_examples/images/thumb/sphx_glr_read_satimg_thumb.png b/doc/source/handling_examples/images/thumb/sphx_glr_read_satimg_thumb.png deleted file mode 100644 index 1ec7c1c3c0f48ec409231fe0a8958e088800fe3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70802 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 diff --git a/doc/source/handling_examples/images/thumb/sphx_glr_read_vector_thumb.png b/doc/source/handling_examples/images/thumb/sphx_glr_read_vector_thumb.png deleted file mode 100644 index af3c71a4bcdc24711b745817a3996bf2f1bc5a49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53375 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 X^NaJ8=cb-+CmfuZgVC*#f(!ovY#MoA diff --git a/doc/source/handling_examples/index.rst b/doc/source/handling_examples/index.rst deleted file mode 100644 index a02235a9e..000000000 --- a/doc/source/handling_examples/index.rst +++ /dev/null @@ -1,37 +0,0 @@ -:orphan: - -Handling -======== - -Examples about warping, reprojecting, resampling cropping, as well as rasterizing and polygonizing of georeferenced data. - - - -.. raw:: html - -

    - - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-gallery - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download all examples in Python source code: handling_examples_python.zip ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download all examples in Jupyter notebooks: handling_examples_jupyter.zip ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_raster.ipynb b/doc/source/handling_examples/read_raster.ipynb deleted file mode 100644 index 72e6c4623..000000000 --- a/doc/source/handling_examples/read_raster.ipynb +++ /dev/null @@ -1,108 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# Loading and understanding Rasters\n\nThis is (right now) a dummy example for showing the functionality of :class:`geoutils.Raster`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n\nimport geoutils as gu" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Example raster:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img = gu.Raster(gu.examples.get_path(\"everest_landsat_b4\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Info:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "print(img)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A plot:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img.show(cmap=\"Greys_r\")\nplt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/doc/source/handling_examples/read_raster.py b/doc/source/handling_examples/read_raster.py deleted file mode 100644 index 617f1ff72..000000000 --- a/doc/source/handling_examples/read_raster.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Loading and understanding Rasters -================================= - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.Raster`. -""" - -import matplotlib.pyplot as plt - -import geoutils as gu - -# %% -# Example raster: -img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) - -# %% -# Info: -print(img) - - -# %% -# A plot: -img.show(cmap="Greys_r") -plt.show() diff --git a/doc/source/handling_examples/read_raster.py.md5 b/doc/source/handling_examples/read_raster.py.md5 deleted file mode 100644 index cff3da644..000000000 --- a/doc/source/handling_examples/read_raster.py.md5 +++ /dev/null @@ -1 +0,0 @@ -3aad6d3dc8ef4c2558a827cad52379c2 \ No newline at end of file diff --git a/doc/source/handling_examples/read_raster.rst b/doc/source/handling_examples/read_raster.rst deleted file mode 100644 index cdad8a617..000000000 --- a/doc/source/handling_examples/read_raster.rst +++ /dev/null @@ -1,131 +0,0 @@ - -.. DO NOT EDIT. -.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. -.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -.. "handling_examples/read_raster.py" -.. LINE NUMBERS ARE GIVEN BELOW. - -.. only:: html - - .. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` - to download the full example code - -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_handling_examples_read_raster.py: - - -Loading and understanding Rasters -================================= - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.Raster`. - -.. GENERATED FROM PYTHON SOURCE LINES 7-12 - -.. code-block:: default - - - import matplotlib.pyplot as plt - - import geoutils as gu - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 13-14 - -Example raster: - -.. GENERATED FROM PYTHON SOURCE LINES 14-16 - -.. code-block:: default - - img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 17-18 - -Info: - -.. GENERATED FROM PYTHON SOURCE LINES 18-21 - -.. code-block:: default - - print(img) - - - - - - -.. rst-class:: sphx-glr-script-out - - .. code-block:: none - - not_loaded - - - - -.. GENERATED FROM PYTHON SOURCE LINES 22-23 - -A plot: - -.. GENERATED FROM PYTHON SOURCE LINES 23-25 - -.. code-block:: default - - img.show(cmap="Greys_r") - plt.show() - - - -.. image-sg:: /handling_examples/images/sphx_glr_read_raster_001.png - :alt: read raster - :srcset: /handling_examples/images/sphx_glr_read_raster_001.png - :class: sphx-glr-single-img - - - - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.096 seconds) - - -.. _sphx_glr_download_handling_examples_read_raster.py: - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-example - - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download Python source code: read_raster.py ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download Jupyter notebook: read_raster.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_raster_codeobj.pickle b/doc/source/handling_examples/read_raster_codeobj.pickle deleted file mode 100644 index e14e3920dd11f0a434a8bea90b63165eeec55f7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 975 zcmb7CJx{|h5JhS!sX{@ZGQi4GmQ4Hsq%KTI-BIKwCbr~!D7HlzkQf`u8|EMJ!??ux zaHO;fS#mnxyZ7$ieVzXNPEM*%xk1Vp4g^gTu9f?ehd9UKng&9D>xYeADTl_9$4UCG zU-jw}64M057-~0&3!IxLHH*_wGDzP>IF*7j?m>{767$pQ+!XoHEf-ojO(}$w`vF6o z>q5B@<6XvRK!q+OP?EDg8=0%?2HVkS;#w`qq97NC&>b^z$xU5~)iRPE-k~^SxRfva z3<*+?4H}#`nN*w7Ai&v5$^?b2L5sT{8+5OsPL5~=Ec;OcQe@)Py diff --git a/doc/source/handling_examples/read_satimg.ipynb b/doc/source/handling_examples/read_satimg.ipynb deleted file mode 100644 index 856d04062..000000000 --- a/doc/source/handling_examples/read_satimg.ipynb +++ /dev/null @@ -1,108 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# SatelliteImage class basics\n\nThis is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n\nimport geoutils as gu" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Example raster:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img = gu.Raster(gu.examples.get_path(\"everest_landsat_b4_cropped\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Info:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "print(img)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A plot:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img.show(cmap=\"Greys_r\")\nplt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/doc/source/handling_examples/read_satimg.py b/doc/source/handling_examples/read_satimg.py deleted file mode 100644 index 9c4d4afd2..000000000 --- a/doc/source/handling_examples/read_satimg.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -SatelliteImage class basics -=========================== - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`. -""" -import matplotlib.pyplot as plt - -import geoutils as gu - -# %% -# Example raster: -img = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) - -# %% -# Info: -print(img) - - -# %% -# A plot: -img.show(cmap="Greys_r") -plt.show() diff --git a/doc/source/handling_examples/read_satimg.py.md5 b/doc/source/handling_examples/read_satimg.py.md5 deleted file mode 100644 index 2a522a091..000000000 --- a/doc/source/handling_examples/read_satimg.py.md5 +++ /dev/null @@ -1 +0,0 @@ -4692bb52ad05dc058226c3b82fad0246 \ No newline at end of file diff --git a/doc/source/handling_examples/read_satimg.rst b/doc/source/handling_examples/read_satimg.rst deleted file mode 100644 index 93524a7b8..000000000 --- a/doc/source/handling_examples/read_satimg.rst +++ /dev/null @@ -1,130 +0,0 @@ - -.. DO NOT EDIT. -.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. -.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -.. "handling_examples/read_satimg.py" -.. LINE NUMBERS ARE GIVEN BELOW. - -.. only:: html - - .. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` - to download the full example code - -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_handling_examples_read_satimg.py: - - -SatelliteImage class basics -=========================== - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`. - -.. GENERATED FROM PYTHON SOURCE LINES 7-11 - -.. code-block:: default - - import matplotlib.pyplot as plt - - import geoutils as gu - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 12-13 - -Example raster: - -.. GENERATED FROM PYTHON SOURCE LINES 13-15 - -.. code-block:: default - - img = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 16-17 - -Info: - -.. GENERATED FROM PYTHON SOURCE LINES 17-20 - -.. code-block:: default - - print(img) - - - - - - -.. rst-class:: sphx-glr-script-out - - .. code-block:: none - - not_loaded - - - - -.. GENERATED FROM PYTHON SOURCE LINES 21-22 - -A plot: - -.. GENERATED FROM PYTHON SOURCE LINES 22-24 - -.. code-block:: default - - img.show(cmap="Greys_r") - plt.show() - - - -.. image-sg:: /handling_examples/images/sphx_glr_read_satimg_001.png - :alt: read satimg - :srcset: /handling_examples/images/sphx_glr_read_satimg_001.png - :class: sphx-glr-single-img - - - - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.077 seconds) - - -.. _sphx_glr_download_handling_examples_read_satimg.py: - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-example - - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download Python source code: read_satimg.py ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download Jupyter notebook: read_satimg.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_satimg_codeobj.pickle b/doc/source/handling_examples/read_satimg_codeobj.pickle deleted file mode 100644 index 0891c1f725007f02407d3e86fa6e29872bb0029a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 991 zcmb7CO-sW-5EWW$N^C)~h+e(aOHTd)p_iV7dX}sX|(^8EonxV6CQ&3GWRnx)*g?eK(ge` zyqNI^Cb}yUQ>>j42{dz0x)&!)B`|#x(L@0kGQc2nCGKbRxhtAuw+xK-%2EuuoQHx) zX|~$On66X7Lk?!EkdmFX*;rp+me`ucFu&1~4Yq8!4_z}epN)!As;?py&^3uuL9Kk4 zrvz9rHfr!*)hgeVK>^-EaRD5+hAnPeY}mbkV%` - to download the full example code - -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_handling_examples_read_vector.py: - - -Loading and understanding Vectors -================================= - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. - -.. GENERATED FROM PYTHON SOURCE LINES 7-12 - -.. code-block:: default - - - import matplotlib.pyplot as plt - - import geoutils as gu - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 13-14 - -Example raster: - -.. GENERATED FROM PYTHON SOURCE LINES 14-16 - -.. code-block:: default - - glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 17-18 - -Info: - -.. GENERATED FROM PYTHON SOURCE LINES 18-21 - -.. code-block:: default - - print(glaciers) - - - - - - -.. rst-class:: sphx-glr-script-out - - .. code-block:: none - - Filename: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/15_rgi60_glacier_outlines.gpkg - Coordinate System: EPSG:4326 - Number of features: 86 - Extent: [86.70393205700003, 27.847778695000045, 87.11409458600008, 28.14549759700003] - Attributes: ['RGIId', 'GLIMSId', 'BgnDate', 'EndDate', 'CenLon', 'CenLat', 'O1Region', 'O2Region', 'Area', 'Zmin', 'Zmax', 'Zmed', 'Slope', 'Aspect', 'Lmax', 'Status', 'Connect', 'Form', 'TermType', 'Surging', 'Linkages', 'Name', 'geometry'] - RGIId ... geometry - 0 RGI60-15.03410 ... POLYGON ((86.92842 27.92877, 86.92866 27.92930... - 1 RGI60-15.03411 ... POLYGON ((86.94077 27.92375, 86.94059 27.92295... - 2 RGI60-15.03412 ... POLYGON ((86.85344 27.94752, 86.85340 27.94752... - 3 RGI60-15.03413 ... POLYGON ((86.84776 27.94845, 86.84776 27.94849... - 4 RGI60-15.03414 ... POLYGON ((86.84567 27.96906, 86.84567 27.96910... - .. ... ... ... - 81 RGI60-15.10070 ... POLYGON ((86.94112 28.11455, 86.94129 28.11452... - 82 RGI60-15.10075 ... POLYGON ((86.95861 28.04669, 86.95847 28.04701... - 83 RGI60-15.10077 ... POLYGON ((86.96454 28.07843, 86.96445 28.07814... - 84 RGI60-15.10078 ... POLYGON ((86.96302 28.04915, 86.96304 28.04920... - 85 RGI60-15.10079 ... POLYGON ((86.97074 28.09496, 86.97039 28.09488... - - [86 rows x 23 columns] - - - - -.. GENERATED FROM PYTHON SOURCE LINES 22-23 - -A plot: - -.. GENERATED FROM PYTHON SOURCE LINES 23-26 - -.. code-block:: default - - for _, glacier in glaciers.ds.iterrows(): - plt.plot(*glacier.geometry.exterior.xy) - plt.show() - - - -.. image-sg:: /handling_examples/images/sphx_glr_read_vector_001.png - :alt: read vector - :srcset: /handling_examples/images/sphx_glr_read_vector_001.png - :class: sphx-glr-single-img - - - - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.114 seconds) - - -.. _sphx_glr_download_handling_examples_read_vector.py: - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-example - - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download Python source code: read_vector.py ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download Jupyter notebook: read_vector.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_vector_codeobj.pickle b/doc/source/handling_examples/read_vector_codeobj.pickle deleted file mode 100644 index cbb1c890478979841887b2f4ff6d94a354f57788..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2764 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 diff --git a/doc/source/handling_examples/searchindex.dir b/doc/source/handling_examples/searchindex.dir deleted file mode 100644 index 38a7775ad..000000000 --- a/doc/source/handling_examples/searchindex.dir +++ /dev/null @@ -1,3 +0,0 @@ -'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) -'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) -'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/handling_examples/sg_execution_times.rst b/doc/source/handling_examples/sg_execution_times.rst deleted file mode 100644 index 426de5f9d..000000000 --- a/doc/source/handling_examples/sg_execution_times.rst +++ /dev/null @@ -1,16 +0,0 @@ - -:orphan: - -.. _sphx_glr_handling_examples_sg_execution_times: - -Computation times -================= -**00:00.286** total execution time for **handling_examples** files: - -+-----------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_handling_examples_read_vector.py` (``read_vector.py``) | 00:00.114 | 0.0 MB | -+-----------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_handling_examples_read_raster.py` (``read_raster.py``) | 00:00.096 | 0.0 MB | -+-----------------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_handling_examples_read_satimg.py` (``read_satimg.py``) | 00:00.077 | 0.0 MB | -+-----------------------------------------------------------------------+-----------+--------+ diff --git a/doc/source/io_examples/images/sphx_glr_read_raster_001.png b/doc/source/io_examples/images/sphx_glr_read_raster_001.png deleted file mode 100644 index a04171162487e0202a3a79e5acfd07f988727f6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 213447 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

    0cVW@Agb5@4j6N}zxwCc7+X&? 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 @rH^(5Spw{h2V$pQBG?g$Al$(`p)X( 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 diff --git a/doc/source/handling_examples/images/sphx_glr_read_satimg_001.png b/doc/source/handling_examples/images/sphx_glr_read_satimg_001.png deleted file mode 100644 index f805df8c33f1640dd836439824e362ebd8666f9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151024 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%0D+tSA>d2NQSEl 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 diff --git a/doc/source/handling_examples/images/sphx_glr_read_vector_001.png b/doc/source/handling_examples/images/sphx_glr_read_vector_001.png deleted file mode 100644 index aba17aa651005c6824b4fd809235efad0491496a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100827 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

  • RBTK6Sy#L_-s*bCz%z(Th8Ad?MAglwc$y08v@Yg{5=Ys6Z zY-I+9Qrn`ZG*CPCV(-gK3NN<9dkEiTv&&Ppl$ zEM1IRxrHjSvY4=vOLJ%iw}k5#POq}jvngd$bsM|Nr%M;MJvu{E6wmUY&H}h;%4TJq z{}!p2NGqG3@9sAralf7S+sYO z+#L@2{#>|wqKKx)V6T>V5E(-7%rfEQd zN!V1V75zrD{az{;E49lQg7)ZV2KSp@J+GA4>&id^R5cRLKsKBJ@OR5ScyoRLRL=p9U z87oFf9tbKtRPcOFQplJG$?9t#E3^Ml%-J*n7T|h0ypL?Fr?sv`oV>M?P&^lK|F3+O z_c-OY$b)az@7#G6Z^fCdSKn8kW6_YaG3uC+R_ttDcE5$WyTT`fi=km*GM&eqq`tWt zisk27?g%wgf0bd$ro$?Gn(EewrImQGG?V!Fnv%I*PhlzT@ShfwvB`X)6xuy2zmZ

    M0ZutWrv23r6lJ^STUW$S+;A*K-lb+}FA(WVVVL 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�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

    KelfWhcf6=N+0+!2S))j7EZ0O*iL#U5#z$Etn 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�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 diff --git a/doc/source/io_examples/images/thumb/sphx_glr_read_satimg_thumb.png b/doc/source/io_examples/images/thumb/sphx_glr_read_satimg_thumb.png deleted file mode 100644 index 1ec7c1c3c0f48ec409231fe0a8958e088800fe3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70802 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 diff --git a/doc/source/io_examples/images/thumb/sphx_glr_read_vector_thumb.png b/doc/source/io_examples/images/thumb/sphx_glr_read_vector_thumb.png deleted file mode 100644 index af3c71a4bcdc24711b745817a3996bf2f1bc5a49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53375 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 X^NaJ8=cb-+CmfuZgVC*#f(!ovY#MoA diff --git a/doc/source/io_examples/index.rst b/doc/source/io_examples/index.rst deleted file mode 100644 index 2cc6d6f7b..000000000 --- a/doc/source/io_examples/index.rst +++ /dev/null @@ -1,96 +0,0 @@ -:orphan: - -Input/output -============ - -Examples about opening, creating, loading or saving raster and vector data. - - - -.. raw:: html - -

    - - -.. toctree:: - :hidden: - - /io_examples/read_satimg - /io_examples/read_raster - /io_examples/read_vector - - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-gallery - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download all examples in Python source code: io_examples_python.zip ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download all examples in Jupyter notebooks: io_examples_jupyter.zip ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/io_examples_jupyter.zip b/doc/source/io_examples/io_examples_jupyter.zip deleted file mode 100644 index cda2f7f0ad581369714b9c0bc68946fae0c939cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8208 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 diff --git a/doc/source/io_examples/read_raster.ipynb b/doc/source/io_examples/read_raster.ipynb deleted file mode 100644 index bee1f43ae..000000000 --- a/doc/source/io_examples/read_raster.ipynb +++ /dev/null @@ -1,144 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# Opening a raster from file\n\nThis example demonstrates the instantiation of a :class:`~geoutils.Raster` from file.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import geoutils as gu" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We open an example raster. The data is, by default, unloaded.\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img = gu.Raster(gu.examples.get_path(\"everest_landsat_b4\"))\nimg" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can print more info on the raster.\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "print(img.info())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`.\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img.show(cmap=\"Greys_r\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Opening can be performed with several parameters, for instance choosing a single band with `index` and re-sampling with `downsample`\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img = gu.Raster(gu.examples.get_path(\"everest_landsat_rgb\"), indexes=2, downsample=4)\nimg" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The data is unloaded by default, even if when specifying a band or re-sampling.\nWe can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`).\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "img.load()\nimg" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/doc/source/io_examples/read_raster.py b/doc/source/io_examples/read_raster.py deleted file mode 100644 index cb9cd1abc..000000000 --- a/doc/source/io_examples/read_raster.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Opening a raster from file -========================== - -This example demonstrates the instantiation of a :class:`~geoutils.Raster` from file. -""" - -import geoutils as gu - -# %% -# We open an example raster. The data is, by default, unloaded. -img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -img - -# %% -# We can print more info on the raster. -print(img.info()) - -# %% -# The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. -img.show(cmap="Greys_r") - -# %% -# Opening can be performed with several parameters, for instance choosing a single band with `index` and re-sampling with `downsample` -img = gu.Raster(gu.examples.get_path("everest_landsat_rgb"), indexes=2, downsample=4) -img - -# %% -# The data is unloaded by default, even if when specifying a band or re-sampling. -# We can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`). -img.load() -img diff --git a/doc/source/io_examples/read_raster.py.md5 b/doc/source/io_examples/read_raster.py.md5 deleted file mode 100644 index f94094c68..000000000 --- a/doc/source/io_examples/read_raster.py.md5 +++ /dev/null @@ -1 +0,0 @@ -97953828ecec7cf0e5d6d5348df67cfa \ No newline at end of file diff --git a/doc/source/io_examples/read_raster.rst b/doc/source/io_examples/read_raster.rst deleted file mode 100644 index 4baa314cf..000000000 --- a/doc/source/io_examples/read_raster.rst +++ /dev/null @@ -1,217 +0,0 @@ - -.. DO NOT EDIT. -.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. -.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -.. "io_examples/read_raster.py" -.. LINE NUMBERS ARE GIVEN BELOW. - -.. only:: html - - .. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` - to download the full example code - -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_io_examples_read_raster.py: - - -Opening a raster from file -========================== - -This example demonstrates the instantiation of a :class:`~geoutils.Raster` from file. - -.. GENERATED FROM PYTHON SOURCE LINES 7-10 - -.. code-block:: default - - - import geoutils as gu - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 11-12 - -We open an example raster. The data is, by default, unloaded. - -.. GENERATED FROM PYTHON SOURCE LINES 12-15 - -.. code-block:: default - - img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) - img - - - - - - -.. raw:: html - -
    -
    Raster(
    -      data=not_loaded
    -      transform=| 30.00, 0.00, 478000.00|
    -                | 0.00,-30.00, 3108140.00|
    -                | 0.00, 0.00, 1.00|
    -      crs=EPSG:32645)
    -
    -
    -
    - -.. GENERATED FROM PYTHON SOURCE LINES 16-17 - -We can print more info on the raster. - -.. GENERATED FROM PYTHON SOURCE LINES 17-19 - -.. code-block:: default - - print(img.info()) - - - - - -.. rst-class:: sphx-glr-script-out - - .. code-block:: none - - Driver: GTiff - Opened from file: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/LE71400412000304SGS00_B4.tif - Filename: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/LE71400412000304SGS00_B4.tif - Loaded? False - Modified since load? False - Size: 800, 655 - Number of bands: 1 - Data types: ('uint8',) - Coordinate System: ['EPSG:32645'] - NoData Value: None - Pixel Size: 30.0, 30.0 - Upper Left Corner: 478000.0, 3088490.0 - Lower Right Corner: 502000.0, 3108140.0 - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 20-21 - -The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. - -.. GENERATED FROM PYTHON SOURCE LINES 21-23 - -.. code-block:: default - - img.show(cmap="Greys_r") - - - - -.. image-sg:: /io_examples/images/sphx_glr_read_raster_001.png - :alt: read raster - :srcset: /io_examples/images/sphx_glr_read_raster_001.png - :class: sphx-glr-single-img - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 24-25 - -Opening can be performed with several parameters, for instance choosing a single band with `index` and re-sampling with `downsample` - -.. GENERATED FROM PYTHON SOURCE LINES 25-28 - -.. code-block:: default - - img = gu.Raster(gu.examples.get_path("everest_landsat_rgb"), indexes=2, downsample=4) - img - - - - - - -.. raw:: html - -
    -
    Raster(
    -      data=not_loaded
    -      transform=| 120.00, 0.00, 478000.00|
    -                | 0.00,-120.00, 3108140.00|
    -                | 0.00, 0.00, 1.00|
    -      crs=EPSG:32645)
    -
    -
    -
    - -.. GENERATED FROM PYTHON SOURCE LINES 29-31 - -The data is unloaded by default, even if when specifying a band or re-sampling. -We can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`). - -.. GENERATED FROM PYTHON SOURCE LINES 31-33 - -.. code-block:: default - - img.load() - img - - - - - -.. raw:: html - -
    -
    Raster(
    -      data=[[[255 255 255 ... 255 255 255]
    -             [255 255 255 ... 255 255 255]
    -             [255 255 255 ... 255 255 255]
    -             ...
    -             [ 38  66  92 ... 114 147 157]
    -             [ 86  54  29 ... 122 142 171]
    -             [ 94  35  35 ... 150 151 223]]]
    -      transform=| 120.00, 0.00, 478000.00|
    -                | 0.00,-120.00, 3108140.00|
    -                | 0.00, 0.00, 1.00|
    -      crs=EPSG:32645)
    -
    -
    -
    - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.104 seconds) - - -.. _sphx_glr_download_io_examples_read_raster.py: - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-example - - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download Python source code: read_raster.py ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download Jupyter notebook: read_raster.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/read_raster_codeobj.pickle b/doc/source/io_examples/read_raster_codeobj.pickle deleted file mode 100644 index 2acd7678b8c6d6c88616dd7aa038c0f9c1fb0243..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1481 zcmbtUO-sW-5EX2-C00@D!K_za{!rol}j=#<6 zW;ba*TB7YGlj+QRZ)P6(?0)}rkMvJEUd$NwIE|xJN#`Z;af1CP>T&g@?#5~)k7*dF zi5h)C+=)<#l{^`vl;Z?~F!lw5%uRq}!6{2!2on9Tn>~~~%ZjEci4(45&z3?+)2YW$ znyR&QAjZoDqaNjIEr5-j=0v0~uWVbvpop`DMh0tg-q2X^5rI)0fV#iy4u1Y8^`Z-^rHZ_grab3L59xC2~{7<~?E{T85#b4yN+GFif z@0;#jiQ31aR5dYGSPm-;u8%l^Rb~Ah{RS(l2?3AI3<#=&7_%4` - to download the full example code - -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_io_examples_read_satimg.py: - - -SatelliteImage class basics -=========================== - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`. - -.. GENERATED FROM PYTHON SOURCE LINES 7-11 - -.. code-block:: default - - import matplotlib.pyplot as plt - - import geoutils as gu - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 12-13 - -Example raster: - -.. GENERATED FROM PYTHON SOURCE LINES 13-15 - -.. code-block:: default - - img = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 16-17 - -Info: - -.. GENERATED FROM PYTHON SOURCE LINES 17-20 - -.. code-block:: default - - print(img) - - - - - - -.. rst-class:: sphx-glr-script-out - - .. code-block:: none - - not_loaded - - - - -.. GENERATED FROM PYTHON SOURCE LINES 21-22 - -A plot: - -.. GENERATED FROM PYTHON SOURCE LINES 22-24 - -.. code-block:: default - - img.show(cmap="Greys_r") - plt.show() - - - -.. image-sg:: /io_examples/images/sphx_glr_read_satimg_001.png - :alt: read satimg - :srcset: /io_examples/images/sphx_glr_read_satimg_001.png - :class: sphx-glr-single-img - - - - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.086 seconds) - - -.. _sphx_glr_download_io_examples_read_satimg.py: - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-example - - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download Python source code: read_satimg.py ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download Jupyter notebook: read_satimg.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/read_satimg_codeobj.pickle b/doc/source/io_examples/read_satimg_codeobj.pickle deleted file mode 100644 index 35b5271c83f9d6c0b81265750790a969e758e9de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 991 zcma)*!AiqG5Qc?TO^Gciig*=->LD-S#erQZWFS*SPz277WU@)W z(=W7#DriIjG0 ze6(SmauzXVw;I*xX)Q!Y=Aug5DOl?7RiooAopdDb^r%m-$|x|SSO?`8Zn&o0fq`18 zn@-gp?RFBfe)*fsX4;>tyYx_gdN@r;dOoYN)&L;6wCyP0UL3D!ldQTms#cH%bXS6 IY&6lqKaRP15dZ)H diff --git a/doc/source/io_examples/read_vector.ipynb b/doc/source/io_examples/read_vector.ipynb deleted file mode 100644 index 425f762b5..000000000 --- a/doc/source/io_examples/read_vector.ipynb +++ /dev/null @@ -1,108 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "%matplotlib inline" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n# Loading and understanding Vectors\n\nThis is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n\nimport geoutils as gu" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Example raster:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "glaciers = gu.Vector(gu.examples.get_path(\"everest_rgi_outlines\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Info:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "print(glaciers)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A plot:\n\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false - }, - "outputs": [], - "source": [ - "for _, glacier in glaciers.ds.iterrows():\n plt.plot(*glacier.geometry.exterior.xy)\nplt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} \ No newline at end of file diff --git a/doc/source/io_examples/read_vector.py b/doc/source/io_examples/read_vector.py deleted file mode 100644 index a3282839e..000000000 --- a/doc/source/io_examples/read_vector.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Loading and understanding Vectors -================================= - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. -""" - -import matplotlib.pyplot as plt - -import geoutils as gu - -# %% -# Example raster: -glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) - -# %% -# Info: -print(glaciers) - - -# %% -# A plot: -for _, glacier in glaciers.ds.iterrows(): - plt.plot(*glacier.geometry.exterior.xy) -plt.show() diff --git a/doc/source/io_examples/read_vector.py.md5 b/doc/source/io_examples/read_vector.py.md5 deleted file mode 100644 index e49c8bd26..000000000 --- a/doc/source/io_examples/read_vector.py.md5 +++ /dev/null @@ -1 +0,0 @@ -610752bf652a95a4e6fda70357a5314d \ No newline at end of file diff --git a/doc/source/io_examples/read_vector.rst b/doc/source/io_examples/read_vector.rst deleted file mode 100644 index f9d9cb713..000000000 --- a/doc/source/io_examples/read_vector.rst +++ /dev/null @@ -1,150 +0,0 @@ - -.. DO NOT EDIT. -.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. -.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: -.. "io_examples/read_vector.py" -.. LINE NUMBERS ARE GIVEN BELOW. - -.. only:: html - - .. note:: - :class: sphx-glr-download-link-note - - Click :ref:`here ` - to download the full example code - -.. rst-class:: sphx-glr-example-title - -.. _sphx_glr_io_examples_read_vector.py: - - -Loading and understanding Vectors -================================= - -This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. - -.. GENERATED FROM PYTHON SOURCE LINES 7-12 - -.. code-block:: default - - - import matplotlib.pyplot as plt - - import geoutils as gu - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 13-14 - -Example raster: - -.. GENERATED FROM PYTHON SOURCE LINES 14-16 - -.. code-block:: default - - glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) - - - - - - - - -.. GENERATED FROM PYTHON SOURCE LINES 17-18 - -Info: - -.. GENERATED FROM PYTHON SOURCE LINES 18-21 - -.. code-block:: default - - print(glaciers) - - - - - - -.. rst-class:: sphx-glr-script-out - - .. code-block:: none - - Filename: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/15_rgi60_glacier_outlines.gpkg - Coordinate System: EPSG:4326 - Number of features: 86 - Extent: [86.70393205700003, 27.847778695000045, 87.11409458600008, 28.14549759700003] - Attributes: ['RGIId', 'GLIMSId', 'BgnDate', 'EndDate', 'CenLon', 'CenLat', 'O1Region', 'O2Region', 'Area', 'Zmin', 'Zmax', 'Zmed', 'Slope', 'Aspect', 'Lmax', 'Status', 'Connect', 'Form', 'TermType', 'Surging', 'Linkages', 'Name', 'geometry'] - RGIId ... geometry - 0 RGI60-15.03410 ... POLYGON ((86.92842 27.92877, 86.92866 27.92930... - 1 RGI60-15.03411 ... POLYGON ((86.94077 27.92375, 86.94059 27.92295... - 2 RGI60-15.03412 ... POLYGON ((86.85344 27.94752, 86.85340 27.94752... - 3 RGI60-15.03413 ... POLYGON ((86.84776 27.94845, 86.84776 27.94849... - 4 RGI60-15.03414 ... POLYGON ((86.84567 27.96906, 86.84567 27.96910... - .. ... ... ... - 81 RGI60-15.10070 ... POLYGON ((86.94112 28.11455, 86.94129 28.11452... - 82 RGI60-15.10075 ... POLYGON ((86.95861 28.04669, 86.95847 28.04701... - 83 RGI60-15.10077 ... POLYGON ((86.96454 28.07843, 86.96445 28.07814... - 84 RGI60-15.10078 ... POLYGON ((86.96302 28.04915, 86.96304 28.04920... - 85 RGI60-15.10079 ... POLYGON ((86.97074 28.09496, 86.97039 28.09488... - - [86 rows x 23 columns] - - - - -.. GENERATED FROM PYTHON SOURCE LINES 22-23 - -A plot: - -.. GENERATED FROM PYTHON SOURCE LINES 23-26 - -.. code-block:: default - - for _, glacier in glaciers.ds.iterrows(): - plt.plot(*glacier.geometry.exterior.xy) - plt.show() - - - -.. image-sg:: /io_examples/images/sphx_glr_read_vector_001.png - :alt: read vector - :srcset: /io_examples/images/sphx_glr_read_vector_001.png - :class: sphx-glr-single-img - - - - - - -.. rst-class:: sphx-glr-timing - - **Total running time of the script:** ( 0 minutes 0.110 seconds) - - -.. _sphx_glr_download_io_examples_read_vector.py: - -.. only:: html - - .. container:: sphx-glr-footer sphx-glr-footer-example - - - .. container:: sphx-glr-download sphx-glr-download-python - - :download:`Download Python source code: read_vector.py ` - - .. container:: sphx-glr-download sphx-glr-download-jupyter - - :download:`Download Jupyter notebook: read_vector.ipynb ` - - -.. only:: html - - .. rst-class:: sphx-glr-signature - - `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/io_examples/read_vector_codeobj.pickle b/doc/source/io_examples/read_vector_codeobj.pickle deleted file mode 100644 index 62eb2e1f0b7f54709df4a5256a9c2d4931fbb6fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2764 zcmbVO&ui2`6jrR;ZBuo(c8iFDAV_=3p&)n@%K8hjtv^r@$~v2!?u^bRVP;}?5uwK} zL=cPzFJ8U+FZiGNCdno{)RW1#Wb;L`5ufiR`^jf`Sn$yZC-DX9%Oq}$fD>ue)|A5~YT zw0EkUZXZ&CnB26iP^X&QvIOgrZyT6)tz2g&m(jiwdB*~yb8P1MI9|&Q7w$l~6s+dK zp(u^+0DTup^pL|gE8j!M)?tT)$R8k~{2oBgfyz-{Efud!x91nr5f0fhR-7ST-D0(MQGJikDduR!l!fqc-PUzA7BiFd+qk$!^|~AwseuRiSWh}T-Es25~u0hHz4az`~ikO4o`M-O&3b@H0AX> z+X_{+o-I(5dthR^l<&Yus4?G=i#!ht5pX4!+E%E_Wwt;~ZjfVeyqHhHa_}`?!fC-m z-3_aHZ$%z<>cjfM*Gg2?jt#Y1qNeUsp{^M+>B9YeSjuZ2M1J~xpahn;J#_HMD7}!v zAg4V-GGu{-3&Ra3>IvFi8_kx0VTwoYYd8>KOmXiw@__0J%-%>+mX80ASsqku&cZPI zkIRc*18Nx#`C^szwQR-gx;m>S?c-yfrXK~{8ngh9(I{kCEmQVFBuU9U--Mrr39#ZE zs*IR5sHmJ8Q4^gRk=ma+R`@WV`Io`GkQsdz`>zy=icOGxv(0PU@09sCLXrCy@m~(; diff --git a/doc/source/io_examples/searchindex.bak b/doc/source/io_examples/searchindex.bak deleted file mode 100644 index 38a7775ad..000000000 --- a/doc/source/io_examples/searchindex.bak +++ /dev/null @@ -1,3 +0,0 @@ -'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) -'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) -'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/io_examples/searchindex.dat b/doc/source/io_examples/searchindex.dat deleted file mode 100644 index a179f4911b395951a6ae047c77b42ea62fd8164a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84326 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
    GCE8D}oUtbusQw-}ba4 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 diff --git a/doc/source/io_examples/searchindex.dir b/doc/source/io_examples/searchindex.dir deleted file mode 100644 index 38a7775ad..000000000 --- a/doc/source/io_examples/searchindex.dir +++ /dev/null @@ -1,3 +0,0 @@ -'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) -'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) -'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/io_examples/sg_execution_times.rst b/doc/source/io_examples/sg_execution_times.rst deleted file mode 100644 index a4b8c23f7..000000000 --- a/doc/source/io_examples/sg_execution_times.rst +++ /dev/null @@ -1,16 +0,0 @@ - -:orphan: - -.. _sphx_glr_io_examples_sg_execution_times: - -Computation times -================= -**00:00.104** total execution time for **io_examples** files: - -+-----------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_io_examples_read_raster.py` (``read_raster.py``) | 00:00.104 | 0.0 MB | -+-----------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_io_examples_read_satimg.py` (``read_satimg.py``) | 00:00.000 | 0.0 MB | -+-----------------------------------------------------------------+-----------+--------+ -| :ref:`sphx_glr_io_examples_read_vector.py` (``read_vector.py``) | 00:00.000 | 0.0 MB | -+-----------------------------------------------------------------+-----------+--------+ diff --git a/examples/io/from_array.py b/examples/io/from_array.py new file mode 100644 index 000000000..2445b758d --- /dev/null +++ b/examples/io/from_array.py @@ -0,0 +1,56 @@ +""" +Creating a raster from array +============================ + +This example demonstrates the creation of a raster through :func:`~geoutils.Raster.from_array`. +""" + +# %% +# We create a data array as :class:`~numpy.ndarray`, a transform as :class:`affine.Affine` and a coordinate reference system (CRS) as :class:`pyproj.CRS`. +import geoutils as gu +import rasterio as rio +import pyproj +import numpy as np + +# A random 3 x 3 masked array +np.random.seed(42) +arr = np.random.randint(0, 255, size=(3, 3), dtype="float32") +# A transform with 3 x 3 pixels in a [0-1, 0-1] bound square +transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3) +# A CRS, here geographic (latitude/longitude) +crs = pyproj.CRS.from_epsg(4326) + +# Create a raster +rast = gu.Raster.from_array( + data = arr, + transform = transform, + crs = crs, + nodata = 255 + ) +rast + +# %% +# We can print info on the raster. +print(rast.info()) + +# %% +# The array has been automatically cast into a :class:`~numpy.ma.MaskedArray`, to respect :class:`~geoutils.Raster.nodata` values. + +# %% +# We could also have created directly from a :class:`~numpy.ma.MaskedArray`. + +# A random mask, that will mask one out of two values on average +mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") +ma = np.ma.masked_array(data=arr, mask=mask) + +rast = gu.Raster.from_array( + data = arr, + transform = transform, + crs = crs, + nodata = 255 + ) +rast + +# %% +# The different functionalities of GeoUtils will respect :class:`~geoutils.Raster.nodata` values, starting with :func:`~geoutils.Raster.show`. +rast.show(cmap="Greys_r") diff --git a/examples/io/read_raster.py b/examples/io/read_raster.py index fc4eb2890..d4299ab5a 100644 --- a/examples/io/read_raster.py +++ b/examples/io/read_raster.py @@ -2,31 +2,31 @@ Opening a raster from file ========================== -This example demonstrates the instantiation of a :class:`~geoutils.Raster` from file. +This example demonstrates the instantiation of a raster through :class:`~geoutils.Raster`. """ -import geoutils as gu - # %% # We open an example raster. The data is, by default, unloaded. -img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -img +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +rast # %% # We can print more info on the raster. -print(img.info()) +print(rast.info()) # %% # The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. -img.show(cmap="Greys_r") +rast.show(cmap="Greys_r") # %% -# Opening can be performed with several parameters, for instance choosing a single band with ``index`` and re-sampling with ``downsample``. -img = gu.Raster(gu.examples.get_path("everest_landsat_rgb"), indexes=2, downsample=4) -img +# Opening can be performed with several parameters, for instance choosing a single band with ``index`` and re-sampling with ``downsample``, to subset a 3-band +# raster to its second band, and using 1 pixel out of 4. +rast = gu.Raster(gu.examples.get_path("everest_landsat_rgb"), indexes=2, downsample=4) +rast # %% -# The data is unloaded by default, even if when specifying a band or re-sampling. -# We can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed `load_data=True` to # :class:`~geoutils.Raster`). -img.load() -img +# The data is not loaded by default, even if when specifying a band or re-sampling. +# We can load it explicitly by calling :func:`~geoutils.Raster.load` (could have also passed ``load_data=True`` to :class:`~geoutils.Raster`). +rast.load() +rast diff --git a/examples/io/read_vector.py b/examples/io/read_vector.py index a3282839e..bf88be7fa 100644 --- a/examples/io/read_vector.py +++ b/examples/io/read_vector.py @@ -1,25 +1,24 @@ """ -Loading and understanding Vectors -================================= +Opening a vector from file +========================== -This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. +This example demonstrates the instantiation of a vector through :class:`geoutils.Vector`. """ -import matplotlib.pyplot as plt - import geoutils as gu # %% -# Example raster: -glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +# We open an example vector. +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +vect # %% -# Info: -print(glaciers) - +# We can print more info on the vector. +print(vect) # %% -# A plot: -for _, glacier in glaciers.ds.iterrows(): +# Let's plot: +import matplotlib.pyplot as plt +for _, glacier in vect.ds.iterrows(): plt.plot(*glacier.geometry.exterior.xy) plt.show() diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index d587d6e92..1f9acbcab 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -626,7 +626,7 @@ def __repr__(self) -> str: # If data not loaded, return and string and avoid calling .data if not self.is_loaded: - str_data = "not_loaded" + str_data = "not_loaded; shape on disk "+str(self._disk_shape) else: str_data = "\n ".join(self.data.__str__().split("\n")) @@ -649,7 +649,7 @@ def _repr_html_(self) -> str: # If data not loaded, return and string and avoid calling .data if not self.is_loaded: - str_data = "not_loaded" + str_data = "not_loaded; shape on disk "+str(self._disk_shape)+"" else: str_data = "\n ".join(self.data.__str__().split("\n")) From bc957b64b98c124ac6a88284120855b8d865cd7f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Feb 2023 22:02:59 -0800 Subject: [PATCH 037/184] Merge and add new gallery folders to gitignore --- examples/io/from_array.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/io/from_array.py b/examples/io/from_array.py index 2445b758d..640f200ce 100644 --- a/examples/io/from_array.py +++ b/examples/io/from_array.py @@ -14,7 +14,7 @@ # A random 3 x 3 masked array np.random.seed(42) -arr = np.random.randint(0, 255, size=(3, 3), dtype="float32") +arr = np.random.normal(size=(5, 5)) # A transform with 3 x 3 pixels in a [0-1, 0-1] bound square transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3) # A CRS, here geographic (latitude/longitude) @@ -35,16 +35,18 @@ # %% # The array has been automatically cast into a :class:`~numpy.ma.MaskedArray`, to respect :class:`~geoutils.Raster.nodata` values. +rast.data # %% # We could also have created directly from a :class:`~numpy.ma.MaskedArray`. # A random mask, that will mask one out of two values on average -mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") +mask = np.random.randint(0, 2, size=(5, 5), dtype="bool") ma = np.ma.masked_array(data=arr, mask=mask) +# This time, we pass directly the masked array rast = gu.Raster.from_array( - data = arr, + data = ma, transform = transform, crs = crs, nodata = 255 @@ -52,5 +54,6 @@ rast # %% -# The different functionalities of GeoUtils will respect :class:`~geoutils.Raster.nodata` values, starting with :func:`~geoutils.Raster.show`. -rast.show(cmap="Greys_r") +# The different functionalities of GeoUtils will respect :class:`~geoutils.Raster.nodata` values, starting with :func:`~geoutils.Raster.show`, +# which will ignore them during plotting (transparent). +rast.show(cmap="copper") From 65a6ce44db4806b3b9b82d77870288f4b4e6bc48 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 21 Feb 2023 00:53:21 -0800 Subject: [PATCH 038/184] Incremental commit on example gallery --- doc/source/conf.py | 2 +- examples/analysis/README.rst | 2 +- examples/analysis/buffer_voronoi.py | 35 ++++++++++++++++ examples/analysis/proximity_metric.py | 47 +++++++++++++++++++++ examples/handling/README.rst | 2 +- examples/handling/crop_raster.py | 52 +++++++++++++++++++++++ examples/handling/warp_raster.py | 60 +++++++++++++++++++++++++++ examples/io/README.rst | 2 +- examples/io/read_satimg.py | 23 +++++----- geoutils/geovector.py | 19 +++++++++ 10 files changed, 228 insertions(+), 16 deletions(-) create mode 100644 examples/analysis/buffer_voronoi.py create mode 100644 examples/analysis/proximity_metric.py create mode 100644 examples/handling/crop_raster.py create mode 100644 examples/handling/warp_raster.py diff --git a/doc/source/conf.py b/doc/source/conf.py index b7336b425..cd36ad1a1 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -116,7 +116,7 @@ html_theme_options = { "use_sidenotes": True, - "repository_url": "https://github.com/GlacioHack/xdem", + "repository_url": "https://github.com/GlacioHack/geoutils", "use_repository_button": True, } diff --git a/examples/analysis/README.rst b/examples/analysis/README.rst index 000bd8622..6744125b5 100644 --- a/examples/analysis/README.rst +++ b/examples/analysis/README.rst @@ -1,5 +1,5 @@ Analysis ======== -Examples about numerical operations on rasters, computing proximity and buffer without overlap. +Examples about numerical operations on rasters, and additional features such as proximity and buffer without overlap. diff --git a/examples/analysis/buffer_voronoi.py b/examples/analysis/buffer_voronoi.py new file mode 100644 index 000000000..4f2442f3f --- /dev/null +++ b/examples/analysis/buffer_voronoi.py @@ -0,0 +1,35 @@ +""" +Metric buffer and without overlap +================================= + +This example demonstrates the metric buffering of a vector using :class:`~geoutils.Vector.buffer_metric`. +""" +# sphinx_gallery_thumbnail_number = 3 +# %% +# We open an example vector +import geoutils as gu +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + +# %% +# We buffer in metric units directly using :func:`~geoutils.Vector.buffer_metric`. Under the hood, this functionality reprojects to a local projection, +# buffers, and converts back to the original CRS. +vect_buff = vect.buffer_metric(buffer_size=500) + +# %% +# Let's plot the raster and vector +ax = vect.ds.plot() +vect_buff.ds.plot(ax=ax, ec='k', fc='none') + +# %% +# Many buffers are overlapping. To compute a buffer without overlap, one can use :func:`~geoutils.Vector.buffer_without_overlap`. +# +vect_buff_nolap = vect.buffer_without_overlap(buffer_size=500) +ax = vect_buff_nolap.ds.plot() +vect.ds.plot(ax=ax, ec='k', fc='none') + +# %% +# We plot with color to see that the attributes are retained for every feature. +import matplotlib.pyplot as plt +ax = vect_buff_nolap.ds.plot(column="RGIId") +vect.ds.plot(ax=ax, ec='k', column="RGIId", alpha=0.5) +plt.show() \ No newline at end of file diff --git a/examples/analysis/proximity_metric.py b/examples/analysis/proximity_metric.py new file mode 100644 index 000000000..4d54bcf9d --- /dev/null +++ b/examples/analysis/proximity_metric.py @@ -0,0 +1,47 @@ +""" +Proximity to raster or vector +============================= + +This example demonstrates the calculation of proximity distances to a raster or vector using :class:`~geoutils.Raster.proximity`. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open an example raster, and a vector for which we select a single feature +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +vect = gu.Vector(vect.ds[vect.ds["RGIId"] == "RGI60-15.10055"]) +rast.crop(vect) + +# Plot the raster and vector +import matplotlib.pyplot as plt +ax = plt.gca() +rast.show(ax=ax, cmap="Blues") +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) + +# %% +# We use the raster as a reference to match for rasterizing the proximity distances with :func:`~geoutils.Vector.proximity`. See :ref:`core-match-ref` for more details. + +proximity = vect.proximity(rast) +proximity.show(cmap="viridis") + +# %% +# Proximity can also be computed to target pixels of a raster, or that of a mask + +# Get mask of pixels within 30 of 200 infrared +import numpy as np +mask_200 = np.abs(rast - 200) < 30 +mask_200.show() + +# %% +# Because a mask is :class:`bool`, no need to pass target pixels + +proximity_mask = mask_200.proximity() +proximity_mask.show(cmap="viridis") + +# %% +# By default, proximity is computed using the georeference unit from a :class:`~geoutils.Raster`'s :attr:`~geoutils.Raster.res`, here **meters**. It can also +# be computed in pixels. + +proximity_mask = mask_200.proximity(distance_unit='pixel') +proximity_mask.show(cmap="viridis") diff --git a/examples/handling/README.rst b/examples/handling/README.rst index c8e11e62e..fe929f99d 100644 --- a/examples/handling/README.rst +++ b/examples/handling/README.rst @@ -1,4 +1,4 @@ Handling ======== -Examples about warping, reprojecting, resampling cropping, as well as rasterizing and polygonizing of georeferenced data. +Examples about **warping**, **reprojecting**, **resampling**, **cropping**, as well as **rasterizing** and **polygonizing** of georeferenced data. diff --git a/examples/handling/crop_raster.py b/examples/handling/crop_raster.py new file mode 100644 index 000000000..e607b2758 --- /dev/null +++ b/examples/handling/crop_raster.py @@ -0,0 +1,52 @@ +""" +Crop a raster +============= + +This example demonstrates the cropping of a raster using :class:`~geoutils.Raster.crop`. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open two example rasters. +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +vect = gu.Vector(vect.ds[vect.ds["RGIId"] == "RGI60-15.10055"]) + +# %% +# The first raster has larger extent and higher resolution than the second one +print(rast.info()) +print(vect.bounds) + +# %% +# Let's plot the raster and vector +import matplotlib.pyplot as plt +ax = plt.gca() +rast.show(ax=ax, cmap="Purples") +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) + +# %% +# **First option:** using the second raster as a reference to match, we reproject the first one. We simply have to pass the second :class:`~geoutils.Raster` +# as single argument to :func:`~geoutils.Raster.crop`. See :ref:`core-match-ref` for more details. + +rast.crop(vect) + +# %% +# Now the bounds should be the same as that of the vector (within the size of a pixel as the grid was not warped). +# +# .. note:: +# By default, :func:`~geoutils.Raster.crop` is done in-place, replacing ``rast``. This behaviour can be modified by passing ``inplace=False``. +# + +ax = plt.gca() +rast.show(ax=ax, cmap="Purples") +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) + +# %% +# **Second option:** we can pass other ``crop_geom`` argument to :func:`~geoutils.Raster.crop`, including another :class:`~geoutils.Raster` or a +# simple :class:`tuple` of bounds. + +rast.crop((rast.bounds.left + 1000, rast.bounds.bottom, rast.bounds.right, rast.bounds.top - 500)) + +ax = plt.gca() +rast.show(ax=ax, cmap="Purples") +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) diff --git a/examples/handling/warp_raster.py b/examples/handling/warp_raster.py new file mode 100644 index 000000000..955ce0a31 --- /dev/null +++ b/examples/handling/warp_raster.py @@ -0,0 +1,60 @@ +""" +Warp a raster +============= + +This example demonstrates the warping of a raster using :class:`~geoutils.Raster.reproject`. +""" +# %% +# We open two example rasters. +import geoutils as gu +rast1 = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +rast2 = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) + +# %% +# The first raster has larger extent and higher resolution than the second one +print(rast1.info()) +print(rast2.info()) + +# %% +# Let's plot the first raster, with the warped extent of the second one +import matplotlib.pyplot as plt +ax = plt.gca() +rast1.show(ax=ax, cmap="Blues") +vect_bounds_rast2 = gu.Vector.from_bounds_projected(rast2) +vect_bounds_rast2.ds.plot(ax=ax, fc='none', ec='r', lw=2) + +# %% +# **First option:** using the second raster as a reference to match, we reproject the first one. We simply have to pass the second :class:`~geoutils.Raster` +# as single argument to :func:`~geoutils.Raster.reproject`. See :ref:`core-match-ref` for more details. +# +# By default, a "bilinear" resampling algorithm is used. Any string or :class:`~rasterio.enums.Resampling` can be passed. + + +rast1_warped = rast1.reproject(rast2) +rast1_warped + +# %% +# .. note:: +# Because no :attr:`geoutils.Raster.nodata` value is defined in the original image, the default value ``255`` for :class:`numpy.uint8` is used. This +# value is detected as already existing in the original raster, however, which raises a ``UserWarning``. If your :attr:`geoutils.Raster.nodata` is not defined, +# use :func:`geoutils.Raster.set_nodata`. +# +# Now the shape and georeferencing should be the same as that of the second raster, shown above. + +print(rast1_warped.info()) + +# %% +# We can plot the two rasters next to one another + +rast1_warped.show(cmap="Reds") +rast2.show(cmap="Blues") + +# %% +# **Second option:** we can pass any georeferencing argument to :func:`~geoutils.Raster.reproject`, such as ``dst_size`` and ``dst_crs``, and will only +# deduce other parameters from the raster from which it is called (for ``dst_crs``, an EPSG code can be passed directly as :class:`int`). + +# Ensure the right nodata value is set +rast2.set_nodata(0) +# Pass the desired georeferencing parameters +rast2_warped = rast2.reproject(dst_size=(100, 100), dst_crs=32645) +print(rast2_warped.info()) diff --git a/examples/io/README.rst b/examples/io/README.rst index f4b389036..0e3915f20 100644 --- a/examples/io/README.rst +++ b/examples/io/README.rst @@ -1,4 +1,4 @@ Input/output ============ -Examples about opening, creating, loading or saving raster and vector data. +Examples about **opening**, **creating**, **loading** or **saving** geospatial data. diff --git a/examples/io/read_satimg.py b/examples/io/read_satimg.py index 9c4d4afd2..e58421359 100644 --- a/examples/io/read_satimg.py +++ b/examples/io/read_satimg.py @@ -1,23 +1,22 @@ """ -SatelliteImage class basics -=========================== +Parsing image metadata +====================== -This is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`. +This example demonstrates the instantiation of an image through :class:`~geoutils.SatelliteImage`. """ -import matplotlib.pyplot as plt import geoutils as gu # %% -# Example raster: -img = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) +# We print the filename of our raster that, as often with satellite data, holds metadata information. +filename = gu.examples.get_path("everest_landsat_b4") +import os +print(os.path.basename(filename)) # %% -# Info: -print(img) - +# We open it as a geo-image, unsilencing the attribute retrieval to see the parsed data. +img = gu.SatelliteImage(gu.examples.get_path("everest_landsat_b4"), silent=False) # %% -# A plot: -img.show(cmap="Greys_r") -plt.show() +# We have now retrieved the metadata. For the rest, the :class:`~geoutils.SatelliteImage` is a subclass of :class:`~geoutils.Raster`, and behaves similarly. +img diff --git a/geoutils/geovector.py b/geoutils/geovector.py index ee247aa32..73bc91df1 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -432,6 +432,25 @@ def rasterize( return output + @classmethod + def from_bounds_projected(cls, raster_or_vector: gu.Raster | VectorType, out_crs: CRS | None = None, densify_pts: int = 5000) -> VectorType: + """Create a vector polygon from projected bounds of a raster or vector. + + :param raster_or_vector: A raster or vector + :param out_crs: In which CRS to compute the bounds + :param densify_pts: Maximum points to be added between image corners to account for nonlinear edges. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + """ + + if out_crs is None: + out_crs = raster_or_vector.crs + + poly = gu.projtools.bounds2poly(raster_or_vector, out_crs=out_crs) + + df = gpd.GeoDataFrame(geometry=[poly], crs=out_crs) + + return cls(df) + def query(self: VectorType, expression: str, inplace: bool = False) -> VectorType: """ Query the Vector dataset with a valid Pandas expression. From 9af03dcc49f058e84195ac9904baef017093b1e3 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 21 Feb 2023 13:19:15 -0800 Subject: [PATCH 039/184] Update sphinx env method --- .gitignore | 4 +++- .readthedocs.yml => .readthedocs.yaml | 8 ++------ dev-environment.yml | 1 - dev-requirements.txt | 24 ------------------------ 4 files changed, 5 insertions(+), 32 deletions(-) rename .readthedocs.yml => .readthedocs.yaml (77%) delete mode 100644 dev-requirements.txt diff --git a/.gitignore b/.gitignore index 2d09d8b18..410255d81 100644 --- a/.gitignore +++ b/.gitignore @@ -141,7 +141,9 @@ geoutils/version.py # End of https://www.gitignore.io/api/python .vim/ doc/source/file.txt -doc/source/auto_examples/ +doc/source/io_examples/ +doc/source/handling_examples/ +doc/source/analysis_examples/ doc/source/gen_modules/ # Directories where example data is downloaded diff --git a/.readthedocs.yml b/.readthedocs.yaml similarity index 77% rename from .readthedocs.yml rename to .readthedocs.yaml index 7209d393d..91f7860ea 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yaml @@ -13,9 +13,5 @@ sphinx: # Optionally build your docs in additional formats such as PDF and ePub formats: [] -python: - version: 3.8 - install: - - requirements: dev-requirements.txt - - method: pip - path: . +conda: + environment: dev-environment.yml diff --git a/dev-environment.yml b/dev-environment.yml index a6a27ac0c..d3e8a61e0 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -15,7 +15,6 @@ dependencies: - graphviz - jinja2<3.1 - sphinx - - myst-parser - myst-nb - sphinx-autodoc-typehints - sphinxcontrib-programoutput diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index 3453961df..000000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,24 +0,0 @@ -rasterio >= 1.3 -geopandas >= 0.10.0 -pyproj -flake8 -pytest -pytest-xdist -pytest-lazy-fixture -sphinx -sphinx-book-theme -pylint -sphinxcontrib_programoutput -sphinx_autodoc_typehints -matplotlib -myst-parser -myst-nb -scipy -pre-commit -typing-extensions -sphinx-gallery -scikit-image -pyyaml -autovizwidget -graphviz -jinja2 < 3.1 From bbc692191c8067bb63e13c52325a64ea8b4d69d2 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 21 Feb 2023 13:26:16 -0800 Subject: [PATCH 040/184] Add mamba to readthedocs build --- .readthedocs.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 91f7860ea..5a07259f8 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,6 +5,11 @@ # Required version: 2 +build: + os: "ubuntu-20.04" + tools: + python: "mambaforge-4.10" + # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/source/conf.py @@ -13,5 +18,6 @@ sphinx: # Optionally build your docs in additional formats such as PDF and ePub formats: [] + conda: environment: dev-environment.yml From cd7cd4f247210b458665500401f63dbdf33329eb Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 21 Feb 2023 13:36:03 -0800 Subject: [PATCH 041/184] Add self package install in dev-env --- dev-environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-environment.yml b/dev-environment.yml index d3e8a61e0..35b4f8e82 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -33,3 +33,4 @@ dependencies: - pip: - sphinx-book-theme + - -e ./ From 3c07e0ffad877edccc08fef745cbceae117f5318 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 21 Feb 2023 13:48:03 -0800 Subject: [PATCH 042/184] Incremental commit on documentation --- doc/source/about_geoutils.md | 4 ++-- doc/source/index.md | 2 +- doc/source/quick_start.md | 4 +++- examples/analysis/README.rst | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 52223ec4c..08f4b6010 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -5,7 +5,7 @@ ## What is GeoUtils? GeoUtils1 is a **[Python](https://www.python.org/) package for the handling and analysis of georeferenced data**, developed with the objective of -making geospatial analysis accessible, intuitive and robust. +making geospatial analysis accessible, efficient and robust. GeoUtils is designed for all Earth and planetary observation science. It is generally **most useful for remote sensing and Earth's surface applications relying on moderate- to high-resolution georeferenced data** (typically < 1 km). Applications that, for analysis, require reprojections, re-gridding, point @@ -26,7 +26,7 @@ We are working on making features fully consistent for the first long-term relea GeoUtils is built on top of [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenced operations, and relies on [NumPy](https://numpy.org/doc/stable/), [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: -- A **common and consistent framework** for rasters and vectors handling and analysis, +- A **common and consistent framework** for efficient rasters and vectors handling and analysis, - A structure following the **principal of least knowledge**2 to foster accessibility, - A **pythonic arithmetic** and **NumPy interfacing** for robust numerical computing and intuitive use. diff --git a/doc/source/index.md b/doc/source/index.md index dc53edabe..17ff5fa27 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -6,7 +6,7 @@ # Welcome to GeoUtils' documentation! ```{epigraph} -GeoUtils is an **accessible**, **intuitive** and **robust** package to quickly handle and analyze geospatial data. +GeoUtils is an **accessible**, **efficient** and **robust** package to handle and analyze geospatial data. ``` ```{important} diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 5d2845147..cb90edd17 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -33,6 +33,7 @@ an {class}`~affine.Affine` as `.transform`, and a {class}`float` or {class}`int` linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata, and the array at the appropriate time. ```{code-cell} ipython3 +:tags: [hide-output] # The opened raster rast ``` @@ -41,6 +42,7 @@ A {class}`~geoutils.Vector` is a composition class with a single main attribute: wrapped directly into {class}`~geoutils.Vector`. ```{code-cell} ipython3 +:tags: [hide-output] # The opened vector vect ``` @@ -163,7 +165,7 @@ For saving, {func}`~geoutils.Raster.save` is used. import matplotlib.pyplot as plt fig = plt.figure() ax = plt.gca() -rast.show(ax=ax, cmap='Reds', cb_title='Normalized infrared') +rast.show(ax=ax, cmap='Reds', cbar_title='Normalized infrared') vect_aoi.ds.plot(ax=ax, fc='none', ec='k', lw=0.5) ``` diff --git a/examples/analysis/README.rst b/examples/analysis/README.rst index 6744125b5..21afb9266 100644 --- a/examples/analysis/README.rst +++ b/examples/analysis/README.rst @@ -1,5 +1,5 @@ Analysis ======== -Examples about numerical operations on rasters, and additional features such as proximity and buffer without overlap. +Examples about **numerical operations** on rasters, and **additional features** such as **proximity** or **buffer without overlap** (Voronoi polygons). From f1b75b7b43843b9ee75f21b1ca0627fa360581a3 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 21 Feb 2023 19:20:51 -0800 Subject: [PATCH 043/184] Add logo to documentation --- doc/source/_static/logo_only_v3.svg | 201 ++++++++++++++++++++++++++ doc/source/_static/logo_v3_2.svg | 213 ++++++++++++++++++++++++++++ doc/source/conf.py | 5 +- doc/source/index.md | 27 +++- 4 files changed, 438 insertions(+), 8 deletions(-) create mode 100644 doc/source/_static/logo_only_v3.svg create mode 100644 doc/source/_static/logo_v3_2.svg diff --git a/doc/source/_static/logo_only_v3.svg b/doc/source/_static/logo_only_v3.svg new file mode 100644 index 000000000..f82cc2b30 --- /dev/null +++ b/doc/source/_static/logo_only_v3.svg @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/source/_static/logo_v3_2.svg b/doc/source/_static/logo_v3_2.svg new file mode 100644 index 000000000..0e0ad5d43 --- /dev/null +++ b/doc/source/_static/logo_v3_2.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eoUtils + + + + + + + + + + + + + + diff --git a/doc/source/conf.py b/doc/source/conf.py index cd36ad1a1..d6714a807 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -113,15 +113,18 @@ # a list of builtin themes. # html_theme = "sphinx_book_theme" +html_favicon = "_static/logo_only_v3.svg" +html_logo = "_static/logo_v3_2.svg" +html_title = "GeoUtils" html_theme_options = { "use_sidenotes": True, "repository_url": "https://github.com/GlacioHack/geoutils", "use_repository_button": True, + "logo_only": True, } # html_logo = "path/to/myimage.png" -html_title = "GeoUtils" html_static_path = ['_static'] diff --git a/doc/source/index.md b/doc/source/index.md index 17ff5fa27..c7da63040 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -1,13 +1,26 @@ -% GeoUtils documentation master file, created by -% sphinx-quickstart on Fri Nov 13 17:43:16 2020. -% You can adapt this file completely to your liking, but it should at least -% contain the root 'toctree' directive. +--- +title: GeoUtils +--- -# Welcome to GeoUtils' documentation! +::::{grid} +:reverse: +:gutter: 2 1 1 1 +:margin: 4 4 1 1 -```{epigraph} -GeoUtils is an **accessible**, **efficient** and **robust** package to handle and analyze geospatial data. +:::{grid-item} +:columns: 4 + +```{image} ./_static/logo_only_v3.svg +:width: 150px ``` +::: + +:::{grid-item} +:columns: 8 +:class: sd-fs-3 + +GeoUtils is an **accessible**, **efficient** and **robust** package to handle and analyze geospatial data. +::: ```{important} :class: margin From 15fb4757e97e10ffd0e45b92888609a5a1d75182 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 22 Feb 2023 15:33:02 -0800 Subject: [PATCH 044/184] Incremental commit on doc --- dev-environment.yml | 1 + doc/source/_static/logo_only_v3.svg | 134 +++++++++++++++-- .../_static/{logo_v3_2.svg => logo_v3.svg} | 140 ++++++++++++++++-- doc/source/api.md | 2 +- doc/source/conf.py | 6 +- doc/source/core_lazy_load.md | 2 +- doc/source/index.md | 56 ++++++- examples/analysis/README.rst | 3 + examples/analysis/array_numerics/README.rst | 2 + examples/analysis/geospatial/README.rst | 2 + .../{ => geospatial}/buffer_voronoi.py | 0 .../{ => geospatial}/proximity_metric.py | 0 examples/analysis/point_estimation/README.rst | 2 + examples/handling/README.rst | 2 + examples/handling/georeferencing/README.rst | 2 + .../{ => georeferencing}/crop_raster.py | 8 +- .../handling/georeferencing/crop_vector.py | 57 +++++++ .../reproj_raster.py} | 10 +- .../handling/georeferencing/reproj_vector.py | 43 ++++++ examples/handling/interface/README.rst | 2 + examples/handling/interface/create_mask.py | 46 ++++++ examples/handling/interface/polygonize.py | 39 +++++ examples/handling/interface/rasterize.py | 57 +++++++ examples/handling/interface/topoints.py | 21 +++ examples/io/README.rst | 2 + examples/io/import_export/README.rst | 2 + examples/io/{ => import_export}/from_array.py | 0 examples/io/import_export/import_raster.py | 36 +++++ examples/io/import_export/import_vector.py | 21 +++ examples/io/open_save/README.rst | 2 + examples/io/{ => open_save}/read_raster.py | 4 +- examples/io/{ => open_save}/read_satimg.py | 4 +- examples/io/{ => open_save}/read_vector.py | 4 +- geoutils/georaster/raster.py | 5 +- geoutils/geovector.py | 13 +- 35 files changed, 680 insertions(+), 50 deletions(-) rename doc/source/_static/{logo_v3_2.svg => logo_v3.svg} (99%) create mode 100644 examples/analysis/array_numerics/README.rst create mode 100644 examples/analysis/geospatial/README.rst rename examples/analysis/{ => geospatial}/buffer_voronoi.py (100%) rename examples/analysis/{ => geospatial}/proximity_metric.py (100%) create mode 100644 examples/analysis/point_estimation/README.rst create mode 100644 examples/handling/georeferencing/README.rst rename examples/handling/{ => georeferencing}/crop_raster.py (89%) create mode 100644 examples/handling/georeferencing/crop_vector.py rename examples/handling/{warp_raster.py => georeferencing/reproj_raster.py} (93%) create mode 100644 examples/handling/georeferencing/reproj_vector.py create mode 100644 examples/handling/interface/README.rst create mode 100644 examples/handling/interface/create_mask.py create mode 100644 examples/handling/interface/polygonize.py create mode 100644 examples/handling/interface/rasterize.py create mode 100644 examples/handling/interface/topoints.py create mode 100644 examples/io/import_export/README.rst rename examples/io/{ => import_export}/from_array.py (100%) create mode 100644 examples/io/import_export/import_raster.py create mode 100644 examples/io/import_export/import_vector.py create mode 100644 examples/io/open_save/README.rst rename examples/io/{ => open_save}/read_raster.py (95%) rename examples/io/{ => open_save}/read_satimg.py (93%) rename examples/io/{ => open_save}/read_vector.py (88%) diff --git a/dev-environment.yml b/dev-environment.yml index 35b4f8e82..59b1c345d 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -32,5 +32,6 @@ dependencies: - rioxarray - pip: + - sphinx-design - sphinx-book-theme - -e ./ diff --git a/doc/source/_static/logo_only_v3.svg b/doc/source/_static/logo_only_v3.svg index f82cc2b30..a6dd0a730 100644 --- a/doc/source/_static/logo_only_v3.svg +++ b/doc/source/_static/logo_only_v3.svg @@ -4,9 +4,9 @@ + + @@ -164,19 +184,19 @@ id="g4796" transform="matrix(0.21214454,0,0,0.21214454,3636.4016,8824.2868)"> + sodipodi:nodetypes="cscsscccscccccccccccccccccccccccccccccc" /> + + + + + + + + + + + + + + + + diff --git a/doc/source/_static/logo_v3_2.svg b/doc/source/_static/logo_v3.svg similarity index 99% rename from doc/source/_static/logo_v3_2.svg rename to doc/source/_static/logo_v3.svg index 0e0ad5d43..cf9bf0b60 100644 --- a/doc/source/_static/logo_v3_2.svg +++ b/doc/source/_static/logo_v3.svg @@ -4,9 +4,9 @@ eoUtils + id="tspan939">eoUtils + + @@ -176,19 +196,19 @@ id="g4796" transform="matrix(0.21214454,0,0,0.21214454,3636.4016,8824.2868)"> + sodipodi:nodetypes="cscsscccscccccccccccccccccccccccccccccc" /> + + + + + + + + + + + + + + + + diff --git a/doc/source/api.md b/doc/source/api.md index cf9f2c234..459281cda 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -1,7 +1,7 @@ (api)= # API reference -This page provides an auto-generated summary of GeoUtils’s API. +This page provides an auto-generated summary of GeoUtils’ API. For more details and examples, refer to the relevant chapters in the main part of the documentation. diff --git a/doc/source/conf.py b/doc/source/conf.py index d6714a807..eb0c9675b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -32,6 +32,7 @@ "sphinx.ext.autosummary", # Create API doc summary texts from the docstrings. "sphinx.ext.inheritance_diagram", # For class inheritance diagrams. "sphinx.ext.graphviz", # To render graphviz diagrams. + "sphinx_design", # To render nice blocks "sphinx_autodoc_typehints", # Include type hints in the API documentation. "sphinxcontrib.programoutput", "sphinx_gallery.gen_gallery", # Examples gallery @@ -40,6 +41,9 @@ "myst_nb" # MySt for rendering Jupyter notebook in documentation ] +# For sphinx design to work properly +myst_enable_extensions = ["colon_fence"] + nb_kernel_rgx_aliases = {".*geoutils.*": "python3"} intersphinx_mapping = { @@ -114,7 +118,7 @@ # html_theme = "sphinx_book_theme" html_favicon = "_static/logo_only_v3.svg" -html_logo = "_static/logo_v3_2.svg" +html_logo = "_static/logo_v3.svg" html_title = "GeoUtils" html_theme_options = { diff --git a/doc/source/core_lazy_load.md b/doc/source/core_lazy_load.md index 28182e1cb..2e38ded41 100644 --- a/doc/source/core_lazy_load.md +++ b/doc/source/core_lazy_load.md @@ -65,7 +65,7 @@ package! ``` Some georeferencing operations be done without loading the entire array Right now, relying directly on Rasterio, GeoUtils supports optimized subsetting -through the {func}`~geoutils.Raster.`crop` method. +through the {func}`~geoutils.Raster.crop` method. ```{code-cell} ipython3 # The previously cropped Raster was loaded without accessing the entire array diff --git a/doc/source/index.md b/doc/source/index.md index c7da63040..34f37bb0b 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -11,7 +11,7 @@ title: GeoUtils :columns: 4 ```{image} ./_static/logo_only_v3.svg -:width: 150px +:width: 300px ``` ::: @@ -19,9 +19,60 @@ title: GeoUtils :columns: 8 :class: sd-fs-3 -GeoUtils is an **accessible**, **efficient** and **robust** package to handle and analyze geospatial data. +GeoUtils is an **accessible**, **efficient** and **reliable** package to analyze geospatial data. +:::: + +**Accessible** owing to its convenient object-based structure, intuitive match-reference operations and familiar geospatial dependencies +([Rasterio](https://rasterio.readthedocs.io/en/latest/), [Rioxarray](https://corteva.github.io/rioxarray/stable/), +[GeoPandas](https://geopandas.org/en/stable/docs.html), [PyProj](https://pyproj4.github.io/pyproj/stable/index.html)). + +**Efficient** owing to its implicit lazy loading functionalities, logical integration with pythonic operators and array interfacing +([NumPy](https://numpy.org/doc/stable/), [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/)). + +**Reliable** owing to its consistent higher-level operations respecting geospatial intricacies such as nodata values and pixel interpretation, ensured by +its testing suite and type checking ([Pytest](https://docs.pytest.org/en/7.2.x/), [Mypy](https://mypy-lang.org/)). + +# Where to start? + +---------------- + +::::{grid} 1 2 2 3 +:gutter: 1 1 1 2 + +:::{grid-item-card} {material-regular}`edit_note;2em` About GeoUtils +:link: about_geoutils +:link-type: ref + +Learn more about why we developed GeoUtils. + ++++ +[Learn more »](about_geoutils) ::: +:::{grid-item-card} {material-regular}`data_exploration;2em` Quick start +:link: quick_start +:link-type: ref + +Run through a short example of the package functionalities. + ++++ +[Learn more »](quick_start) +::: + +:::{grid-item-card} {material-regular}`preview;2em` Features +:link: core_index +:link-type: ref + +Dive into the full documentation. + ++++ +[Learn more »](core_index) +::: + +:::: + +---------------- + ```{important} :class: margin GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for @@ -29,6 +80,7 @@ GeoUtils is in early stages of development and its features might evolve rapidly We are working on making features fully consistent for the first long-term release ``v0.1`` (likely sometime in 2023). ``` +# Table of contents ```{toctree} :caption: Getting started diff --git a/examples/analysis/README.rst b/examples/analysis/README.rst index 21afb9266..67dff9231 100644 --- a/examples/analysis/README.rst +++ b/examples/analysis/README.rst @@ -3,3 +3,6 @@ Analysis Examples about **numerical operations** on rasters, and **additional features** such as **proximity** or **buffer without overlap** (Voronoi polygons). +Some examples rely on NumPy or pythonic operators on :class:`Rasters` (see :ref:`core-py-ops` and :ref:`core-array-funcs`). + +Others also benefit from match-reference (see :ref:`core-match-ref`). \ No newline at end of file diff --git a/examples/analysis/array_numerics/README.rst b/examples/analysis/array_numerics/README.rst new file mode 100644 index 000000000..7ab702432 --- /dev/null +++ b/examples/analysis/array_numerics/README.rst @@ -0,0 +1,2 @@ +Raster numerics +--------------- \ No newline at end of file diff --git a/examples/analysis/geospatial/README.rst b/examples/analysis/geospatial/README.rst new file mode 100644 index 000000000..51e73b398 --- /dev/null +++ b/examples/analysis/geospatial/README.rst @@ -0,0 +1,2 @@ +Distance estimation +------------------- \ No newline at end of file diff --git a/examples/analysis/buffer_voronoi.py b/examples/analysis/geospatial/buffer_voronoi.py similarity index 100% rename from examples/analysis/buffer_voronoi.py rename to examples/analysis/geospatial/buffer_voronoi.py diff --git a/examples/analysis/proximity_metric.py b/examples/analysis/geospatial/proximity_metric.py similarity index 100% rename from examples/analysis/proximity_metric.py rename to examples/analysis/geospatial/proximity_metric.py diff --git a/examples/analysis/point_estimation/README.rst b/examples/analysis/point_estimation/README.rst new file mode 100644 index 000000000..47288d7ca --- /dev/null +++ b/examples/analysis/point_estimation/README.rst @@ -0,0 +1,2 @@ +Point estimation +---------------- \ No newline at end of file diff --git a/examples/handling/README.rst b/examples/handling/README.rst index fe929f99d..957209c13 100644 --- a/examples/handling/README.rst +++ b/examples/handling/README.rst @@ -2,3 +2,5 @@ Handling ======== Examples about **warping**, **reprojecting**, **resampling**, **cropping**, as well as **rasterizing** and **polygonizing** of georeferenced data. + +All of these methods support simply passing a reference to match (see :ref:`core-match-ref`). \ No newline at end of file diff --git a/examples/handling/georeferencing/README.rst b/examples/handling/georeferencing/README.rst new file mode 100644 index 000000000..9ab18adb8 --- /dev/null +++ b/examples/handling/georeferencing/README.rst @@ -0,0 +1,2 @@ +Geo-transformations +------------------- \ No newline at end of file diff --git a/examples/handling/crop_raster.py b/examples/handling/georeferencing/crop_raster.py similarity index 89% rename from examples/handling/crop_raster.py rename to examples/handling/georeferencing/crop_raster.py index e607b2758..0c0e9831a 100644 --- a/examples/handling/crop_raster.py +++ b/examples/handling/georeferencing/crop_raster.py @@ -2,23 +2,23 @@ Crop a raster ============= -This example demonstrates the cropping of a raster using :class:`~geoutils.Raster.crop`. +This example demonstrates the cropping of a raster using :func:`geoutils.Raster.crop`. """ # sphinx_gallery_thumbnail_number = 2 # %% -# We open two example rasters. +# We open a raster and vector, and subset the latter. import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) vect = gu.Vector(vect.ds[vect.ds["RGIId"] == "RGI60-15.10055"]) # %% -# The first raster has larger extent and higher resolution than the second one +# The first raster has larger extent and higher resolution than the second one. print(rast.info()) print(vect.bounds) # %% -# Let's plot the raster and vector +# Let's plot the raster and vector. import matplotlib.pyplot as plt ax = plt.gca() rast.show(ax=ax, cmap="Purples") diff --git a/examples/handling/georeferencing/crop_vector.py b/examples/handling/georeferencing/crop_vector.py new file mode 100644 index 000000000..5cd355597 --- /dev/null +++ b/examples/handling/georeferencing/crop_vector.py @@ -0,0 +1,57 @@ +""" +Crop a vector +============= + +This example demonstrates the cropping of a vector using :func:`geoutils.Vector.crop`. +""" +# sphinx_gallery_thumbnail_number = 3 +# %% +# We open a raster and vector. +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + +# %% +# Let's plot the raster and vector. The raster has smaller extent than the vector. +import matplotlib.pyplot as plt +ax = plt.gca() +rast.show(ax=ax, cmap="Greys_r", alpha=0.7) +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) + +# %% +# **First option:** using the raster as a reference to match, we crop the vector. We simply have to pass the :class:`~geoutils.Raster` as single argument to +# :func:`~geoutils.Vector.crop`. See :ref:`core-match-ref` for more details. + +vect.crop(rast) + +# %% +# .. note:: +# By default, :func:`~geoutils.Vector.crop` is done in-place, replacing ``vect``. This behaviour can be modified by passing ``inplace=False``. +# + +ax = plt.gca() +rast.show(ax=ax, cmap="Greys_r", alpha=0.7) +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) + +# %% +# The :func:`~geoutils.Vector.crop` keeps all features with geometries intersecting the extent to crop to. We can also force a clipping of the geometries +# within the bounds using ``clip=True``. + +vect.crop(rast, clip=True) +ax = plt.gca() +rast.show(ax=ax, cmap="Greys_r", alpha=0.7) +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) + +# %% +# **Second option:** we can pass other ``crop_geom`` argument to :func:`~geoutils.Vector.crop`, including another :class:`~geoutils.Vector` or a +# simple :class:`tuple` of bounds. + +bounds = rast.get_bounds_projected(out_crs=vect.crs) +vect.crop(cropGeom=(bounds.left + 0.5*(bounds.right - bounds.left), + bounds.bottom, + bounds.right, + bounds.top)) + +ax = plt.gca() +rast.show(ax=ax, cmap="Greys_r", alpha=0.7) +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) diff --git a/examples/handling/warp_raster.py b/examples/handling/georeferencing/reproj_raster.py similarity index 93% rename from examples/handling/warp_raster.py rename to examples/handling/georeferencing/reproj_raster.py index 955ce0a31..7d3472777 100644 --- a/examples/handling/warp_raster.py +++ b/examples/handling/georeferencing/reproj_raster.py @@ -1,8 +1,8 @@ """ -Warp a raster -============= +Reproject a raster +=================) -This example demonstrates the warping of a raster using :class:`~geoutils.Raster.reproject`. +This example demonstrates the reprojection of a raster using :func:`geoutils.Raster.reproject`. """ # %% # We open two example rasters. @@ -11,12 +11,12 @@ rast2 = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) # %% -# The first raster has larger extent and higher resolution than the second one +# The first raster has larger extent and higher resolution than the second one. print(rast1.info()) print(rast2.info()) # %% -# Let's plot the first raster, with the warped extent of the second one +# Let's plot the first raster, with the warped extent of the second one. import matplotlib.pyplot as plt ax = plt.gca() rast1.show(ax=ax, cmap="Blues") diff --git a/examples/handling/georeferencing/reproj_vector.py b/examples/handling/georeferencing/reproj_vector.py new file mode 100644 index 000000000..9823aae74 --- /dev/null +++ b/examples/handling/georeferencing/reproj_vector.py @@ -0,0 +1,43 @@ +""" +Reproject a vector +================== + +This example demonstrates the reprojection of a vector using :func:`geoutils.Vector.reproject`. +""" +# sphinx_gallery_thumbnail_number = 3 + +# %% +# We open a raster and vector. +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + +# %% +# The two objects are in different projections. +print(rast.info()) +print(vect.info()) + +# %% +# Let's plot the two in their original projection. +import matplotlib.pyplot as plt +rast.show(cmap="Greys_r") +vect.ds.plot(fc='none', ec='tab:purple', lw=3) + +# %% +# **First option:** using the raster as a reference to match, we reproject the vector. We simply have to pass the :class:`~geoutils.Raster` as an argument +# to :func:`~geoutils.Vector.reproject`. See :ref:`core-match-ref` for more details. + +vect_reproj = vect.reproject(rast) + +# %% +# We can plot the vector in its new projection. + +vect_reproj.ds.plot(fc='none', ec='tab:purple', lw=3) + +# %% +# **Second option:** we can pass the georeferencing argument ``dst_crs`` to :func:`~geoutils.Vector.reproject` (an EPSG code can be passed directly as +# :class:`int`). + +# Reproject in UTM zone 45N +vect_reproj = vect.reproject(dst_crs=32645) +vect_reproj \ No newline at end of file diff --git a/examples/handling/interface/README.rst b/examples/handling/interface/README.rst new file mode 100644 index 000000000..c32885faf --- /dev/null +++ b/examples/handling/interface/README.rst @@ -0,0 +1,2 @@ +Raster-vector interfacing +------------------------- \ No newline at end of file diff --git a/examples/handling/interface/create_mask.py b/examples/handling/interface/create_mask.py new file mode 100644 index 000000000..ef9c67332 --- /dev/null +++ b/examples/handling/interface/create_mask.py @@ -0,0 +1,46 @@ +""" +Mask from a vector +================== + +This example demonstrates the creation of a mask from a vector using :func:`geoutils.Vector.create_mask`. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open a raster and vector. +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + +# %% +# Let's plot the raster and vector. +import matplotlib.pyplot as plt +ax = plt.gca() +rast.show(ax=ax, cmap="Purples") +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) + +# %% +# **First option:** using the raster as a reference to match, we create a mask for the vector in any projection and georeferenced grid. We simply have to pass +# the :class:`~geoutils.Raster` as single argument to :func:`~geoutils.Vector.rasterize`. See :ref:`core-match-ref` for more details. + +vect_rasterized = vect.create_mask(rast) +vect_rasterized.show() + +# %% +# .. note:: +# This is equivalent to using :func:`~geoutils.Vector.rasterize` with ``in_value=1`` and ``out_value=0`` and will return a :class:`~geoutils.Mask`. + +vect_rasterized + +# %% +# **Second option:** we can pass any georeferencing parameter to :func:`~geoutils.Raster.create_mask`. Any unpassed attribute will be deduced from the +# :class:`~geoutils.Vector` itself, except from the :attr:`~geoutils.Raster.shape` to rasterize that will default to 1000 x 1000. + + +# vect_rasterized = vect.create_mask(xres=500) +# vect_rasterized.show() + +# %% +# .. important:: +# The :attr:`~geoutils.Raster.shape` or the :attr:`~geoutils.Raster.res` are the only unknown arguments to rasterize a :class:`~geoutils.Vector`, +# one or the other can be passed. +# diff --git a/examples/handling/interface/polygonize.py b/examples/handling/interface/polygonize.py new file mode 100644 index 000000000..a95923aee --- /dev/null +++ b/examples/handling/interface/polygonize.py @@ -0,0 +1,39 @@ +""" +Polygonize a raster +=================== + +This example demonstrates the polygonizing of a raster using :func:`geoutils.Raster.polygonize` and :func:`geoutils.Mask.polygonize`. +""" +# sphinx_gallery_thumbnail_number = 3 +# %% +# We open a raster. +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+5000, rast.bounds.bottom+5000]) +# %% +# Let's plot the raster. +rast.show(cmap="terrain") + +# %% +# We polygonize the raster. + +rast_polygonized = rast.polygonize() +rast_polygonized.ds.plot() + +# %% +# By default, :func:`~geoutils.Raster.polygonize` will try to polygonize target all valid values. Instead, one can specify discrete values to target by +# passing a number or :class:`list`, or a range of values by passing a :class:`tuple`. + +# A range of values to polygonize +rast_polygonized = rast.polygonize((2500, 3000)) +rast_polygonized.ds.plot() + +# %% +# An even simpler way to do this is to compute a :func:`~geoutils.Mask` to polygonize using logical comparisons on the :func:`~geoutils.Raster`. + +rast_polygonized = ((2500 < rast) & (rast < 3000)).polygonize() +rast_polygonized.ds.plot() + +# %% +# .. note:: +# See :ref:`core-py-ops` for more details on casting to :func:`~geoutils.Mask`. \ No newline at end of file diff --git a/examples/handling/interface/rasterize.py b/examples/handling/interface/rasterize.py new file mode 100644 index 000000000..bcc9b497f --- /dev/null +++ b/examples/handling/interface/rasterize.py @@ -0,0 +1,57 @@ +""" +Rasterize a vector +================== + +This example demonstrates the rasterizing of a vector using :func:`geoutils.Vector.rasterize`. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open a raster and vector. +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + +# %% +# Let's plot the raster and vector. +import matplotlib.pyplot as plt +ax = plt.gca() +rast.show(ax=ax, cmap="Purples") +vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) + +# %% +# **First option:** using the raster as a reference to match, we rasterize the vector in any projection and georeferenced grid. We simply have to pass the +# :class:`~geoutils.Raster` as single argument to :func:`~geoutils.Vector.rasterize`. See :ref:`core-match-ref` for more details. + +vect_rasterized = vect.rasterize(rast) +vect_rasterized.show(cmap="viridis") + +# %% +# By default, :func:`~geoutils.Vector.rasterize` will burn the index of the :class:`~geoutils.Vector`'s features in their geometry. We can specify the ``in_value`` to burn a +# single value, or any iterable with the same length as there are features in the :class:`~geoutils.Vector`. An ``out_value`` can be passed to burn +# outside the geometries. +# + +vect_rasterized = vect.rasterize(rast, in_value=1) +vect_rasterized.show() + +# %% +# +# .. note:: +# If the rasterized ``in_value`` is fixed to 1 and ``out_value`` to 0 (default), then :func:`~geoutils.Vector.rasterize` is creating a boolean mask. +# This is equivalent to using :func:`~geoutils.Vector.create_mask`, and will return a :class:`~geoutils.Mask`. + +vect_rasterized + +# %% +# **Second option:** we can pass any georeferencing parameter to :func:`~geoutils.Raster.rasterize`. Any unpassed attribute will be deduced from the +# :class:`~geoutils.Vector` itself, except from the :attr:`~geoutils.Raster.shape` to rasterize that will default to 1000 x 1000. + + +# vect_rasterized = vect.rasterize(xres=500) +# vect_rasterized.show() + +# %% +# .. important:: +# The :attr:`~geoutils.Raster.shape` or the :attr:`~geoutils.Raster.res` are the only unknown arguments to rasterize a :class:`~geoutils.Vector`, +# one or the other can be passed. +# diff --git a/examples/handling/interface/topoints.py b/examples/handling/interface/topoints.py new file mode 100644 index 000000000..ab5f5b56c --- /dev/null +++ b/examples/handling/interface/topoints.py @@ -0,0 +1,21 @@ +""" +Raster to points +================ + +This example demonstrates the conversion of a raster to point vector using :func:`geoutils.Raster.to_points`. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open a raster. +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+500, rast.bounds.bottom+500]) +# %% +# Let's plot the raster. +rast.show(cmap="terrain") + +# %% +# We convert the raster to points. + +pts_rast = rast.to_points(as_frame=True) +pts_rast.plot(column="b1", cmap="terrain", legend=True) diff --git a/examples/io/README.rst b/examples/io/README.rst index 0e3915f20..e3556990b 100644 --- a/examples/io/README.rst +++ b/examples/io/README.rst @@ -2,3 +2,5 @@ Input/output ============ Examples about **opening**, **creating**, **loading** or **saving** geospatial data. + +With :class:`Rasters`, data is only loaded when necessary (see :ref:`core-lazy-load`). \ No newline at end of file diff --git a/examples/io/import_export/README.rst b/examples/io/import_export/README.rst new file mode 100644 index 000000000..a805d05d2 --- /dev/null +++ b/examples/io/import_export/README.rst @@ -0,0 +1,2 @@ +Import and export +----------------- \ No newline at end of file diff --git a/examples/io/from_array.py b/examples/io/import_export/from_array.py similarity index 100% rename from examples/io/from_array.py rename to examples/io/import_export/from_array.py diff --git a/examples/io/import_export/import_raster.py b/examples/io/import_export/import_raster.py new file mode 100644 index 000000000..2f24521b3 --- /dev/null +++ b/examples/io/import_export/import_raster.py @@ -0,0 +1,36 @@ +""" +From/To Rasterio +================ + +This example demonstrates importing and exporting a :class:`rasterio.io.DatasetReader` or :class:`rasterio.io.DatasetReader` from and to a +:class:`~geoutils.Raster`. +""" + +# %% +# A raster can be imported from a :class:`rasterio.io.DatasetReader` or :class:`rasterio.io.MemoryFile` simply by instantiating :class:`~geoutils.Raster`. +import geoutils as gu +import rasterio as rio + +ds = rio.DatasetReader(gu.examples.get_path("everest_landsat_b4")) +rast = gu.Raster(ds) +rast + +# %% +# The data is unloaded, as when instantiated with a filename. +# The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. +rast.show(cmap="Greys_r") + +# %% +# We can also pass a :class:`rasterio.io.MemoryFile` during instantiation. + +mem = rio.MemoryFile(open(gu.examples.get_path("everest_landsat_b4"), "rb")) +rast = gu.Raster(mem) +rast + +# %% +# The data is, as expected, already in memory. +# +# Finally, we can export a :class:`~geoutils.Raster` to a :class:`rasterio.io.DatasetReader` of a :class:`rasterio.io.MemoryFile` using +# :class:`~geoutils.Raster.to_rio_dataset` + +rast.to_rio_dataset() \ No newline at end of file diff --git a/examples/io/import_export/import_vector.py b/examples/io/import_export/import_vector.py new file mode 100644 index 000000000..fde216ff1 --- /dev/null +++ b/examples/io/import_export/import_vector.py @@ -0,0 +1,21 @@ +""" +From/To GeoPandas +================= + +This example demonstrates importing or exporting a :class:`geopandas.GeoDataFrame` from and to a :class:`~geoutils.Vector`. +""" + +# %% +# A vector can be imported from a :class:`geopandas.GeoDataFrame` simply by instantiating :class:`~geoutils.Vector`. + +import geoutils as gu +import geopandas as gpd + +ds = gpd.read_file(gu.examples.get_path("everest_rgi_outlines")) +vect = gu.Vector(ds) +vect + +# %% +# To export, the :class:`geopandas.GeoDataFrame` is always stored as an attribute as :class:`~geoutils.Vector` is composed from it. See :ref:`core-composition`. + +vect.ds diff --git a/examples/io/open_save/README.rst b/examples/io/open_save/README.rst new file mode 100644 index 000000000..06ea0aeed --- /dev/null +++ b/examples/io/open_save/README.rst @@ -0,0 +1,2 @@ +Open and save from files +------------------------ \ No newline at end of file diff --git a/examples/io/read_raster.py b/examples/io/open_save/read_raster.py similarity index 95% rename from examples/io/read_raster.py rename to examples/io/open_save/read_raster.py index d4299ab5a..7ae9a087e 100644 --- a/examples/io/read_raster.py +++ b/examples/io/open_save/read_raster.py @@ -1,6 +1,6 @@ """ -Opening a raster from file -========================== +Opening a raster +================ This example demonstrates the instantiation of a raster through :class:`~geoutils.Raster`. """ diff --git a/examples/io/read_satimg.py b/examples/io/open_save/read_satimg.py similarity index 93% rename from examples/io/read_satimg.py rename to examples/io/open_save/read_satimg.py index e58421359..2d160ef7a 100644 --- a/examples/io/read_satimg.py +++ b/examples/io/open_save/read_satimg.py @@ -1,6 +1,6 @@ """ -Parsing image metadata -====================== +Parsing metadata +================ This example demonstrates the instantiation of an image through :class:`~geoutils.SatelliteImage`. """ diff --git a/examples/io/read_vector.py b/examples/io/open_save/read_vector.py similarity index 88% rename from examples/io/read_vector.py rename to examples/io/open_save/read_vector.py index bf88be7fa..f679d327c 100644 --- a/examples/io/read_vector.py +++ b/examples/io/open_save/read_vector.py @@ -1,6 +1,6 @@ """ -Opening a vector from file -========================== +Opening a vector +================ This example demonstrates the instantiation of a vector through :class:`geoutils.Vector`. """ diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index b072b266e..1c5404019 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -2764,7 +2764,7 @@ def to_points( return points def polygonize( - self, in_value: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = 1 + self, in_value: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = "all" ) -> Vector: """ Return a GeoDataFrame polygonized from a raster. @@ -2802,8 +2802,7 @@ def polygonize( # mask all valid values elif in_value == "all": - vals_for_msk = list(set(self.data.flatten())) - bool_msk = np.isin(self.data, vals_for_msk).astype("uint8") + bool_msk = (~self.data.mask).astype("uint8") else: diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 73bc91df1..df4f0e961 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -109,6 +109,8 @@ def copy(self: VectorType) -> VectorType: def crop( self: VectorType, cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + clip: bool, + *, inplace: Literal[True] = True, ) -> None: ... @@ -117,6 +119,8 @@ def crop( def crop( self: VectorType, cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + clip: bool, + *, inplace: Literal[False], ) -> VectorType: ... @@ -124,10 +128,11 @@ def crop( def crop( self: VectorType, cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + clip: bool = False, inplace: bool = True, ) -> VectorType | None: """ - Crop the Vector to given extent, or bounds of a raster or vector. + Crop the Vector to given extent, or bounds of a raster or vector. Optionally, clip geometries to that extent (by default keeps all intersecting). Reprojection is done on the fly if georeferenced objects have different projections. @@ -135,6 +140,7 @@ def crop( coordinates. If cropGeom is a Raster, crop() will crop to the boundary of the raster as returned by Raster.ds.bounds. If cropGeom is a Vector, crop() will crop to the bounding geometry. If cropGeom is a list of coordinates, the order is assumed to be [xmin, ymin, xmax, ymax]. + :param clip: Whether to clip the geometry to the given extent (by default keeps all intersecting). :param inplace: Update the vector inplace or return copy. """ if isinstance(cropGeom, (gu.Raster, Vector)): @@ -148,11 +154,14 @@ def crop( # Need to separate the two options, inplace update if inplace: self.ds = self.ds.cx[xmin:xmax, ymin:ymax] + if clip: + self.ds = self.ds.clip(mask=(xmin, ymin, xmax, ymax)) return None # Or create a copy otherwise else: new_vector = self.copy() new_vector.ds = new_vector.ds.cx[xmin:xmax, ymin:ymax] + new_vector.ds = new_vector.ds.clip(mask=(xmin, ymin, xmax, ymax)) return new_vector def reproject( @@ -337,7 +346,7 @@ def rasterize( :param rst: A raster to be used as reference for the output grid :param crs: A pyproj or rasterio CRS object (Default to rst.crs if not None then self.crs) - :param xres: Output raster spatial resolution in x. Only is rst is None. + :param xres: Output raster spatial resolution in x. Only if rst is None. Must be in units of crs, if set. :param yres: Output raster spatial resolution in y. Only if rst is None. Must be in units of crs, if set. (Default to xres) From c636e85cd62e0bad97ad6da77a7afd07a88a2fcf Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 22 Feb 2023 21:42:35 -0800 Subject: [PATCH 045/184] Incremental commit on doc --- doc/source/about_geoutils.md | 17 ++++++++++------- doc/source/index.md | 8 ++++---- .../handling/georeferencing/reproj_raster.py | 3 +-- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 08f4b6010..7ddaf5252 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -5,11 +5,14 @@ ## What is GeoUtils? GeoUtils1 is a **[Python](https://www.python.org/) package for the handling and analysis of georeferenced data**, developed with the objective of -making geospatial analysis accessible, efficient and robust. +making such analysis accessible, efficient and reliable. -GeoUtils is designed for all Earth and planetary observation science. It is generally **most useful for remote sensing and Earth's surface applications -relying on moderate- to high-resolution georeferenced data** (typically < 1 km). Applications that, for analysis, require reprojections, re-gridding, point -interpolation, and other types of fine-grid analysis with millions of pixels. +In a few words, GeoUtils can be described as a **convenience** wrapper package for end-users focusing on geospatial analysis. It allows to write shorter +code through consistent higher-level operations, implicit object behaviour and numerical interfacing. + +GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for remote sensing and Earth's surface +applications** that rely on moderate- to high-resolution georeferenced data. All applications that, for analysis, require robust reprojections, re-gridding, +point interpolation, and other types of fine-grid analysis with millions of pixels. ```{margin} 1With name standing for *Geospatial Utilities*. @@ -26,16 +29,16 @@ We are working on making features fully consistent for the first long-term relea GeoUtils is built on top of [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenced operations, and relies on [NumPy](https://numpy.org/doc/stable/), [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: -- A **common and consistent framework** for efficient rasters and vectors handling and analysis, +- A **common and consistent framework** for efficient rasters and vectors handling, - A structure following the **principal of least knowledge**2 to foster accessibility, -- A **pythonic arithmetic** and **NumPy interfacing** for robust numerical computing and intuitive use. +- A **pythonic arithmetic** and **NumPy interfacing** for robust numerical computing and intuitive analysis. ```{margin} 2Or the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) for software development. ``` In particular, GeoUtils: -- Rarely requires more than **single-line operations** due to its object-based structure, +- Rarely requires more than **single-line operations** thanks to its object-based structure, - Strives to rely on **lazy-operations** under-the-hood to avoid unnecessary data loading, - Allows for **match-reference operations** to facilitate geospatial handling, - Re-implements **several of [GDAL](https://gdal.org/)'s missing features** (Proximity, DEM, Calc, etc), diff --git a/doc/source/index.md b/doc/source/index.md index 34f37bb0b..f3d97a91e 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -40,7 +40,7 @@ its testing suite and type checking ([Pytest](https://docs.pytest.org/en/7.2.x/) :gutter: 1 1 1 2 :::{grid-item-card} {material-regular}`edit_note;2em` About GeoUtils -:link: about_geoutils +:link: about-geoutils :link-type: ref Learn more about why we developed GeoUtils. @@ -50,17 +50,17 @@ Learn more about why we developed GeoUtils. ::: :::{grid-item-card} {material-regular}`data_exploration;2em` Quick start -:link: quick_start +:link: quick-start :link-type: ref -Run through a short example of the package functionalities. +Run a short example of the package functionalities. +++ [Learn more »](quick_start) ::: :::{grid-item-card} {material-regular}`preview;2em` Features -:link: core_index +:link: core-index :link-type: ref Dive into the full documentation. diff --git a/examples/handling/georeferencing/reproj_raster.py b/examples/handling/georeferencing/reproj_raster.py index 7d3472777..9d33cb5b7 100644 --- a/examples/handling/georeferencing/reproj_raster.py +++ b/examples/handling/georeferencing/reproj_raster.py @@ -1,6 +1,6 @@ """ Reproject a raster -=================) +================== This example demonstrates the reprojection of a raster using :func:`geoutils.Raster.reproject`. """ @@ -29,7 +29,6 @@ # # By default, a "bilinear" resampling algorithm is used. Any string or :class:`~rasterio.enums.Resampling` can be passed. - rast1_warped = rast1.reproject(rast2) rast1_warped From 9928eb085af717a08116855d0666e160a8dc1e76 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 23 Feb 2023 16:21:54 -0800 Subject: [PATCH 046/184] Incremental commit on doc --- doc/source/about_geoutils.md | 12 ++-- doc/source/how_to_install.md | 15 ++--- examples/analysis/README.rst | 2 +- .../array_numerics/numpy_interfacing.py | 46 +++++++++++++++ .../array_numerics/python_arithmetic.py | 41 +++++++++++++ .../analysis/geospatial/buffer_voronoi.py | 2 +- .../analysis/geospatial/proximity_metric.py | 2 +- examples/analysis/point_estimation/README.rst | 2 - examples/analysis/point_extraction/README.rst | 2 + .../point_extraction/interpolation.py | 53 +++++++++++++++++ .../analysis/point_extraction/reduction.py | 55 ++++++++++++++++++ examples/io/import_export/import_raster.py | 8 +-- examples/io/import_export/import_vector.py | 9 ++- examples/io/open_save/myraster.tif | Bin 0 -> 23791 bytes examples/io/open_save/read_raster.py | 13 ++++- examples/io/open_save/read_vector.py | 14 ++++- geoutils/georaster/raster.py | 10 +++- 17 files changed, 252 insertions(+), 34 deletions(-) create mode 100644 examples/analysis/array_numerics/numpy_interfacing.py create mode 100644 examples/analysis/array_numerics/python_arithmetic.py delete mode 100644 examples/analysis/point_estimation/README.rst create mode 100644 examples/analysis/point_extraction/README.rst create mode 100644 examples/analysis/point_extraction/interpolation.py create mode 100644 examples/analysis/point_extraction/reduction.py create mode 100644 examples/io/open_save/myraster.tif diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 7ddaf5252..67734efd7 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -7,16 +7,18 @@ GeoUtils1 is a **[Python](https://www.python.org/) package for the handling and analysis of georeferenced data**, developed with the objective of making such analysis accessible, efficient and reliable. -In a few words, GeoUtils can be described as a **convenience** wrapper package for end-users focusing on geospatial analysis. It allows to write shorter -code through consistent higher-level operations, implicit object behaviour and numerical interfacing. +```{margin} +1With name standing for *Geospatial Utilities*. +``` + +In a few words, GeoUtils can be described as a **convenience wrapper package for end-users** focusing on geospatial analysis. It allows to write shorter +code through consistent higher-level operations, implicit object behaviour and numerical interfacing. In addition, GeoUtils adds **analysis-oriented +functions** that require many steps to perform with other packages, and which are robustly tested. GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for remote sensing and Earth's surface applications** that rely on moderate- to high-resolution georeferenced data. All applications that, for analysis, require robust reprojections, re-gridding, point interpolation, and other types of fine-grid analysis with millions of pixels. -```{margin} -1With name standing for *Geospatial Utilities*. -``` ```{important} GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for diff --git a/doc/source/how_to_install.md b/doc/source/how_to_install.md index ce1a51d39..0b7950231 100644 --- a/doc/source/how_to_install.md +++ b/doc/source/how_to_install.md @@ -2,13 +2,13 @@ # How to install -## Installing with `mamba` (recommended) +## Installing with ``mamba`` (recommended) ```bash mamba install -c conda-forge geoutils ``` -```{note} +```{important} Solving dependencies can take a long time with `conda`, `mamba` significantly speeds up the process. Install it with: conda install mamba -n base -c conda-forge @@ -16,7 +16,7 @@ Solving dependencies can take a long time with `conda`, `mamba` significantly sp Once installed, the same commands can be run by simply replacing `conda` by `mamba`. More details available in the [mamba documentation](https://mamba.readthedocs.io/en/latest/). ``` -## Installing with `pip` +## Installing with ``pip`` ```bash pip install geoutils @@ -29,11 +29,8 @@ Setting up GDAL and PROJ may require some extra steps, depending on your operati ## Installing for contributors ```bash -git clone https://github.com/GlacioHack/xdem.git -cd ./xdem -mamba env create -f dev-environment.yml -mamba activate xdem -pip install -e . +git clone https://github.com/GlacioHack/geoutils.git +mamba env create -f geoutils/dev-environment.yml ``` -After installing, check that everything is working by running the tests: `pytest -rA`. +After installing, you can check that everything is working by running the tests: `pytest -rA`. diff --git a/examples/analysis/README.rst b/examples/analysis/README.rst index 67dff9231..a66dffd93 100644 --- a/examples/analysis/README.rst +++ b/examples/analysis/README.rst @@ -1,7 +1,7 @@ Analysis ======== -Examples about **numerical operations** on rasters, and **additional features** such as **proximity** or **buffer without overlap** (Voronoi polygons). +Examples about **numerical operations** on rasters, and **additional analysis features** such as **distance estimation** or **point extraction**. Some examples rely on NumPy or pythonic operators on :class:`Rasters` (see :ref:`core-py-ops` and :ref:`core-array-funcs`). diff --git a/examples/analysis/array_numerics/numpy_interfacing.py b/examples/analysis/array_numerics/numpy_interfacing.py new file mode 100644 index 000000000..9df052518 --- /dev/null +++ b/examples/analysis/array_numerics/numpy_interfacing.py @@ -0,0 +1,46 @@ +""" +NumPy interfacing +================= + +This example demonstrates NumPy interfacing with rasters on :class:`Rasters`. See :ref:`core-array-funcs` for more details. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open a raster +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) + +# %% We plot the original raster. +rast.show(cmap='terrain') + +# %% +# NumPy interfacing allows to use any NumPy function directly on the raster + +import numpy as np + +# Get the x and y gradient as 1D arrays +gradient_y, gradient_x = np.gradient(rast) +# Estimate the orientation in degrees casting to 2D +aspect = np.arctan2(-gradient_x, gradient_y) +aspect = (aspect * 180 / np.pi) + np.pi +# We copy the new array into a raster +asp = rast.copy(new_array=aspect) + +asp.show(cmap='twilight', cbar_title="Aspect (degrees)") + +# %% +# +# .. important:: +# For rigorous slope and aspect calculation (matching that of GDAL), **check-out our sister package** `xDEM `_. +# We can make numpy logical operations to isolate the terrain oriented South and above three thousand meters. The rasters will be cast to a :class:`Mask`. + +# Not supported yet, fix first +# mask = np.logical_and.reduce((asp > -45, asp < 45, rast > 3000)) +# mask + +# %% +# We plot the mask. + +# mask.show() + + diff --git a/examples/analysis/array_numerics/python_arithmetic.py b/examples/analysis/array_numerics/python_arithmetic.py new file mode 100644 index 000000000..9b2f66390 --- /dev/null +++ b/examples/analysis/array_numerics/python_arithmetic.py @@ -0,0 +1,41 @@ +""" +Python arithmetic +================= + +This example demonstrates arithmetic operations using raster arithmetic on :class:`Rasters`. See :ref:`core-py-ops` for more details. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open a raster +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +rast + +# %% We plot the original raster. +rast.show(cmap='Greys_r') + +# %% +# Performing arithmetic operations implicitly loads the data. +rast = (rast + 1.)**0.5 / 5 +rast.show(cmap='Greys_r') + +# %% +# +# .. important:: +# Arithmetic operations cast to new :class:`dtypes` automatically following NumPy coercion rules. If we had written ``(rast + 1)``, +# this calculation would have conserved the original :class:`numpy.uint8` :class:`dtype` of the raster. +# Logical comparison operations will naturally cast to a :class:`Mask`. + +mask = rast == 200 +mask + +# %% +# :class:`Masks` support python logical operators to be combined together + +mask = (rast >= 3) | (rast % 2 == 0) & (rast != 80) +mask.show() + +# %% +# Finally, :class:`Masks` can be used for indexing and assigning to a :class:`Rasters` + +values = rast[mask] diff --git a/examples/analysis/geospatial/buffer_voronoi.py b/examples/analysis/geospatial/buffer_voronoi.py index 4f2442f3f..940dbfd7d 100644 --- a/examples/analysis/geospatial/buffer_voronoi.py +++ b/examples/analysis/geospatial/buffer_voronoi.py @@ -2,7 +2,7 @@ Metric buffer and without overlap ================================= -This example demonstrates the metric buffering of a vector using :class:`~geoutils.Vector.buffer_metric`. +This example demonstrates the metric buffering of a vector using :func:`~geoutils.Vector.buffer_metric` and :func:`~geoutils.Vector.buffer_without_overlap`. """ # sphinx_gallery_thumbnail_number = 3 # %% diff --git a/examples/analysis/geospatial/proximity_metric.py b/examples/analysis/geospatial/proximity_metric.py index 4d54bcf9d..ee7815b47 100644 --- a/examples/analysis/geospatial/proximity_metric.py +++ b/examples/analysis/geospatial/proximity_metric.py @@ -2,7 +2,7 @@ Proximity to raster or vector ============================= -This example demonstrates the calculation of proximity distances to a raster or vector using :class:`~geoutils.Raster.proximity`. +This example demonstrates the calculation of proximity distances to a raster or vector using :func:`~geoutils.Raster.proximity`. """ # sphinx_gallery_thumbnail_number = 2 # %% diff --git a/examples/analysis/point_estimation/README.rst b/examples/analysis/point_estimation/README.rst deleted file mode 100644 index 47288d7ca..000000000 --- a/examples/analysis/point_estimation/README.rst +++ /dev/null @@ -1,2 +0,0 @@ -Point estimation ----------------- \ No newline at end of file diff --git a/examples/analysis/point_extraction/README.rst b/examples/analysis/point_extraction/README.rst new file mode 100644 index 000000000..37fd6dacd --- /dev/null +++ b/examples/analysis/point_extraction/README.rst @@ -0,0 +1,2 @@ +Point extraction +---------------- \ No newline at end of file diff --git a/examples/analysis/point_extraction/interpolation.py b/examples/analysis/point_extraction/interpolation.py new file mode 100644 index 000000000..4993dd8cf --- /dev/null +++ b/examples/analysis/point_extraction/interpolation.py @@ -0,0 +1,53 @@ +""" +Regular-grid interpolation +========================== + +This example demonstrates raster interpolation to point values using :func:`~geoutils.Raster.interp_points`. +""" +# sphinx_gallery_thumbnail_number = 2 +# %% +# We open an example raster, a digital elevation model in South America +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+2000, rast.bounds.bottom+2000]) + +# Plot the raster +rast.show(cmap="terrain") + +# %% +# We generate a random subsample of 100 points to interpolate, and extract the coordinates + +import numpy as np +import geopandas as gpd +# Replace by Raster function once done (valid coords) +np.random.seed(42) +x_coords = np.random.uniform(rast.bounds.left + 50, rast.bounds.right - 50, 50) +y_coords = np.random.uniform(rast.bounds.bottom + 50, rast.bounds.top - 50, 50) + +vals = rast.interp_points(pts=list(zip(x_coords, y_coords))) + +# %% +# Replace by Vector function once done +ds = gpd.GeoDataFrame(geometry=gpd.points_from_xy(x=x_coords, y=y_coords), crs=rast.crs) +ds["vals"] = vals +ds.plot(column="vals", cmap="terrain", legend=True, vmin=np.nanmin(rast), vmax=np.nanmax(rast), marker='x') + +# %% +# .. important:: +# The interpretation of where raster values are located differ, see XXX. The parameter ``shift_area_or_point`` (off by default) can be turned on to ensure +# that the pixel interpretation of your dataset is correct. + +# %% +# Let's look and redefine our pixel interpretation into ``"Point"``. This will shift interpolation by half a pixel. + +print(rast.tags["AREA_OR_POINT"]) +rast.tags["AREA_OR_POINT"] = "Point" + +# %% +# We can interpolate again by shifting according to our interpretation, and changing the resampling algorithm (default to "linear"). + +vals_shifted = rast.interp_points(pts=list(zip(x_coords, y_coords)), shift_area_or_point=True, mode="quintic") +np.nanmean(vals - vals_shifted) + +# %% +# The mean difference in interpolated values is quite significant, with a 2 meter bias! \ No newline at end of file diff --git a/examples/analysis/point_extraction/reduction.py b/examples/analysis/point_extraction/reduction.py new file mode 100644 index 000000000..9090eff28 --- /dev/null +++ b/examples/analysis/point_extraction/reduction.py @@ -0,0 +1,55 @@ +""" +Window point reduction +====================== + +This example demonstrates raster reduction to point values using :func:`~geoutils.Raster.value_at_coords`. +""" +# sphinx_gallery_thumbnail_number = 3 +# %% +# We open an example raster, a digital elevation model in South America +import geoutils as gu +rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+2000, rast.bounds.bottom+2000]) + +# Plot the raster +rast.show(cmap="terrain") + +# %% +# We generate a random subsample of 100 coordinates to extract + +import numpy as np +import geopandas as gpd +# Replace by Raster function once done +np.random.seed(42) +x_coords = np.random.uniform(rast.bounds.left + 50, rast.bounds.right - 50, 50) +y_coords = np.random.uniform(rast.bounds.bottom + 50, rast.bounds.top - 50, 50) + +vals = rast.value_at_coords(x=x_coords, y=y_coords) + +# %% +# Replace by Vector function once done +ds = gpd.GeoDataFrame(geometry=gpd.points_from_xy(x=x_coords, y=y_coords), crs=rast.crs) +ds["vals"] = vals +ds.plot(column="vals", cmap="terrain", legend=True, vmin=np.nanmin(rast), vmax=np.nanmax(rast)) + +# %% +# By default, :func:`~geoutils.Raster.value_at_coords` extracts the closest pixel value. But it can also be passed a window size and reductor function to +# extract an average value or other statistic based on neighbouring pixels. + +vals_reduced = rast.value_at_coords(x=x_coords, y=y_coords, window=5, reducer_function=np.nanmedian) + +np.nanmean(vals - vals_reduced) + +# %% +# The mean difference in extracted values is quite significant at 0.3 meters! +# We can visualize how the sampling took place in window + +# Replace by Vector fonction once done +coords = rast.coords(grid=True) +x_closest = rast.copy(new_array=coords[0]).value_at_coords(x=x_coords, y=y_coords) +y_closest = rast.copy(new_array=coords[1]).value_at_coords(x=x_coords, y=y_coords) +from shapely import box +geometry = [box(x - 2*rast.res[0], y - 2*rast.res[1], x + 2*rast.res[0], y + 2*rast.res[1]) for x, y in zip(x_closest, y_closest)] +ds = gpd.GeoDataFrame(geometry=geometry, crs=rast.crs) +ds["vals"] = vals_reduced +ds.plot(column="vals", cmap="terrain", legend=True, vmin=np.nanmin(rast), vmax=np.nanmax(rast)) diff --git a/examples/io/import_export/import_raster.py b/examples/io/import_export/import_raster.py index 2f24521b3..f81340345 100644 --- a/examples/io/import_export/import_raster.py +++ b/examples/io/import_export/import_raster.py @@ -1,5 +1,5 @@ """ -From/To Rasterio +From/to Rasterio ================ This example demonstrates importing and exporting a :class:`rasterio.io.DatasetReader` or :class:`rasterio.io.DatasetReader` from and to a @@ -11,19 +11,19 @@ import geoutils as gu import rasterio as rio -ds = rio.DatasetReader(gu.examples.get_path("everest_landsat_b4")) +ds = rio.DatasetReader(gu.examples.get_path("exploradores_aster_dem")) rast = gu.Raster(ds) rast # %% # The data is unloaded, as when instantiated with a filename. # The data will be loaded explicitly by any function requiring its :attr:`~geoutils.Raster.data`, such as :func:`~geoutils.Raster.show`. -rast.show(cmap="Greys_r") +rast.show(cmap="terrain") # %% # We can also pass a :class:`rasterio.io.MemoryFile` during instantiation. -mem = rio.MemoryFile(open(gu.examples.get_path("everest_landsat_b4"), "rb")) +mem = rio.MemoryFile(open(gu.examples.get_path("exploradores_aster_dem"), "rb")) rast = gu.Raster(mem) rast diff --git a/examples/io/import_export/import_vector.py b/examples/io/import_export/import_vector.py index fde216ff1..e276e7f7d 100644 --- a/examples/io/import_export/import_vector.py +++ b/examples/io/import_export/import_vector.py @@ -1,5 +1,5 @@ """ -From/To GeoPandas +From/to GeoPandas ================= This example demonstrates importing or exporting a :class:`geopandas.GeoDataFrame` from and to a :class:`~geoutils.Vector`. @@ -11,10 +11,15 @@ import geoutils as gu import geopandas as gpd -ds = gpd.read_file(gu.examples.get_path("everest_rgi_outlines")) +ds = gpd.read_file(gu.examples.get_path("exploradores_rgi_outlines")) vect = gu.Vector(ds) vect +# %% +# We plot the vector. + +vect.ds.plot(column="RGIId") + # %% # To export, the :class:`geopandas.GeoDataFrame` is always stored as an attribute as :class:`~geoutils.Vector` is composed from it. See :ref:`core-composition`. diff --git a/examples/io/open_save/myraster.tif b/examples/io/open_save/myraster.tif new file mode 100644 index 0000000000000000000000000000000000000000..c33b81ad3c1552adcf3fa2cac398a75a508f8c43 GIT binary patch literal 23791 zcmZs>b8K%x*Zy7i)V6Kgwr#towr#toZclBS-`ciq+kMadJohho{&;t?a(%L9&E7MU zOm;GJNl8%y{Q?34LIeT=1_c8CN2C7&_`h8Ak3s&+|EU7~KMe6-{!bqyU@#!?f4cuK z(Ed*z{l854FOTuxn(2Rx`+sWq{tv7CABGQx1p@t7`}{A0LSTU){we&wkJW~Q0ulaW z{(oE_4g!S#kNy5Lh=0!tIuH;eG7!)Z1`v?dzrg?Zg6w~7&X_Nfv}}X9MntVn zoN`9t;Vdf+e)NE;(&}u#eL3|ye{j8|cu&ItA%3oj_kkP2g9JcDJ}hqBbY*T5bWyl; z*4hhNdRXRf3sURB-#tDycOKHrzPvEI|L#^PV$+g4rj;lx-^|ss;Rt7p#FQ2##f^oH zkIRRSDvn%E0K*0hTVZ=%l<<7ZO zB+tb89o^i*8p?IsMrSN`1Z&pp9){F?#&V>Z!!wxo zW~GuM|Lc5c;MT?GiV={^LD2jD066quU}9xurA5BE!otmNbJ;)rQ^`s*l$oZip3h=N zHpW^Gb=;8D0TNx=pJuz{L=#JipGauvxGMU-O*yz@Y#ho70vt{LcgPwdkC1Ux%Lh5S zI2}>g5R;4~S~!tRQVTa#*cUdS@)lF_OB6Lsvg*zbDmc<><*eUhLSUa9kj`k4N`sto z(%p7=6~5d9Zskz)+sfus(VatbZv-+_0%h4N&D>(z0FU7MrmTi$QgQ8a*d7!LRuSQcWh#0cXu%X!Uq`FZD+wbHV%8VmX-lzy*|=YH!(U~3XUB+cxDE{t z9QlN*uTNAnzf50b@vk-5cEM4UUtfPwg1_Yv4#jQPaqybL2V;v1Oy`V&UrWQx!h*75tq%|Meo%fakpgyhNZNEEbW`qRnkl$bPBfa|-!J-?9sf2RI%!wjNiKBuF#9qOO|9CMa-AxDt z`ENF`LCdJxg-bB+&Lyr&qK4-)7CE&~vKDCRXs^>!a=Us@x}HN7_5WbQs?zd%iuhg+ z-VnIf>&gJN-ml!EoEgmcvN?Lr9pFuS4UU)(IU1YH0XjiHIerdX$yN;wk8Qu}?eg*j zgvcfqFvs^RtyxmbG@%0nMiPu}BMf^T8W<@Jv*LH`aFdi@xAbe`#Kdlep^Oqim{v%q zCsnQ9A}u+GoZR+4{ZoDcA=LPZzJMFwcJ2-kU|Ch0!N~qDGdnscgFC*RRH5aAe5tE9 zhEJ3&QZx#o6t!Hf(G$(%i@`&MgAj9VQYE7xyPj!HN4=mlZTK@v*#B%~2J#Fa145rk zh>tT2?P1UUWnZKh1~X(tOdQDs+mk77Vpgm@>KFLY@>6Ha;dl=9>fbkQU@M;sSw0#) zsOGt!3Kn-V?FkQrdcvxV0!W3t>B($v@QOvGKyt2QB@V=oo=qhQ=(M3>HYu$mgWAy9 zTKq#XJzKmY6_d*4fEp-eJW^m(pqewj-}fdimrEr$p%Sp;o;5UNvnoKT%0!KgHysr- z45P`^)6>~vzlIlBVki@D(A^9DEu&d)UrO+*$YmhyC>R*QKBAKaLWRn#qR;4chf~>Z za_FRln}aT{_%sSTB`9OSq9lzO`R24o`ji<9hy31Fi@%< z0BDkgMgK%c#T=zqGWnCGB`PUdHcP2a78oOD`(wA!Qq2QGW@d{v5R9^dZV>bXVUy{z zB#yB$fLKz5)9u3x}szW5lvbTT@U=7_=}W?9v3at zkh6ZUuJ`*PoKD9_>{^5`bJ-+(W@|%mb1>9@D2#{y4Y1&j!QeM9nxRTNZ{}HNQahgk zTU`8PqVbDmEXC_|dc=z$c^af~w)@`W$<$BmNi>$01*4dX-qHQCma-0YT~#-$Kj|jF zV3(6O0~;}}B+17=#;?NLC0AzWtQ5>fiVrFN#}ks4I!c0QPnmcQ+m#f^du zr7&5=LgDk-0koM(a&=>;XG~&gedNHPwzgn~R5w)eTC6lcQtSRq`N&bUA#J7@@q1b3or_b#vyiO1PHu zc{auUIQ=FgHGx}WqekF>eYy_5Ul}^k~jK5feLysKBpg>6Ds3V}^B+(W3^D!Bj z4R_-7Hi(h+-mGog<%(hBvCm26)F_4%m=)kIM0J^CLp4%FaL9@?nhBsXW=7w3`_+L5 zkIH3A(eb+(@V=5F!*ixEgUpNI8WM0aQ7&RT{;G^#PKSZtx{T`xKz!xuU+QtxoNLAy z82hSZXH+z$W?*LKs+d$a(}kDZKi-iwmk&~6!6@u8YDJtf{#1Xsy^FgEOpXI9)q4kl zf<9k2@Yih1Iap+;9aL;{r40mDHfAn*>L+Y5^6itd(1DzZ`( ztv8;xSy6jO#!aRwlX`lJt5=v*A%==7M%vKlh(-w^4~^mp3{jD5n z+4QrhRjiL-8;>6Mki9dvlY(JOXC)oTDP-bzokAQjFAJs%Iiz18z(Zaa@Yk!T;dQ{} z5bp29@~JR3BeIvNc5-z5*Lmt&E-y+6W<2jCUjpmBfb0Z(I;PiPZqMISvjq9;_(2rj z1?6KoMWBz=oK^1IDl_HXxw zzvC*PEx!CFEa8dBL=1=qLI$1c?mg(CV8%phM{wdqnEdTEB_#>T>RH7y44-s6x;I*g zcUSA~p39nK>^^G@Omco}Z8Rfz39Kx+SF~1k!YHHMj=ZjK02ann=oa_ogFgrUlsr!a z5WO58g%%Hm79Y{yz3!PYBD+5*>~frT|6(BP+Kcl<9&q0Mb=$^&hEDLtMF|~X%B2n0 z$Mv_v&Y{8Iw4;~qL$I>4zI~DP&t&-|6FbgaA|`Swya~#$H)$=-obr2q!ETAm-zg`@ zOEt;27t?%_$o$IwiPCK@A!i2!(m0uXKIxi0Kov8zDZPQn^EJPC0^WeI|X|y6TCa`e5z-h0YySOH?Ymlw--gG8dv>vuU;GS7| zuaPGX<1iLU02?21IsJn}(Mp&Oo<~pHX1XCzP;5D`obFmQU=iguRpy-ZA`_ICdJ^RB zz-g&jC~E24YWsMkQ7B1R_)1>(C@wv|<`;V<+4ue6=n|#o(K&u6mpwXjI$H0jDTBkNcE8-x0h@~<68k!C_8U}L ze$4Y(C+sb;>QpK`VeekFctfGFYAv9_16!#g$;WNfjzS93NX*2{nX-nn3&@ThJt4H` znhKo+?fSqL^%UAFh|t))iS$?ip4I>XzdNtd9p4tn(;EW3zvu=X9>&(6qfbMTb|hS| zS>5=TvDMiE?FK7e@9!v#D-b1v=b8L{W#jd%?U@kb;+tegbgpzyzRtlG=y9r6sqD#TMP7ct#zdL9bW_2AS48PN_+4Q7x@ z)$MvZrBMF9q+;fvm+NVNfZzO#g8HpNl5sX9-sdg;$(AoIS8o7v!Qf`E-q+t(uYW5U zxr2{)Y}?>+eZ%$$+o9uqE*`FHCLiu?)6w2h!c$34wX9pULW^6LlXh1)HY-CFOrh#{ zm3ngE^U%()qWhE?BY}{1i#9ao;z7#mOKjrU&M)V#Nwlcj-NS-Ch;2EE% zz@)#u>f=OlNu_&@4kC|ma9=NYy}lq~QG6B7>cF>xt_qR+>ejh-<5$5wXuY+!ESpn+R3?Ewi31cI7N_O`eyd)iUD>1He&w&C@b?_6RC?Zkq- z@ksmbyAEGq&30kZAPP0IjJr%Hkg;RdwAAdkAV&4s9tY9I%Delp_B9O2-d+_zZ>Sbz z?9fhv*rZbKTBU}88-lS`LjJ@ufhz*~H$>Vg>Xge>;Po>0Z#OjxR;Q;=+Z7@A0X0QY zLbItZ&^e*1rNOPNkpYKK&l5$cvUqkb2FeeC$aW1&HtYgU5=AG#f7`oSq zcP-o+GTuT$&IXai`S~rjmKEJZS*i1?R@9QM$KWU)G)$EH)1CNNfHGX;hBNx=P0?lWpCG!!pFl zl9!vFG`Xi4G~nZBK*~@`tClHX=Hxxu@?X>XS88P>ZBQIC>gLeRX~g(%H(B3UD3gk(CpWMP z1eFwtPhqqwPI0v2%Dz9CyEZZ?B4QNOQ7FvuJ=z_6SH5_j~jA{0Z)k6oSE_{ zwttMi*`F+WtUZCOP~)&dW0=r75M*(y7^<^>yAfP5qem}J)qI(#z1e=iP;Ze@eHB7> z4BL>SZ|B-gOiVXP?=P^(WmrM!j=SX%v?L%u+Y+J5>+^XV)nt$;j(HI z$qIJY@m_r0l9s9inqLx{A{PJPWr_UtCL|JypDWe`iEB*cJIJwWN7;;+y@@jdIL{@qMPGTT;ZO*M65eQiBFUwYe zn0CKu6Kw_0HhZM11JCb#zb^WiAJ9Mek+lCg)`rwc9$OYT;@{c2(bw3z=%ANI0=6P% zOArK+`bC-$f(T-H+WkUY5K7%GCE>*o6o3khmPQPMu9z80ebQqOO-gie%{P_VaPz|#xV)}1T4nuzWBGEa|l&~uUj*k%Pbv#u_OU7h!l>C3;Wo@-%j z`nc!+bPCZfDB7uCe{Y0p#M4>fmPEHIO#Na(*)%kU-0kcvUn3ya5;?*@fN`rESYg6O zFBsJX1qtSK;Lc%{6*V~_sJjY*4NXx?fShKkS2CJ&SiB^T_5Wh0V-DBIPD6jHvnrqf zk^$gk{LymC$r*Fgg!wCBs_4{^Y=J01d7EqR0>+bFSbp2MA3MQun@ZqqcmN*YuN~>H zJ$(PYFPlh`YAV!NY2vA6d#dePI62~BnkhN`eEzYMu#X}L3aEqL#ffSH95CL9f_-eL zu*uA1L5?vIhui>l`GW44EdwH8;h;{uob|Mv(%-%?-*{)DBXk^^{;JfArD{H%(1M$RiO}*RS=Xr#nc0QE1*<;G$dvu1=#BWIrO4DjLV=Bp zmH0}?I+=U{<7=xw9X;xo?Smdo>BqdVw8#bDNg(sS586!tb}*@T%G$fSn8Fz^YG0G5 z@G%O#G4yT05(kIktg<(62G?IpT|v$9RNcYMGd~bzJz|JXjeJKsLx_PKHaN@LHgHJ* z=1>eNExQ+62FuLqgo$yzeGOgZ)8@LMc&2eMd_NhLTjTahqj?DoVi zED_?}~~^QjlmQ)gs$cPv;_K6PJo zRYQAvgzXoX9;YE#LQtC-IKMk!MS%f@g;5}TW%{aG0rd_7VVSH@@Vx2vdGLd)7Ol+C zv`jJ&U2Q{jeUi~gsoBP_y2iY+v*8tTurCmEyKiiWaM#;>&eW&=jjXP{4-eoFUVm0F zY&v&*d#xO=kr=ueKp8!Y%LN2z0xwNyUcAtRX%`h03;uRi;IK)}Eh#wD6me7fJ)b;& z{7LkO(`T&k56&iY-`?X=Gi-rYEb_XhIhlDL+q<0`vnHRn4NlJt`c7Av^D3&FVXfZ# z(frNz)>L~n$wVRLG1}UI4lek#>URF47JWU^*sUG zy12N5biPH;?1t1!L)f?&oR~Q4Phm9O%IF}A?v71;RS!!cc`5BOMLEVY{7D4~n`9cM zYcR~aw|BGE0?QYjQkroKQlW%Q`Lwmj){IdhKt&4Ri=D5`A4a(=LerKn z2^%GT#<)b_#w>v=`wtdoncWzkU4Y|od9fD3{unUZdUMdX-z5d&ezeKF62FHA) zMd!;qTv!s+L^01zbj2D+YO{em6x(i1#kt&3Mc45s1jOQS?#^?FGLSd?#-46C`gR)h zM%Fm@cw3ZQM$g{OhwT@R_edT;f1XBiN9kWgh`eeM8C9v_9+&Cao@$M= zPc+->^2#zwn$4Z-Q{AIL-Pg>ntfT&?SK{aq6fFBz;j)ldKw5o$qnWdCDG?f!peb+S z@xGgNaxCNnnz=6E!pS6x{6~L}mg~nHh;XKn67a0atGHSV1mAA~+wVT^Jv{M|z zaC$u_9h#VLXys3PNoet7`bKG9J(&(&M3sZt^o6^= z0oW;fLHJ@$sY6@b{Yl`&xvFtzk^rb`#;6J|Wih43U~Yau3wz^gLsoLsbqHxtt>EJf zl}=Gc0gVOphlP>y6gVy>Cawny{Xm>3zeIbHJFr@!mx@YjQ8Z?yKCy*lYbVVrs5kh? zY)G4I2zt@0%uzz{w<&b=j5N*0PELMx;kHLd&3!L}J{w~LIbzJycmM-gdy?fQb(QB8 z5yTNu6;77nWOjZeolSRmFU)hlv7@8!_axJ$EbHBfXlb(cqL3Qt$|eqv&q>hW;h#~% zmR`NhZo?nr2&ugaOJ$nXR`8G`B}TTzx+P9k;-W6`Lz`sfK=? z(mDIsctZ$b)xg3^l#wm47sOr09&{lapXxWB3r)a7=lN-=ZKr3xD>;-Rwq z{BT{qDk2LptUgr^U;6^zX0%be-WSAF%j$Rx3S!q$?sqtUXHacfg!bKc9!xzevA!Wr zBgaEi8_U|celaXKtw#}&1kER4H+_%K?8Koo^&)`oN!lCYGIX$ZcJS5F7q+w?weBpb zxxhBdI!G9RpS0F-dQkH)U4G0cnL{4i-q{d9{ce*F6T@T?zs(~y-zoy zV#Er1Mdc^*L5KSH83@M}rr!$A(q5su?Ui-6)Zt1&-6tg<^p`Sc^NN5B7~*X^&tAXQ zB@IIG#|~ZI&`*v&jBM2t8q!5=fN!|b7SG8;U`h}JE_4$2n=dL_7kQ?^e5jwI8RhJz zulab%_+7_^R%#3!xOl({lIY2fWTt$QGhLSdyh1n`9c?J8 zh3|Im&u(WxZWqf@88tn|#oeESeJcZH7zf|3^Ugpjw3!3|zW%!D$tlUmD9H6Lo1l#y zQ1h`<)70}54aX5_6)%kZh0~?Ri1+do$FY5*>y@`t5*UB`Z1H z=Kr~ahKDxQ>2619CGu4$_zRweNudBID_NO5%&Q~JTq2|i8R;D^aGwHjL@2F_6{=kR zIa7(5F%Jb60oQpi>ITwQKNeYdCScsI1cmh$D^{;|dW8Bkqh-Mg9=b`RQCnHThr)ie zwL%N)>eq2Q8D{Sl}yI)`E`EV1P`@NYb zZ3tyetYKnPElKAj{(p8z_$t2_S+Z-QRKXR(WJS+i`z59<^7B16A zY4h^JdW-`#jwZToj1jN%fB{vDy1viL2}D$dPYoRx=s-Wb{V88;GQxX5Y|^LQAum=5EKNXZ{@*@2MQd_(xdjh4%uu3GYj5JH(wFw5E& z!f=p5U5YtO=1t`Cn6;Bb#Td-57QrFJ?h*t)(pKN1dz}w+y8=`}dq}Jp!>vvG4wtx}#x_E?O<_uSFlwkTiel656MKDu~E6_ffY8s3FWSe|i z6*}jkFxJ_}Fg^H%UltFNs1SBBf~X2lCR*sMxwZ`Ssn2T#l>ZcVlO>T#V_X^ov1Sud;OX++kK5{vJ*sj9k^9$_uJKg5AYQ$N^_+38=6EmiA~3fk zuPHN7n%yXah^+4!Iix{|(#iSCVCNo1ge^Ft6a~()u=mAcZOwPohzp}RAG)j-K7bVR z`Rq?5ekJ-m;_Nn{ydV^yjm3fOWAH=yn&4<9V2c6_1dWC#3XH850V%L@4DaQU2EvOE zOGz&*wA20l%X4dy))EpoW~52EMt7BtD<1+ld1un)bsLT-P$kqGo$lrrt7-kvS?vT* zD6A^bX>j(#lIixvY!!!u=zecs@QlpJZbVs2-G_&db1XehJ~@h=`Z34lkBX+460fws zG`sHk-0=_{N$#?m5!w6#7{6E;y^6}$g1O8MbYL90c9kVt&sU$FX=$}b?yu~i0GRAC z=Q-op%~VSWHF$z-Nwb7)UDeYquQZ%~IYVG`yVKDUe`sabNf9be*_#m5g;XrQ5jxqm zzZy|6&qxt6XCKkvV~d(QSYCNxZsTQ-7GiA#Bu^7E_dEo5eVgnQ%`P_{^Ziw07T~CD z>giDsjj1vT2x4<5Ys409zjr^C_`7AE2bPW^15?$GRoff2jIk_VtQ3O<+E3~A=T8bI zM)HKhVJq&e0_&2%ZpouD`9B?j%$+H>j=Qr%)~tM`Gwnsv1U(Ld`f zcSQZTVPvg1t07)l%KWG+VxTX5#ajHk>C-X#$h8}j#7XCB6HdAVrd8rYN^s*nxn3@A zHWIoPUPGxqi#kGH>W=aZz{Q_g6&moS0OFZ<Z?bsz3v4hhd2OeMS*j0cQ;gKvz+NXk?Uaf0M};48GCdH`}JYuM#W@sGg+i%%1vY6gpdgqPZ$Oq<{i0is3Hv- zHdqT;CwS)OvJI>YCUY63ra1{L%pQKMf8&XcSuM8uB+-%V39yq5LncnvLNmg%elQrs zuC_d~Lp>qAlQkM{5#DmQzLMnFalA&r-JiBAm$It2v&>w_D$;1RuEmDgcR^NJ7eEWb z1(ExTfC6py!9bu2$P7VFq#Lv@LpFJ43Saolj=Na67p8F`Fltvkfq>lmj8E;fRzJ?> z2}<0vx6q+WTP;Ad@VeBNz;rr)|1SBtZT^`Sx!B^r)o|dnmTFb->yTmmYtGol91fAsQ>A@)Q0|gX6)17$#NO1re%g)KVAdlfpb{AoQ?-Fr=^(7q>$CfJ5#u)$lKoezi zBK-axn!|e?n`)Gf+#ZZ=Z`J2Wh_^Lo(>b!ICs{T!bvfZj%NU203do5w_$k;28PIdFd3oXE~ zYTv$Hmy5;RuF@il*l{!C%i3pf)9gG0jvlmojBdA0RfQ_j0H*LWMKyGzvo}6+|z3f-FlXGtzTwzvt{IA~+ zYPwXVcHhk-k598wH<6S?<@5URGUwOlX2aqI#OS1?pSGgJG6d4n2s+Nq>7Nbxh zlE=Bx1s(zA<@GE*EZ*K>!kU<3MIn8mmTa#(yUTkpkSN~|g-pF9hcLg_3e1_~QS6rI z*NB6X#z7ljT7ej+c#@I}^KWf4mH|}j~r8^7(X9Ka-7IhmoB}1DmHFhzla)t;{f@Xyb;Cs z8AU^Jl1O+zgLGm6T@0exN4V;zi7vLrO`iLkf9LC4& z{2Z%Y4Apm4_{fnbVd>FvXGfT&N^?HPq0`S?tM7^GQbGj&GQ0oNf8~tY3(Dx(Lpbt0 zcil9QY0nR@OY`SfTb{SC4+<~(EmaoBMaKQyTp3_(wr}A>8{#GYX5zr_V+hGXfyC5A zQWTBCvAXUM)gpa%2#v}V&bm^F0m<-I+=q;+Q4jkC=TX;MDaTXi6JjB&aXO2^=~W?j zYP%cGuR8U1(1u-taF!#;qH*9@aTsbc#&`2oK)RvJ9OwGRi6cn;m`xYo-`I`&Lx zy1fg$y%}ukKz90N#FTgxpl7L@TSr_qYaY!fUK1#gTJ%kweOcsf10&Gue#M%YCz>+l zjkb9Lb+xsIFECYgM)VUZVzQ#grz>kBqT|O3cLr%*kX9A>(=}KRoU5e_p?^^U5TrcY`n1|!L6Y3cQ)-XbWyt(K zY?gI1CBK!qsAK6P#J9GRGG6b@iFjtpqqBF4Dwk$$m5?CnRf`uYyr{~veNzkcW$lkGsy?R!DI{h;mj=*oCYfESQ%qu~h}pUE5Xp z=kVrtiX;!#-<1E$$uc1*sJ zyH10L52r**XRYzn44U>6{LB(Dj+hZ-%tP(YVXYfALI+Y*>}at&ULsvKTxK2#=T5Sz zkGg~`-jAoOe7vm;=fs*8Lj~rs1G9~%9AFj+84IosIS600NF^C%d9aV{dWo5@5AoI( zopjo{oYA*TJa>j5p6~3&&NrUdF5AO{9eSR41L=_?#Sou|nrafzZd)dhLZz?}AwrL! zoYK4TB4Z=g;ccl^AdrQga#j<$G0@&%0Ls-zI1B#W8F%(xKaC#Q6#r|zDM zI$YqGJWz%EnNE6YSZ@frFD^wJb_S#LxcAn&mrdXWPv(~nycy7?h>{*L9a>khWs3c6 zno&omw#~hVOzp8a(;?m=oCyEltEs);U64-qr8$-!3mCW<>C&EfE1wl#7~Yzfw=VZIN`2VL&hGh`Bu;$OHZKT>YC*S62bufepVMaH-tzn)Dh9MkS6r_NWQR&%^#BNF*r+hA0XMwBK~iK! zOY%sKlO;#ee-0jR(~2RZxj>97j+@7u&nwfQSoC!~^HLrBxUBW?3s_ikvEa~!Kerv3 z+>L-sbRq0>Z9Zc5%m8HAKcUv6%$9Jma>U`}y<0yA>AM=b(^@Wm58R;ynWHZ8SGkx9 zqRta_KDX4CW?Dm0%~{JB7`57i_8!-+=KS!eyH-L)syx}TsADSp!Q_Tfg-|w_k%A#c zmYk%C5r$fNHa3i{pko)qy10<@cn11w9ffq0)y>v?WVeMOZUTLESeSkcSx`hl>-$B!odU13yf`r*(91!FifF{b0{`SU#9O1jUMUM65Z($*52Kx`m@~C^b7gBa`szCQ*Y$#WlqOw+sZ2LS@ak6wR{1Y6eVJQ? zJJ!mmSI~Ms&6n@c(h(D&ra<&th*J`OckS9rI}PFN)i9OqtolpFRv^)>>S$QR1p|5JomnvO-H?P@k*TKdUzKT+NnUNZFv`UAwogcFV;*14E^c=c}e8(-56JrUx|%3DqQxhP`LK9upccj&)j7H}jAez@2tvLaZx8fATQilZzANRLvhl!Xl@e&EF)@oF$Q5UjfLU%kh!#brh z;>c2o!0q`VAPo%&TRj?QH{lu>oJeu9Io4D!Kv9h(5+-P4ql1qiMO;QKqliD`$jKE? zq<8DcBG{+Jm|KJl4vn-s+aKRix8b7#BZ3ip-G{BQSU7v)bF+MXmWmJrGQb=KSuDU( zjfn=4)N5jbggX3+WK*P4GaXZ0q=tpo1~#bg;yc$^t>Q&0E__h5=S4k}dZZ~=&72l2 z*Q|Gmo!zhN;JLyMZ4Lw%GfHp(fesZ*mTcm8ZIy{kmP(dp>rIqqU9HO86Q@n4$L}og zuGUcie%+5vqU;UZhCspS8xx3;nOajk+}Epi^{DZuFg%;FS~)5aqKXU<>#ws!jarl4 z2?=TZ>?+eZD?qfapr+2JOs-lXsKY7oaw({M#QAWrm07{wImlH#CBQY z`0G#?wO^sky}+I(#UoAz;@5s_s9 zgBLr9^bzRfpQqeYXH3oT@VYfbr8%gw`rJLa3Gm-wT&-mMbyj+ONXaYLy9MbV!eH(k z2&N0$X$~+Lkh!h7Kp2BwPVz3q;i9#48 zolA*j@^v4Sju#|8U$DgD%@uD)8cXEqC{XHD z{qoa>hmh71yp~&{J1A2-#`emqgoin!;0@xXL1`w`D&))b00W>kyN1Ea*$+(>6|vMx zc+FtNDZf{r+%kk#7E#R&ty=hx(`0MIf(lv8L~;Oh_4Rwl>TA*uahn=15Q(Sj2gu!k z<2$itqYCNQ%<2h^X#+0w(e&p&pK^v=+Or?bXNqr_EO(-G7yrC?ayewDWHo!`Wf?R( zu4G|lL^(_1poy#3Y4nCVC9sTU;Yp;?zuqeK5s@8Gy1owety(5_P$8g!mO7Qv#3+k3 z+jT|@=dNtb`gnnp>Xx`q&#?Fl<4bH5w5B~#_8TZNoc;A_8z1v30;rd}l7A$7pxhje zSc_?9DQm=#&)UFo{d)KeGC!#a2w~Wkal%j6?gZ=yhqCu{xmQ>G&&W7^n_64B3QQF# zJ(!__Ehx)LK`~@lrFjCl=7xw>Bk0)5*0muN1QySyiW`o&%xqr7H!gVfy=QIbb*9Ku zh0fO(Y!eZ9EG<*zN7&kZ_&Y^v=OmN;iu3geZMV1QR&hpMUU2#R52UA4gBrz&RX z{uo7xVu_fHTOQWA`eyPy{e!7^VW35M+oKeJRTNN43;@Ld*W!(v|{_9 zq1{>9r*Y`y0clz0V$6dw)30xC@h@6CxahpSVINXU1x{CD&lF!ZdLu_-lNigCP_USV ztJr3jcCM`PENgSA_JFRhhtAIO9&AO~Fa3DSrk{_kq^Nne%2%hgiHjwh^5}jMZ0GWD zZB+&(Xwv89eDc-XD`I{Fz&65mmmEPHJH!j#nEfci*_|z`AH9>JR8h_kad_FQ_GvrnZ9?UEqS)KgZ5s}vpJ|AbdlQqj}W9l9uVXmANUO_Cfv zTH5yV+kMMg`@;Jq^VxJh8xP#udH!j8sLK`~I8Z8YuPnB}`KNAOmixt%5Y$Lg6-z|(iWdG<(K&t@rZNxOZ#Mgroxa+Fmj@UY z!^_@`iA|-i=}Uw)x5v*}1(KvU(?&4hF|tvF-Y>5kX9og3PeO)Ri*)vFo^gb5Tm9}K z5P3W(d)hFPF}8_+206X&?+qKhHEqE|1Sj``r}#0bX*~6 zJ@*~BJ?Fu`E0judcPBGnfh<_Dvbt($5luOKZB5=dUgq*hTvb`YPFh+)NYe!rE`D&h z&d2=5!rhF*U@EF%o5ZVEtzEuu<;rCg8i$1?U^o1;m<)kbmsXV2z^qMvpKUx=PA*|H z5UNI1qSe+oVfZL_y`Vj}Gf5?PKQ2VNnU3vuUxF7(1(rJhJxoz$$_Q`jj<-+T^^{b0Y0y?(F!WF=4$JpJ^`hhVNM^7H($ z{_#Dtdvgu8i*jpr!_oxn_w>lp4YQJl*s} z{^I0IuJSd++>yyL0Ev%qeqb=A1cm41D$@ z08c;!CVt%=1Yqrwni50ETW4i3jUtSVa$O>qO;VaOZ>- z%z-DDLGSLCp8)72D(`G0{?37)MsGg7_u-sQo%b(o`egT+OXn_M-FzXZ_8Ine(}tU$ zp9PCkQmHVL$z-t+7-n-Y>>kl(QYocYZw+60TzW3L+}(F1Yi<;0>EWZ4UXUb4V?UxXgCVqFE6P&8J@1Mw>O_T^Kt*hu0w0j z?0SEPPV*0&Nvr^ zs5FSpL&4(hH$dIz<-gA<03|dTuGX-aE~yQr)3_|uj!8EbXD=IFrV;QlL~K@w{IThy zpFRNh)$d=8jM_3g2>@&Z-`{`woYu{or>=e-`MP}Q*3%PDo0{JZX6=|5oj7o%Zu7D8 zXHOjo@t^nIFfafLwIn72Lo^zL!3vuM<)aE*-(j%Uf}3AF9%6IZSm4I9{u^~i!NW8L zola=Z;%gMXZN*h;ly}xp50(ARI!{^$0}eU@n|rv$i$AV6a&@Z^5Ee z2ARUvrc2)g*IH$YoipH4M9|gy0oVsX0{~4IzkkMS6dZkU=fZDKz*&Ourtcna2VIA9 z2X|y{|32>)xG|+0F)AGZd>7Gs2X`yL8Dtg65+f!5@d`Ll;ZNip$K9JTYPSx9Ql53tDQ275pL0E7jp90b3Fml&~XcRruvufyiO zeD>Rm;Od>TT_C^e{KTsA{mXXjJpARCn^xzjYbCYBzaEo9g?FK-@RJBctjCY4Q2W>&e;!c-2zPTK&^^{;;S zy`izD72_Lbg>M7Lp4$U}YvBH|6W8w^`|Z1vSA2nnr3IeQ`P0{SHg7hLv}?6mgqv@4 zVrGvHlR8=tvLrIQ9#@D3I4)7@ENR|MUm&PAIow9Me*hfEq;7Z4)`wDsulL^9v*ej{ z2Ci{>99jUnjO*4^w<+5$Gh1Dewo1KutlVM2IVPAwCQ&(Jhgf>Fysl~4*1mKR9uj9u zN%G9PoD5nQIC%hoir&zU;emkyM@PmN;o&=nKA&?qZ94>xojCjS=8gWTPlrZ3Y8~mF zn_N!&P~Bqf5`~IFkq3ws_ei6WiKJp&g6n+-r%NYQD`a+0Ae5663i>$`k6vQok3B_$ z=1|t<#h5+l22(zSJpSBYQZj zb@lqejJ)RIshi=W@cqEKI~Ue~@1ARNdhh5^xIFS%iN}%oXrxK2GHFyA%@4%pS+*H` z9O=}2k|IYinbDweWZIn?rAnvaDVgZC8;w=xNBi=(fho{%KHSl$)c^4NB8Y@YI&!x;`({QrYv)`0|mqHM{RVqkjtAd!Yw7M*4R4<9!fW4kIJeKK(4}`zG-3 z$VXWh@rTpJT7g0(HmcPkOow|a)zax9!F<0ab$4q?{y^8LNiEe#m=uV? zBH@-o@4H*;2Tal#2cov*757zaYp-nj{Fx$q^8W*_{;tRI17Hq(G%H8gW9(cCE^nWH z6!y*o1rhYQ7D=B3z_Ncgsa0TvyY$x?{_p&BQ7qh_u3N!A)@Mh3uJg^ax$wFsHVLSS${r ztQ#Pv;MX|~H@1ZKgv$c&{hOsqugj`H)heyp>hiW1AJbdy7DEtxUg2@)WZa4Lzpldt zaNHZw36+E^4ijA8FzQh=Sv*O$p`xrFD9&9e} z$yma}cp4=x<(q4(`&Un0n2iT;9%}mW(F>U_rOy$t|6ch2mlue->JyRj0$2+E4C9`( zUV&1tw`o}xqf4f9`@rE7-P5+Nf?Kzh7FVd?L8RMmK&NSA=oVMi9&kHs;1#_!wKitd zThR))+2{4?^gQ}lc~w_OuT+JwNK`IrD)Npm$zB;)lsq|;w1uQ(ljS0@Qdv^h*?Qs= zf`Z^thEQo~`GYX)=T?5b0DcUY4`!>U*FD4EIo|vPyb3IGn@J{N!w#boNBl4FpF2Il z?C0tNZ*H1dZ7&_(wy(Yn0Do~yO@l!yX5wPK-kM=Ct7%2meN9tqyUj>ye8M6OLa?mL z#Z_G9qL%h6?&TAec>%3dB9;&2OdQ-m_`i^@-Db~NJWKt0_^;>Mzn^oy``c{!7`z(p z?tCSBmqx9`DO3$tCAN+}>-ln@K0;ID+T#2M3wJOLfcyT0u|XAXP_o&4gsPtIX?4E#@UN}6*!Th5oa{sDVri-$8=N{>}$&Lz&6JG;_cRfP^>>msN? zukbnPri`V%rL7JfjguT5vml;ENks)}zEMxfdDL51Srlvv3ME2gf6mCzSY=~A<{Q`r z&O``D=KkWWO@4>17r_V^-`DrutE9aIz6E~;yRuxak{|zszSA~R5*G58>uG$iifLv+#-_(ra=(QfJ|xx(dP_yZm-MKvKEn|=SRgO==|5;oHu{| zoAIeE&~qG|5S!^#hOvClvYx5QuCD5#o5V&w`_+5k4*1~|I5+(Kg7q9$@B3}r?EK9h zvRi&v_tydNzrcpv%+`o;VN8%jdF!?LZ;~-bP5W?9i2_kTOBhs`!Pd&S)B<~LXL~gw zqDChskdw%Y==j*UnCLg&rZF$n)Vs$IXoNh0#PZIvvF^1!Z3F4|35t6UwC^Kg_iM+& zu}?>c`tV?XMeycx#s%kRJ>=|Vi?GI{*}Le$uwm|j{}yg|yt9U)h>7`)S`ghGrRm5~vuI^mZN`x>fn~_dKFt>)bXi-f3f|SxV)wTxRASctnmxygE z>NnKZHXRa&2!fB=4o+kqTm2ESY*u;u!KTjK+#f2;VP=OT410zNgM9E@ju*0j7Wse{#wPM0jH@N-v%G16+U0(Ygr5YbCUN;0P-?@{#=Z)RLWl+s zF;A-TN<{k1(`dP;X6D_3=FWP|X;fG-xv{0XW1z9QBflt2ap|z)a7$0uogY9uad6!} zY#2JdwIm$nJ-i)mZXj-lXMTPzhv3@ICh!#emvHsYi|=QHsVQ&8!5GF@i5)?&QmyjK z1#*}J(UK4i#t3BhFKaDvs}}nf#>OOJCcjW8iPfI3&vRv3xVoX~t^%_lCttUxb4lG; z^<+>%4KEQ!nxlNi-@S6>MDH&I%Zz242k(LJ?{54U9JqTEd`4V5&qusj*jZe;-R`{$ zDj$U9+WX&VvioI35;-AWNTN#Qs78!J@vsadktrkwDT%9vlS=HvZH*x@j=aqvCy_Wj z1}{;HefV>FiP>06fnj4=r&ys(uO?i~mgc@S_0E40(tQc%OARe!jjMNd);ACln3Q0(&P!n-A&06Ft?5WKYi!j>j^Il6yX5z2Sn;+i}8jE}?jY2@2RBe)Ui}}6! z)^(#RRxU2@A*;hPqveV@RVAg{rdx@CWG!l*^kn?@?k#X@9Q5@rA5+yl?P;kRn0_?6 z34oKmk=x^E`(BdjKeNS!t8>Fm0Dk)YYwGk%Ba{>bB@#wr5|V<-;6xTXoqD8O2 zu^_q}-0;cV3aQL#{MDJCZki6Q&KogE{zG3P$60C&THvjj`0wb4JBaAR3<=Yi!*@N-4a9Ie98SbK2%!JqVCL2z>Xq7*nuz+=&q z(HJhqqENU{IwaJ2@=8|*6zrIJ@gzwCl!TB{Qw5pnhrtQ6In!@edyS~j>*5>MH&0KG z_IJt5K0%(@SDs}TX+>gzbbUZ$3)pl{F%L&1x(}Ba#B{y(80GU3{2aj^@dWi&FKmWtpU6iQNfb6ibn#oZf=^qhp4 zg!yC^6+!rXxg*7yO~fAD422_lVGKjUl{0ZcQ`!2>4a;{Lc}4bRE~!3bwn+t4k$Szs z>Tud5Qiv;7C_HX;jxmoNes3|NST`9_j7=^e(53s5vfF$*l?0Y($|-f?2$*wy}+UgB{}`eNzC(BzlcNAeD=>!$dN}i}Sf^ zl~ScnbI8_ZmP%-_#ctGU3=W+!Xf-=W*Ux0+=Cy%e8@jhQg>O;a>p|ZGaO8PlyY$J* z11nd3GppG(@OK~}Dd2?I==c;iiwyA?$z;A(MdK?MX$=~iyFsYnM!gg903o< zWfqY*HYX&}$(RKewOUIgs?2)1Av44`TG?RBcu^>OQ&^fB;`i|n#0~!bGx1th)y;mK z0e=q=mrGArNFh^HG?+q4BJ)@BxACz~m;W`sNj zYf16sxJ;wfsHqeL^Omld9P1oeRh*}{$=HP0JO&fj5BaQVQZ9zDPzaGTgz<4ZY+|WK zS8UL$R3?>GuOqgP(PgpMbanbo8b>HB%{8!fa%#Hr)b#In;q)i7k0<~62R$$bpPih{ z5~WI$XekTgk|-<&vl^=N*}6TMEBiR~n5d|QQS;`-7*j~G8l8liqEpGu3Nc=~quDBv z`jBKMlP&Qrsb4p_rfo3CV!Lj$dq03vfZT#9+ZK z5>3ct548lcv(2zrW^su4p|oS*yT{;z(UIapS8e+^xD&}oq(#zi_vX03S3@c{o1MgB z(O@WlQ>s#l87j)Z1~XjF{w*9wtc~GPBLhmWncV88rL^ zF_poCInqGg^!C1%c0WhM6Ns2Xfk+@TtKLAUa&n6zGSY!^F zoDiLq1k>qg8ikE8g{V|1h>MO%h*?NcP`L@Dg`%7{qGDl>1XV~hN`bz#UP0#yDF~uf za3ucHtlrUDuMA}*C9^O&E|Qry}Hm6k&7XADvU194^Z{8O(Y0b6oQ_J_q?kk! z1%}uPErmg4vtb^O6iXt`TMXhWylR9j%$Ohbc7-rSL98dIlCOk=h%q6V^cIexOuTY& zYkQ85j*=Fpk}$hQEH;i6TMRNOLT54K7LrqGxuqJnTq-c-5ot+{C0(gX_vnk73TB?3 zI=ZR1YNo!oBgf(OrWKF1`E@J0N?hg=mtUS7O&bo$&sDDh;(ZxpqPwHivIk3r|q zxhR_fLk1a-N<3^ajmhzEFJv=G7{pKsL?(xSBq`SKgc2npCY{UIx{Ly1BaoAmQy4rh z*Va*6lkY%M5)&7ZI8p}jF0rK`!=w=NU`G6+#JDJ-G^A5t7Gr6qNfXf8bvg@fl@*>_ zVm+K+_SM#Ae|dXnuEF9eEo*9PSw6I}Aa`h{=gQ-KH*2}&H_nXytrk0qRH&9!DkWZg zbb7)AnnPNJ7-I1#WF3u&0pX;S6qreaVJ45BG+!B$G<6D$)E6_2uuO!oiP#duVQw;? zoxCuCg=6?Y>rkOuL|#ONAxtS`;qq0^JR+QAGGQu4A|XkLpV%%+7sZh zi8m}Loy*bbG&*xUXNgZL6{46@VOBCA$UvpV5z8!^nHU#4f8J~C*r?cBCpaRHdLiK* zXo-m_h*Zc`DBG&?4RRELU{Xw~)MSBpmJFq| zz^qVtbcJ4PMy$MO`txr(RckYs`~tQ%c2|hy-fB-?b46ZOMP=Qx70X*{GV}Y%Y;X4U zU+3gr=4|-ElPAE#G9rV`K#C^+5A@}w!g!n=QTuBf1pxjxCf%HCYtkjXB=^X@bC7`p(Qy4f`SZ!rYenO zW>%?KX=TQ6%$yw>IkU7iwYfLYnEul9?!CTjq?O5+UYnni$8;{GEMI%y#}|vrY2DB?!;HjjW*{n2C>G0( zV?d-Y4Ep^f$6HCh&)=oFmQ%CTyud@zgn;9eO3j5avLtW{M}aS_p05lcJ|sqfS%;!f zg5O@W6otWUjNu3{Rwzsq)3$|YhOGg8nEFVX2g&D&5b8Qrb!BlEfN Any: if window is not None: value = reducer_function(value.flatten()) else: - value = value[0, 0] + value = value[0, 0, 0] else: value = None return value @@ -2385,7 +2389,7 @@ def format_value(value: Any) -> Any: if return_window: output_win = list_windows[0] else: - output_val = list_values # type: ignore + output_val = np.array(list_values) # type: ignore if return_window: output_win = list_windows @@ -2419,7 +2423,7 @@ def coords(self, offset: str = "corner", grid: bool = True) -> tuple[np.ndarray, xx += dx / 2 # shift by half a pixel yy += dy / 2 if grid: - meshgrid: tuple[np.ndarray, np.ndarray] = np.meshgrid(xx[:-1], yy[:-1]) # drop the last element + meshgrid: tuple[np.ndarray, np.ndarray] = np.meshgrid(xx[:-1], np.flip(yy[:-1])) # drop the last element return meshgrid else: return xx[:-1], yy[:-1] From af7ed0bf69bbcf70ad82b1b5493e978eecec7ac0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 14:41:15 -0800 Subject: [PATCH 047/184] Add Vector.show and simplify gallery examples --- .../handling/georeferencing/crop_raster.py | 15 +- .../handling/georeferencing/crop_vector.py | 21 +-- .../handling/georeferencing/reproj_raster.py | 9 +- .../handling/georeferencing/reproj_vector.py | 4 +- examples/handling/interface/create_mask.py | 8 +- examples/handling/interface/polygonize.py | 6 +- examples/handling/interface/rasterize.py | 10 +- examples/handling/interface/topoints.py | 11 +- examples/io/import_export/import_vector.py | 2 +- examples/io/open_save/read_vector.py | 7 +- geoutils/georaster/raster.py | 58 ++++--- geoutils/georaster/satimg.py | 57 +++++-- geoutils/geovector.py | 154 +++++++++++++++++- 13 files changed, 261 insertions(+), 101 deletions(-) diff --git a/examples/handling/georeferencing/crop_raster.py b/examples/handling/georeferencing/crop_raster.py index 0c0e9831a..2bcebbebf 100644 --- a/examples/handling/georeferencing/crop_raster.py +++ b/examples/handling/georeferencing/crop_raster.py @@ -20,9 +20,8 @@ # %% # Let's plot the raster and vector. import matplotlib.pyplot as plt -ax = plt.gca() -rast.show(ax=ax, cmap="Purples") -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) +rast.show(cmap="Purples") +vect.show(ref_crs=rast, fc='none', ec='k', lw=2) # %% # **First option:** using the second raster as a reference to match, we reproject the first one. We simply have to pass the second :class:`~geoutils.Raster` @@ -37,9 +36,8 @@ # By default, :func:`~geoutils.Raster.crop` is done in-place, replacing ``rast``. This behaviour can be modified by passing ``inplace=False``. # -ax = plt.gca() -rast.show(ax=ax, cmap="Purples") -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) +rast.show(ax="new", cmap="Purples") +vect.show(ref_crs=rast, fc='none', ec='k', lw=2) # %% # **Second option:** we can pass other ``crop_geom`` argument to :func:`~geoutils.Raster.crop`, including another :class:`~geoutils.Raster` or a @@ -47,6 +45,5 @@ rast.crop((rast.bounds.left + 1000, rast.bounds.bottom, rast.bounds.right, rast.bounds.top - 500)) -ax = plt.gca() -rast.show(ax=ax, cmap="Purples") -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) +rast.show(ax="new", cmap="Purples") +vect.show(ref_crs=rast, fc='none', ec='k', lw=2) diff --git a/examples/handling/georeferencing/crop_vector.py b/examples/handling/georeferencing/crop_vector.py index 5cd355597..767b2738f 100644 --- a/examples/handling/georeferencing/crop_vector.py +++ b/examples/handling/georeferencing/crop_vector.py @@ -13,10 +13,8 @@ # %% # Let's plot the raster and vector. The raster has smaller extent than the vector. -import matplotlib.pyplot as plt -ax = plt.gca() -rast.show(ax=ax, cmap="Greys_r", alpha=0.7) -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) +rast.show(cmap="Greys_r", alpha=0.7) +vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) # %% # **First option:** using the raster as a reference to match, we crop the vector. We simply have to pass the :class:`~geoutils.Raster` as single argument to @@ -29,18 +27,16 @@ # By default, :func:`~geoutils.Vector.crop` is done in-place, replacing ``vect``. This behaviour can be modified by passing ``inplace=False``. # -ax = plt.gca() -rast.show(ax=ax, cmap="Greys_r", alpha=0.7) -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) +rast.show(ax="new", cmap="Greys_r", alpha=0.7) +vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) # %% # The :func:`~geoutils.Vector.crop` keeps all features with geometries intersecting the extent to crop to. We can also force a clipping of the geometries # within the bounds using ``clip=True``. vect.crop(rast, clip=True) -ax = plt.gca() -rast.show(ax=ax, cmap="Greys_r", alpha=0.7) -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) +rast.show(ax="new", cmap="Greys_r", alpha=0.7) +vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) # %% # **Second option:** we can pass other ``crop_geom`` argument to :func:`~geoutils.Vector.crop`, including another :class:`~geoutils.Vector` or a @@ -52,6 +48,5 @@ bounds.right, bounds.top)) -ax = plt.gca() -rast.show(ax=ax, cmap="Greys_r", alpha=0.7) -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='tab:purple', lw=3) +rast.show(ax="new", cmap="Greys_r", alpha=0.7) +vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) diff --git a/examples/handling/georeferencing/reproj_raster.py b/examples/handling/georeferencing/reproj_raster.py index 9d33cb5b7..c248a267f 100644 --- a/examples/handling/georeferencing/reproj_raster.py +++ b/examples/handling/georeferencing/reproj_raster.py @@ -18,10 +18,9 @@ # %% # Let's plot the first raster, with the warped extent of the second one. import matplotlib.pyplot as plt -ax = plt.gca() -rast1.show(ax=ax, cmap="Blues") +rast1.show(cmap="Blues") vect_bounds_rast2 = gu.Vector.from_bounds_projected(rast2) -vect_bounds_rast2.ds.plot(ax=ax, fc='none', ec='r', lw=2) +vect_bounds_rast2.show(fc='none', ec='r', lw=2) # %% # **First option:** using the second raster as a reference to match, we reproject the first one. We simply have to pass the second :class:`~geoutils.Raster` @@ -45,8 +44,8 @@ # %% # We can plot the two rasters next to one another -rast1_warped.show(cmap="Reds") -rast2.show(cmap="Blues") +rast1_warped.show(ax="new", cmap="Reds") +rast2.show(ax="new", cmap="Blues") # %% # **Second option:** we can pass any georeferencing argument to :func:`~geoutils.Raster.reproject`, such as ``dst_size`` and ``dst_crs``, and will only diff --git a/examples/handling/georeferencing/reproj_vector.py b/examples/handling/georeferencing/reproj_vector.py index 9823aae74..7bdc8f35a 100644 --- a/examples/handling/georeferencing/reproj_vector.py +++ b/examples/handling/georeferencing/reproj_vector.py @@ -21,7 +21,7 @@ # Let's plot the two in their original projection. import matplotlib.pyplot as plt rast.show(cmap="Greys_r") -vect.ds.plot(fc='none', ec='tab:purple', lw=3) +vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) # %% # **First option:** using the raster as a reference to match, we reproject the vector. We simply have to pass the :class:`~geoutils.Raster` as an argument @@ -32,7 +32,7 @@ # %% # We can plot the vector in its new projection. -vect_reproj.ds.plot(fc='none', ec='tab:purple', lw=3) +vect_reproj.show(ax="new", fc='none', ec='tab:purple', lw=3) # %% # **Second option:** we can pass the georeferencing argument ``dst_crs`` to :func:`~geoutils.Vector.reproject` (an EPSG code can be passed directly as diff --git a/examples/handling/interface/create_mask.py b/examples/handling/interface/create_mask.py index ef9c67332..3b0765399 100644 --- a/examples/handling/interface/create_mask.py +++ b/examples/handling/interface/create_mask.py @@ -13,17 +13,15 @@ # %% # Let's plot the raster and vector. -import matplotlib.pyplot as plt -ax = plt.gca() -rast.show(ax=ax, cmap="Purples") -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) +rast.show(cmap="Purples") +vect.show(ref_crs=rast, fc='none', ec='k', lw=2) # %% # **First option:** using the raster as a reference to match, we create a mask for the vector in any projection and georeferenced grid. We simply have to pass # the :class:`~geoutils.Raster` as single argument to :func:`~geoutils.Vector.rasterize`. See :ref:`core-match-ref` for more details. vect_rasterized = vect.create_mask(rast) -vect_rasterized.show() +vect_rasterized.show(ax="new") # %% # .. note:: diff --git a/examples/handling/interface/polygonize.py b/examples/handling/interface/polygonize.py index a95923aee..259de5e8b 100644 --- a/examples/handling/interface/polygonize.py +++ b/examples/handling/interface/polygonize.py @@ -18,7 +18,7 @@ # We polygonize the raster. rast_polygonized = rast.polygonize() -rast_polygonized.ds.plot() +rast_polygonized.show(ax="new") # %% # By default, :func:`~geoutils.Raster.polygonize` will try to polygonize target all valid values. Instead, one can specify discrete values to target by @@ -26,13 +26,13 @@ # A range of values to polygonize rast_polygonized = rast.polygonize((2500, 3000)) -rast_polygonized.ds.plot() +rast_polygonized.show(ax="new") # %% # An even simpler way to do this is to compute a :func:`~geoutils.Mask` to polygonize using logical comparisons on the :func:`~geoutils.Raster`. rast_polygonized = ((2500 < rast) & (rast < 3000)).polygonize() -rast_polygonized.ds.plot() +rast_polygonized.show(ax="new") # %% # .. note:: diff --git a/examples/handling/interface/rasterize.py b/examples/handling/interface/rasterize.py index bcc9b497f..ed9f915be 100644 --- a/examples/handling/interface/rasterize.py +++ b/examples/handling/interface/rasterize.py @@ -13,17 +13,15 @@ # %% # Let's plot the raster and vector. -import matplotlib.pyplot as plt -ax = plt.gca() -rast.show(ax=ax, cmap="Purples") -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) +rast.show(cmap="Purples") +vect.show(ref_crs=rast, fc='none', ec='k', lw=2) # %% # **First option:** using the raster as a reference to match, we rasterize the vector in any projection and georeferenced grid. We simply have to pass the # :class:`~geoutils.Raster` as single argument to :func:`~geoutils.Vector.rasterize`. See :ref:`core-match-ref` for more details. vect_rasterized = vect.rasterize(rast) -vect_rasterized.show(cmap="viridis") +vect_rasterized.show(ax="new", cmap="viridis") # %% # By default, :func:`~geoutils.Vector.rasterize` will burn the index of the :class:`~geoutils.Vector`'s features in their geometry. We can specify the ``in_value`` to burn a @@ -32,7 +30,7 @@ # vect_rasterized = vect.rasterize(rast, in_value=1) -vect_rasterized.show() +vect_rasterized.show(ax="new") # %% # diff --git a/examples/handling/interface/topoints.py b/examples/handling/interface/topoints.py index ab5f5b56c..d862910bc 100644 --- a/examples/handling/interface/topoints.py +++ b/examples/handling/interface/topoints.py @@ -15,7 +15,12 @@ rast.show(cmap="terrain") # %% -# We convert the raster to points. +# We convert the raster to points. By default, this returns a vector with columb geometry burned. -pts_rast = rast.to_points(as_frame=True) -pts_rast.plot(column="b1", cmap="terrain", legend=True) +pts_rast = rast.to_points() +pts_rast + +# %% +# We plot the point vector. + +pts_rast.show(column="b1", cmap="terrain", legend=True) diff --git a/examples/io/import_export/import_vector.py b/examples/io/import_export/import_vector.py index e276e7f7d..818581626 100644 --- a/examples/io/import_export/import_vector.py +++ b/examples/io/import_export/import_vector.py @@ -18,7 +18,7 @@ # %% # We plot the vector. -vect.ds.plot(column="RGIId") +vect.show(column="RGIId") # %% # To export, the :class:`geopandas.GeoDataFrame` is always stored as an attribute as :class:`~geoutils.Vector` is composed from it. See :ref:`core-composition`. diff --git a/examples/io/open_save/read_vector.py b/examples/io/open_save/read_vector.py index 94ede7b8a..6e4311a81 100644 --- a/examples/io/open_save/read_vector.py +++ b/examples/io/open_save/read_vector.py @@ -20,11 +20,8 @@ print(vect) # %% -# Let's plot: -import matplotlib.pyplot as plt -for _, glacier in vect.ds.iterrows(): - plt.plot(*glacier.geometry.exterior.xy) -plt.show() +# Let's plot by vector area +vect.show(column="Area", cbar_title="Area (km²)") # %% # Finally, a vector is saved using :func:`~geoutils.Vector.save`. diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 671e20c4e..c7f6c395e 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -2103,24 +2103,26 @@ def show( alpha: float | int | None = None, cbar_title: str | None = None, add_cbar: bool = True, - ax: matplotlib.axes.Axes | None = None, + ax: matplotlib.axes.Axes | Literal["new"] | None = None, + return_axes: bool = False, **kwargs: Any, ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: - r"""Show/display the image, with axes in projection of image. + r"""Show/display the raster, with axes in projection of image. This method is a wrapper to rasterio.plot.show. Any \*\*kwargs which - you give this method will be passed to rasterio.plot.show. + you give this method will be passed to it. :param index: Which band to plot, from 1 to self.count (default is all). :param cmap: The figure's colormap. Default is plt.rcParams['image.cmap']. :param vmin: Colorbar minimum value. Default is data min. - :param vmax: Colorbar maximum value. Default is data min. + :param vmax: Colorbar maximum value. Default is data max. + :param alpha: Transparency of raster and colorbar. :param cbar_title: Colorbar label. Default is None. :param add_cbar: Set to True to display a colorbar. Default is True. - :param ax: A figure ax to be used for plotting. If None, will create default figure and axes, - and plot figure directly. + :param ax: A figure ax to be used for plotting. If None, will plot on current axes. If "new", will create a new axis. + :param return_axes: Whether to return axes. - :returns: if ax is not None, returns (ax, cbar) where cbar is the colorbar (None if add_cbar is False) + :returns: None, or (ax, caxes) if return_axes is True You can also pass in \*\*kwargs to be used by the underlying imshow or @@ -2178,11 +2180,18 @@ def show( # Create axes if ax is None: - fig, ax0 = plt.subplots() + # If no figure exists, get a new axis + if len(plt.get_fignums()) == 0: + ax0 = plt.gca() + # Otherwise, get first axis + else: + ax0 = plt.gcf().axes[0] + elif isinstance(ax, str) and ax.lower() == "new": + _, ax0 = plt.subplots() elif isinstance(ax, matplotlib.axes.Axes): ax0 = ax else: - raise ValueError("ax must be a matplotlib.axes.Axes instance or None") + raise ValueError("ax must be a matplotlib.axes.Axes instance, 'new' or None.") # Use data array directly, as rshow on self.ds will re-load data rshow( @@ -2209,12 +2218,9 @@ def show( else: cbar = None - # If ax not set, figure should be plotted directly - if ax is None: - plt.show() - return None - - return ax0, cbar + # If returning axes + if return_axes: + return ax, cax def value_at_coords( self, @@ -2689,19 +2695,19 @@ def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int | @overload def to_points( - self, subset: float | int, as_frame: Literal[True], pixel_offset: Literal["center", "corner"] - ) -> gpd.GeoDataFrame: + self, subset: float | int, as_array: Literal[True], pixel_offset: Literal["center", "corner"] + ) -> Vector: ... @overload def to_points( - self, subset: float | int, as_frame: Literal[False], pixel_offset: Literal["center", "corner"] + self, subset: float | int, as_array: Literal[False], pixel_offset: Literal["center", "corner"] ) -> np.ndarray: ... def to_points( - self, subset: float | int = 1, as_frame: bool = False, pixel_offset: Literal["center", "corner"] = "center" - ) -> np.ndarray: + self, subset: float | int = 1, as_array: bool = False, pixel_offset: Literal["center", "corner"] = "center" + ) -> np.ndarray | Vector: """ Subset a point cloud of the raster. @@ -2711,11 +2717,11 @@ def to_points( If the raster is not loaded, sampling will be done from disk without loading the entire Raster. Formats: - * `as_frame` == None | False: A numpy ndarray of shape (N, 2 + count) with the columns [x, y, b1, b2..]. - * `as_frame` == True: A GeoPandas GeoDataFrame with the columns ["b1", "b2", ..., "geometry"] + * `as_array` == False: A vector with dataframe columns ["b1", "b2", ..., "geometry"], + * `as_array` == True: A numpy ndarray of shape (N, 2 + count) with the columns [x, y, b1, b2..]. :param subset: The point count or fraction. If 'subset' > 1, it's parsed as a count. - :param as_frame: Return a GeoDataFrame with a geometry column and crs instead of an ndarray. + :param as_array: Return an array instead of a vector. :param pixel_offset: The point at which to associate the pixel coordinate with ('corner' == upper left). :raises ValueError: If the subset count or fraction is poorly formatted. @@ -2757,13 +2763,13 @@ def to_points( # Merge the coordinates and pixel data into a point cloud. points = np.vstack((x_coords.reshape(1, -1), y_coords.reshape(1, -1), pixel_data)).T - if as_frame: - points = gpd.GeoDataFrame( + if not as_array: + points = Vector(gpd.GeoDataFrame( points[:, 2:], columns=[f"b{i}" for i in range(1, pixel_data.shape[0] + 1)], geometry=gpd.points_from_xy(points[:, 0], points[:, 1]), crs=self.crs, - ) + )) return points diff --git a/geoutils/georaster/satimg.py b/geoutils/georaster/satimg.py index 26b2c6dd3..70a90690d 100644 --- a/geoutils/georaster/satimg.py +++ b/geoutils/georaster/satimg.py @@ -304,12 +304,12 @@ def __init__( super().__init__(filename_or_dataset, attrs=attrs, load_data=load_data, indexes=indexes) # priority to user input - self.datetime = datetime - self.tile_name = tile_name - self.satellite = satellite - self.sensor = sensor - self.product = product - self.version = version + self._datetime = datetime + self._tile_name = tile_name + self._satellite = satellite + self._sensor = sensor + self._product = product + self._version = version # trying to get metadata from separate metadata file if read_from_meta and self.filename is not None and fn_meta is not None: @@ -347,25 +347,50 @@ def __parse_metadata_from_fn(self, silent: bool = False) -> None: print("No metadata could be read from filename.") return - for n in name_attrs: - a = self.__getattribute__(n) - a_fn = attrs[name_attrs.index(n)] - if a is None and a_fn is not None: + for name in name_attrs: + attr = self.__getattribute__(name) + attr_fn = attrs[name_attrs.index(name)] + if attr is None and attr_fn is not None: if not silent: - print("From filename: setting " + n + " as " + str(a_fn)) - setattr(self, n, a_fn) - elif a is not None and attrs[name_attrs.index(n)] is not None: + print("From filename: setting " + name + " as " + str(attr_fn)) + # Set hidden attribute first + setattr(self, "_"+name, attr_fn) + elif attr is not None and attrs[name_attrs.index(name)] is not None: if not silent: print( "Leaving user input of " - + str(a) + + str(attr) + " for attribute " - + n + + name + " despite reading " - + str(attrs[name_attrs.index(n)]) + + str(attrs[name_attrs.index(name)]) + "from filename" ) + @property + def datetime(self) -> dt.datetime | None: + return self._datetime + + @property + def satellite(self) -> str | None: + return self._satellite + + @property + def sensor(self) -> str | None: + return self._sensor + + @property + def product(self) -> str | None: + return self._product + + @property + def version(self) -> str | None: + return self._version + + @property + def tile_name(self) -> str | None: + return self._tile_name + def __parse_metadata_from_file(self, fn_meta: str | None) -> None: warnings.warn(f"Parse metadata from file not implemented. {fn_meta}") diff --git a/geoutils/geovector.py b/geoutils/geovector.py index df4f0e961..ac722970e 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -8,11 +8,13 @@ import warnings from collections import abc from numbers import Number -from typing import Literal, TypeVar, overload +from typing import Literal, TypeVar, overload, Any import fiona import geopandas as gpd +import matplotlib import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1 import make_axes_locatable import numpy as np import rasterio as rio import rasterio.errors @@ -48,22 +50,46 @@ def __init__(self, filename: str | pathlib.Path | gpd.GeoDataFrame): # This warning shows up in numpy 1.21 (2021-07-09) warnings.filterwarnings("ignore", ".*attribute.*array_interface.*Polygon.*") ds = gpd.read_file(filename) - self.ds = ds - self.name: str | gpd.GeoDataFrame | None = filename + self._ds = ds + self._name: str | gpd.GeoDataFrame | None = filename elif isinstance(filename, gpd.GeoDataFrame): - self.ds = filename - self.name = None + self._ds = filename + self._name = None else: raise ValueError("filename argument not recognised.") self._crs = self.ds.crs def __repr__(self) -> str: - return str(self.ds.__repr__()) + """Representation of vector""" + + # Get the representation of ds + str_ds= "\n ".join(self.__str__().split("\n")) + + s = self.__class__.__name__ + "(\n" + \ + " ds=" + str_ds + \ + "\n crs=" + self.crs.__str__() + \ + "\n bounds=" + self.bounds.__str__() + ")" + + return s + + def _repr_html_(self) -> str: + """Representation of vector for html""" + + str_ds = "\n ".join(self.ds.__str__().split("\n")) + + # Over-ride Raster's method to remove nodata value (always None) + # Use
     to keep white spaces,  to keep line breaks
    +        s = '
    ' + self.__class__.__name__ + '(\n' + \
    +            "  ds=" + str_ds + \
    +            "\n  crs=" + self.crs.__str__() + \
    +            "\n  bounds=" + self.bounds.__repr__() + ")
    " + + return s def __str__(self) -> str: """Provide string of information about Raster.""" - return self.info() + return self.ds.__str__() def __getitem__(self, value: gu.Raster | Vector | list[float] | tuple[float, ...]) -> Vector: """Subset the Raster object: calls the crop method with default parameters""" @@ -87,6 +113,99 @@ def info(self) -> str: return "".join(as_str) + def show(self, + ref_crs: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | CRS | int | None = None, + cmap: matplotlib.colors.Colormap | str | None = None, + vmin: float | int | None = None, + vmax: float | int | None = None, + alpha: float | int | None = None, + cbar_title: str | None = None, + add_cbar: bool = False, + ax: matplotlib.axes.Axes | None | Literal["new"] = None, + return_axes: bool = False, + **kwargs: Any, + ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: + """Show/display the vector, with axes in projection of image. + + This method is a wrapper to geopandas.GeoDataFrame.plot. Any \*\*kwargs which + you give this method will be passed to it. + + :param ref_crs: The CRS to match when plotting. + :param cmap: The colormap. Default is plt.rcParams['image.cmap']. + :param vmin: Colorbar minimum value. Default is data min. + :param vmax: Colorbar maximum value. Default is data max. + :param alpha: Transparency of raster and colorbar. + :param cbar_title: Colorbar label. Default is None. + :param add_cbar: Set to True to display a colorbar. Default is True. + :param ax: A figure ax to be used for plotting. If None, will plot on current axes. If "new", will create a new axis. + :param return_axes: Whether to return axes. + + :returns: None, or (ax, caxes) if return_axes is True + """ + + # Ensure that the vector is in the same crs as a reference + if isinstance(ref_crs, (gu.Raster, rio.io.DatasetReader, Vector, gpd.GeoDataFrame, str)): + vect_reproj = self.reproject(dst_ref=ref_crs) + elif isinstance(ref_crs, (CRS, int)): + vect_reproj = self.reproject(dst_crs=ref_crs) + else: + vect_reproj = self + + # Create axes, or get current ones by default (like in matplotlib) + if ax is None: + # If no figure exists, get a new axis + if len(plt.get_fignums()) == 0: + ax0 = plt.gca() + # Otherwise, get first axis + else: + ax0 = plt.gcf().axes[0] + elif isinstance(ax, str) and ax.lower() == "new": + _, ax0 = plt.subplots() + elif isinstance(ax, matplotlib.axes.Axes): + ax0 = ax + else: + raise ValueError("ax must be a matplotlib.axes.Axes instance, 'new' or None.") + + # Update with this function's arguments + if add_cbar: + legend = True + else: + legend = False + + if "legend" in list(kwargs.keys()): + legend = kwargs.pop("legend") + else: + legend = False + + # Get colormap arguments that might have been passed in the keyword args + if "legend_kwds" in list(kwargs.keys()) and legend: + legend_kwds = kwargs.pop("legend_kwds") + if "label" in list(legend_kwds): + cbar_title = legend_kwds.pop("label") + else: + legend_kwds = None + + # Add colorbar + if add_cbar or cbar_title: + divider = make_axes_locatable(ax0) + cax = divider.append_axes("right", size="5%", pad=0.05) + norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) + cbar = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm) #, orientation="horizontal", ticklocation="top") + cbar.solids.set_alpha(alpha) + + if cbar_title is not None: + cbar.set_label(cbar_title) + else: + cax = None + cbar = None + + # Plot + vect_reproj.ds.plot(ax=ax0, cax=cax, cmap=cmap, vmin=vmin, vmax=vmax, alpha=alpha, legend=legend, legend_kwds=legend_kwds, **kwargs) + + # If returning axes + if return_axes: + return ax, cax + @property def bounds(self) -> rio.coords.BoundingBox: """Get a bounding box of the total bounds of the Vector.""" @@ -98,6 +217,27 @@ def crs(self) -> rio.crs.CRS: self._crs = self.ds.crs return self._crs + @property + def ds(self) -> gpd.GeoDataFrame: + """Getter method for dataset""" + return self._ds + + @ds.setter + def ds(self, new_ds: gpd.GeoDataFrame | gpd.GeoSeries) -> None: + """Setting a new GeoDataFrame""" + + if isinstance(new_ds, gpd.GeoDataFrame): + self._ds = new_ds + elif isinstance(new_ds, gpd.GeoSeries): + self._ds = gpd.GeoDataFrame(geometry=new_ds) + else: + raise ValueError("The dataset of a vector must be set with a GeoSeries or a GeoDataFrame.") + + @property + def name(self) -> str | None: + """Filename if exists""" + return self._name + def copy(self: VectorType) -> VectorType: """Return a copy of the Vector.""" # Utilise the copy method of GeoPandas From 48e5452f4eb3abb8375c93190ba5a1d76023f6ca Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 15:25:24 -0800 Subject: [PATCH 048/184] Fix sphinx errors --- doc/source/conf.py | 7 +++++ doc/source/core_composition.md | 12 ++++++--- doc/source/index.md | 4 +-- doc/source/raster_class.md | 4 +-- .../array_numerics/numpy_interfacing.py | 1 + .../array_numerics/python_arithmetic.py | 1 + examples/io/open_save/read_raster.py | 1 + examples/io/open_save/read_vector.py | 1 + geoutils/georaster/raster.py | 22 +++++++--------- geoutils/geovector.py | 26 +++++++++---------- 10 files changed, 46 insertions(+), 33 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index eb0c9675b..9fe4305d0 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -55,6 +55,7 @@ "geopandas": ("https://geopandas.org/en/stable", None), "xarray": ("https://docs.xarray.dev/en/stable/", None), "xdem": ("https://xdem.readthedocs.io/en/latest", None), + "rioxarray": ("https://corteva.github.io/rioxarray/stable/", None) } sphinx_gallery_conf = { @@ -111,6 +112,12 @@ # "special-members": "__init__", # } +# To ignore warnings due to having myst-nb reading the .ipynb created by sphinx-gallery +# Should eventually be fixed, see: https://github.com/executablebooks/MyST-NB/issues/363 +def setup(app): + # Ignore .ipynb files + app.registry.source_suffix.pop(".ipynb", None) + # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index c5fb753d8..5aeb36ee8 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -33,8 +33,8 @@ raster ``` From these **four main attributes**, many other derivatives attributes exist, such as {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.res` to -describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster. -name` or {attr}`~geoutils.Raster.driver`, see {ref}`raster-class` for a summary, or the {ref}`dedicated sections of the API` for a full listing. +describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster.name` or +{attr}`~geoutils.Raster.driver`, see {ref}`raster-class` for a summary, or the {ref}`dedicated sections of the API` for a full listing. ```{note} By default, {attr}`~geoutils.Raster.data` is not loaded during instantiation. See {ref}`core-lazy-load` for more details. @@ -74,8 +74,12 @@ in order to easily call them from the vector object, and build additional method ```{code-cell} ipython3 :tags: [hide-output] - # Initiate a Vector from disk -vector = examples.get_path("exploradores_rgi_outlines") +vector = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) vector ``` +```{code-cell} ipython3 +:tags: [hide-output] +# Show summarized information +print(vector.info()) +``` diff --git a/doc/source/index.md b/doc/source/index.md index f3d97a91e..859878b38 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -32,10 +32,10 @@ GeoUtils is an **accessible**, **efficient** and **reliable** package to analyze **Reliable** owing to its consistent higher-level operations respecting geospatial intricacies such as nodata values and pixel interpretation, ensured by its testing suite and type checking ([Pytest](https://docs.pytest.org/en/7.2.x/), [Mypy](https://mypy-lang.org/)). -# Where to start? - ---------------- +# Where to start? + ::::{grid} 1 2 2 3 :gutter: 1 1 1 2 diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 45ff209dd..9f79369e4 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -324,7 +324,7 @@ A {class}`~geoutils.Raster` can be exported to different formats, to facilitate Those include exporting to: - a {class}`xarray.Dataset` with {class}`~geoutils.Raster.to_xarray`, - a {class}`rasterio.io.DatasetReader` with {class}`~geoutils.Raster.to_rio_dataset`, -- a {class}`numpy.ndarray`, {class}`geopandas.GeoDataFrame` or {class}`geoutils.Vector` as a point cloud with {class}`~geoutils.Raster.to_points`. +- a {class}`numpy.ndarray` or {class}`geoutils.Vector` as a point cloud with {class}`~geoutils.Raster.to_points`. ```{code-cell} ipython3 # Export to rasterio dataset-reader through a memoryfile @@ -333,7 +333,7 @@ raster_reproj.to_rio_dataset() ```{code-cell} ipython3 # Export to geopandas dataframe -raster_reproj.to_points(as_frame=True) +raster_reproj.to_points() ``` diff --git a/examples/analysis/array_numerics/numpy_interfacing.py b/examples/analysis/array_numerics/numpy_interfacing.py index 9df052518..3f6a68665 100644 --- a/examples/analysis/array_numerics/numpy_interfacing.py +++ b/examples/analysis/array_numerics/numpy_interfacing.py @@ -32,6 +32,7 @@ # # .. important:: # For rigorous slope and aspect calculation (matching that of GDAL), **check-out our sister package** `xDEM `_. +# # We can make numpy logical operations to isolate the terrain oriented South and above three thousand meters. The rasters will be cast to a :class:`Mask`. # Not supported yet, fix first diff --git a/examples/analysis/array_numerics/python_arithmetic.py b/examples/analysis/array_numerics/python_arithmetic.py index 9b2f66390..47b539b0a 100644 --- a/examples/analysis/array_numerics/python_arithmetic.py +++ b/examples/analysis/array_numerics/python_arithmetic.py @@ -24,6 +24,7 @@ # .. important:: # Arithmetic operations cast to new :class:`dtypes` automatically following NumPy coercion rules. If we had written ``(rast + 1)``, # this calculation would have conserved the original :class:`numpy.uint8` :class:`dtype` of the raster. +# # Logical comparison operations will naturally cast to a :class:`Mask`. mask = rast == 200 diff --git a/examples/io/open_save/read_raster.py b/examples/io/open_save/read_raster.py index b0d2e7b32..81308cf99 100644 --- a/examples/io/open_save/read_raster.py +++ b/examples/io/open_save/read_raster.py @@ -15,6 +15,7 @@ # # .. note:: # A raster can also be instantiated with a :class:`rasterio.io.DatasetReader` or a :class:`rasterio.io.MemoryFile`, see :ref:`sphx_glr_io_examples_import_export_import_raster.py`. +# # We can print more info on the raster. print(rast.info()) diff --git a/examples/io/open_save/read_vector.py b/examples/io/open_save/read_vector.py index 6e4311a81..3cdda9b12 100644 --- a/examples/io/open_save/read_vector.py +++ b/examples/io/open_save/read_vector.py @@ -16,6 +16,7 @@ # # .. note:: # A vector can also be instantiated with a :class:`geopandas.GeoDataFrame`, see :ref:`sphx_glr_io_examples_import_export_import_vector.py`. +# # We can print more info on the vector. print(vect) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index c7f6c395e..ab44de077 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -44,10 +44,10 @@ try: import rioxarray + _has_rioxarray = True except ImportError: + rioxarray = None _has_rioxarray = False -else: - _has_rioxarray = True RasterType = TypeVar("RasterType", bound="Raster") @@ -1665,10 +1665,10 @@ def reproject( defaults to self.crs. :param dst_size: Raster size to write to (x, y). Do not use with dst_res. :param dst_bounds: a BoundingBox object or a dictionary containing left, bottom, right, top bounds in the - source CRS. + source CRS. :param dst_res: Pixel size in units of target CRS. Either 1 value or (xres, yres). Do not use with dst_size. :param dst_nodata: nodata value of the destination. If set to None, will use the same as source, - and if source is None, will use GDAL's default. + and if source is None, will use GDAL's default. :param dst_dtype: Set data type of output. :param src_nodata: nodata value of the source. If set to None, will read from the metadata. :param resampling: A rasterio Resampling method @@ -1676,7 +1676,7 @@ def reproject( :param n_threads: The number of worker threads. Defaults to (os.cpu_count() - 1). :param memory_limit: The warp operation memory limit in MB. Larger values may perform better. - :returns: Raster + :returns: Reprojected raster """ @@ -2032,7 +2032,7 @@ def to_xarray(self, name: str | None = None) -> rioxarray.DataArray: if not _has_rioxarray: raise ImportError("rioxarray is required for this functionality.") - xr = rioxarray.open_rasterio(self.ds) + xr = rioxarray.open_rasterio(self.to_rio_dataset()) if name is not None: xr.name = name @@ -2576,17 +2576,15 @@ def interp_points( to ensure that the interpolation of points is done at the right location. See parameter description of shift_area_or_point for more details. - :param pts: Point(s) at which to interpolate raster value. If points fall outside of image, - value returned is nan. Shape should be (N,2)'. + :param pts: Point(s) at which to interpolate raster value. If points fall outside of image, value + returned is nan. Shape should be (N,2). :param input_latlon: Whether the input is in latlon, unregarding of Raster CRS - :param mode: One of 'linear', 'cubic', or 'quintic'. Determines what type of spline is - used to interpolate the raster value at each point. For more information, see - scipy.interpolate.interp2d. Default is linear. + :param mode: One of 'linear', 'cubic', or 'quintic'. Determines what type of spline is used to + interpolate the raster value at each point. For more information, see scipy.interpolate.interp2d. Default is linear. :param index: The band to use (from 1 to self.count). :param shift_area_or_point: Shifts index to center pixel coordinates if GDAL's AREA_OR_POINT attribute (in self.tags) is "Point", keeps the corner pixel coordinate for "Area". - :returns rpts: Array of raster value(s) for the given points. """ assert mode in [ diff --git a/geoutils/geovector.py b/geoutils/geovector.py index ac722970e..d4dfec24f 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -382,7 +382,7 @@ def create_mask( :param yres: Output raster spatial resolution in y. Only if rst is None. (Default to xres) :param bounds: Output raster bounds (left, bottom, right, top). Only if rst is None (Default to self bounds) :param buffer: Size of buffer to be added around the features, in the raster's projection units. - If a negative value is set, will erode the features. + If a negative value is set, will erode the features. :returns: A Mask object contain a boolean array """ @@ -489,13 +489,13 @@ def rasterize( :param xres: Output raster spatial resolution in x. Only if rst is None. Must be in units of crs, if set. :param yres: Output raster spatial resolution in y. Only if rst is None. - Must be in units of crs, if set. (Default to xres) + Must be in units of crs, if set. (Default to xres) :param bounds: Output raster bounds (left, bottom, right, top). Only if rst is None Must be in same system as crs, if set. (Default to self bounds). :param in_value: Value(s) to be burned inside the polygons (Default is self.ds.index + 1) :param out_value: Value to be burned outside the polygons (Default is 0) - :returns: array containing the burned geometries + :returns: Raster or mask containing the burned geometries """ # If input rst is string, open as Raster if isinstance(rst, str): @@ -718,7 +718,7 @@ def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coo :param out_crs: Output CRS :param densify_pts: Maximum points to be added between image corners to account for nonlinear edges. - Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. """ # Calculate new bounds @@ -738,21 +738,21 @@ def buffer_without_overlap(self, buffer_size: int | float, metric: bool = True, The algorithm may also yield unexpected results on very simple geometries. Note: A similar functionality is provided by momepy (http://docs.momepy.org) and is probably more robust. - It could be implemented in GeoPandas in the future: https://github.com/geopandas/geopandas/issues/2015 - - :examples: - >>> outlines = gu.Vector(gu.examples.get_path('everest_rgi_outlines')) - >>> outlines = gu.Vector(outlines.ds.to_crs('EPSG:32645')) - >>> buffer = outlines.buffer_without_overlap(500) - >>> ax = buffer.ds.plot() # doctest: +SKIP - >>> outlines.ds.plot(ax=ax, ec='k', fc='none') # doctest: +SKIP - >>> plt.show() # doctest: +SKIP + It could be implemented in GeoPandas in the future: https://github.com/geopandas/geopandas/issues/2015. :param buffer_size: Buffer size in self's coordinate system units. :param metric: Whether to perform the buffering in a local metric system (default: True). :param plot: Set to True to show intermediate plots, useful for understanding or debugging. :returns: A Vector containing the buffered geometries. + + :examples: On glacier outlines. + >>> outlines = gu.Vector(gu.examples.get_path('everest_rgi_outlines')) + >>> outlines = gu.Vector(outlines.ds.to_crs('EPSG:32645')) + >>> buffer = outlines.buffer_without_overlap(500) + >>> ax = buffer.ds.plot() # doctest: +SKIP + >>> outlines.ds.plot(ax=ax, ec='k', fc='none') # doctest: +SKIP + >>> plt.show() # doctest: +SKIP """ from geoutils.projtools import latlon_to_utm, utm_to_epsg From f2cbf80596d920d7e11aa4ee86f5039dc6520328 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 15:52:41 -0800 Subject: [PATCH 049/184] Fix tests --- geoutils/georaster/raster.py | 2 +- tests/test_docs.py | 39 ------ tests/test_georaster.py | 242 ++++++++++++++++++----------------- tests/test_satimg.py | 57 ++++----- 4 files changed, 153 insertions(+), 187 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index ab44de077..7aa3fa8a0 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1522,7 +1522,7 @@ def __array_function__( first_arg = args[0].data.compressed() elif func.__name__ in ["gradient"]: - first_arg = args[0].data.squeeze() + first_arg = args[0].data[0, :, :] # Otherwise, we run the numpy function normally (most take masks into account) else: diff --git a/tests/test_docs.py b/tests/test_docs.py index d26535c4e..5dc53b1a3 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -10,45 +10,6 @@ class TestDocs: docs_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../", "doc/") n_threads = os.getenv("N_CPUS") - def test_example_code(self) -> None: - """Try running each python script in the doc/source/code\ - directory and check that it doesn't raise an error.""" - current_dir = os.getcwd() - os.chdir(os.path.join(self.docs_dir, "source")) - - def run_code(filename: str) -> None: - """Run a python script in one thread.""" - with open(filename) as infile: - # Run everything except plt.show() calls. - with warnings.catch_warnings(): - # When running the code asynchronously, matplotlib complains a bit - ignored_warnings = [ - "Starting a Matplotlib GUI outside of the main thread", - ".*fetching the attribute.*Polygon.*", - ] - # This is a GeoPandas issue - warnings.simplefilter("error") - - for warning_text in ignored_warnings: - warnings.filterwarnings("ignore", warning_text) - try: - exec(infile.read().replace("plt.show()", "plt.close()")) - except Exception as exception: - raise RuntimeError(f"Failed on {filename}") from exception - - filenames = [os.path.join("code", filename) for filename in os.listdir("code/") if filename.endswith(".py")] - - for filename in filenames: - run_code(filename) - """ - with concurrent.futures.ThreadPoolExecutor( - max_workers=int(self.n_threads) if self.n_threads is not None else None - ) as executor: - list(executor.map(run_code, filenames)) - """ - - os.chdir(current_dir) - def test_build(self) -> None: """Try building the docs and see if it works.""" # Remove the build directory if it exists. diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 27ca5d14a..32bfa98c5 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -17,8 +17,6 @@ from pylint import epylint import geoutils as gu -import geoutils.georaster as gr -import geoutils.geovector as gv import geoutils.projtools as pt from geoutils import examples from geoutils.georaster.raster import _default_nodata, _default_rio_attrs @@ -79,28 +77,28 @@ def test_init(self, example: str) -> None: """Test that all possible inputs work properly in Raster class init""" # First, string filename - r0 = gr.Raster(example) - assert isinstance(r0, gr.Raster) + r0 = gu.Raster(example) + assert isinstance(r0, gu.Raster) # Second, filename in a pathlib object path = pathlib.Path(example) - r1 = gr.Raster(path) - assert isinstance(r1, gr.Raster) + r1 = gu.Raster(path) + assert isinstance(r1, gu.Raster) # Third, passing a Raster itself (points back to Raster passed) - r2 = gr.Raster(r0) - assert isinstance(r2, gr.Raster) + r2 = gu.Raster(r0) + assert isinstance(r2, gu.Raster) # Fourth, rio.Dataset ds = rio.open(example) - r3 = gr.Raster(ds) - assert isinstance(r3, gr.Raster) + r3 = gu.Raster(ds) + assert isinstance(r3, gu.Raster) assert r3.filename is not None # Finally, as memoryfile memfile = rio.MemoryFile(open(example, "rb")) - r4 = gr.Raster(memfile) - assert isinstance(r4, gr.Raster) + r4 = gu.Raster(memfile) + assert isinstance(r4, gu.Raster) # All rasters should be equal assert all( @@ -114,7 +112,7 @@ def test_init(self, example: str) -> None: assert r2.data[0, 0, 0] != r0.data[0, 0, 0] # However, if we reinstantiate now that the data is loaded, it should point to the same - r5 = gr.Raster(r0) + r5 = gu.Raster(r0) r0.data[0, 0, 0] += 5 assert r5.data[0, 0, 0] == r0.data[0, 0, 0] @@ -123,14 +121,14 @@ def test_init(self, example: str) -> None: assert r0.count != r2.count # Test that loaded data are always masked_arrays (but the mask may be empty, i.e. 'False') - assert np.ma.isMaskedArray(gr.Raster(example, masked=True).data) - assert np.ma.isMaskedArray(gr.Raster(example, masked=False).data) + assert np.ma.isMaskedArray(gu.Raster(example, masked=True).data) + assert np.ma.isMaskedArray(gu.Raster(example, masked=False).data) @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_info(self, example: str) -> None: """Test that the information summary is consistent with that of rasterio""" - r = gr.Raster(example) + r = gu.Raster(example) # Check all is good with passing attributes with rio.open(example) as dataset: @@ -167,14 +165,14 @@ def test_loading(self) -> None: Test that loading metadata and data works for all possible cases. """ # Test 0 - test that loading explicitly via load() or implicitly via .data is similar - r_explicit = gr.Raster(self.landsat_b4_path, load_data=True) - r_implicit = gr.Raster(self.landsat_b4_path) + r_explicit = gu.Raster(self.landsat_b4_path, load_data=True) + r_implicit = gu.Raster(self.landsat_b4_path) assert r_explicit.raster_equal(r_implicit) # Test 1 - loading metadata only, single band # For the first example with Landsat B4 - r = gr.Raster(self.landsat_b4_path) + r = gu.Raster(self.landsat_b4_path) assert not r.is_loaded assert r.driver == "GTiff" @@ -192,7 +190,7 @@ def test_loading(self) -> None: assert r.crs == rio.crs.CRS.from_epsg(32645) # And the second example with ASTER DEM - r2 = gr.Raster(self.aster_dem_path) + r2 = gu.Raster(self.aster_dem_path) assert not r2.is_loaded assert r2.driver == "GTiff" @@ -219,7 +217,7 @@ def test_loading(self) -> None: assert r.data.shape == (r.count, r.height, r.width) # Test 3 - single band, loading data - r = gr.Raster(self.landsat_b4_path, load_data=True) + r = gu.Raster(self.landsat_b4_path, load_data=True) assert r.is_loaded assert r.count == 1 assert r.count_on_disk == 1 @@ -228,7 +226,7 @@ def test_loading(self) -> None: assert r.data.shape == (r.count, r.height, r.width) # Test 4 - multiple bands, load all bands - r = gr.Raster(self.landsat_rgb_path, load_data=True) + r = gu.Raster(self.landsat_rgb_path, load_data=True) assert r.count == 3 assert r.count_on_disk == 3 assert r.indexes == ( @@ -244,7 +242,7 @@ def test_loading(self) -> None: assert r.data.shape == (r.count, r.height, r.width) # Test 5 - multiple bands, load one band only - r = gr.Raster(self.landsat_rgb_path, load_data=True, indexes=1) + r = gu.Raster(self.landsat_rgb_path, load_data=True, indexes=1) assert r.count == 1 assert r.count_on_disk == 3 assert r.indexes == (1,) @@ -252,7 +250,7 @@ def test_loading(self) -> None: assert r.data.shape == (r.count, r.height, r.width) # Test 6 - multiple bands, load a list of bands - r = gr.Raster(self.landsat_rgb_path, load_data=True, indexes=[2, 3]) + r = gu.Raster(self.landsat_rgb_path, load_data=True, indexes=[2, 3]) assert r.count == 2 assert r.count_on_disk == 3 assert r.indexes == (1, 2) @@ -264,7 +262,7 @@ def test_to_rio_dataset(self, example: str): """Test the export to a rasterio dataset""" # Open raster and export to rio dataset - rst = gr.Raster(example) + rst = gu.Raster(example) rio_ds = rst.to_rio_dataset() # Check that the output is indeed a MemoryFile @@ -333,9 +331,9 @@ def test_data_setter(self, dtype: str, nodata_init: str | None) -> None: # -- First test: consistency with input array -- # 3 cases: classic array without mask, masked_array without mask and masked_array with mask - r1 = gr.Raster.from_array(data=arr, transform=transform, crs=None, nodata=nodata) - r2 = gr.Raster.from_array(data=np.ma.masked_array(arr), transform=transform, crs=None, nodata=nodata) - r3 = gr.Raster.from_array(data=np.ma.masked_array(arr, mask=mask), transform=transform, crs=None, nodata=nodata) + r1 = gu.Raster.from_array(data=arr, transform=transform, crs=None, nodata=nodata) + r2 = gu.Raster.from_array(data=np.ma.masked_array(arr), transform=transform, crs=None, nodata=nodata) + r3 = gu.Raster.from_array(data=np.ma.masked_array(arr, mask=mask), transform=transform, crs=None, nodata=nodata) # Check nodata is correct assert r1.nodata == nodata @@ -353,9 +351,9 @@ def test_data_setter(self, dtype: str, nodata_init: str | None) -> None: # -- Second test: passing a 2D array -- # 3 cases: classic array without mask, masked_array without mask and masked_array with mask - r1 = gr.Raster.from_array(data=arr.squeeze(), transform=transform, crs=None, nodata=nodata) - r2 = gr.Raster.from_array(data=np.ma.masked_array(arr).squeeze(), transform=transform, crs=None, nodata=nodata) - r3 = gr.Raster.from_array( + r1 = gu.Raster.from_array(data=arr.squeeze(), transform=transform, crs=None, nodata=nodata) + r2 = gu.Raster.from_array(data=np.ma.masked_array(arr).squeeze(), transform=transform, crs=None, nodata=nodata) + r3 = gu.Raster.from_array( data=np.ma.masked_array(arr, mask=mask).squeeze(), transform=transform, crs=None, nodata=nodata ) @@ -387,24 +385,24 @@ def test_data_setter(self, dtype: str, nodata_init: str | None) -> None: match="Setting default nodata {:.0f} to mask non-finite values found in the array, as " "no nodata value was defined.".format(_default_nodata(dtype)), ): - r1 = gr.Raster.from_array( + r1 = gu.Raster.from_array( data=arr_with_unmasked_nodata, transform=transform, crs=None, nodata=nodata ) - r2 = gr.Raster.from_array( + r2 = gu.Raster.from_array( data=np.ma.masked_array(arr_with_unmasked_nodata), transform=transform, crs=None, nodata=nodata ) - r3 = gr.Raster.from_array( + r3 = gu.Raster.from_array( data=np.ma.masked_array(arr_with_unmasked_nodata, mask=mask), transform=transform, crs=None, nodata=nodata, ) else: - r1 = gr.Raster.from_array(data=arr_with_unmasked_nodata, transform=transform, crs=None, nodata=nodata) - r2 = gr.Raster.from_array( + r1 = gu.Raster.from_array(data=arr_with_unmasked_nodata, transform=transform, crs=None, nodata=nodata) + r2 = gu.Raster.from_array( data=np.ma.masked_array(arr_with_unmasked_nodata), transform=transform, crs=None, nodata=nodata ) - r3 = gr.Raster.from_array( + r3 = gu.Raster.from_array( data=np.ma.masked_array(arr_with_unmasked_nodata, mask=mask), transform=transform, crs=None, @@ -429,7 +427,7 @@ def test_data_setter(self, dtype: str, nodata_init: str | None) -> None: assert np.array_equal(r3.data.mask, np.logical_or(mask, ~np.isfinite(arr_with_unmasked_nodata))) # Check that setting data with a different data type results in an error - rst = gr.Raster.from_array(data=arr, transform=transform, crs=None, nodata=nodata) + rst = gu.Raster.from_array(data=arr, transform=transform, crs=None, nodata=nodata) if "int" in dtype: new_dtype = "float32" else: @@ -465,7 +463,7 @@ def test_get_nanarray(self, example: str) -> None: # -- First, we test without returning a mask -- # Get nanarray - rst = gr.Raster(example) + rst = gu.Raster(example) rst_copy = rst.copy() rst_arr = rst.get_nanarray() @@ -499,13 +497,13 @@ def test_downsampling(self) -> None: Check that self.data is correct when using downsampling """ # Test single band - r = gr.Raster(self.landsat_b4_path, downsample=4) + r = gu.Raster(self.landsat_b4_path, downsample=4) assert r.data.shape == (1, 164, 200) assert r.height == 164 assert r.width == 200 # Test multiple band - r = gr.Raster(self.landsat_rgb_path, downsample=2) + r = gu.Raster(self.landsat_rgb_path, downsample=2) assert r.data.shape == (3, 328, 400) # Test that xy2ij are consistent with new image @@ -525,10 +523,10 @@ def test_add_sub(self) -> None: # Create fake rasters with random values in 0-255 and dtype uint8 width = height = 5 transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) - r1 = gr.Raster.from_array( + r1 = gu.Raster.from_array( np.random.randint(0, 255, (height, width), dtype="uint8"), transform=transform, crs=None ) - r2 = gr.Raster.from_array( + r2 = gu.Raster.from_array( np.random.randint(0, 255, (height, width), dtype="uint8"), transform=transform, crs=None ) @@ -548,7 +546,7 @@ def test_add_sub(self) -> None: assert np.array_equal(r3.dtypes, ["uint8"]) # Test with dtype Float32 - r1 = gr.Raster.from_array( + r1 = gu.Raster.from_array( np.random.randint(0, 255, (height, width)).astype("float32"), transform=transform, crs=None ) r3 = -r1 @@ -565,7 +563,7 @@ def test_add_sub(self) -> None: # Check that errors are properly raised # different shapes - r1 = gr.Raster.from_array( + r1 = gu.Raster.from_array( np.random.randint(0, 255, (height + 1, width)).astype("float32"), transform=transform, crs=None ) expected_message = "Both rasters must have the same shape, transform and CRS." @@ -576,7 +574,7 @@ def test_add_sub(self) -> None: r1.__sub__(r2) # different CRS - r1 = gr.Raster.from_array( + r1 = gu.Raster.from_array( np.random.randint(0, 255, (height, width)).astype("float32"), transform=transform, crs=rio.crs.CRS.from_epsg(4326), @@ -590,7 +588,7 @@ def test_add_sub(self) -> None: # different transform transform2 = rio.transform.from_bounds(0, 0, 2, 2, width, height) - r1 = gr.Raster.from_array( + r1 = gu.Raster.from_array( np.random.randint(0, 255, (height, width)).astype("float32"), transform=transform2, crs=None ) @@ -616,7 +614,7 @@ def test_copy(self, example: str) -> None: # -- First and second test: copying create a new memory file that has all the same attributes -- # Open data, modify, and copy - r = gr.Raster(example) + r = gu.Raster(example) r.data += 5 r2 = r.copy() @@ -624,7 +622,7 @@ def test_copy(self, example: str) -> None: assert r is not r2 # Check the object is a Raster - assert isinstance(r2, gr.Raster) + assert isinstance(r2, gu.Raster) # Copy should have no filename assert r2.filename is None @@ -682,7 +680,7 @@ def test_is_modified(self, example: str) -> None: Test that changing the data updates is_modified as desired """ # After loading, should not be modified - r = gr.Raster(example) + r = gu.Raster(example) assert not r.is_modified # This should not trigger the hash @@ -694,7 +692,7 @@ def test_is_modified(self, example: str) -> None: assert not r.is_modified # This will - r = gr.Raster(example) + r = gu.Raster(example) r.data = r.data + 5 assert r.is_modified @@ -704,13 +702,13 @@ def test_masking(self, example: str) -> None: Test self.set_mask """ # Test boolean mask - r = gr.Raster(example) + r = gu.Raster(example) mask = r.data.data == np.min(r.data.data) r.set_mask(mask) assert (np.count_nonzero(mask) > 0) & np.array_equal(mask > 0, r.data.mask) # Test non-boolean mask with values > 0 - r = gr.Raster(example) + r = gu.Raster(example) mask = np.where(r.data.data == np.min(r.data.data), 32, 0) r.set_mask(mask) assert (np.count_nonzero(mask) > 0) & np.array_equal(mask > 0, r.data.mask) @@ -723,14 +721,14 @@ def test_masking(self, example: str) -> None: assert np.count_nonzero(~r.data.mask[mask > 0]) == 0 # Test that shape of first dimension is ignored if equal to 1 - r = gr.Raster(example) + r = gu.Raster(example) if r.data.shape[0] == 1: mask = (r.data.data == np.min(r.data.data)).squeeze() r.set_mask(mask) assert (np.count_nonzero(mask) > 0) & np.array_equal(mask > 0, r.data.mask.squeeze()) # Test that proper issue is raised if shape is incorrect - r = gr.Raster(example) + r = gu.Raster(example) wrong_shape = np.array(r.data.shape) + 1 mask = np.zeros(wrong_shape) with pytest.raises(ValueError, match="mask must be of the same shape as existing data*"): @@ -747,7 +745,7 @@ def test_crop_and_getitem(self, data: list[str]) -> None: """Test for crop method, also called by square brackets through __getitem__""" raster_path, outlines_path = data - r = gr.Raster(raster_path) + r = gu.Raster(raster_path) # -- Test with cropGeom being a list/tuple -- ## cropGeom: list[float] = list(r.bounds) @@ -919,7 +917,7 @@ def test_reproject(self, example: str) -> None: warnings.simplefilter("error") # Reference raster to be used - r = gr.Raster(example) + r = gu.Raster(example) # -- Check proper errors are raised if nodata are not set -- # r_nodata = r.copy() @@ -972,7 +970,7 @@ def test_reproject(self, example: str) -> None: # data is cropped to the same extent new_data = r.data[:, rand_int::, rand_int::] - r2b = gr.Raster.from_array(data=new_data, transform=new_transform, crs=r.crs, nodata=r.nodata) + r2b = gu.Raster.from_array(data=new_data, transform=new_transform, crs=r.crs, nodata=r.nodata) # Create a raster with different resolution dst_res = r.res[0] * 2 / 3 @@ -1121,7 +1119,7 @@ def test_reproject(self, example: str) -> None: # Test that reproject works from self.ds and yield same result as from in-memory array # TO DO: fix issue that default behavior sets nodata to 255 and masks valid values r3 = r.reproject(dst_crs=out_crs, dst_nodata=0) - r = gr.Raster(example, load_data=False) + r = gu.Raster(example, load_data=False) r4 = r.reproject(dst_crs=out_crs, dst_nodata=0) assert r3.raster_equal(r4) @@ -1151,7 +1149,7 @@ def test_reproject(self, example: str) -> None: def test_intersection(self, example: list[str]) -> None: """Check the behaviour of the intersection function""" # Load input raster - r = gr.Raster(example) + r = gu.Raster(example) bounds_orig: list[float] = list(r.bounds) # For the Landsat dataset, to avoid warnings with crop @@ -1321,7 +1319,7 @@ def test_xy2ij_and_interp(self) -> None: # First, we try on a Raster with a Point interpretation in its "AREA_OR_POINT" metadata: values interpolated # at the center of pixel - r = gr.Raster(self.landsat_b4_path) + r = gu.Raster(self.landsat_b4_path) assert r.tags["AREA_OR_POINT"] == "Point" xmin, ymin, xmax, ymax = r.bounds @@ -1397,7 +1395,7 @@ def test_xy2ij_and_interp(self) -> None: # Second, test after a crop: the Raster now has an Area interpretation, those should fall right on the integer # pixel indexes - r2 = gr.Raster(self.landsat_b4_crop_path) + r2 = gu.Raster(self.landsat_b4_crop_path) r.crop(r2) assert r.tags["AREA_OR_POINT"] == "Area" @@ -1435,7 +1433,7 @@ def test_value_at_coords(self) -> None: # -- Tests 1: check based on indexed values -- # Open raster - r = gr.Raster(self.landsat_b4_crop_path) + r = gu.Raster(self.landsat_b4_crop_path) # Random test point that raised an error itest0 = 118 @@ -1462,7 +1460,7 @@ def test_value_at_coords(self) -> None: # 2/ Band argument # Get the indexes for the multi-band Raster - r_multi = gr.Raster(self.landsat_rgb_path) + r_multi = gu.Raster(self.landsat_rgb_path) itest, jtest = r_multi.xy2ij(xtest0, ytest0) itest = itest[0] jtest = jtest[0] @@ -1535,7 +1533,7 @@ def test_set_nodata(self, example: str) -> None: """ # Read raster and save a copy to compare later - r = gr.Raster(example) + r = gu.Raster(example) r_copy = r.copy() old_nodata = r.nodata # We chose nodata that doesn't exist in the raster yet for both our examples (for uint8, the default value of @@ -1776,7 +1774,7 @@ def test_default_nodata(self) -> None: def test_astype(self) -> None: warnings.simplefilter("error") - r = gr.Raster(self.landsat_b4_path) + r = gu.Raster(self.landsat_b4_path) # Test changing dtypes that does not modify the data for dtype in [np.uint8, np.uint16, np.float32, np.float64, "float32"]: @@ -1831,7 +1829,7 @@ def test_show_cbar(self, example, figsize) -> None: Test cbar matches plot height. """ # Plot raster with cbar - r0 = gr.Raster(example) + r0 = gu.Raster(example) fig, ax = plt.subplots(figsize=(figsize, figsize)) r0.show( ax=ax, @@ -1857,8 +1855,8 @@ def test_show_cbar(self, example, figsize) -> None: def test_show(self) -> None: # Read single band raster and RGB raster - img = gr.Raster(self.landsat_b4_path) - img_RGB = gr.Raster(self.landsat_rgb_path) + img = gu.Raster(self.landsat_b4_path) + img_RGB = gu.Raster(self.landsat_rgb_path) # Test default plot ax = plt.subplot(111) @@ -1902,7 +1900,7 @@ def test_show(self) -> None: def test_save(self, example: str) -> None: # Read single band raster - img = gr.Raster(example) + img = gu.Raster(example) # Temporary folder temp_dir = tempfile.TemporaryDirectory() @@ -1910,7 +1908,7 @@ def test_save(self, example: str) -> None: # Save file to temporary file, with defaults opts temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) img.save(temp_file.name) - saved = gr.Raster(temp_file.name) + saved = gu.Raster(temp_file.name) assert img.raster_equal(saved) # Try to save with a pathlib path (create a new temp file for Windows) @@ -1923,14 +1921,14 @@ def test_save(self, example: str) -> None: metadata = {"Type": "test"} temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) img.save(temp_file.name, co_opts=co_opts, metadata=metadata) - saved = gr.Raster(temp_file.name) + saved = gu.Raster(temp_file.name) assert img.raster_equal(saved) assert saved.tags["Type"] == "test" # Test that nodata value is enforced when masking - since value 0 is not used, data should be unchanged temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) img.save(temp_file.name, nodata=0) - saved = gr.Raster(temp_file.name) + saved = gu.Raster(temp_file.name) assert np.ma.allequal(img.data, saved.data) assert saved.nodata == 0 @@ -1940,7 +1938,7 @@ def test_save(self, example: str) -> None: if img.nodata is not None: temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) img.save(temp_file.name) - saved = gr.Raster(temp_file.name) + saved = gu.Raster(temp_file.name) assert np.array_equal(img.data.mask, saved.data.mask) # Test that a warning is raised if nodata is not set and a mask exists (defined above) @@ -1956,7 +1954,7 @@ def test_save(self, example: str) -> None: def test_coords(self) -> None: - img = gr.Raster(self.landsat_b4_path) + img = gu.Raster(self.landsat_b4_path) xx, yy = img.coords(offset="corner") assert xx.min() == pytest.approx(img.bounds.left) assert xx.max() == pytest.approx(img.bounds.right - img.res[0]) @@ -1985,7 +1983,7 @@ def test_value_at_coords2(self) -> None: """ Check that values returned at selected pixels correspond to what is expected, both for original CRS and lat/lon. """ - img = gr.Raster(self.landsat_b4_path) + img = gu.Raster(self.landsat_b4_path) # Lower right pixel x, y = [img.bounds.right - img.res[0], img.bounds.bottom + img.res[1]] @@ -2006,23 +2004,23 @@ def test_from_array(self) -> None: # Test that from_array works if nothing is changed # -> most tests already performed in test_copy, no need for more - img = gr.Raster(self.landsat_b4_path) - out_img = gr.Raster.from_array(img.data, img.transform, img.crs, nodata=img.nodata) + img = gu.Raster(self.landsat_b4_path) + out_img = gu.Raster.from_array(img.data, img.transform, img.crs, nodata=img.nodata) assert out_img.raster_equal(img) # Test that changes to data are taken into account bias = 5 - out_img = gr.Raster.from_array(img.data + bias, img.transform, img.crs, nodata=img.nodata) + out_img = gu.Raster.from_array(img.data + bias, img.transform, img.crs, nodata=img.nodata) assert np.ma.allequal(out_img.data, img.data + bias) # Test that nodata is properly taken into account - out_img = gr.Raster.from_array(img.data + 5, img.transform, img.crs, nodata=0) + out_img = gu.Raster.from_array(img.data + 5, img.transform, img.crs, nodata=0) assert out_img.nodata == 0 # Test that data mask is taken into account img.data.mask = np.zeros((img.shape), dtype="bool") img.data.mask[0, 0, 0] = True - out_img = gr.Raster.from_array(img.data, img.transform, img.crs, nodata=0) + out_img = gu.Raster.from_array(img.data, img.transform, img.crs, nodata=0) assert out_img.data.mask[0, 0, 0] def test_type_hints(self) -> None: @@ -2031,7 +2029,7 @@ def test_type_hints(self) -> None: temp_dir = tempfile.TemporaryDirectory() temp_path = os.path.join(temp_dir.name, "code.py") - # r = gr.Raster(self.landsat_b4_path) + # r = gu.Raster(self.landsat_b4_path) # Load the attributes to check attributes = ["transform", "crs", "nodata", "name", "driver", "is_loaded", "filename"] @@ -2066,7 +2064,7 @@ def test_type_hints(self) -> None: def test_split_bands(self) -> None: - img = gr.Raster(self.landsat_rgb_path) + img = gu.Raster(self.landsat_rgb_path) red, green, blue = img.split_bands(copy=False) @@ -2129,8 +2127,8 @@ def test_resampling_str(self) -> None: if "not a valid rasterio.enums.Resampling method" not in str(exception): raise exception - img1 = gr.Raster(self.landsat_b4_path) - img2 = gr.Raster(self.landsat_b4_crop_path) + img1 = gu.Raster(self.landsat_b4_path) + img2 = gu.Raster(self.landsat_b4_crop_path) img1.set_nodata(0) img2.set_nodata(0) @@ -2143,7 +2141,7 @@ def test_resampling_str(self) -> None: def test_polygonize(self, example: str) -> None: """Test that polygonize doesn't raise errors.""" - img = gr.Raster(example) + img = gu.Raster(example) # -- Test 1: basic functioning of polygonize -- @@ -2157,7 +2155,7 @@ def test_polygonize(self, example: str) -> None: # Check that these two areas are approximately equal assert polygon_area == pytest.approx(pixel_area) - assert isinstance(polygonized, gv.Vector) + assert isinstance(polygonized, gu.Vector) assert polygonized.crs == img.crs # -- Test 2: data types -- @@ -2294,25 +2292,26 @@ def test_to_points(self) -> None: ) # Sample the whole raster (fraction==1) - points = img1.to_points(1) + points = img1.to_points(1, as_array=True) # Validate that 25 points were sampled (equating to img1.height * img1.width) with x, y, and band0 values. assert isinstance(points, np.ndarray) assert points.shape == (25, 3) assert np.array_equal(np.asarray(points[:, 0]), np.tile(np.linspace(0.5, 4.5, 5), 5)) - assert img1.to_points(0.2).shape == (5, 3) + assert img1.to_points(0.2, as_array=True).shape == (5, 3) img2 = gu.Raster(self.landsat_rgb_path, load_data=False) - points = img2.to_points(10) + points = img2.to_points(10, as_array=True) assert points.shape == (10, 5) assert not img2.is_loaded - points_frame = img2.to_points(10, as_frame=True) + points_frame = img2.to_points(10) - assert np.array_equal(points_frame.columns, ["b1", "b2", "b3", "geometry"]) + assert isinstance(points_frame, gu.Vector) + assert np.array_equal(points_frame.ds.columns, ["b1", "b2", "b3", "geometry"]) assert points_frame.crs == img2.crs @@ -2452,16 +2451,16 @@ class TestArithmetic: # TODO: Add the case where a mask exists in the array, as in test_data_setter width = height = 5 transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) - r1 = gr.Raster.from_array(np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None) - r2 = gr.Raster.from_array(np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None) + r1 = gu.Raster.from_array(np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None) + r2 = gu.Raster.from_array(np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None) # Tests with different dtype - r1_f32 = gr.Raster.from_array( + r1_f32 = gu.Raster.from_array( np.random.randint(1, 255, (height, width)).astype("float32"), transform=transform, crs=None ) # Test with nodata value set - r1_nodata = gr.Raster.from_array( + r1_nodata = gu.Raster.from_array( np.random.randint(1, 255, (height, width)).astype("float32"), transform=transform, crs=None, @@ -2469,7 +2468,7 @@ class TestArithmetic: ) # Test with 0 values - r2_zero = gr.Raster.from_array( + r2_zero = gu.Raster.from_array( np.random.randint(1, 255, (height, width)).astype("float32"), transform=transform, crs=None, @@ -2478,20 +2477,20 @@ class TestArithmetic: r2_zero.data[0, 0, 0] = 0 # Create rasters with different shape, crs or transforms for testing errors - r1_wrong_shape = gr.Raster.from_array( + r1_wrong_shape = gu.Raster.from_array( np.random.randint(0, 255, (height + 1, width)).astype("float32"), transform=transform, crs=None, ) - r1_wrong_crs = gr.Raster.from_array( + r1_wrong_crs = gu.Raster.from_array( np.random.randint(0, 255, (height, width)).astype("float32"), transform=transform, crs=rio.crs.CRS.from_epsg(4326), ) transform2 = rio.transform.from_bounds(0, 0, 2, 2, width, height) - r1_wrong_transform = gr.Raster.from_array( + r1_wrong_transform = gu.Raster.from_array( np.random.randint(0, 255, (height, width)).astype("float32"), transform=transform2, crs=None ) @@ -2633,7 +2632,7 @@ def test_ops_2args_expl(self, op: str) -> None: r3 = getattr(r1, op)(r2) ctype = np.find_common_type([r1.data.dtype, r2.data.dtype], []) numpy_output = getattr(r1.data.astype(ctype), op)(r2.data.astype(ctype)) - assert isinstance(r3, gr.Raster) + assert isinstance(r3, gu.Raster) assert np.all(r3.data == numpy_output) assert r3.data.dtype == numpy_output.dtype if np.sum(r3.data.mask) == 0: @@ -2647,7 +2646,7 @@ def test_ops_2args_expl(self, op: str) -> None: r1_copy = r1.copy() r2_copy = r2.copy() r3 = getattr(r1, op)(r2) - assert isinstance(r3, gr.Raster) + assert isinstance(r3, gu.Raster) assert r1.raster_equal(r1_copy) assert r2.raster_equal(r2_copy) @@ -2683,7 +2682,7 @@ def test_ops_2args_expl(self, op: str) -> None: # Test with a numpy array r1 = self.r1_f32 r3 = getattr(r1, op)(array) - assert isinstance(r3, gr.Raster) + assert isinstance(r3, gu.Raster) assert np.all(r3.data == getattr(r1.data, op)(array)) if np.sum(r3.data.mask) == 0: assert r3.nodata is None @@ -2692,7 +2691,7 @@ def test_ops_2args_expl(self, op: str) -> None: # Test with an integer r3 = getattr(r1, op)(intval) - assert isinstance(r3, gr.Raster) + assert isinstance(r3, gu.Raster) assert np.all(r3.data == getattr(r1.data, op)(intval)) if np.sum(r3.data.mask) == 0: assert r3.nodata is None @@ -2702,7 +2701,7 @@ def test_ops_2args_expl(self, op: str) -> None: # Test with a float value r3 = getattr(r1, op)(floatval) dtype = np.dtype(rio.dtypes.get_minimum_dtype(floatval)) - assert isinstance(r3, gr.Raster) + assert isinstance(r3, gu.Raster) assert r3.data.dtype == dtype assert np.all(r3.data == getattr(r1.data, op)(np.array(floatval).astype(dtype))) if np.sum(r3.data.mask) == 0: @@ -2712,7 +2711,7 @@ def test_ops_2args_expl(self, op: str) -> None: # Test with child class r3 = getattr(satimg, op)(intval) - assert isinstance(r3, gr.SatelliteImage) + assert isinstance(r3, gu.SatelliteImage) reflective_ops = [["__add__", "__radd__"], ["__mul__", "__rmul__"]] @@ -2770,16 +2769,16 @@ def test_reflectivity(self, ops: list[str]) -> None: def from_array( cls: type[TestArithmetic], data: np.ndarray | np.ma.masked_array, - rst_ref: gr.RasterType, + rst_ref: gu.RasterType, nodata: int | float | list[int] | list[float] | None = None, - ) -> gr.Raster: + ) -> gu.Raster: """ Generate a Raster from numpy array, with set georeferencing. Used for testing only. """ if nodata is None: nodata = rst_ref.nodata - return gr.Raster.from_array(data, crs=rst_ref.crs, transform=rst_ref.transform, nodata=nodata) + return gu.Raster.from_array(data, crs=rst_ref.crs, transform=rst_ref.transform, nodata=nodata) def test_ops_2args_implicit(self) -> None: """ @@ -2979,7 +2978,7 @@ def test_numpy_functions(self, dtype: str) -> None: assert np.mean(raster) == 12.0 # Check that rasters don't become arrays when using simple arithmetic. - assert isinstance(raster + 1, gr.Raster) + assert isinstance(raster + 1, gu.Raster) # Test the data setter method by creating a new array raster.data = array + 2 @@ -2990,7 +2989,7 @@ def test_numpy_functions(self, dtype: str) -> None: # Test raster += array - assert isinstance(raster, gr.Raster) + assert isinstance(raster, gu.Raster) assert np.median(raster) == 26.0 @@ -3102,7 +3101,7 @@ def test_array_ufunc_1nin_1nout(self, ufunc_str: str, nodata_init: None | str, d # Create Raster ma1 = np.ma.masked_array(data=self.arr1.astype(dtype), mask=self.mask1) - rst = gr.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata) + rst = gu.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata) # Get ufunc ufunc = getattr(np, ufunc_str) @@ -3163,8 +3162,8 @@ def test_array_ufunc_2nin_1nout( ma1 = np.ma.masked_array(data=self.arr1.astype(dtype1), mask=self.mask1) ma2 = np.ma.masked_array(data=self.arr2.astype(dtype2), mask=self.mask2) - rst1 = gr.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata1) - rst2 = gr.Raster.from_array(ma2, transform=self.transform, crs=None, nodata=nodata2) + rst1 = gu.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata1) + rst2 = gu.Raster.from_array(ma2, transform=self.transform, crs=None, nodata=nodata2) ufunc = getattr(np, ufunc_str) @@ -3238,7 +3237,7 @@ def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: N # Create Raster ma1 = np.ma.masked_array(data=self.arr1.astype(dtype), mask=self.mask1) - rst = gr.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata) + rst = gu.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata) # Get array func arrfunc = getattr(np, arrfunc_str) @@ -3266,12 +3265,19 @@ def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: N # For the median, the statistic is computed by masking the values through np.ma.median output_rst = arrfunc(rst) output_ma = np.ma.median(rst.data) + elif "gradient" in arrfunc_str: + # For the gradient, we need to take a single band + output_rst = arrfunc(rst) + output_ma = np.gradient(rst.data[0, :, :]) else: output_rst = arrfunc(rst) output_ma = arrfunc(rst.data) + # Gradient is the only supported array function returning two arguments for now + if "gradient" in arrfunc_str: + assert np.ma.allequal(output_rst[0], output_ma[0]) and np.ma.allequal(output_rst[1], output_ma[1]) # This test is for when the NumPy function reduces the dimension of the array but not completely - if isinstance(output_rst, np.ndarray): + elif isinstance(output_rst, np.ndarray): assert np.ma.allequal(output_rst, output_ma) # This test is for when the NumPy function reduces the dimension to a single number else: @@ -3305,8 +3311,8 @@ def test_array_functions_2nin( ma1 = np.ma.masked_array(data=self.arr1.astype(dtype1), mask=self.mask1) ma2 = np.ma.masked_array(data=self.arr2.astype(dtype2), mask=self.mask2) - rst1 = gr.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata1) - rst2 = gr.Raster.from_array(ma2, transform=self.transform, crs=None, nodata=nodata2) + rst1 = gu.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=nodata1) + rst2 = gu.Raster.from_array(ma2, transform=self.transform, crs=None, nodata=nodata2) # Get array func arrfunc = getattr(np, arrfunc_str) diff --git a/tests/test_satimg.py b/tests/test_satimg.py index 6662cd075..4217555c6 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -9,8 +9,7 @@ import pytest import rasterio as rio -import geoutils -import geoutils.georaster as gr +import geoutils as gu from geoutils import examples DO_PLOT = False @@ -28,18 +27,18 @@ def test_init(self, example: str) -> None: """ # from filename, checking option - img = gr.SatelliteImage(example, read_from_fn=False) - img = gr.SatelliteImage(example) - assert isinstance(img, gr.SatelliteImage) + img = gu.SatelliteImage(example, read_from_fn=False) + img = gu.SatelliteImage(example) + assert isinstance(img, gu.SatelliteImage) # from SatelliteImage - img2 = gr.SatelliteImage(img) - assert isinstance(img2, gr.SatelliteImage) + img2 = gu.SatelliteImage(img) + assert isinstance(img2, gu.SatelliteImage) # from Raster - r = gr.Raster(example) - img3 = gr.SatelliteImage(r) - assert isinstance(img3, gr.SatelliteImage) + r = gu.Raster(example) + img3 = gu.SatelliteImage(r) + assert isinstance(img3, gu.SatelliteImage) assert img.raster_equal(img2) assert img.raster_equal(img3) @@ -64,13 +63,13 @@ def __exit__(self, *args) -> None: # type: ignore sys.stdout = self._stdout with Capturing() as output1: - gr.SatelliteImage(example, silent=False) + gu.SatelliteImage(example, silent=False) # check the metadata reading outputs to console assert len(output1) > 0 with Capturing() as output2: - gr.SatelliteImage(example, silent=True) + gu.SatelliteImage(example, silent=True) # check nothing outputs to console assert len(output2) == 0 @@ -82,22 +81,22 @@ def test_add_sub(self) -> None: # Create fake rasters with random values in 0-255 and dtype uint8 width = height = 5 transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) - satimg1 = gr.SatelliteImage.from_array( + satimg1 = gu.SatelliteImage.from_array( np.random.randint(0, 255, (height, width), dtype="uint8"), transform=transform, crs=None ) - satimg2 = gr.SatelliteImage.from_array( + satimg2 = gu.SatelliteImage.from_array( np.random.randint(0, 255, (height, width), dtype="uint8"), transform=transform, crs=None ) # Check that output type is same - other tests are in test_georaster.py sat_out = -satimg1 - assert isinstance(sat_out, gr.SatelliteImage) + assert isinstance(sat_out, gu.SatelliteImage) sat_out = satimg1 + satimg2 - assert isinstance(sat_out, gr.SatelliteImage) + assert isinstance(sat_out, gu.SatelliteImage) sat_out = satimg1 - satimg2 # type: ignore - assert isinstance(sat_out, gr.SatelliteImage) + assert isinstance(sat_out, gu.SatelliteImage) @pytest.mark.parametrize("example", [landsat_b4, aster_dem]) # type: ignore def test_copy(self, example: str) -> None: @@ -108,7 +107,7 @@ def test_copy(self, example: str) -> None: - if r is copied, r.data changed, r2.data should be unchanged """ # Open dataset, update data and make a copy - r = gr.SatelliteImage(example) + r = gu.SatelliteImage(example) r.data += 5 r2 = r.copy() @@ -116,7 +115,7 @@ def test_copy(self, example: str) -> None: assert r is not r2 # Check the object is a SatelliteImage - assert isinstance(r2, gr.SatelliteImage) + assert isinstance(r2, gu.SatelliteImage) # check all immutable attributes are equal georaster_attrs = [ @@ -135,7 +134,7 @@ def test_copy(self, example: str) -> None: satimg_attrs = ["satellite", "sensor", "product", "version", "tile_name", "datetime"] # using list directly available in Class attrs = georaster_attrs + satimg_attrs - all_attrs = attrs + gr.satimg.satimg_attrs + all_attrs = attrs + gu.georaster.satimg.satimg_attrs for attr in all_attrs: assert r.__getattribute__(attr) == r2.__getattribute__(attr) @@ -192,7 +191,7 @@ def test_filename_parsing(self) -> None: ] for names in copied_names: - attrs = geoutils.georaster.satimg.parse_metadata_from_fn(names) + attrs = gu.georaster.satimg.parse_metadata_from_fn(names) i = copied_names.index(names) assert satellites[i] == attrs[0] assert sensors[i] == attrs[1] @@ -207,17 +206,17 @@ def test_sw_tile_naming_parsing(self) -> None: test_latlon = [(14, -65), (-14, 65), (14, -65), (14, -65), (14, -65), (0, 0)] for tile in test_tiles: - assert geoutils.georaster.satimg.sw_naming_to_latlon(tile)[0] == test_latlon[test_tiles.index(tile)][0] - assert geoutils.georaster.satimg.sw_naming_to_latlon(tile)[1] == test_latlon[test_tiles.index(tile)][1] + assert gu.georaster.satimg.sw_naming_to_latlon(tile)[0] == test_latlon[test_tiles.index(tile)][0] + assert gu.georaster.satimg.sw_naming_to_latlon(tile)[1] == test_latlon[test_tiles.index(tile)][1] for latlon in test_latlon: - assert geoutils.georaster.satimg.latlon_to_sw_naming(latlon) == test_tiles[test_latlon.index(latlon)] + assert gu.georaster.satimg.latlon_to_sw_naming(latlon) == test_tiles[test_latlon.index(latlon)] # check possible exceptions, rounded lat/lon belong to their southwest border - assert geoutils.georaster.satimg.latlon_to_sw_naming((0, 0)) == "N00E000" + assert gu.georaster.satimg.latlon_to_sw_naming((0, 0)) == "N00E000" # those are the same point, should give same naming - assert geoutils.georaster.satimg.latlon_to_sw_naming((-90, 0)) == "S90E000" - assert geoutils.georaster.satimg.latlon_to_sw_naming((90, 0)) == "S90E000" + assert gu.georaster.satimg.latlon_to_sw_naming((-90, 0)) == "S90E000" + assert gu.georaster.satimg.latlon_to_sw_naming((90, 0)) == "S90E000" # same here - assert geoutils.georaster.satimg.latlon_to_sw_naming((0, -180)) == "N00W180" - assert geoutils.georaster.satimg.latlon_to_sw_naming((0, 180)) == "N00W180" + assert gu.georaster.satimg.latlon_to_sw_naming((0, -180)) == "N00W180" + assert gu.georaster.satimg.latlon_to_sw_naming((0, 180)) == "N00W180" From 3d22013a85897eed5cfe0c177eaa665bba8e252b Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 16:13:47 -0800 Subject: [PATCH 050/184] Fix tests --- doc/source/api.md | 2 +- geoutils/georaster/raster.py | 6 ++--- geoutils/georaster/satimg.py | 50 +++++++++++++++++++++++++++++++++--- geoutils/geovector.py | 9 ++++--- tests/test_georaster.py | 24 ++++++++--------- tests/test_geovector.py | 2 +- 6 files changed, 69 insertions(+), 24 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index 459281cda..f3e81ec95 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -144,7 +144,7 @@ documentation. :toctree: gen_modules/ Raster.raster_equal - Raster.equal_georeferenced_grid + Raster.georeferenced_grid_equal ``` ### Arithmetic with other rasters, arrays or numbers diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 7aa3fa8a0..7fdfdb481 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -677,7 +677,7 @@ def __getitem__(self, value: Raster | Vector | list[float] | tuple[float, ...]) """Subset the Raster object: calls the crop method with default parameters""" # If input is Mask with the same shape and georeferencing, index in 1D - if isinstance(value, Mask) and self.equal_georeferenced_grid(value): + if isinstance(value, Mask) and self.georeferenced_grid_equal(value): return self.data[value.data] # Otherwise, subset with crop @@ -1375,7 +1375,7 @@ def copy(self: RasterType, new_array: np.ndarray | None = None) -> RasterType: return cp - def equal_georeferenced_grid(self: RasterType, raster: RasterType) -> bool: + def georeferenced_grid_equal(self: RasterType, raster: RasterType) -> bool: """ Check that grid shape, geotransform and CRS are equal. @@ -2309,7 +2309,7 @@ def format_value(value: Any) -> Any: if window is not None: value = reducer_function(value.flatten()) else: - value = value[0, 0, 0] + value = value[0, 0] else: value = None return value diff --git a/geoutils/georaster/satimg.py b/geoutils/georaster/satimg.py index 70a90690d..cbb732105 100644 --- a/geoutils/georaster/satimg.py +++ b/geoutils/georaster/satimg.py @@ -25,7 +25,7 @@ def parse_landsat(gname: str) -> list[Any]: attrs.append(lsat_sensor[gname[1]]) attrs.append(None) attrs.append(None) - attrs.append((int(gname[3:6]), int(gname[6:9]))) + attrs.append((gname[3:6] + gname[6:9])) year = int(gname[9:13]) doy = int(gname[13:16]) attrs.append(dt.datetime.fromordinal(dt.date(year - 1, 12, 31).toordinal() + doy)) @@ -35,7 +35,7 @@ def parse_landsat(gname: str) -> list[Any]: attrs.append(lsat_sensor[split_name[0][1]]) attrs.append(None) attrs.append(None) - attrs.append((int(split_name[2][0:3]), int(split_name[2][3:6]))) + attrs.append((split_name[2][0:3] + split_name[2][3:6])) attrs.append(dt.datetime.strptime(split_name[3], "%Y%m%d")) attrs.append(attrs[3].date()) return attrs @@ -354,7 +354,7 @@ def __parse_metadata_from_fn(self, silent: bool = False) -> None: if not silent: print("From filename: setting " + name + " as " + str(attr_fn)) # Set hidden attribute first - setattr(self, "_"+name, attr_fn) + setattr(self, name, attr_fn) elif attr is not None and attrs[name_attrs.index(name)] is not None: if not silent: print( @@ -367,30 +367,74 @@ def __parse_metadata_from_fn(self, silent: bool = False) -> None: + "from filename" ) + + @property def datetime(self) -> dt.datetime | None: return self._datetime + @datetime.setter + def datetime(self, value: dt.datetime | None) -> None: + if isinstance(value, (dt.datetime, np.datetime64)) or value is None: + self._datetime = value + else: + raise ValueError('Datetime must be set with a python or NumPy datetime.') + @property def satellite(self) -> str | None: return self._satellite + @satellite.setter + def satellite(self, value: str | None) -> None: + if isinstance(value, str) or value is None: + self._satellite = value + else: + raise ValueError('Satellite must be set with a string.') + @property def sensor(self) -> str | None: return self._sensor + @sensor.setter + def sensor(self, value: str | None) -> None: + if isinstance(value, str) or value is None: + self._sensor = value + else: + raise ValueError('Sensor must be set with a string.') + @property def product(self) -> str | None: return self._product + @product.setter + def product(self, value: str | None) -> None: + if isinstance(value, str) or value is None: + self._product = value + else: + raise ValueError('Product must be set with a string.') + @property def version(self) -> str | None: return self._version + @version.setter + def version(self, value: str | None) -> None: + if isinstance(value, str) or value is None: + self._version = value + else: + raise ValueError('Version must be set with a string.') + @property def tile_name(self) -> str | None: return self._tile_name + @tile_name.setter + def tile_name(self, value: str | None) -> None: + if isinstance(value, str) or value is None: + self._tile_name = value + else: + raise ValueError('Tile name must be set with a string.') + def __parse_metadata_from_file(self, fn_meta: str | None) -> None: warnings.warn(f"Parse metadata from file not implemented. {fn_meta}") diff --git a/geoutils/geovector.py b/geoutils/geovector.py index d4dfec24f..98166d106 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -293,15 +293,16 @@ def crop( # Need to separate the two options, inplace update if inplace: - self.ds = self.ds.cx[xmin:xmax, ymin:ymax] + self._ds = self.ds.cx[xmin:xmax, ymin:ymax] if clip: - self.ds = self.ds.clip(mask=(xmin, ymin, xmax, ymax)) + self._ds = self.ds.clip(mask=(xmin, ymin, xmax, ymax)) return None # Or create a copy otherwise else: new_vector = self.copy() - new_vector.ds = new_vector.ds.cx[xmin:xmax, ymin:ymax] - new_vector.ds = new_vector.ds.clip(mask=(xmin, ymin, xmax, ymax)) + new_vector._ds = new_vector.ds.cx[xmin:xmax, ymin:ymax] + if clip: + new_vector._ds = new_vector.ds.clip(mask=(xmin, ymin, xmax, ymax)) return new_vector def reproject( diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 32bfa98c5..05ef4cf3c 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2263,7 +2263,7 @@ def test_proximity_parameters(self) -> None: prox1 = raster1.proximity() # The raster should have the same extent, resolution and CRS - assert raster1.equal_georeferenced_grid(prox1) + assert raster1.georeferenced_grid_equal(prox1) # It should change with target values specified prox2 = raster1.proximity(target_values=[255]) @@ -2549,49 +2549,49 @@ def test_equal_georeferenced_grid(self) -> None: # -- Test 1: based on a copy -- r1 = self.r1 r2 = r1.copy() - assert r1.equal_georeferenced_grid(r2) + assert r1.georeferenced_grid_equal(r2) # Change data r2.data += 1 - assert r1.equal_georeferenced_grid(r2) + assert r1.georeferenced_grid_equal(r2) # Change mask (False by default) r2 = r1.copy() r2.data[0, 0] = np.ma.masked - assert r1.equal_georeferenced_grid(r2) + assert r1.georeferenced_grid_equal(r2) # Change fill_value (999999 by default) r2 = r1.copy() r2.data.fill_value = 0 - assert r1.equal_georeferenced_grid(r2) + assert r1.georeferenced_grid_equal(r2) # Change dtype r2 = r1.copy() r2 = r2.astype("float32") - assert r1.equal_georeferenced_grid(r2) + assert r1.georeferenced_grid_equal(r2) # Change transform r2 = r1.copy() r2.transform = rio.transform.from_bounds(0, 0, 1, 1, self.width + 1, self.height) - assert not r1.equal_georeferenced_grid(r2) + assert not r1.georeferenced_grid_equal(r2) # Change CRS r2 = r1.copy() r2.crs = rio.crs.CRS.from_epsg(4326) - assert not r1.equal_georeferenced_grid(r2) + assert not r1.georeferenced_grid_equal(r2) # Change nodata r2 = r1.copy() r2.set_nodata(34) - assert r1.equal_georeferenced_grid(r2) + assert r1.georeferenced_grid_equal(r2) # -- Test 2: based on another Raster with one different georeferenced grid attribute -- - assert not r1.equal_georeferenced_grid(self.r1_wrong_crs) + assert not r1.georeferenced_grid_equal(self.r1_wrong_crs) - assert not r1.equal_georeferenced_grid(self.r1_wrong_shape) + assert not r1.georeferenced_grid_equal(self.r1_wrong_shape) - assert not r1.equal_georeferenced_grid(self.r1_wrong_transform) + assert not r1.georeferenced_grid_equal(self.r1_wrong_transform) # List of operations with two operands ops_2args = [ diff --git a/tests/test_geovector.py b/tests/test_geovector.py index 552bcb22d..27f5cb26f 100644 --- a/tests/test_geovector.py +++ b/tests/test_geovector.py @@ -192,7 +192,7 @@ def test_proximity(self) -> None: prox1 = vector.proximity(raster=raster1) # The proximity should have the same extent, resolution and CRS - assert raster1.equal_georeferenced_grid(prox1) + assert raster1.georeferenced_grid_equal(prox1) # With the base geometry vector.proximity(raster=raster1, geometry_type="geometry") From 0e5842641938b6340abc7b3e552203f0b55dac1a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 16:55:32 -0800 Subject: [PATCH 051/184] Linting --- doc/source/about_geoutils.md | 32 ++--- doc/source/api.md | 20 +-- doc/source/conf.py | 33 +++-- doc/source/core_array_funcs.md | 22 +-- doc/source/core_composition.md | 28 ++-- doc/source/core_index.md | 4 +- doc/source/core_inheritance.md | 18 +-- doc/source/core_lazy_load.md | 17 +-- doc/source/core_match_ref.md | 41 +++--- doc/source/core_py_ops.md | 34 ++--- doc/source/index.md | 18 +-- doc/source/mask_class.md | 26 ++-- doc/source/proj_tools.md | 2 +- doc/source/quick_start.md | 68 ++++----- doc/source/raster_class.md | 71 +++++----- doc/source/rasters_index.md | 4 +- doc/source/satimg_class.md | 6 +- doc/source/vector_class.md | 4 +- doc/source/vectors_index.md | 2 +- examples/analysis/README.rst | 2 +- examples/analysis/array_numerics/README.rst | 2 +- .../array_numerics/numpy_interfacing.py | 7 +- .../array_numerics/python_arithmetic.py | 7 +- examples/analysis/geospatial/README.rst | 2 +- .../analysis/geospatial/buffer_voronoi.py | 10 +- .../analysis/geospatial/proximity_metric.py | 7 +- examples/analysis/point_extraction/README.rst | 2 +- .../point_extraction/interpolation.py | 10 +- .../analysis/point_extraction/reduction.py | 12 +- examples/handling/README.rst | 2 +- examples/handling/georeferencing/README.rst | 2 +- .../handling/georeferencing/crop_raster.py | 8 +- .../handling/georeferencing/crop_vector.py | 14 +- .../handling/georeferencing/reproj_raster.py | 4 +- .../handling/georeferencing/reproj_vector.py | 8 +- examples/handling/interface/README.rst | 2 +- examples/handling/interface/create_mask.py | 3 +- examples/handling/interface/polygonize.py | 5 +- examples/handling/interface/rasterize.py | 3 +- examples/handling/interface/topoints.py | 3 +- examples/io/README.rst | 2 +- examples/io/import_export/README.rst | 2 +- examples/io/import_export/from_array.py | 21 +-- examples/io/import_export/import_raster.py | 5 +- examples/io/import_export/import_vector.py | 3 +- examples/io/open_save/README.rst | 2 +- examples/io/open_save/read_raster.py | 3 +- examples/io/open_save/read_satimg.py | 1 + examples/io/open_save/read_vector.py | 2 +- geoutils/georaster/__init__.py | 2 +- geoutils/georaster/raster.py | 134 ++++++++++++------ geoutils/georaster/satimg.py | 18 ++- geoutils/geovector.py | 125 ++++++++++------ tests/test_docs.py | 1 - 54 files changed, 496 insertions(+), 390 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 67734efd7..118744680 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -4,19 +4,19 @@ ## What is GeoUtils? -GeoUtils1 is a **[Python](https://www.python.org/) package for the handling and analysis of georeferenced data**, developed with the objective of -making such analysis accessible, efficient and reliable. +GeoUtils1 is a **[Python](https://www.python.org/) package for the handling and analysis of georeferenced data**, developed with the objective of +making such analysis accessible, efficient and reliable. ```{margin} 1With name standing for *Geospatial Utilities*. ``` -In a few words, GeoUtils can be described as a **convenience wrapper package for end-users** focusing on geospatial analysis. It allows to write shorter -code through consistent higher-level operations, implicit object behaviour and numerical interfacing. In addition, GeoUtils adds **analysis-oriented +In a few words, GeoUtils can be described as a **convenience wrapper package for end-users** focusing on geospatial analysis. It allows to write shorter +code through consistent higher-level operations, implicit object behaviour and numerical interfacing. In addition, GeoUtils adds **analysis-oriented functions** that require many steps to perform with other packages, and which are robustly tested. -GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for remote sensing and Earth's surface -applications** that rely on moderate- to high-resolution georeferenced data. All applications that, for analysis, require robust reprojections, re-gridding, +GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for remote sensing and Earth's surface +applications** that rely on moderate- to high-resolution georeferenced data. All applications that, for analysis, require robust reprojections, re-gridding, point interpolation, and other types of fine-grid analysis with millions of pixels. @@ -28,8 +28,8 @@ We are working on making features fully consistent for the first long-term relea ## Why use GeoUtils? -GeoUtils is built on top of [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) -and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenced operations, and relies on [NumPy](https://numpy.org/doc/stable/), +GeoUtils is built on top of [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) +and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenced operations, and relies on [NumPy](https://numpy.org/doc/stable/), [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: - A **common and consistent framework** for efficient rasters and vectors handling, - A structure following the **principal of least knowledge**2 to foster accessibility, @@ -53,19 +53,19 @@ More on these core features of GeoUtils in the {ref}`quick-start`, or {ref}`core ## Why the need for GeoUtils? -Recent community efforts have improved open-source geospatial analysis in Python, allowing to **move away from the low-level functions and +Recent community efforts have improved open-source geospatial analysis in Python, allowing to **move away from the low-level functions and complexity of [GDAL and OGR](https://gdal.org/)**'s Python bindings for raster and vector handling. Those efforts include in particular [Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html). -However, these new packages still maintain a relatively low-level API to serve all types of geospatial informatics users, **slowing down end-users focusing -on data analysis**. As a result, interfacing between vector data and raster data is delicate and simple higher-level operation (such as +However, these new packages still maintain a relatively low-level API to serve all types of geospatial informatics users, **slowing down end-users focusing +on data analysis**. As a result, interfacing between vector data and raster data is delicate and simple higher-level operation (such as reprojection to match a reference) are not always computed consistently in the community. -Additionally, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses mostly on reading, projecting and writing, and thus **requires array extraction -and re-encapsulation** either before, during or after any numerical computation to interface with other georeferencing packages. +Additionally, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses mostly on reading, projecting and writing, and thus **requires array extraction +and re-encapsulation** either before, during or after any numerical computation to interface with other georeferencing packages. -Finally, **many common geospatial analysis tools are generally unavailable** in existing packages (e.g., boolean-masking from vectors, -[proximity](https://gdal.org/programs/gdal_proximity.html) estimation, metric buffering) as they rely on a combination of lower-level operations. +Finally, **many common geospatial analysis tools are generally unavailable** in existing packages (e.g., boolean-masking from vectors, +[proximity](https://gdal.org/programs/gdal_proximity.html) estimation, metric buffering) as they rely on a combination of lower-level operations. ```{admonition} Conclusion Having higher-level geospatial tools implemented in a **consistent** manner and tested for **robustness** is essential for the wider geospatial community. -``` \ No newline at end of file +``` diff --git a/doc/source/api.md b/doc/source/api.md index f3e81ec95..743207566 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -1,8 +1,8 @@ (api)= # API reference -This page provides an auto-generated summary of GeoUtils’ API. -For more details and examples, refer to the relevant chapters in the main part of the +This page provides an auto-generated summary of GeoUtils’ API. +For more details and examples, refer to the relevant chapters in the main part of the documentation. ```{eval-rst} @@ -106,9 +106,9 @@ documentation. Raster.astype Raster.set_mask Raster.set_nodata - Raster.get_nanarray + Raster.get_nanarray ``` - + ### I/O methods ```{eval-rst} @@ -134,7 +134,7 @@ documentation. Raster.shift Raster.get_bounds_projected Raster.intersection - Raster.outside_image + Raster.outside_image ``` ### Testing methods @@ -160,7 +160,7 @@ documentation. Raster.__truediv__ Raster.__floordiv__ Raster.__mod__ - Raster.__pow__ + Raster.__pow__ ``` And reverse operations. @@ -176,7 +176,7 @@ And reverse operations. Raster.__lt__ Raster.__le__ Raster.__gt__ - Raster.__ge__ + Raster.__ge__ ``` ### Array interface with NumPy @@ -186,7 +186,7 @@ And reverse operations. :toctree: gen_modules/ Raster.__array_ufunc__ - Raster.__array_function__ + Raster.__array_function__ ``` ## SatelliteImage @@ -311,5 +311,5 @@ And reverse operations. ```{eval-rst} .. autosummary:: :toctree: gen_modules/ - - Vector.get_bounds_projected \ No newline at end of file + + Vector.get_bounds_projected diff --git a/doc/source/conf.py b/doc/source/conf.py index 9fe4305d0..34168b40b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -31,14 +31,14 @@ "matplotlib.sphinxext.plot_directive", # Render matplotlib figures from code. "sphinx.ext.autosummary", # Create API doc summary texts from the docstrings. "sphinx.ext.inheritance_diagram", # For class inheritance diagrams. - "sphinx.ext.graphviz", # To render graphviz diagrams. - "sphinx_design", # To render nice blocks + "sphinx.ext.graphviz", # To render graphviz diagrams. + "sphinx_design", # To render nice blocks "sphinx_autodoc_typehints", # Include type hints in the API documentation. "sphinxcontrib.programoutput", "sphinx_gallery.gen_gallery", # Examples gallery "sphinx.ext.intersphinx", # "myst_parser", !! Not needed with myst_nb !! # Form of Markdown that works with sphinx, used a lot by the Sphinx Book Theme - "myst_nb" # MySt for rendering Jupyter notebook in documentation + "myst_nb", # MySt for rendering Jupyter notebook in documentation ] # For sphinx design to work properly @@ -55,7 +55,7 @@ "geopandas": ("https://geopandas.org/en/stable", None), "xarray": ("https://docs.xarray.dev/en/stable/", None), "xdem": ("https://xdem.readthedocs.io/en/latest", None), - "rioxarray": ("https://corteva.github.io/rioxarray/stable/", None) + "rioxarray": ("https://corteva.github.io/rioxarray/stable/", None), } sphinx_gallery_conf = { @@ -64,7 +64,11 @@ os.path.join(os.path.dirname(__file__), "../", "../", "examples/handling"), os.path.join(os.path.dirname(__file__), "../", "../", "examples/analysis"), ], # path to your example scripts - "gallery_dirs": ["io_examples", "handling_examples", "analysis_examples"], # path to where to save gallery generated output + "gallery_dirs": [ + "io_examples", + "handling_examples", + "analysis_examples", + ], # path to where to save gallery generated output "inspect_global_variables": True, # Make links to the class/function definitions. "reference_url": { # The module you locally document uses None @@ -82,15 +86,19 @@ } # For matplotlib figures generate with sphinx plot: (suffix, dpi) -plot_formats = [('.png', 400)] +plot_formats = [(".png", 400)] # To avoid long path names in inheritance diagrams -inheritance_alias = {"geoutils.georaster.raster.Raster": "geoutils.Raster", "geoutils.georaster.raster.Mask": "geoutils.Mask", - "geoutils.georaster.satimg.SatelliteImage": "geoutils.SatelliteImage", "geoutils.geovector.Vector": "geoutils.Vector", - "xdem.dem.DEM": "xdem.DEM"} +inheritance_alias = { + "geoutils.georaster.raster.Raster": "geoutils.Raster", + "geoutils.georaster.raster.Mask": "geoutils.Mask", + "geoutils.georaster.satimg.SatelliteImage": "geoutils.SatelliteImage", + "geoutils.geovector.Vector": "geoutils.Vector", + "xdem.dem.DEM": "xdem.DEM", +} # To avoid fuzzy PNGs -graphviz_output_format = 'svg' +graphviz_output_format = "svg" # Add any paths that contain templates here, relative to this directory. templates_path = [os.path.join(os.path.dirname(__file__), "_templates")] @@ -118,6 +126,7 @@ def setup(app): # Ignore .ipynb files app.registry.source_suffix.pop(".ipynb", None) + # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for @@ -137,10 +146,10 @@ def setup(app): # html_logo = "path/to/myimage.png" -html_static_path = ['_static'] +html_static_path = ["_static"] html_css_files = [ - 'css/custom.css', + "css/custom.css", ] # Add any paths that contain custom static files (such as style sheets) here, diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index b3148d5d3..05dfa9a8a 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -7,23 +7,23 @@ kernelspec: # Masked-array NumPy interface -NumPy possesses an [array interface](https://numpy.org/doc/stable/reference/arrays.interface.html) that allows to properly map their functions on objects +NumPy possesses an [array interface](https://numpy.org/doc/stable/reference/arrays.interface.html) that allows to properly map their functions on objects that depend on {class}`ndarrays`. GeoUtils utilizes this interface to work with all {class}`Rasters` and their subclasses. ## Universal functions -A first category of NumPy functions supported by {class}`Rasters` through the array interface is that of -[universal functions](https://numpy.org/doc/stable/reference/ufuncs.html), which operate on {class}`ndarrays` in an element-by-element -fashion. Examples of such functions are {func}`~numpy.add`, {func}`~numpy.absolute`, {func}`~numpy.isnan` or {func}`~numpy.sin`, and they number at more +A first category of NumPy functions supported by {class}`Rasters` through the array interface is that of +[universal functions](https://numpy.org/doc/stable/reference/ufuncs.html), which operate on {class}`ndarrays` in an element-by-element +fashion. Examples of such functions are {func}`~numpy.add`, {func}`~numpy.absolute`, {func}`~numpy.isnan` or {func}`~numpy.sin`, and they number at more than 90. -Universal functions can take one or two inputs, and return one or two outputs. Through GeoUtils, as long as one of the two inputs is a {class}`Rasters`, +Universal functions can take one or two inputs, and return one or two outputs. Through GeoUtils, as long as one of the two inputs is a {class}`Rasters`, the output will be a {class}`~geoutils.Raster`. If there is a second input, it can be a {class}`~geoutils.Raster` or {class}`~numpy.ndarray` with matching georeferencing or shape, respectively. -These functions inherently support the casting of different {attr}`~geoutils.Raster.dtypes` and values masked by {attr}`~geoutils.Raster.nodata` in the +These functions inherently support the casting of different {attr}`~geoutils.Raster.dtypes` and values masked by {attr}`~geoutils.Raster.nodata` in the {class}`~numpy.ma.MaskedArray`. Below, we re-use the same example created in {ref}`core-py-ops`. @@ -67,7 +67,7 @@ np.add(arr, raster) np.modf(raster) ``` -Similar to with Python operators, NumPy's [logical comparison functions](https://numpy.org/doc/stable/reference/ufuncs.html#comparison-functions) cast +Similar to with Python operators, NumPy's [logical comparison functions](https://numpy.org/doc/stable/reference/ufuncs.html#comparison-functions) cast {class}`Rasters` to a {class}`~geoutils.Mask`. ```{code-cell} ipython3 @@ -77,8 +77,8 @@ np.greater(raster, raster + np.random.normal(size=np.shape(arr))) ## Array functions -The second and last category of NumPy array functions supported by {class}`Rasters` through the array interface is that of array functions, -which are all other non-universal functions that can be applied to an array. Those function always modify the dimensionality of the output, such as +The second and last category of NumPy array functions supported by {class}`Rasters` through the array interface is that of array functions, +which are all other non-universal functions that can be applied to an array. Those function always modify the dimensionality of the output, such as {func}`~numpy.mean`, {func}`~numpy.count_nonzero` or {func}`~numpy.nanmax`. Consequently, the output is the same as it would be with {class}`ndarrays`. @@ -99,9 +99,9 @@ np.count_nonzero(raster, axis=2) Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), -[masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). +[masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). A full list of supported array function is available in {attr}`geoutils.georaster.raster.handled_array_funcs`. ## Respecting masked values -TODO: finalize once masked-array recursion bug is fixed \ No newline at end of file +TODO: finalize once masked-array recursion bug is fixed diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index 5aeb36ee8..6e5f69cae 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -8,9 +8,9 @@ kernelspec: # Composition from Rasterio and GeoPandas GeoUtils' main classes {class}`~geoutils.Raster` and {class}`~geoutils.Vector` are linked to [Rasterio](https://rasterio.readthedocs.io/en/latest/) and -[GeoPandas](https://geopandas.org/en/stable/docs.html), respectively, through class composition. +[GeoPandas](https://geopandas.org/en/stable/docs.html), respectively, through class composition. -They directly rely on their robust geospatial handling functionalities, as well of that of [PyProj](https://pyproj4.github.io/pyproj/stable/index.html), and +They directly rely on their robust geospatial handling functionalities, as well of that of [PyProj](https://pyproj4.github.io/pyproj/stable/index.html), and add a layer on top for interfacing between rasters and vectors with higher-level operations, performing easier numerical analysis, and adding more advanced geospatial functionalities. ## The {class}`~geoutils.Raster` class composition @@ -32,8 +32,8 @@ raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) raster ``` -From these **four main attributes**, many other derivatives attributes exist, such as {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.res` to -describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster.name` or +From these **four main attributes**, many other derivatives attributes exist, such as {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.res` to +describe georeferencing. When a {class}`~geoutils.Raster` is based on an **on-disk** dataset, other attributes exist such as {attr}`~geoutils.Raster.name` or {attr}`~geoutils.Raster.driver`, see {ref}`raster-class` for a summary, or the {ref}`dedicated sections of the API` for a full listing. ```{note} @@ -47,29 +47,29 @@ print(raster.info()) ``` ```{important} -The {class}`~geoutils.Raster` is not a composition of either a {class}`rasterio.io.DatasetReader`, a {class}`rasterio.io.MemoryFile` or a {class}`rasterio.io.DatasetWriter`. -It is only linked to those objects to initiate a {class}`~geoutils.Raster` instance which first loads the metadata (notably the three main metadata attributes -{attr}`~geoutils.Raster.crs`, {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.nodata`). -Then, explicity or implicitly, it can also {class}`~geoutils.Raster.load` the array data when an **on-disk** dataset exists, or {class}`~geoutils.Raster.save` the **in-memory** +The {class}`~geoutils.Raster` is not a composition of either a {class}`rasterio.io.DatasetReader`, a {class}`rasterio.io.MemoryFile` or a {class}`rasterio.io.DatasetWriter`. +It is only linked to those objects to initiate a {class}`~geoutils.Raster` instance which first loads the metadata (notably the three main metadata attributes +{attr}`~geoutils.Raster.crs`, {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.nodata`). +Then, explicitly or implicitly, it can also {class}`~geoutils.Raster.load` the array data when an **on-disk** dataset exists, or {class}`~geoutils.Raster.save` the **in-memory** dataset to a file. ``` -A {class}`~geoutils.Raster` generally exists only as an **in-memory** dataset, not linked to anything else than a {class}`numpy.ma.MaskedArray` in {attr}`~geoutils.Raster.data` -(for instance, when creating from {func}`~geoutils.Raster.from_array`). If a numerical operation is performed on a {class}`~geoutils.Raster` opened from an +A {class}`~geoutils.Raster` generally exists only as an **in-memory** dataset, not linked to anything else than a {class}`numpy.ma.MaskedArray` in {attr}`~geoutils.Raster.data` +(for instance, when creating from {func}`~geoutils.Raster.from_array`). If a numerical operation is performed on a {class}`~geoutils.Raster` opened from an **on-disk** dataset, the new dataset will be modified **in-memory**. ```{note} -To check if a {class}`~geoutils.Raster` was loaded from the **on-disk** data, use the {attr}`~geoutils.Raster.is_loaded` attribute. +To check if a {class}`~geoutils.Raster` was loaded from the **on-disk** data, use the {attr}`~geoutils.Raster.is_loaded` attribute. -To check if the {class}`~geoutils.Raster` was modified since loading from the **on-disk** data, use the {attr}`~geoutils.Raster.is_modified` attribute. +To check if the {class}`~geoutils.Raster` was modified since loading from the **on-disk** data, use the {attr}`~geoutils.Raster.is_modified` attribute. ``` ## The {class}`~geoutils.Vector` class composition A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`. -Because lazy loading is a lesser priority with vector data, a {class}`~geoutils.Vector` directly loads its {attr}`~geoutils.Vector.ds`. Besides, many -higher-level geospatial methods are already available in {class}`~geopandas.GeoDataFrame`. We thus only wrap those directly into {class}`~geoutils.Vector`, +Because lazy loading is a lesser priority with vector data, a {class}`~geoutils.Vector` directly loads its {attr}`~geoutils.Vector.ds`. Besides, many +higher-level geospatial methods are already available in {class}`~geopandas.GeoDataFrame`. We thus only wrap those directly into {class}`~geoutils.Vector`, in order to easily call them from the vector object, and build additional methods on top. ```{code-cell} ipython3 diff --git a/doc/source/core_index.md b/doc/source/core_index.md index 774309eba..bdabbd054 100644 --- a/doc/source/core_index.md +++ b/doc/source/core_index.md @@ -3,11 +3,11 @@ ```{toctree} :maxdepth: 2 - + core_composition core_match_ref core_py_ops core_array_funcs core_lazy_load core_inheritance -``` \ No newline at end of file +``` diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index bd59916d5..43a4819a4 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -8,14 +8,14 @@ kernelspec: Inheritance is practical to naturally pass down parent methods and attributes to child classes. -Many subtypes of {class}`Rasters` geospatial data exist that require additional attributes and methods, yet might benefit from methods +Many subtypes of {class}`Rasters` geospatial data exist that require additional attributes and methods, yet might benefit from methods implemented in GeoUtils. ## Overview of {class}`~geoutils.Raster` inheritance Below is a diagram showing current {class}`~geoutils.Raster` inheritance, which extends into other packages such as [xDEM](https://xdem.readthedocs.io/en/latest/index.html) -for analyzing digital elevation models. +for analyzing digital elevation models. ```{eval-rst} .. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM @@ -23,17 +23,17 @@ for analyzing digital elevation models. ``` ```{note} -The {class}`~xdem.DEM` class re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) (and more) to derive topographic attributes -(hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same results. +The {class}`~xdem.DEM` class re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) (and more) to derive topographic attributes +(hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same results. Among others, it also adds a {attr}`~xdem.DEM.vref` property to consistently manage vertical referencing (ellipsoid, geoids). -If you are DEM-enthuisiastic, **[check-out our sister-package xDEM](https://xdem.readthedocs.io/en/latest/index.html)** for the analysis of digital +If you are DEM-enthuisiastic, **[check-out our sister-package xDEM](https://xdem.readthedocs.io/en/latest/index.html)** for the analysis of digital elevation models. ``` ## The internal {class}`~geoutils.SatelliteImage` subclass -GeoUtils subclasses {class}`Rasters` to {class}`SatelliteImages` for remote sensing users interested in parsing +GeoUtils subclasses {class}`Rasters` to {class}`SatelliteImages` for remote sensing users interested in parsing metadata from space- or airborne imagery. Based on the filename, or auxiliary files, the {class}`~geoutils.SatelliteImage` class attempts to automatically parse a `.datetime`, `.sensor`, `tile_name`, @@ -61,9 +61,9 @@ geoimg ## And beyond -Many types of geospatial data can be viewed as a subclass of {class}`Rasters`, which have more attributes and require their own methods: +Many types of geospatial data can be viewed as a subclass of {class}`Rasters`, which have more attributes and require their own methods: **spectral images**, **velocity fields**, **phase difference maps**, etc... -If you are interested to build your own subclass of {class}`~geoutils.Raster`, you can take example of the structure of {class}`geoutils.SatelliteImage` and -{class}`xdem.DEM`. Then, just add any of your own attributes and methods, and overload parent methods if necessary! Don't hesitate to reach out on our +If you are interested to build your own subclass of {class}`~geoutils.Raster`, you can take example of the structure of {class}`geoutils.SatelliteImage` and +{class}`xdem.DEM`. Then, just add any of your own attributes and methods, and overload parent methods if necessary! Don't hesitate to reach out on our GitHub if you have a subclassing project. diff --git a/doc/source/core_lazy_load.md b/doc/source/core_lazy_load.md index 2e38ded41..72811c57e 100644 --- a/doc/source/core_lazy_load.md +++ b/doc/source/core_lazy_load.md @@ -13,8 +13,8 @@ In GeoUtils, we implicitly load and pass only metadata, until the data is actual ## Lazy instantiation of {class}`Rasters` -By default, GeoUtils instantiate a {class}`~geoutils.Raster` from an **on-disk** file without loading its {attr}`geoutils.Raster.data` array. It only loads its -metadata ({attr}`~geoutils.Raster.transform`, {attr}`~geoutils.Raster.crs`, {attr}`~geoutils.Raster.nodata` and derivatives, as well as +By default, GeoUtils instantiate a {class}`~geoutils.Raster` from an **on-disk** file without loading its {attr}`geoutils.Raster.data` array. It only loads its +metadata ({attr}`~geoutils.Raster.transform`, {attr}`~geoutils.Raster.crs`, {attr}`~geoutils.Raster.nodata` and derivatives, as well as {attr}`~geoutils.Raster.name` and {attr}`~geoutils.Raster.driver`). ```{code-cell} ipython3 @@ -28,7 +28,7 @@ raster = gu.Raster(gu.examples.get_path("everest_landsat_b4")) raster ``` -To load the data explicity during instantiation opening, `load_data=True` can be passed to {class}`~geoutils.Raster`. Or the {func}`~geoutils.Raster.load` +To load the data explicitly during instantiation opening, `load_data=True` can be passed to {class}`~geoutils.Raster`. Or the {func}`~geoutils.Raster.load` method can be called after. The two are equivalent. ```{code-cell} ipython3 @@ -42,10 +42,10 @@ raster_toload ## Lazy passing of georeferencing metadata -Operations relying on georeferencing metadata of {class}`Rasters` or {class}`Vectors` are always done by respecting the +Operations relying on georeferencing metadata of {class}`Rasters` or {class}`Vectors` are always done by respecting the possible lazy loading of the objects. -For instance, using any {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a match-reference for a geospatial operation (see {ref}`core-match-ref`) will +For instance, using any {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a match-reference for a geospatial operation (see {ref}`core-match-ref`) will always conserve the lazy loading of that match-reference object. ```{code-cell} ipython3 @@ -60,17 +60,14 @@ smaller_raster ## Optimized geospatial subsetting ```{important} -These features are a work in progress, we aim to make GeoUtils more lazy-friendly through [Dask](https://docs.dask.org/en/stable/) in future versions of the +These features are a work in progress, we aim to make GeoUtils more lazy-friendly through [Dask](https://docs.dask.org/en/stable/) in future versions of the package! ``` -Some georeferencing operations be done without loading the entire array Right now, relying directly on Rasterio, GeoUtils supports optimized subsetting +Some georeferencing operations be done without loading the entire array Right now, relying directly on Rasterio, GeoUtils supports optimized subsetting through the {func}`~geoutils.Raster.crop` method. ```{code-cell} ipython3 # The previously cropped Raster was loaded without accessing the entire array raster ``` - - - diff --git a/doc/source/core_match_ref.md b/doc/source/core_match_ref.md index 098593a68..f999c6d68 100644 --- a/doc/source/core_match_ref.md +++ b/doc/source/core_match_ref.md @@ -9,12 +9,12 @@ Match-reference means to **match** the georeferencing of a dataset to that of an ## Why the need for match-reference? -End-users of geospatial tools largely focus on analyzing their data. To this end, they first **need to reconcile data sources**, often provided **in different +End-users of geospatial tools largely focus on analyzing their data. To this end, they first **need to reconcile data sources**, often provided **in different projections, resolutions, and bounds**. This is generally performed by matching all dataset to a certain reference. -Such functionalities are not always available from lower-level packages, in order to keep geoinformatics flexible for more diverse uses. This -**breaks the principle of least knowledge**, however, for which a user should not need to understand more information than just what is required to run an -operation. +Such functionalities are not always available from lower-level packages, in order to keep geoinformatics flexible for more diverse uses. This +**breaks the principle of least knowledge**, however, for which a user should not need to understand more information than just what is required to run an +operation. **GeoUtils allows for match-reference for nearly all geospatial handling operations**, with consistent behaviour across functionalities for intuitive use. @@ -22,29 +22,29 @@ operation. The rules of using match-reference with {class}`Rasters` or {class}`Vectors` are always the same: - - If the **reference** passed is a {class}`~geoutils.Vector`, it can enforce a matching of its {attr}`~geoutils.Vector.bounds` and/or of its {attr}`~geoutils.Vector.crs` (its only two + - If the **reference** passed is a {class}`~geoutils.Vector`, it can enforce a matching of its {attr}`~geoutils.Vector.bounds` and/or of its {attr}`~geoutils.Vector.crs` (its only two georeferencing attributes), - - If the **reference** is a {class}`~geoutils.Raster`, it can also enforce a matching of any aspect of its {attr}`~geoutils.Raster.transform` (i.e, its + - If the **reference** is a {class}`~geoutils.Raster`, it can also enforce a matching of any aspect of its {attr}`~geoutils.Raster.transform` (i.e, its {attr}`~geoutils.Raster.res`, {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.shape`) and/or of its {attr}`~geoutils.Raster.crs`. Which of these attributes are eventually used to enforce the matching **depends entirely on the nature of the operation**, which are listed below. ## Geospatial handling rules for match-reference -Geospatial handling methods all support match-reference, and **always enforce the same georeferencing attributes** for either {class}`~geoutils.Raster` +Geospatial handling methods all support match-reference, and **always enforce the same georeferencing attributes** for either {class}`~geoutils.Raster` or {class}`~geoutils.Vector`: -```{list-table} +```{list-table} :widths: 30 30 30 :header-rows: 1 - + * - **Operation** - Enforced on {class}`~geoutils.Raster` - Enforced on {class}`~geoutils.Vector` * - {func}`~geoutils.Raster.warp` - {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` - {attr}`~geoutils.Vector.bounds`1 and {attr}`~geoutils.Vector.crs` - * - {func}`~geoutils.Raster.reproject` + * - {func}`~geoutils.Raster.reproject` - {attr}`~geoutils.Raster.crs` - {attr}`~geoutils.Vector.crs` * - {func}`~geoutils.Raster.crop` @@ -61,24 +61,24 @@ or {class}`~geoutils.Vector`: ## Other operations supporting match-reference -There are **other geospatial operation that also support match-reference arguments**. Unlike the geospatial handling methods described above, these do not aim +There are **other geospatial operation that also support match-reference arguments**. Unlike the geospatial handling methods described above, these do not aim at modifying the georeferencing of {class}`Rasters` or {class}`Vectors`. Instead, they simply require the georeferencing metadata. ### From vector to raster -The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geoutils.Vector` to {class}`~geoutils.Raster` accepts a {class}`~geoutils.Raster` to define the -grid and georeferencing. The behaviour is similar for {func}`~geoutils.Vector.create_mask`, that directly relies on {func}`~geoutils.Vector.rasterize` to +The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geoutils.Vector` to {class}`~geoutils.Raster` accepts a {class}`~geoutils.Raster` to define the +grid and georeferencing. The behaviour is similar for {func}`~geoutils.Vector.create_mask`, that directly relies on {func}`~geoutils.Vector.rasterize` to rasterize directly into a boolean {class}`~geoutils.Raster.Mask`. -In addition, the {func}`~geoutils.Vector.proximity` operation to compute proximity distances from the vector also relies on a +In addition, the {func}`~geoutils.Vector.proximity` operation to compute proximity distances from the vector also relies on a {func}`~geoutils.Vector.rasterize`, and therefore also accepts a {class}`~geoutils.Raster` as reference. Therefore, the behaviour is consistent for all {class}`~geoutils.Vector` methods that can be passed a {class}`~geoutils.Raster`: -```{list-table} +```{list-table} :widths: 50 50 :header-rows: 1 - + * - **Operation on {class}`~geoutils.Vector`** - **Behaviour** * - {func}`~geoutils.Vector.rasterize` @@ -91,20 +91,19 @@ Therefore, the behaviour is consistent for all {class}`~geoutils.Vector` methods ### And inversely -However, in the case of {class}`~geoutils.Raster` methods that yield a {class}`~geoutils.Vector` or {class}`~geoutils.Raster`, a reference is rarely needed. +However, in the case of {class}`~geoutils.Raster` methods that yield a {class}`~geoutils.Vector` or {class}`~geoutils.Raster`, a reference is rarely needed. This is because this reference is derived directly from the input {class}`~geoutils.Raster` itself, harnessing the object-based structure of GeoUtils. The user can always {func}`~geoutils.Vector.crop` or {func}`~geoutils.Vector.reproject` the output afterwards, if desired. -```{list-table} +```{list-table} :widths: 50 50 :header-rows: 1 - + * - **Operation on {class}`~geoutils.Raster`** - **Behaviour** * - {func}`~geoutils.Raster.polygonize` - - Using `.self` ({class}`~geoutils.Raster`) as reference for {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` + - Using `.self` ({class}`~geoutils.Raster`) as reference for {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` * - {func}`~geoutils.Raster.proximity` - Using `.self` ({class}`~geoutils.Raster`) as reference for {attr}`~geoutils.Raster.transform` and {attr}`~geoutils.Raster.crs` ``` - diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index 09e831d88..5cb199bda 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -8,16 +8,16 @@ kernelspec: # Support of pythonic operators GeoUtils integrates pythonic operators for shorter, more intuitive code, and to perform arithmetic and logical operations consistently. - + These operators work on {class}`Rasters` much as they would on {class}`ndarrays`, with some more details. ## Arithmetic of {class}`~geoutils.Raster` classes -Arithmetic operators ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, -{func}`**`, {func}`%`) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, +Arithmetic operators ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, +{func}`**`, {func}`%`) can be used on a {class}`~geoutils.Raster` in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. -For an operation with another {class}`~geoutils.Raster`, the georeferencing ({attr}`~geoutils.Raster.crs` and {attr}`~geoutils.Raster.transform`) must match. +For an operation with another {class}`~geoutils.Raster`, the georeferencing ({attr}`~geoutils.Raster.crs` and {attr}`~geoutils.Raster.transform`) must match. For another {class}`~numpy.ndarray`, the {attr}`~geoutils.Raster.shape` must match. The operation always returns a {class}`~geoutils.Raster`. ```{code-cell} ipython3 @@ -58,17 +58,17 @@ raster / arr raster - (raster**0.5) ``` -If an unmasked {class}`~numpy.ndarray` is passed, it will internally be cast into a {class}`~numpy.ma.MaskedArray` to respect the propagation of -{class}`~geoutils.Raster.nodata` values. Additionally, the {attr}`~geoutils.Raster.dtypes` are also reconciled as they would for {class}`~numpy.ndarray`, +If an unmasked {class}`~numpy.ndarray` is passed, it will internally be cast into a {class}`~numpy.ma.MaskedArray` to respect the propagation of +{class}`~geoutils.Raster.nodata` values. Additionally, the {attr}`~geoutils.Raster.dtypes` are also reconciled as they would for {class}`~numpy.ndarray`, following [standard NumPy coercion rules](https://numpy.org/doc/stable/reference/generated/numpy.find_common_type.html). ## Logical comparisons cast to {class}`~geoutils.Mask` -Logical comparison operators ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, -{func}`<`) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or +Logical comparison operators ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, +{func}`<`) can be used on a {class}`~geoutils.Raster`, also in combination with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. -Those operation always return a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster` with a boolean {class}`~numpy.ma.MaskedArray` +Those operation always return a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster` with a boolean {class}`~numpy.ma.MaskedArray` as {class}`~geoutils.Raster.data`. ```{code-cell} ipython3 @@ -78,13 +78,13 @@ mask ``` ```{note} -A {class}`~geoutils.Mask`'s {attr}`~geoutils.Raster.data` remains a {class}`~numpy.ma.MaskedArray`. Therefore, it still maps unvalid values through its +A {class}`~geoutils.Mask`'s {attr}`~geoutils.Raster.data` remains a {class}`~numpy.ma.MaskedArray`. Therefore, it still maps invalid values through its {attr}`~numpy.ma.MaskedArray.mask`, but has no associated {attr}`~geoutils.Raster.nodata`. ``` ## Logical bitwise operations on {class}`~geoutils.Mask` -Logical bitwise operators ({func}`~ `, {func}`& `, {func}`| `, {func}`^ `) can be used to +Logical bitwise operators ({func}`~ `, {func}`& `, {func}`| `, {func}`^ `) can be used to combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and always output a {class}`~geoutils.Mask`. ```{code-cell} ipython3 @@ -97,17 +97,17 @@ mask ## Indexing a {class}`~geoutils.Raster` with a {class}`~geoutils.Mask` -Finally, indexing and index assignment operations ({func}`[] `, {func}`[]= `) are both supported by -{class}`Rasters`. +Finally, indexing and index assignment operations ({func}`[] `, {func}`[]= `) are both supported by +{class}`Rasters`. -For indexing, they can be passed either a {class}`~geoutils.Mask` with the same georeferencing, or a boolean {class}`~numpy.ndarray` of the same shape. +For indexing, they can be passed either a {class}`~geoutils.Mask` with the same georeferencing, or a boolean {class}`~numpy.ndarray` of the same shape. For assignment, either a {class}`~geoutils.Raster` with the same georeferencing, or any {class}`~numpy.ndarray` of the same shape is expected. -When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the indexed values of the {class}`~geoutils.Mask` **excluding those masked in its -{class}`~geoutils.Raster.data`'s {class}`~numpy.ma.MaskedArray` (for instance, nodata values present during a previous logical comparison)**. To bypass this +When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the indexed values of the {class}`~geoutils.Mask` **excluding those masked in its +{class}`~geoutils.Raster.data`'s {class}`~numpy.ma.MaskedArray` (for instance, nodata values present during a previous logical comparison)**. To bypass this behaviour, simply index without the mask using {attr}`Mask.data.data`. ```{code-cell} ipython3 # Indexing the raster with the previous mask raster[mask] -``` \ No newline at end of file +``` diff --git a/doc/source/index.md b/doc/source/index.md index 859878b38..facc74725 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -22,14 +22,14 @@ title: GeoUtils GeoUtils is an **accessible**, **efficient** and **reliable** package to analyze geospatial data. :::: -**Accessible** owing to its convenient object-based structure, intuitive match-reference operations and familiar geospatial dependencies -([Rasterio](https://rasterio.readthedocs.io/en/latest/), [Rioxarray](https://corteva.github.io/rioxarray/stable/), +**Accessible** owing to its convenient object-based structure, intuitive match-reference operations and familiar geospatial dependencies +([Rasterio](https://rasterio.readthedocs.io/en/latest/), [Rioxarray](https://corteva.github.io/rioxarray/stable/), [GeoPandas](https://geopandas.org/en/stable/docs.html), [PyProj](https://pyproj4.github.io/pyproj/stable/index.html)). -**Efficient** owing to its implicit lazy loading functionalities, logical integration with pythonic operators and array interfacing +**Efficient** owing to its implicit lazy loading functionalities, logical integration with pythonic operators and array interfacing ([NumPy](https://numpy.org/doc/stable/), [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/)). -**Reliable** owing to its consistent higher-level operations respecting geospatial intricacies such as nodata values and pixel interpretation, ensured by +**Reliable** owing to its consistent higher-level operations respecting geospatial intricacies such as nodata values and pixel interpretation, ensured by its testing suite and type checking ([Pytest](https://docs.pytest.org/en/7.2.x/), [Mypy](https://mypy-lang.org/)). ---------------- @@ -52,7 +52,7 @@ Learn more about why we developed GeoUtils. :::{grid-item-card} {material-regular}`data_exploration;2em` Quick start :link: quick-start :link-type: ref - + Run a short example of the package functionalities. +++ @@ -94,7 +94,7 @@ quick_start ```{toctree} :caption: Features :maxdepth: 2 - + core_index rasters_index vectors_index @@ -104,7 +104,7 @@ proj_tools ```{toctree} :caption: Examples :maxdepth: 2 - + io_examples/index handling_examples/index analysis_examples/index @@ -112,8 +112,8 @@ analysis_examples/index ```{toctree} :caption: Reference -:maxdepth: 2 - +:maxdepth: 2 + api background ``` diff --git a/doc/source/mask_class.md b/doc/source/mask_class.md index 4af6e8c67..dc97c6f07 100644 --- a/doc/source/mask_class.md +++ b/doc/source/mask_class.md @@ -20,20 +20,20 @@ A {class}`~geoutils.Mask` is a subclass of {class}`~geoutils.Raster` that contai A {class}`~geoutils.Mask` also inherits the same derivative attributes as a {class}`~geoutils.Raster`. ```{note} -There is no {class}`~geoutils.Raster.nodata` value defined in a {class}`~geoutils.Mask`, as it only take binary values. However, the -{class}`numpy.ma.MaskedArray` still has a {class}`~geoutils.Raster.data.mask` for unvalid values. +There is no {class}`~geoutils.Raster.nodata` value defined in a {class}`~geoutils.Mask`, as it only take binary values. However, the +{class}`numpy.ma.MaskedArray` still has a {class}`~geoutils.Raster.data.mask` for invalid values. ``` ## Open and save -{class}`Masks` can be created from files through instantiation with {class}`~geoutils.Mask`, and inherit the {func}`~geoutils.Raster.save` +{class}`Masks` can be created from files through instantiation with {class}`~geoutils.Mask`, and inherit the {func}`~geoutils.Raster.save` method from {class}`~geoutils.Raster`. ```{important} -Most raster file formats such a [GeoTIFFs](https://gdal.org/drivers/raster/gtiff.html) **do not support {class}`bool` array {class}`dtypes` +Most raster file formats such a [GeoTIFFs](https://gdal.org/drivers/raster/gtiff.html) **do not support {class}`bool` array {class}`dtypes` on-disk**, and **most of Rasterio functionalities also do not support {class}`bool` {class}`dtypes`**. -To address this, during opening, saving and geospatial handling operations, {class}`Masks` are automatically converted to and from {class}`numpy.uint8`. +To address this, during opening, saving and geospatial handling operations, {class}`Masks` are automatically converted to and from {class}`numpy.uint8`. The {class}`~geoutils.Raster.nodata` of a {class}`~geoutils.Mask` can now be defined to save to a file, and defaults to `255`. ``` @@ -49,7 +49,7 @@ mask ## Cast from {class}`~geoutils.Raster` -{class}`Masks` are automatically cast by a logical comparison operation performed on a {class}`~geoutils.Raster` with either another +{class}`Masks` are automatically cast by a logical comparison operation performed on a {class}`~geoutils.Raster` with either another {class}`~geoutils.Raster`, a {class}`~numpy.ndarray` or a number. ```{code-cell} ipython3 @@ -65,7 +65,7 @@ See {ref}`core-py-ops` for more details. ## Create from {class}`~numpy.ndarray` -The class method {func}`geoutils.Raster.from_array`, inherited by the {class}`~geoutils.Mask` subclass, automatically casts to a {class}`~geoutils.Mask` if +The class method {func}`geoutils.Raster.from_array`, inherited by the {class}`~geoutils.Mask` subclass, automatically casts to a {class}`~geoutils.Mask` if the input {class}`~numpy.ndarray` is of {class}`bool` {class}`~numpy.dtype`. ```{code-cell} ipython3 @@ -104,7 +104,7 @@ mask ## Create from {class}`~geoutils.Vector` -{class}`Masks` can also be created from a {class}`~geoutils.Vector` using {class}`~geoutils.Vector.create_mask`, which rasterizes all input +{class}`Masks` can also be created from a {class}`~geoutils.Vector` using {class}`~geoutils.Vector.create_mask`, which rasterizes all input geometries to a boolean array through {class}`~geoutils.Vector.rasterize`. Georeferencing attributes to create the {class}`~geoutils.Mask` can also be passed individually, using `bounds`, `crs`, `xres` and `yres`. @@ -122,7 +122,7 @@ See {ref}`core-match-ref` for more details on the match-reference functionality. ## Arithmetic -{class}`Masks` support Python's logical bitwise operators ({func}`~ `, {func}`& `, {func}`|`, +{class}`Masks` support Python's logical bitwise operators ({func}`~ `, {func}`& `, {func}`|`, {func}`^ `) with other {class}`Masks`, and always output a {class}`~geoutils.Mask`. ```{code-cell} ipython3 @@ -136,7 +136,7 @@ See {ref}`core-match-ref` for more details on the match-reference functionality. {class}`Rasters`. ```{important} -When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the indexed values of the {class}`~geoutils.Mask` **excluding those masked in its +When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the indexed values of the {class}`~geoutils.Mask` **excluding those masked in its {class}`~geoutils.Raster.data`'s {class}`~numpy.ma.MaskedArray`**. ``` @@ -151,10 +151,10 @@ See {ref}`py-ops-indexing` for more details. ## Polygonize (overloaded from {class}`~geoutils.Raster`) -{class}`Masks` have simplified class methods overloaded from {class}`Rasters` when one or several attributes of the methods +{class}`Masks` have simplified class methods overloaded from {class}`Rasters` when one or several attributes of the methods become implicit in the case of {class}`bool` data. -The {func}`~geoutils.Mask.polygonize` function is one of those, implicitly applying to the `True` values of the mask as target pixels. It outputs a +The {func}`~geoutils.Mask.polygonize` function is one of those, implicitly applying to the `True` values of the mask as target pixels. It outputs a {class}`~geoutils.Vector` of the input mask. ```{code-cell} ipython3 @@ -166,7 +166,7 @@ mask.polygonize() ## Proximity (overloaded from {class}`~geoutils.Raster`) -The {func}`~geoutils.Mask.proximity` function is another overloaded method of {class}`~geoutils.Raster` implicitly applying to the `True` values of the mask as +The {func}`~geoutils.Mask.proximity` function is another overloaded method of {class}`~geoutils.Raster` implicitly applying to the `True` values of the mask as target pixels. It outputs a {class}`~geoutils.Raster` of the distances to the input mask. ```{code-cell} ipython3 diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md index f8e0a5be7..cbcf3dea9 100644 --- a/doc/source/proj_tools.md +++ b/doc/source/proj_tools.md @@ -10,4 +10,4 @@ Convenient `pyproj` wrappers. ## Create a tiling -## Shape-preserving vector reprojection \ No newline at end of file +## Shape-preserving vector reprojection diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index cb90edd17..0eb035f28 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -28,8 +28,8 @@ rast = gu.Raster(filename_rast) vect = gu.Vector(filename_vect) ``` -A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as `.data`, a {class}`~pyproj.crs.CRS` as `.crs`, -an {class}`~affine.Affine` as `.transform`, and a {class}`float` or {class}`int` as `.nodata`. When a file exists on disk, {class}`~geoutils.Raster` is +A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as `.data`, a {class}`~pyproj.crs.CRS` as `.crs`, +an {class}`~affine.Affine` as `.transform`, and a {class}`float` or {class}`int` as `.nodata`. When a file exists on disk, {class}`~geoutils.Raster` is linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata, and the array at the appropriate time. ```{code-cell} ipython3 @@ -38,8 +38,8 @@ linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata, rast ``` -A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as `.ds`, for which most methods are -wrapped directly into {class}`~geoutils.Vector`. +A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as `.ds`, for which most methods are +wrapped directly into {class}`~geoutils.Vector`. ```{code-cell} ipython3 :tags: [hide-output] @@ -47,13 +47,13 @@ wrapped directly into {class}`~geoutils.Vector`. vect ``` -All other attributes are derivatives of those main attributes, or of the filename on disk. Attributes of {class}`~geoutils.Raster` and -{class}`~geoutils.Vector` update with geospatial operations on themselves. +All other attributes are derivatives of those main attributes, or of the filename on disk. Attributes of {class}`~geoutils.Raster` and +{class}`~geoutils.Vector` update with geospatial operations on themselves. ## Geospatial handling and match-reference -Geospatial operations are based on class methods, such as {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Nearly all of these methods can be -passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a **reference to match** during the operation. A **reference {class}`~geoutils.Vector`** +Geospatial operations are based on class methods, such as {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Nearly all of these methods can be +passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a **reference to match** during the operation. A **reference {class}`~geoutils.Vector`** enforces a matching of `.bounds` and/or `.crs`, while a **reference {class}`~geoutils.Raster`** can also enforce a matching of `.res`, depending on the nature of the operation. @@ -67,30 +67,30 @@ rast.crop(vect) rast_proximity_to_vec = vect.proximity(rast) ``` -All methods can be also be passed any number of georeferencing arguments such as `.shape` or `.res`, and will logically deduce others from the input, much +All methods can be also be passed any number of georeferencing arguments such as `.shape` or `.res`, and will logically deduce others from the input, much as in [GDAL](https://gdal.org/)'s command line. ```{note} -Right now, the array `.data` of `rast` is still not loaded. Applying {func}`~geoutils.Raster.crop` does not yet require loading, +Right now, the array `.data` of `rast` is still not loaded. Applying {func}`~geoutils.Raster.crop` does not yet require loading, and `rast`'s metadata is sufficient to provide a georeferenced grid for {func}`~geoutils.Vector.proximity`. The array will only be loaded when necessary. ``` -Additionally, in GeoUtils, **methods that apply to the same georeferencing attributes have consistent naming**1 across {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. +Additionally, in GeoUtils, **methods that apply to the same georeferencing attributes have consistent naming**1 across {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. ```{margin} 1The names of geospatial handling methods is largely based on [GDAL and OGR](https://gdal.org/)'s, with the notable exception of {func}`~geoutils.Vector.reproject` that better applies to vectors than `warp`. ``` -```{list-table} +```{list-table} :widths: 30 30 30 :header-rows: 1 - + * - **Class** - {class}`~geoutils.Raster` - {class}`~geoutils.Vector` - * - Warping/Reprojection - - {func}`~geoutils.Raster.reproject` + * - Warping/Reprojection + - {func}`~geoutils.Raster.reproject` - {func}`~geoutils.Vector.reproject` * - Cropping/Clipping - {func}`~geoutils.Raster.crop` @@ -98,19 +98,19 @@ Additionally, in GeoUtils, **methods that apply to the same georeferencing attri * - Rasterize/Polygonize - {func}`~geoutils.Raster.polygonize` - {func}`~geoutils.Vector.rasterize` - * - Proximity + * - Proximity - {func}`~geoutils.Raster.proximity` - {func}`~geoutils.Vector.proximity` ``` -A {func}`~geoutils.Raster.reproject` involves a change in `.crs` or `.transform`, while a {func}`~geoutils.Raster.crop` only involves a change in `.bounds`. -Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, and the other way around for {func}`~geoutils.Vector.rasterize`. +A {func}`~geoutils.Raster.reproject` involves a change in `.crs` or `.transform`, while a {func}`~geoutils.Raster.crop` only involves a change in `.bounds`. +Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, and the other way around for {func}`~geoutils.Vector.rasterize`. ## Pythonic arithmetic and NumPy interface -All {class}`~geoutils.Raster` objects support Python arithmetic ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, -{func}`**`, {func}`%`) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or +All {class}`~geoutils.Raster` objects support Python arithmetic ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, +{func}`**`, {func}`%`) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. For other {class}`~geoutils.Raster`, the georeferencing must match, while only the shape for other {class}`~numpy.ndarray`. ```{code-cell} ipython3 @@ -118,7 +118,7 @@ number. For other {class}`~geoutils.Raster`, the georeferencing must match, whil rast += 1 ``` -Additionally, the class {class}`~geoutils.Raster` possesses a NumPy masked-array interface that allows to apply to it any [NumPy universal function](https://numpy.org/doc/stable/reference/ufuncs.html) and +Additionally, the class {class}`~geoutils.Raster` possesses a NumPy masked-array interface that allows to apply to it any [NumPy universal function](https://numpy.org/doc/stable/reference/ufuncs.html) and most other NumPy array functions, while logically casting `dtypes` and respecting `.nodata` values. ```{code-cell} ipython3 @@ -129,7 +129,7 @@ rast = (rast - np.min(rast)) / (np.max(rast) - np.min(rast)) ## Casting to {class}`~geoutils.Mask`, indexing and overload -All {class}`~geoutils.Raster` classes also support Python logical comparison operators ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, +All {class}`~geoutils.Raster` classes also support Python logical comparison operators ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, {func}`<`), or more complex NumPy logical functions. Those operations automatically casts them into a {class}`~geoutils.Mask`, a subclass of {class}`~geoutils.Raster`. ```{code-cell} ipython3 @@ -144,7 +144,7 @@ Masks can then be used for indexing a {class}`~geoutils.Raster`, which returns a values_aoi = rast[mask_aoi] ``` -Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Using {func}`~geoutils.Raster.polygonize` with a +Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Using {func}`~geoutils.Raster.polygonize` with a {class}`~geoutils.Mask` is straightforward, for instance, to retrieve a {class}`~geoutils.Vector` of the area-of-interest: ```{code-cell} ipython3 @@ -155,8 +155,8 @@ vect_aoi = mask_aoi.polygonize() ## Plotting and saving geospatial data -Finally, GeoUtils includes basic plotting tools wrapping directly {func}`rasterio.plot.show` and {func}`geopandas.GeoDataFrame.plot` for {class}`~geoutils.Raster` and {class}`~geoutils.Vector`, respectively. -The plotting function was renamed {func}`~geoutils.Raster.show` everywhere, for consistency. +Finally, GeoUtils includes basic plotting tools wrapping directly {func}`rasterio.plot.show` and {func}`geopandas.GeoDataFrame.plot` for {class}`~geoutils.Raster` and {class}`~geoutils.Vector`, respectively. +The plotting function was renamed {func}`~geoutils.Raster.show` everywhere, for consistency. For saving, {func}`~geoutils.Raster.save` is used. @@ -171,12 +171,12 @@ vect_aoi.ds.plot(ax=ax, fc='none', ec='k', lw=0.5) ```{admonition} Wrap-up In a few lines, we: - - **easily handled georeferencing** operations on rasters and vectors, - - performed numerical calculations **inherently respecting unvalid data**, + - **easily handled georeferencing** operations on rasters and vectors, + - performed numerical calculations **inherently respecting invalid data**, - **naturally casted to a mask** from a logical operation on raster, and - **straightforwardly vectorized a mask** by harnessing overloaded subclass methods. -**Our result:** a vector of high infrared absorption indexes at least 200 meters away from glaciers +**Our result:** a vector of high infrared absorption indexes at least 200 meters away from glaciers near Everest, which likely corresponds to **perennial snowfields**. For a **bonus** example on parsing satellite metadata and DEMs, continue below. @@ -186,7 +186,7 @@ Otherwise, for more **hands-on** examples, explore GeoUtils' gallery of examples ## **Bonus:** Parsing metadata with {class}`~geoutils.SatelliteImage` -In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses +In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses metadata recognized from the filename or auxiliary files. ```{code-cell} ipython3 @@ -200,15 +200,15 @@ print(os.path.basename(filename_rast)) rast = gu.SatelliteImage(filename_rast, silent=False) ``` -There are many possible subclass to derive from a {class}`~geoutils.Raster`. Here's an **overview of current {class}`~geoutils.Raster` class inheritance**, which extends into -[xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: +There are many possible subclass to derive from a {class}`~geoutils.Raster`. Here's an **overview of current {class}`~geoutils.Raster` class inheritance**, which extends into +[xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: ```{eval-rst} .. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM :top-classes: geoutils.georaster.raster.Raster ``` ```{note} -The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) -(and more) to derive topographic attributes (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same +The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) +(and more) to derive topographic attributes (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same results. -``` \ No newline at end of file +``` diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 9f79369e4..18bbe8633 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -24,26 +24,26 @@ For more details on {class}`~geoutils.Raster` class composition, see {ref}`core- A {class}`~geoutils.Raster` also contains many derivative attributes, with naming generally consistent with that of a {class}`rasterio.io.DatasetReader`. -A first category includes georeferencing attributes directly derived from {attr}`~geoutils.Raster.transform`, namely: {attr}`~geoutils.Raster.shape`, +A first category includes georeferencing attributes directly derived from {attr}`~geoutils.Raster.transform`, namely: {attr}`~geoutils.Raster.shape`, {attr}`~geoutils.Raster.height`, {attr}`~geoutils.Raster.width`, {attr}`~geoutils.Raster.res`, {attr}`~geoutils.Raster.bounds`. -A second category concerns the attributes derived from the raster array shape and type: {attr}`~geoutils.Raster.count`, {attr}`~geoutils.Raster.indexes` and +A second category concerns the attributes derived from the raster array shape and type: {attr}`~geoutils.Raster.count`, {attr}`~geoutils.Raster.indexes` and {attr}`~geoutils.Raster.dtypes`. The two former refer to the number of bands loaded in a {class}`~geoutils.Raster`, and their indexes. ```{important} -The {attr}`~geoutils.Raster.indexes` of {class}`rasterio.io.DatasetReader` start from 1 and not 0, be careful when instantiating or loading from a +The {attr}`~geoutils.Raster.indexes` of {class}`rasterio.io.DatasetReader` start from 1 and not 0, be careful when instantiating or loading from a multi-band raster! ``` -Finally, the remaining attributes are only relevant when instantiating from a **on-disk** file: {attr}`~geoutils.Raster.name`, {attr}`~geoutils.Raster.driver`, +Finally, the remaining attributes are only relevant when instantiating from a **on-disk** file: {attr}`~geoutils.Raster.name`, {attr}`~geoutils.Raster.driver`, {attr}`~geoutils.Raster.count_on_disk`, {attr}`~geoutils.Raster.indexes_on_disk`, {attr}`~geoutils.Raster.is_loaded` and {attr}`~geoutils.Raster.is_modified`. ```{note} -The {attr}`~geoutils.Raster.count` and {attr}`~geoutils.Raster.indexes` attributes always exist, while {attr}`~geoutils.Raster.count_on_disk` and +The {attr}`~geoutils.Raster.count` and {attr}`~geoutils.Raster.indexes` attributes always exist, while {attr}`~geoutils.Raster.count_on_disk` and {attr}`~geoutils.Raster.indexes_on_disk` only refers to the number of bands on the **on-disk** dataset, if it exists. -For example, {attr}`~geoutils.Raster.count` and {attr}`~geoutils.Raster.count_on_disk` will differ when a single band is loaded from a +For example, {attr}`~geoutils.Raster.count` and {attr}`~geoutils.Raster.count_on_disk` will differ when a single band is loaded from a 3-band **on-disk** file, by passing a single index to the `indexes` argument in {class}`~geoutils.Raster` or {func}`~geoutils.Raster.load`. ``` @@ -51,7 +51,7 @@ The complete list of {class}`~geoutils.Raster` attributes with description is av ## Open and save -A {class}`~geoutils.Raster` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path`, a {class}`rasterio.io.DatasetReader` or a +A {class}`~geoutils.Raster` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path`, a {class}`rasterio.io.DatasetReader` or a {class}`rasterio.io.MemoryFile`. @@ -88,7 +88,7 @@ os.remove("myraster.tif") ## Create from {class}`~numpy.ndarray` -A {class}`~geoutils.Raster` is created from an array by calling the class method {func}`~geoutils.Raster.from_array` and passing the +A {class}`~geoutils.Raster` is created from an array by calling the class method {func}`~geoutils.Raster.from_array` and passing the {ref}`four main attributes`. ```{code-cell} ipython3 @@ -113,8 +113,8 @@ raster ``` ```{important} -The {attr}`~geoutils.Raster.data` attribute can be passed as an unmasked {class}`~numpy.ndarray`. That array will be converted to a {class}`~numpy.ma.MaskedArray` -with a {class}`~numpy.ma.MaskedArray.mask` on all {class}`~numpy.nan` and {class}`~numpy.inf` in the {attr}`~geoutils.Raster.data`, and on all values +The {attr}`~geoutils.Raster.data` attribute can be passed as an unmasked {class}`~numpy.ndarray`. That array will be converted to a {class}`~numpy.ma.MaskedArray` +with a {class}`~numpy.ma.MaskedArray.mask` on all {class}`~numpy.nan` and {class}`~numpy.inf` in the {attr}`~geoutils.Raster.data`, and on all values matching the {attr}`~geoutils.Raster.nodata` value passed to {func}`~geoutils.Raster.from_array`. ``` @@ -127,7 +127,7 @@ The array of a {class}`~geoutils.Raster` is available in {class}`~geoutils.Raste raster.data ``` -For those less familiar with {class}`MaskedArrays` and the associated functions in NumPy, an unmasked {class}`~numpy.ndarray` filled with +For those less familiar with {class}`MaskedArrays` and the associated functions in NumPy, an unmasked {class}`~numpy.ndarray` filled with {class}`~numpy.nan` on masked values can be extracted using {func}`~geoutils.Raster.get_nanarray`. ```{code-cell} ipython3 @@ -136,19 +136,19 @@ raster.get_nanarray() ``` ```{important} -Getting a {class}`~numpy.ndarray` filled with {class}`~numpy.nan` will automatically cast the {class}`dtype` to {class}`numpy.float32`. This +Getting a {class}`~numpy.ndarray` filled with {class}`~numpy.nan` will automatically cast the {class}`dtype` to {class}`numpy.float32`. This might result in larger memory usage than in the original {class}`~geoutils.Raster` (if of {class}`int` type). -Thanks to the {ref}`core-array-funcs`, **NumPy functions applied directly to a {class}`~geoutils.Raster` will respect {class}`~geoutils.Raster.nodata` +Thanks to the {ref}`core-array-funcs`, **NumPy functions applied directly to a {class}`~geoutils.Raster` will respect {class}`~geoutils.Raster.nodata` values** as well as if computing with the {class}`~numpy.ma.MaskedArray` or an unmasked {class}`~numpy.ndarray` filled with {class}`~numpy.nan`. -Additionally, the {class}`~geoutils.Raster` will automatically cast between different {class}`dtypes`, and possibly re-define missing +Additionally, the {class}`~geoutils.Raster` will automatically cast between different {class}`dtypes`, and possibly re-define missing {class}`nodatas`. ``` ## Arithmetic -A {class}`~geoutils.Raster` can be applied any pythonic arithmetic operation ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, +A {class}`~geoutils.Raster` can be applied any pythonic arithmetic operation ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, {func}`**`, {func}`%`) with another {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. It will output one or two {class}`Rasters`. NumPy coercion rules apply for {class}`dtypes`. ```{code-cell} ipython3 @@ -156,7 +156,7 @@ A {class}`~geoutils.Raster` can be applied any pythonic arithmetic operation ({f (raster + 1)/2 ``` -A {class}`~geoutils.Raster` can also be applied any pythonic logical comparison operation ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, +A {class}`~geoutils.Raster` can also be applied any pythonic logical comparison operation ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, {func}`<`) with another {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. It will cast to a {class}`~geoutils.Mask`. ```{code-cell} ipython3 @@ -168,7 +168,7 @@ See {ref}`core-py-ops` for more details. ## Array interface -A {class}`~geoutils.Raster` can be applied any NumPy universal functions and most mathematical, logical or masked-array functions with another +A {class}`~geoutils.Raster` can be applied any NumPy universal functions and most mathematical, logical or masked-array functions with another {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. ```{code-cell} ipython3 @@ -187,12 +187,12 @@ See {ref}`core-array-funcs` for more details. ## Reproject -Reprojecting a {class}`~geoutils.Raster` is done through the{func}`~geoutils.Raster.reproject`, which enforces new {attr}`~geoutils.Raster.transform` and/or +Reprojecting a {class}`~geoutils.Raster` is done through the{func}`~geoutils.Raster.reproject`, which enforces new {attr}`~geoutils.Raster.transform` and/or {class}`~geoutils.Raster.crs`. ```{important} As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils. -Vector` as argument. +Vector` as argument. A {class}`~geoutils.Raster` reference will enforce to match its {attr}`~geoutils.Raster.transform` and {class}`~geoutils.Raster.crs`. A {class}`~geoutils.Vector` reference will enforce to match its {attr}`~geoutils.Vector.bounds` and {class}`~geoutils.Vector.crs`. @@ -200,7 +200,7 @@ A {class}`~geoutils.Vector` reference will enforce to match its {attr}`~geoutils See {ref}`core-match-ref` for more details. ``` -The {func}`~geoutils.Raster.reproject` function can also be passed any individual arguments such as `dst_bounds`, to enforce specific georeferencing +The {func}`~geoutils.Raster.reproject` function can also be passed any individual arguments such as `dst_bounds`, to enforce specific georeferencing attributes. For more details, see the {ref}`specific section and function descriptions in the API`. ```{code-cell} ipython3 @@ -212,8 +212,8 @@ print(raster.bounds) ```{code-cell} ipython3 # Reproject to smaller bounds and higher resolution raster_reproj = raster.reproject( - dst_res=0.1, - dst_bounds={"left": 0, "bottom": 0, "right": 0.75, "top": 0.75}, + dst_res=0.1, + dst_bounds={"left": 0, "bottom": 0, "right": 0.75, "top": 0.75}, resampling="cubic") raster_reproj ``` @@ -225,7 +225,7 @@ print(raster.bounds) ``` ```{note} -In GeoUtils, `"bilinear"` is the default resampling method. A simple {class}`str` matching the naming of a {class}`rasterio.enums.Resampling` method can be +In GeoUtils, `"bilinear"` is the default resampling method. A simple {class}`str` matching the naming of a {class}`rasterio.enums.Resampling` method can be passed. Resampling methods are listed in **[the dedicated section of Rasterio's API](https://rasterio.readthedocs.io/en/latest/api/rasterio.enums.html#rasterio.enums.Resampling)**. @@ -233,15 +233,15 @@ Resampling methods are listed in **[the dedicated section of Rasterio's API](htt ## Crop -Cropping a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.crop` function, which enforces new {attr}`~geoutils.Raster.bounds`. +Cropping a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.crop` function, which enforces new {attr}`~geoutils.Raster.bounds`. ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument. +As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument. See {ref}`core-match-ref` for more details. ``` -The {func}`~geoutils.Raster.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). +The {func}`~geoutils.Raster.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). For more details, see the {ref}`specific section and function descriptions in the API`. @@ -253,13 +253,13 @@ raster_crop ## Polygonize -Polygonizing a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.polygonize` function, which converts target pixels into a multi-polygon +Polygonizing a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.polygonize` function, which converts target pixels into a multi-polygon {class}`~geoutils.Vector`. By default, values equal to `1` are used. ```{note} -For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly targets `True` values and thus does not require target pixels. See +For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly targets `True` values and thus does not require target pixels. See {ref}`mask-class-poly-overloaded`. ``` @@ -272,11 +272,11 @@ vector_lt_100 ## Proximity -Computing proximity from a {class}`~geoutils.Raster` is done through by the {func}`~geoutils.Raster.proximity` function, which computes the distance to any +Computing proximity from a {class}`~geoutils.Raster` is done through by the {func}`~geoutils.Raster.proximity` function, which computes the distance to any target pixels in the {class}`~geoutils.Raster`. ```{note} -For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.proximity` implicitly targets `True` values and thus does not require target pixels. See +For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.proximity` implicitly targets `True` values and thus does not require target pixels. See {ref}`mask-class-prox-overloaded`. ``` @@ -299,7 +299,7 @@ proximity_lt_100_from_vector Interpolating or extracting {class}`~geoutils.Raster` values at specific points can be done through: - the {func}`~geoutils.Raster.value_at_coords` function, who extracts the single closest pixel or a surrounding window for each coordinate, on which can be applied reducing any function ({func}`numpy.ma.mean` by default), or -- the {func}`~geoutils.Raster.interp_points` function, who interpolates the {class}`~geoutils.Raster`'s regular grid to each coordinate using a +- the {func}`~geoutils.Raster.interp_points` function, who interpolates the {class}`~geoutils.Raster`'s regular grid to each coordinate using a resampling algorithm. ```{code-cell} ipython3 @@ -313,7 +313,7 @@ raster_reproj.interp_points([(0.5, 0.5)], mode="quintic") ``` ```{note} -Both {func}`~geoutils.Raster.value_at_coords` and {func}`~geoutils.Raster.interp_points` can be passed a single coordinate as {class}`floats`, or a +Both {func}`~geoutils.Raster.value_at_coords` and {func}`~geoutils.Raster.interp_points` can be passed a single coordinate as {class}`floats`, or a {class}`list` of coordinates. ``` @@ -322,8 +322,8 @@ Both {func}`~geoutils.Raster.value_at_coords` and {func}`~geoutils.Raster.interp A {class}`~geoutils.Raster` can be exported to different formats, to facilitate inter-compatibility with different packages and code versions. Those include exporting to: -- a {class}`xarray.Dataset` with {class}`~geoutils.Raster.to_xarray`, -- a {class}`rasterio.io.DatasetReader` with {class}`~geoutils.Raster.to_rio_dataset`, +- a {class}`xarray.Dataset` with {class}`~geoutils.Raster.to_xarray`, +- a {class}`rasterio.io.DatasetReader` with {class}`~geoutils.Raster.to_rio_dataset`, - a {class}`numpy.ndarray` or {class}`geoutils.Vector` as a point cloud with {class}`~geoutils.Raster.to_points`. ```{code-cell} ipython3 @@ -335,6 +335,3 @@ raster_reproj.to_rio_dataset() # Export to geopandas dataframe raster_reproj.to_points() ``` - - - diff --git a/doc/source/rasters_index.md b/doc/source/rasters_index.md index 48b5555c7..f48c4e0ff 100644 --- a/doc/source/rasters_index.md +++ b/doc/source/rasters_index.md @@ -5,8 +5,8 @@ ```{toctree} :maxdepth: 2 - + raster_class mask_class satimg_class -``` \ No newline at end of file +``` diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md index 4aa791ff0..42a90aefe 100644 --- a/doc/source/satimg_class.md +++ b/doc/source/satimg_class.md @@ -14,7 +14,7 @@ Below, a summary of the {class}`~geoutils.SatelliteImage` object and its methods A {class}`~geoutils.SatelliteImage` is a subclass of {class}`~geoutils.Raster` that contains all its main attributes, and retains additional ones: - a {class}`numpy.datetime64` as {class}`~geoutils.SatelliteImage.datetime`, and - several {class}`strings` for`~geoutils.SatelliteImage.satellite`, {class}`~geoutils.SatelliteImage.sensor`, {class}`~geoutils.SatelliteImage. version`, {class}`~geoutils.SatelliteImage.tile_name` and {class}`~geoutils.SatelliteImage.product`. - + A {class}`~geoutils.SatelliteImage` also inherits the same derivative attributes as a {class}`~geoutils.Raster`. ## Metadata parsing @@ -34,7 +34,7 @@ Right now are supported: The {class}`~geoutils.SatelliteImage.datetime` is always parsed or deduced. -For tiled products such as SRTM, the tile naming is also retrieved, which can be converted to geographic extent with {func}`geoutils.georaster.satimg.parse_tile_attr_from_name`. +For tiled products such as SRTM, the tile naming is also retrieved, which can be converted to geographic extent with {func}`geoutils.georaster.satimg.parse_tile_attr_from_name`. ```{code-cell} ipython3 import geoutils as gu @@ -49,6 +49,6 @@ geoimg2 = gu.SatelliteImage(gu.examples.get_path("everest_landsat_b4"), silent=F ``` ```{important} -The {class}`~geoutils.SatelliteImage` class is still in development, and we hope to further refine it in the future using metadata classes able to parse +The {class}`~geoutils.SatelliteImage` class is still in development, and we hope to further refine it in the future using metadata classes able to parse auxiliary files metadata (such as [here](https://github.com/jlandmann/glaciersat/blob/master/glaciersat/core/imagery.py)). ``` diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index 8050b83b2..cd1bc4b8d 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -31,7 +31,7 @@ and to allow the addition of more complex vector functionalities. ## Rasterize -The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geoutils.Vector` to {class}`~geoutils.Raster` inherently requires a +The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geoutils.Vector` to {class}`~geoutils.Raster` inherently requires a {attr}`~geoutils.Raster.res` or {attr}`~geoutils.Raster.shape` attribute to define the grid. While those can be passed on their own. ## Proximity @@ -41,5 +41,3 @@ The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geouti ## Buffering - - diff --git a/doc/source/vectors_index.md b/doc/source/vectors_index.md index 955a100e0..5b0ee1b31 100644 --- a/doc/source/vectors_index.md +++ b/doc/source/vectors_index.md @@ -6,4 +6,4 @@ :maxdepth: 2 vector_class -``` \ No newline at end of file +``` diff --git a/examples/analysis/README.rst b/examples/analysis/README.rst index a66dffd93..8aa59895e 100644 --- a/examples/analysis/README.rst +++ b/examples/analysis/README.rst @@ -5,4 +5,4 @@ Examples about **numerical operations** on rasters, and **additional analysis fe Some examples rely on NumPy or pythonic operators on :class:`Rasters` (see :ref:`core-py-ops` and :ref:`core-array-funcs`). -Others also benefit from match-reference (see :ref:`core-match-ref`). \ No newline at end of file +Others also benefit from match-reference (see :ref:`core-match-ref`). diff --git a/examples/analysis/array_numerics/README.rst b/examples/analysis/array_numerics/README.rst index 7ab702432..1d67e5a0f 100644 --- a/examples/analysis/array_numerics/README.rst +++ b/examples/analysis/array_numerics/README.rst @@ -1,2 +1,2 @@ Raster numerics ---------------- \ No newline at end of file +--------------- diff --git a/examples/analysis/array_numerics/numpy_interfacing.py b/examples/analysis/array_numerics/numpy_interfacing.py index 3f6a68665..7d4a702bb 100644 --- a/examples/analysis/array_numerics/numpy_interfacing.py +++ b/examples/analysis/array_numerics/numpy_interfacing.py @@ -8,10 +8,11 @@ # %% # We open a raster import geoutils as gu + rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) # %% We plot the original raster. -rast.show(cmap='terrain') +rast.show(cmap="terrain") # %% # NumPy interfacing allows to use any NumPy function directly on the raster @@ -26,7 +27,7 @@ # We copy the new array into a raster asp = rast.copy(new_array=aspect) -asp.show(cmap='twilight', cbar_title="Aspect (degrees)") +asp.show(cmap="twilight", cbar_title="Aspect (degrees)") # %% # @@ -43,5 +44,3 @@ # We plot the mask. # mask.show() - - diff --git a/examples/analysis/array_numerics/python_arithmetic.py b/examples/analysis/array_numerics/python_arithmetic.py index 47b539b0a..e632165a8 100644 --- a/examples/analysis/array_numerics/python_arithmetic.py +++ b/examples/analysis/array_numerics/python_arithmetic.py @@ -8,16 +8,17 @@ # %% # We open a raster import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) rast # %% We plot the original raster. -rast.show(cmap='Greys_r') +rast.show(cmap="Greys_r") # %% # Performing arithmetic operations implicitly loads the data. -rast = (rast + 1.)**0.5 / 5 -rast.show(cmap='Greys_r') +rast = (rast + 1.0) ** 0.5 / 5 +rast.show(cmap="Greys_r") # %% # diff --git a/examples/analysis/geospatial/README.rst b/examples/analysis/geospatial/README.rst index 51e73b398..21ae13c58 100644 --- a/examples/analysis/geospatial/README.rst +++ b/examples/analysis/geospatial/README.rst @@ -1,2 +1,2 @@ Distance estimation -------------------- \ No newline at end of file +------------------- diff --git a/examples/analysis/geospatial/buffer_voronoi.py b/examples/analysis/geospatial/buffer_voronoi.py index 940dbfd7d..068ae418c 100644 --- a/examples/analysis/geospatial/buffer_voronoi.py +++ b/examples/analysis/geospatial/buffer_voronoi.py @@ -8,6 +8,7 @@ # %% # We open an example vector import geoutils as gu + vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) # %% @@ -18,18 +19,19 @@ # %% # Let's plot the raster and vector ax = vect.ds.plot() -vect_buff.ds.plot(ax=ax, ec='k', fc='none') +vect_buff.ds.plot(ax=ax, ec="k", fc="none") # %% # Many buffers are overlapping. To compute a buffer without overlap, one can use :func:`~geoutils.Vector.buffer_without_overlap`. # vect_buff_nolap = vect.buffer_without_overlap(buffer_size=500) ax = vect_buff_nolap.ds.plot() -vect.ds.plot(ax=ax, ec='k', fc='none') +vect.ds.plot(ax=ax, ec="k", fc="none") # %% # We plot with color to see that the attributes are retained for every feature. import matplotlib.pyplot as plt + ax = vect_buff_nolap.ds.plot(column="RGIId") -vect.ds.plot(ax=ax, ec='k', column="RGIId", alpha=0.5) -plt.show() \ No newline at end of file +vect.ds.plot(ax=ax, ec="k", column="RGIId", alpha=0.5) +plt.show() diff --git a/examples/analysis/geospatial/proximity_metric.py b/examples/analysis/geospatial/proximity_metric.py index ee7815b47..37d754463 100644 --- a/examples/analysis/geospatial/proximity_metric.py +++ b/examples/analysis/geospatial/proximity_metric.py @@ -8,6 +8,7 @@ # %% # We open an example raster, and a vector for which we select a single feature import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) vect = gu.Vector(vect.ds[vect.ds["RGIId"] == "RGI60-15.10055"]) @@ -15,9 +16,10 @@ # Plot the raster and vector import matplotlib.pyplot as plt + ax = plt.gca() rast.show(ax=ax, cmap="Blues") -vect.reproject(rast).ds.plot(ax=ax, fc='none', ec='k', lw=2) +vect.reproject(rast).ds.plot(ax=ax, fc="none", ec="k", lw=2) # %% # We use the raster as a reference to match for rasterizing the proximity distances with :func:`~geoutils.Vector.proximity`. See :ref:`core-match-ref` for more details. @@ -30,6 +32,7 @@ # Get mask of pixels within 30 of 200 infrared import numpy as np + mask_200 = np.abs(rast - 200) < 30 mask_200.show() @@ -43,5 +46,5 @@ # By default, proximity is computed using the georeference unit from a :class:`~geoutils.Raster`'s :attr:`~geoutils.Raster.res`, here **meters**. It can also # be computed in pixels. -proximity_mask = mask_200.proximity(distance_unit='pixel') +proximity_mask = mask_200.proximity(distance_unit="pixel") proximity_mask.show(cmap="viridis") diff --git a/examples/analysis/point_extraction/README.rst b/examples/analysis/point_extraction/README.rst index 37fd6dacd..61a01bc92 100644 --- a/examples/analysis/point_extraction/README.rst +++ b/examples/analysis/point_extraction/README.rst @@ -1,2 +1,2 @@ Point extraction ----------------- \ No newline at end of file +---------------- diff --git a/examples/analysis/point_extraction/interpolation.py b/examples/analysis/point_extraction/interpolation.py index 4993dd8cf..9f48c36f2 100644 --- a/examples/analysis/point_extraction/interpolation.py +++ b/examples/analysis/point_extraction/interpolation.py @@ -8,8 +8,9 @@ # %% # We open an example raster, a digital elevation model in South America import geoutils as gu + rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) -rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+2000, rast.bounds.bottom+2000]) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 2000, rast.bounds.bottom + 2000]) # Plot the raster rast.show(cmap="terrain") @@ -17,8 +18,9 @@ # %% # We generate a random subsample of 100 points to interpolate, and extract the coordinates -import numpy as np import geopandas as gpd +import numpy as np + # Replace by Raster function once done (valid coords) np.random.seed(42) x_coords = np.random.uniform(rast.bounds.left + 50, rast.bounds.right - 50, 50) @@ -30,7 +32,7 @@ # Replace by Vector function once done ds = gpd.GeoDataFrame(geometry=gpd.points_from_xy(x=x_coords, y=y_coords), crs=rast.crs) ds["vals"] = vals -ds.plot(column="vals", cmap="terrain", legend=True, vmin=np.nanmin(rast), vmax=np.nanmax(rast), marker='x') +ds.plot(column="vals", cmap="terrain", legend=True, vmin=np.nanmin(rast), vmax=np.nanmax(rast), marker="x") # %% # .. important:: @@ -50,4 +52,4 @@ np.nanmean(vals - vals_shifted) # %% -# The mean difference in interpolated values is quite significant, with a 2 meter bias! \ No newline at end of file +# The mean difference in interpolated values is quite significant, with a 2 meter bias! diff --git a/examples/analysis/point_extraction/reduction.py b/examples/analysis/point_extraction/reduction.py index 9090eff28..0fe084d89 100644 --- a/examples/analysis/point_extraction/reduction.py +++ b/examples/analysis/point_extraction/reduction.py @@ -8,8 +8,9 @@ # %% # We open an example raster, a digital elevation model in South America import geoutils as gu + rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) -rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+2000, rast.bounds.bottom+2000]) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 2000, rast.bounds.bottom + 2000]) # Plot the raster rast.show(cmap="terrain") @@ -17,8 +18,9 @@ # %% # We generate a random subsample of 100 coordinates to extract -import numpy as np import geopandas as gpd +import numpy as np + # Replace by Raster function once done np.random.seed(42) x_coords = np.random.uniform(rast.bounds.left + 50, rast.bounds.right - 50, 50) @@ -49,7 +51,11 @@ x_closest = rast.copy(new_array=coords[0]).value_at_coords(x=x_coords, y=y_coords) y_closest = rast.copy(new_array=coords[1]).value_at_coords(x=x_coords, y=y_coords) from shapely import box -geometry = [box(x - 2*rast.res[0], y - 2*rast.res[1], x + 2*rast.res[0], y + 2*rast.res[1]) for x, y in zip(x_closest, y_closest)] + +geometry = [ + box(x - 2 * rast.res[0], y - 2 * rast.res[1], x + 2 * rast.res[0], y + 2 * rast.res[1]) + for x, y in zip(x_closest, y_closest) +] ds = gpd.GeoDataFrame(geometry=geometry, crs=rast.crs) ds["vals"] = vals_reduced ds.plot(column="vals", cmap="terrain", legend=True, vmin=np.nanmin(rast), vmax=np.nanmax(rast)) diff --git a/examples/handling/README.rst b/examples/handling/README.rst index 957209c13..6a555e5c4 100644 --- a/examples/handling/README.rst +++ b/examples/handling/README.rst @@ -3,4 +3,4 @@ Handling Examples about **warping**, **reprojecting**, **resampling**, **cropping**, as well as **rasterizing** and **polygonizing** of georeferenced data. -All of these methods support simply passing a reference to match (see :ref:`core-match-ref`). \ No newline at end of file +All of these methods support simply passing a reference to match (see :ref:`core-match-ref`). diff --git a/examples/handling/georeferencing/README.rst b/examples/handling/georeferencing/README.rst index 9ab18adb8..1c1ac7d3f 100644 --- a/examples/handling/georeferencing/README.rst +++ b/examples/handling/georeferencing/README.rst @@ -1,2 +1,2 @@ Geo-transformations -------------------- \ No newline at end of file +------------------- diff --git a/examples/handling/georeferencing/crop_raster.py b/examples/handling/georeferencing/crop_raster.py index 2bcebbebf..2aeefbee3 100644 --- a/examples/handling/georeferencing/crop_raster.py +++ b/examples/handling/georeferencing/crop_raster.py @@ -8,6 +8,7 @@ # %% # We open a raster and vector, and subset the latter. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) vect = gu.Vector(vect.ds[vect.ds["RGIId"] == "RGI60-15.10055"]) @@ -20,8 +21,9 @@ # %% # Let's plot the raster and vector. import matplotlib.pyplot as plt + rast.show(cmap="Purples") -vect.show(ref_crs=rast, fc='none', ec='k', lw=2) +vect.show(ref_crs=rast, fc="none", ec="k", lw=2) # %% # **First option:** using the second raster as a reference to match, we reproject the first one. We simply have to pass the second :class:`~geoutils.Raster` @@ -37,7 +39,7 @@ # rast.show(ax="new", cmap="Purples") -vect.show(ref_crs=rast, fc='none', ec='k', lw=2) +vect.show(ref_crs=rast, fc="none", ec="k", lw=2) # %% # **Second option:** we can pass other ``crop_geom`` argument to :func:`~geoutils.Raster.crop`, including another :class:`~geoutils.Raster` or a @@ -46,4 +48,4 @@ rast.crop((rast.bounds.left + 1000, rast.bounds.bottom, rast.bounds.right, rast.bounds.top - 500)) rast.show(ax="new", cmap="Purples") -vect.show(ref_crs=rast, fc='none', ec='k', lw=2) +vect.show(ref_crs=rast, fc="none", ec="k", lw=2) diff --git a/examples/handling/georeferencing/crop_vector.py b/examples/handling/georeferencing/crop_vector.py index 767b2738f..fd3cf2290 100644 --- a/examples/handling/georeferencing/crop_vector.py +++ b/examples/handling/georeferencing/crop_vector.py @@ -8,13 +8,14 @@ # %% # We open a raster and vector. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) # %% # Let's plot the raster and vector. The raster has smaller extent than the vector. rast.show(cmap="Greys_r", alpha=0.7) -vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) +vect.show(ref_crs=rast, fc="none", ec="tab:purple", lw=3) # %% # **First option:** using the raster as a reference to match, we crop the vector. We simply have to pass the :class:`~geoutils.Raster` as single argument to @@ -28,7 +29,7 @@ # rast.show(ax="new", cmap="Greys_r", alpha=0.7) -vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) +vect.show(ref_crs=rast, fc="none", ec="tab:purple", lw=3) # %% # The :func:`~geoutils.Vector.crop` keeps all features with geometries intersecting the extent to crop to. We can also force a clipping of the geometries @@ -36,17 +37,14 @@ vect.crop(rast, clip=True) rast.show(ax="new", cmap="Greys_r", alpha=0.7) -vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) +vect.show(ref_crs=rast, fc="none", ec="tab:purple", lw=3) # %% # **Second option:** we can pass other ``crop_geom`` argument to :func:`~geoutils.Vector.crop`, including another :class:`~geoutils.Vector` or a # simple :class:`tuple` of bounds. bounds = rast.get_bounds_projected(out_crs=vect.crs) -vect.crop(cropGeom=(bounds.left + 0.5*(bounds.right - bounds.left), - bounds.bottom, - bounds.right, - bounds.top)) +vect.crop(cropGeom=(bounds.left + 0.5 * (bounds.right - bounds.left), bounds.bottom, bounds.right, bounds.top)) rast.show(ax="new", cmap="Greys_r", alpha=0.7) -vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) +vect.show(ref_crs=rast, fc="none", ec="tab:purple", lw=3) diff --git a/examples/handling/georeferencing/reproj_raster.py b/examples/handling/georeferencing/reproj_raster.py index c248a267f..f996021c1 100644 --- a/examples/handling/georeferencing/reproj_raster.py +++ b/examples/handling/georeferencing/reproj_raster.py @@ -7,6 +7,7 @@ # %% # We open two example rasters. import geoutils as gu + rast1 = gu.Raster(gu.examples.get_path("everest_landsat_b4")) rast2 = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) @@ -18,9 +19,10 @@ # %% # Let's plot the first raster, with the warped extent of the second one. import matplotlib.pyplot as plt + rast1.show(cmap="Blues") vect_bounds_rast2 = gu.Vector.from_bounds_projected(rast2) -vect_bounds_rast2.show(fc='none', ec='r', lw=2) +vect_bounds_rast2.show(fc="none", ec="r", lw=2) # %% # **First option:** using the second raster as a reference to match, we reproject the first one. We simply have to pass the second :class:`~geoutils.Raster` diff --git a/examples/handling/georeferencing/reproj_vector.py b/examples/handling/georeferencing/reproj_vector.py index 7bdc8f35a..a398a6aa5 100644 --- a/examples/handling/georeferencing/reproj_vector.py +++ b/examples/handling/georeferencing/reproj_vector.py @@ -9,6 +9,7 @@ # %% # We open a raster and vector. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) @@ -20,8 +21,9 @@ # %% # Let's plot the two in their original projection. import matplotlib.pyplot as plt + rast.show(cmap="Greys_r") -vect.show(ref_crs=rast, fc='none', ec='tab:purple', lw=3) +vect.show(ref_crs=rast, fc="none", ec="tab:purple", lw=3) # %% # **First option:** using the raster as a reference to match, we reproject the vector. We simply have to pass the :class:`~geoutils.Raster` as an argument @@ -32,7 +34,7 @@ # %% # We can plot the vector in its new projection. -vect_reproj.show(ax="new", fc='none', ec='tab:purple', lw=3) +vect_reproj.show(ax="new", fc="none", ec="tab:purple", lw=3) # %% # **Second option:** we can pass the georeferencing argument ``dst_crs`` to :func:`~geoutils.Vector.reproject` (an EPSG code can be passed directly as @@ -40,4 +42,4 @@ # Reproject in UTM zone 45N vect_reproj = vect.reproject(dst_crs=32645) -vect_reproj \ No newline at end of file +vect_reproj diff --git a/examples/handling/interface/README.rst b/examples/handling/interface/README.rst index c32885faf..20a0dbd58 100644 --- a/examples/handling/interface/README.rst +++ b/examples/handling/interface/README.rst @@ -1,2 +1,2 @@ Raster-vector interfacing -------------------------- \ No newline at end of file +------------------------- diff --git a/examples/handling/interface/create_mask.py b/examples/handling/interface/create_mask.py index 3b0765399..a21a56d79 100644 --- a/examples/handling/interface/create_mask.py +++ b/examples/handling/interface/create_mask.py @@ -8,13 +8,14 @@ # %% # We open a raster and vector. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) # %% # Let's plot the raster and vector. rast.show(cmap="Purples") -vect.show(ref_crs=rast, fc='none', ec='k', lw=2) +vect.show(ref_crs=rast, fc="none", ec="k", lw=2) # %% # **First option:** using the raster as a reference to match, we create a mask for the vector in any projection and georeferenced grid. We simply have to pass diff --git a/examples/handling/interface/polygonize.py b/examples/handling/interface/polygonize.py index 259de5e8b..0d241a2ae 100644 --- a/examples/handling/interface/polygonize.py +++ b/examples/handling/interface/polygonize.py @@ -8,8 +8,9 @@ # %% # We open a raster. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) -rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+5000, rast.bounds.bottom+5000]) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 5000, rast.bounds.bottom + 5000]) # %% # Let's plot the raster. rast.show(cmap="terrain") @@ -36,4 +37,4 @@ # %% # .. note:: -# See :ref:`core-py-ops` for more details on casting to :func:`~geoutils.Mask`. \ No newline at end of file +# See :ref:`core-py-ops` for more details on casting to :func:`~geoutils.Mask`. diff --git a/examples/handling/interface/rasterize.py b/examples/handling/interface/rasterize.py index ed9f915be..e7489d623 100644 --- a/examples/handling/interface/rasterize.py +++ b/examples/handling/interface/rasterize.py @@ -8,13 +8,14 @@ # %% # We open a raster and vector. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) # %% # Let's plot the raster and vector. rast.show(cmap="Purples") -vect.show(ref_crs=rast, fc='none', ec='k', lw=2) +vect.show(ref_crs=rast, fc="none", ec="k", lw=2) # %% # **First option:** using the raster as a reference to match, we rasterize the vector in any projection and georeferenced grid. We simply have to pass the diff --git a/examples/handling/interface/topoints.py b/examples/handling/interface/topoints.py index d862910bc..4b58e2f00 100644 --- a/examples/handling/interface/topoints.py +++ b/examples/handling/interface/topoints.py @@ -8,8 +8,9 @@ # %% # We open a raster. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) -rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left+500, rast.bounds.bottom+500]) +rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 500, rast.bounds.bottom + 500]) # %% # Let's plot the raster. rast.show(cmap="terrain") diff --git a/examples/io/README.rst b/examples/io/README.rst index e3556990b..57008f221 100644 --- a/examples/io/README.rst +++ b/examples/io/README.rst @@ -3,4 +3,4 @@ Input/output Examples about **opening**, **creating**, **loading** or **saving** geospatial data. -With :class:`Rasters`, data is only loaded when necessary (see :ref:`core-lazy-load`). \ No newline at end of file +With :class:`Rasters`, data is only loaded when necessary (see :ref:`core-lazy-load`). diff --git a/examples/io/import_export/README.rst b/examples/io/import_export/README.rst index a805d05d2..777f8b964 100644 --- a/examples/io/import_export/README.rst +++ b/examples/io/import_export/README.rst @@ -1,2 +1,2 @@ Import and export ------------------ \ No newline at end of file +----------------- diff --git a/examples/io/import_export/from_array.py b/examples/io/import_export/from_array.py index 640f200ce..5e58c415f 100644 --- a/examples/io/import_export/from_array.py +++ b/examples/io/import_export/from_array.py @@ -5,12 +5,13 @@ This example demonstrates the creation of a raster through :func:`~geoutils.Raster.from_array`. """ +import numpy as np +import pyproj +import rasterio as rio + # %% # We create a data array as :class:`~numpy.ndarray`, a transform as :class:`affine.Affine` and a coordinate reference system (CRS) as :class:`pyproj.CRS`. import geoutils as gu -import rasterio as rio -import pyproj -import numpy as np # A random 3 x 3 masked array np.random.seed(42) @@ -21,12 +22,7 @@ crs = pyproj.CRS.from_epsg(4326) # Create a raster -rast = gu.Raster.from_array( - data = arr, - transform = transform, - crs = crs, - nodata = 255 - ) +rast = gu.Raster.from_array(data=arr, transform=transform, crs=crs, nodata=255) rast # %% @@ -45,12 +41,7 @@ ma = np.ma.masked_array(data=arr, mask=mask) # This time, we pass directly the masked array -rast = gu.Raster.from_array( - data = ma, - transform = transform, - crs = crs, - nodata = 255 - ) +rast = gu.Raster.from_array(data=ma, transform=transform, crs=crs, nodata=255) rast # %% diff --git a/examples/io/import_export/import_raster.py b/examples/io/import_export/import_raster.py index f81340345..034194507 100644 --- a/examples/io/import_export/import_raster.py +++ b/examples/io/import_export/import_raster.py @@ -6,10 +6,11 @@ :class:`~geoutils.Raster`. """ +import rasterio as rio + # %% # A raster can be imported from a :class:`rasterio.io.DatasetReader` or :class:`rasterio.io.MemoryFile` simply by instantiating :class:`~geoutils.Raster`. import geoutils as gu -import rasterio as rio ds = rio.DatasetReader(gu.examples.get_path("exploradores_aster_dem")) rast = gu.Raster(ds) @@ -33,4 +34,4 @@ # Finally, we can export a :class:`~geoutils.Raster` to a :class:`rasterio.io.DatasetReader` of a :class:`rasterio.io.MemoryFile` using # :class:`~geoutils.Raster.to_rio_dataset` -rast.to_rio_dataset() \ No newline at end of file +rast.to_rio_dataset() diff --git a/examples/io/import_export/import_vector.py b/examples/io/import_export/import_vector.py index 818581626..af5a6e185 100644 --- a/examples/io/import_export/import_vector.py +++ b/examples/io/import_export/import_vector.py @@ -8,9 +8,10 @@ # %% # A vector can be imported from a :class:`geopandas.GeoDataFrame` simply by instantiating :class:`~geoutils.Vector`. -import geoutils as gu import geopandas as gpd +import geoutils as gu + ds = gpd.read_file(gu.examples.get_path("exploradores_rgi_outlines")) vect = gu.Vector(ds) vect diff --git a/examples/io/open_save/README.rst b/examples/io/open_save/README.rst index 06ea0aeed..ff68c4a82 100644 --- a/examples/io/open_save/README.rst +++ b/examples/io/open_save/README.rst @@ -1,2 +1,2 @@ Open and save from files ------------------------- \ No newline at end of file +------------------------ diff --git a/examples/io/open_save/read_raster.py b/examples/io/open_save/read_raster.py index 81308cf99..f0fb45b69 100644 --- a/examples/io/open_save/read_raster.py +++ b/examples/io/open_save/read_raster.py @@ -8,6 +8,7 @@ # %% # We open an example raster. The data is, by default, unloaded. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) rast @@ -37,4 +38,4 @@ # %% # Finally, a raster is saved using :func:`~geoutils.Raster.save`: -rast.save("myraster.tif") \ No newline at end of file +rast.save("myraster.tif") diff --git a/examples/io/open_save/read_satimg.py b/examples/io/open_save/read_satimg.py index 2d160ef7a..fab40cbac 100644 --- a/examples/io/open_save/read_satimg.py +++ b/examples/io/open_save/read_satimg.py @@ -11,6 +11,7 @@ # We print the filename of our raster that, as often with satellite data, holds metadata information. filename = gu.examples.get_path("everest_landsat_b4") import os + print(os.path.basename(filename)) # %% diff --git a/examples/io/open_save/read_vector.py b/examples/io/open_save/read_vector.py index 3cdda9b12..b6fb21700 100644 --- a/examples/io/open_save/read_vector.py +++ b/examples/io/open_save/read_vector.py @@ -27,4 +27,4 @@ # %% # Finally, a vector is saved using :func:`~geoutils.Vector.save`. -# vect.save("myvector.shp") \ No newline at end of file +# vect.save("myvector.shp") diff --git a/geoutils/georaster/__init__.py b/geoutils/georaster/__init__.py index 87cc9171c..a02755db4 100644 --- a/geoutils/georaster/__init__.py +++ b/geoutils/georaster/__init__.py @@ -1,4 +1,4 @@ from geoutils.georaster.raster import Raster, RasterType, Mask # noqa isort:skip -from geoutils.georaster.satimg import SatelliteImage +from geoutils.georaster.satimg import SatelliteImage # noqa __all__ = ["RasterType", "Raster"] diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 7fdfdb481..7459ffa21 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -44,6 +44,7 @@ try: import rioxarray + _has_rioxarray = True except ImportError: rioxarray = None @@ -107,6 +108,7 @@ ] handled_array_funcs = _HANDLED_FUNCTIONS_1NIN + _HANDLED_FUNCTIONS_2NIN + # Function to set the default nodata values for any given dtype # Similar to GDAL for int types, but without absurdly long nodata values for floats. # For unsigned types, the maximum value is chosen (with a max of 99999). @@ -195,7 +197,7 @@ def _load_rio( * resampling : to set the resampling algorithm """ # If out_shape is passed, no need to account for transform and shape - if kwargs['out_shape'] is not None: + if kwargs["out_shape"] is not None: window = None else: if transform is not None and shape is not None: @@ -489,7 +491,9 @@ def indexes_on_disk(self) -> None | tuple[int, ...]: def indexes(self) -> tuple[int, ...]: """Return the indexes of bands loaded in memory if they are, otherwise on disk.""" if self._indexes is not None and not self.is_loaded: - return self._indexes + if isinstance(self._indexes, int): + return (self._indexes,) + return tuple(self._indexes) # if self._indexes_loaded is not None: # if isinstance(self._indexes_loaded, int): # return (self._indexes_loaded, ) @@ -627,19 +631,27 @@ def __repr__(self) -> str: # If data not loaded, return and string and avoid calling .data if not self.is_loaded: - str_data = "not_loaded; shape on disk "+str(self._disk_shape) + str_data = "not_loaded; shape on disk " + str(self._disk_shape) else: str_data = "\n ".join(self.data.__str__().split("\n")) # Above and below, we align left spaces for multi-line object representation (arrays) after return to lines # And call the class name for easier inheritance to subclasses (avoid overloading to just rewrite subclass name) - s = self.__class__.__name__+"(\n"+\ - " data=" + str_data +\ - "\n transform="+ "\n ".join(self.transform.__str__().split("\n"))+\ - "\n crs=" + self.crs.__str__()+\ - "\n nodata=" + self.nodata.__str__()+")" + s = ( + self.__class__.__name__ + + "(\n" + + " data=" + + str_data + + "\n transform=" + + "\n ".join(self.transform.__str__().split("\n")) + + "\n crs=" + + self.crs.__str__() + + "\n nodata=" + + self.nodata.__str__() + + ")" + ) - return s + return str(s) # L = [getattr(self, item) for item in self._saved_attrs] # s: str = "{}.{}({})".format(type(self).__module__, type(self).__qualname__, ", ".join(map(str, L))) @@ -650,18 +662,26 @@ def _repr_html_(self) -> str: # If data not loaded, return and string and avoid calling .data if not self.is_loaded: - str_data = "not_loaded; shape on disk "+str(self._disk_shape)+"" + str_data = "not_loaded; shape on disk " + str(self._disk_shape) + "" else: str_data = "\n ".join(self.data.__str__().split("\n")) # Over-ride Raster's method to remove nodata value (always None) # Use
     to keep white spaces,  to keep line breaks
    -        s = '
    '+self.__class__.__name__+'(\n' + \
    -            "  data=" + str_data + \
    -            "\n  transform=" + "\n            ".join(self.transform.__str__().split("\n")) + \
    -            "\n  crs=" + self.crs.__str__() + ")
    " + s = ( + '
    '
    +            + self.__class__.__name__
    +            + "(\n"
    +            + "  data="
    +            + str_data
    +            + "\n  transform="
    +            + "\n            ".join(self.transform.__str__().split("\n"))
    +            + "\n  crs="
    +            + self.crs.__str__()
    +            + ")
    " + ) - return s + return str(s) def __str__(self) -> str: """Provide simplified string for print() about Raster.""" @@ -671,7 +691,7 @@ def __str__(self) -> str: else: s = self.data.__str__() - return s + return str(s) def __getitem__(self, value: Raster | Vector | list[float] | tuple[float, ...]) -> np.ndarray | Raster: """Subset the Raster object: calls the crop method with default parameters""" @@ -2119,7 +2139,8 @@ def show( :param alpha: Transparency of raster and colorbar. :param cbar_title: Colorbar label. Default is None. :param add_cbar: Set to True to display a colorbar. Default is True. - :param ax: A figure ax to be used for plotting. If None, will plot on current axes. If "new", will create a new axis. + :param ax: A figure ax to be used for plotting. If None, will plot on current axes. + If "new", will create a new axis. :param return_axes: Whether to return axes. :returns: None, or (ax, caxes) if return_axes is True @@ -2221,6 +2242,8 @@ def show( # If returning axes if return_axes: return ax, cax + else: + return None def value_at_coords( self, @@ -2369,7 +2392,11 @@ def format_value(value: Any) -> Any: # if self.count == 1: with rio.open(self.filename) as raster: data = raster.read( - window=rio_window, fill_value=self.nodata, boundless=boundless, masked=masked, indexes=index, + window=rio_window, + fill_value=self.nodata, + boundless=boundless, + masked=masked, + indexes=index, ) value = format_value(data) win = data @@ -2379,7 +2406,8 @@ def format_value(value: Any) -> Any: # with rio.open(self.filename) as raster: # for b in self.indexes: # data = raster.read( - # window=rio_window, fill_value=self.nodata, boundless=boundless, masked=masked, indexes=b + # window=rio_window, fill_value=self.nodata, boundless=boundless, + # masked=masked, indexes=b # ) # val = format_value(data) # value[b] = val @@ -2580,7 +2608,8 @@ def interp_points( returned is nan. Shape should be (N,2). :param input_latlon: Whether the input is in latlon, unregarding of Raster CRS :param mode: One of 'linear', 'cubic', or 'quintic'. Determines what type of spline is used to - interpolate the raster value at each point. For more information, see scipy.interpolate.interp2d. Default is linear. + interpolate the raster value at each point. For more information, see scipy.interpolate.interp2d. + Default is linear. :param index: The band to use (from 1 to self.count). :param shift_area_or_point: Shifts index to center pixel coordinates if GDAL's AREA_OR_POINT attribute (in self.tags) is "Point", keeps the corner pixel coordinate for "Area". @@ -2762,12 +2791,14 @@ def to_points( points = np.vstack((x_coords.reshape(1, -1), y_coords.reshape(1, -1), pixel_data)).T if not as_array: - points = Vector(gpd.GeoDataFrame( - points[:, 2:], - columns=[f"b{i}" for i in range(1, pixel_data.shape[0] + 1)], - geometry=gpd.points_from_xy(points[:, 0], points[:, 1]), - crs=self.crs, - )) + points = Vector( + gpd.GeoDataFrame( + points[:, 2:], + columns=[f"b{i}" for i in range(1, pixel_data.shape[0] + 1)], + geometry=gpd.points_from_xy(points[:, 0], points[:, 1]), + crs=self.crs, + ) + ) return points @@ -2883,7 +2914,6 @@ def proximity( # Subclass Mask for manipulating boolean Rasters class Mask(Raster): - def __init__( self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], @@ -2926,12 +2956,18 @@ def __repr__(self) -> str: str_data = "\n ".join(self.data.__str__().split("\n")) # Over-ride Raster's method to remove nodata value (always None) - s = "Mask(\n"+\ - " data=" + str_data +\ - "\n transform="+ "\n ".join(self.transform.__str__().split("\n"))+\ - "\n crs=" + self.crs.__str__()+")" + s = ( + "Mask(\n" + + " data=" + + str_data + + "\n transform=" + + "\n ".join(self.transform.__str__().split("\n")) + + "\n crs=" + + self.crs.__str__() + + ")" + ) - return s + return str(s) def _repr_html_(self) -> str: """Convert to HTML representation for notebooks and docs""" @@ -2944,12 +2980,18 @@ def _repr_html_(self) -> str: # Over-ride Raster's method to remove nodata value (always None) # Use
     to keep white spaces,  to keep line breaks
    -        s = '
    Mask(\n' + \
    -            "  data=" + str_data + \
    -            "\n  transform=" + "\n            ".join(self.transform.__str__().split("\n")) + \
    -            "\n  crs=" + self.crs.__str__() + ")
    " + s = ( + '
    Mask(\n'
    +            + "  data="
    +            + str_data
    +            + "\n  transform="
    +            + "\n            ".join(self.transform.__str__().split("\n"))
    +            + "\n  crs="
    +            + self.crs.__str__()
    +            + ")
    " + ) - return s + return str(s) def reproject( self: Mask, @@ -3078,16 +3120,24 @@ def proximity( # TODO: Adapt target_value into int | list in Raster.proximity # If target values is passed but does not correspond to 0 or 1, raise a warning - # if target_values is not None and not isinstance(target_values, (int, np.integer, float, np.floating)) or target_values not in [0, 1]: + # if target_values is not None and not isinstance(target_values, (int, np.integer, + # float, np.floating)) or target_values not in [0, 1]: # warnings.warn("In-value converted to 1 for polygonizing boolean mask.") # target_values = [1] # Convert to unsigned integer and pass to parent method self._data = self.data.astype("uint8") - # Need to cast output to Raster before computing proximity, as output will not be boolean (super() would instantiate Mask() again) + # Need to cast output to Raster before computing proximity, as output will not be boolean + # (super() would instantiate Mask() again) raster = Raster({"data": self.data, "transform": self.transform, "crs": self.crs, "nodata": self.nodata}) - return raster.proximity(vector=vector, target_values=target_values, geometry_type=geometry_type, in_or_out=in_or_out, distance_unit=distance_unit) + return raster.proximity( + vector=vector, + target_values=target_values, + geometry_type=geometry_type, + in_or_out=in_or_out, + distance_unit=distance_unit, + ) # Logical bitwise operations def __and__(self: Mask, other: Mask) -> Mask: @@ -3110,9 +3160,7 @@ def __xor__(self: Mask, other: Mask) -> Mask: def __invert__(self: Mask) -> Mask: - return self.from_array( - data=~self.data, transform=self.transform, crs=self.crs, nodata=self.nodata - ) + return self.from_array(data=~self.data, transform=self.transform, crs=self.crs, nodata=self.nodata) # ----------------------------------------- diff --git a/geoutils/georaster/satimg.py b/geoutils/georaster/satimg.py index cbb732105..0bb0dd301 100644 --- a/geoutils/georaster/satimg.py +++ b/geoutils/georaster/satimg.py @@ -25,7 +25,7 @@ def parse_landsat(gname: str) -> list[Any]: attrs.append(lsat_sensor[gname[1]]) attrs.append(None) attrs.append(None) - attrs.append((gname[3:6] + gname[6:9])) + attrs.append(gname[3:6] + gname[6:9]) year = int(gname[9:13]) doy = int(gname[13:16]) attrs.append(dt.datetime.fromordinal(dt.date(year - 1, 12, 31).toordinal() + doy)) @@ -35,7 +35,7 @@ def parse_landsat(gname: str) -> list[Any]: attrs.append(lsat_sensor[split_name[0][1]]) attrs.append(None) attrs.append(None) - attrs.append((split_name[2][0:3] + split_name[2][3:6])) + attrs.append(split_name[2][0:3] + split_name[2][3:6]) attrs.append(dt.datetime.strptime(split_name[3], "%Y%m%d")) attrs.append(attrs[3].date()) return attrs @@ -367,8 +367,6 @@ def __parse_metadata_from_fn(self, silent: bool = False) -> None: + "from filename" ) - - @property def datetime(self) -> dt.datetime | None: return self._datetime @@ -378,7 +376,7 @@ def datetime(self, value: dt.datetime | None) -> None: if isinstance(value, (dt.datetime, np.datetime64)) or value is None: self._datetime = value else: - raise ValueError('Datetime must be set with a python or NumPy datetime.') + raise ValueError("Datetime must be set with a python or NumPy datetime.") @property def satellite(self) -> str | None: @@ -389,7 +387,7 @@ def satellite(self, value: str | None) -> None: if isinstance(value, str) or value is None: self._satellite = value else: - raise ValueError('Satellite must be set with a string.') + raise ValueError("Satellite must be set with a string.") @property def sensor(self) -> str | None: @@ -400,7 +398,7 @@ def sensor(self, value: str | None) -> None: if isinstance(value, str) or value is None: self._sensor = value else: - raise ValueError('Sensor must be set with a string.') + raise ValueError("Sensor must be set with a string.") @property def product(self) -> str | None: @@ -411,7 +409,7 @@ def product(self, value: str | None) -> None: if isinstance(value, str) or value is None: self._product = value else: - raise ValueError('Product must be set with a string.') + raise ValueError("Product must be set with a string.") @property def version(self) -> str | None: @@ -422,7 +420,7 @@ def version(self, value: str | None) -> None: if isinstance(value, str) or value is None: self._version = value else: - raise ValueError('Version must be set with a string.') + raise ValueError("Version must be set with a string.") @property def tile_name(self) -> str | None: @@ -433,7 +431,7 @@ def tile_name(self, value: str | None) -> None: if isinstance(value, str) or value is None: self._tile_name = value else: - raise ValueError('Tile name must be set with a string.') + raise ValueError("Tile name must be set with a string.") def __parse_metadata_from_file(self, fn_meta: str | None) -> None: warnings.warn(f"Parse metadata from file not implemented. {fn_meta}") diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 98166d106..47024d739 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -8,17 +8,17 @@ import warnings from collections import abc from numbers import Number -from typing import Literal, TypeVar, overload, Any +from typing import Any, Literal, TypeVar, overload import fiona import geopandas as gpd import matplotlib import matplotlib.pyplot as plt -from mpl_toolkits.axes_grid1 import make_axes_locatable import numpy as np import rasterio as rio import rasterio.errors import shapely +from mpl_toolkits.axes_grid1 import make_axes_locatable from rasterio import features, warp from rasterio.crs import CRS from scipy.spatial import Voronoi @@ -64,12 +64,19 @@ def __repr__(self) -> str: """Representation of vector""" # Get the representation of ds - str_ds= "\n ".join(self.__str__().split("\n")) - - s = self.__class__.__name__ + "(\n" + \ - " ds=" + str_ds + \ - "\n crs=" + self.crs.__str__() + \ - "\n bounds=" + self.bounds.__str__() + ")" + str_ds = "\n ".join(self.__str__().split("\n")) + + s = str( + self.__class__.__name__ + + "(\n" + + " ds=" + + str_ds + + "\n crs=" + + self.crs.__str__() + + "\n bounds=" + + self.bounds.__str__() + + ")" + ) return s @@ -80,20 +87,30 @@ def _repr_html_(self) -> str: # Over-ride Raster's method to remove nodata value (always None) # Use
     to keep white spaces,  to keep line breaks
    -        s = '
    ' + self.__class__.__name__ + '(\n' + \
    -            "  ds=" + str_ds + \
    -            "\n  crs=" + self.crs.__str__() + \
    -            "\n  bounds=" + self.bounds.__repr__() + ")
    " + s = str( + '
    '
    +            + self.__class__.__name__
    +            + "(\n"
    +            + "  ds="
    +            + str_ds
    +            + "\n  crs="
    +            + self.crs.__str__()
    +            + "\n  bounds="
    +            + self.bounds.__repr__()
    +            + ")
    " + ) return s def __str__(self) -> str: """Provide string of information about Raster.""" - return self.ds.__str__() + + return str(self.ds.__str__()) def __getitem__(self, value: gu.Raster | Vector | list[float] | tuple[float, ...]) -> Vector: """Subset the Raster object: calls the crop method with default parameters""" - return self.crop(cropGeom=value, inplace=False) + + return self.crop(cropGeom=value, clip=False, inplace=False) def info(self) -> str: """ @@ -113,19 +130,20 @@ def info(self) -> str: return "".join(as_str) - def show(self, - ref_crs: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | CRS | int | None = None, - cmap: matplotlib.colors.Colormap | str | None = None, - vmin: float | int | None = None, - vmax: float | int | None = None, - alpha: float | int | None = None, - cbar_title: str | None = None, - add_cbar: bool = False, - ax: matplotlib.axes.Axes | None | Literal["new"] = None, - return_axes: bool = False, - **kwargs: Any, - ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: - """Show/display the vector, with axes in projection of image. + def show( + self, + ref_crs: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | CRS | int | None = None, + cmap: matplotlib.colors.Colormap | str | None = None, + vmin: float | int | None = None, + vmax: float | int | None = None, + alpha: float | int | None = None, + cbar_title: str | None = None, + add_cbar: bool = False, + ax: matplotlib.axes.Axes | None | Literal["new"] = None, + return_axes: bool = False, + **kwargs: Any, + ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: + r"""Show/display the vector, with axes in projection of image. This method is a wrapper to geopandas.GeoDataFrame.plot. Any \*\*kwargs which you give this method will be passed to it. @@ -137,7 +155,8 @@ def show(self, :param alpha: Transparency of raster and colorbar. :param cbar_title: Colorbar label. Default is None. :param add_cbar: Set to True to display a colorbar. Default is True. - :param ax: A figure ax to be used for plotting. If None, will plot on current axes. If "new", will create a new axis. + :param ax: A figure ax to be used for plotting. If None, will plot on current axes. If "new", + will create a new axis. :param return_axes: Whether to return axes. :returns: None, or (ax, caxes) if return_axes is True @@ -190,7 +209,9 @@ def show(self, divider = make_axes_locatable(ax0) cax = divider.append_axes("right", size="5%", pad=0.05) norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) - cbar = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm) #, orientation="horizontal", ticklocation="top") + cbar = matplotlib.colorbar.ColorbarBase( + cax, cmap=cmap, norm=norm + ) # , orientation="horizontal", ticklocation="top") cbar.solids.set_alpha(alpha) if cbar_title is not None: @@ -200,11 +221,23 @@ def show(self, cbar = None # Plot - vect_reproj.ds.plot(ax=ax0, cax=cax, cmap=cmap, vmin=vmin, vmax=vmax, alpha=alpha, legend=legend, legend_kwds=legend_kwds, **kwargs) + vect_reproj.ds.plot( + ax=ax0, + cax=cax, + cmap=cmap, + vmin=vmin, + vmax=vmax, + alpha=alpha, + legend=legend, + legend_kwds=legend_kwds, + **kwargs, + ) # If returning axes if return_axes: return ax, cax + else: + return None @property def bounds(self) -> rio.coords.BoundingBox: @@ -251,7 +284,7 @@ def crop( cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], clip: bool, *, - inplace: Literal[True] = True, + inplace: Literal[True], ) -> None: ... @@ -265,6 +298,16 @@ def crop( ) -> VectorType: ... + @overload + def crop( + self: VectorType, + cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + clip: bool, + *, + inplace: bool = True, + ) -> VectorType | None: + ... + def crop( self: VectorType, cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], @@ -272,7 +315,8 @@ def crop( inplace: bool = True, ) -> VectorType | None: """ - Crop the Vector to given extent, or bounds of a raster or vector. Optionally, clip geometries to that extent (by default keeps all intersecting). + Crop the Vector to given extent, or bounds of a raster or vector. Optionally, clip geometries + to that extent (by default keeps all intersecting). Reprojection is done on the fly if georeferenced objects have different projections. @@ -583,7 +627,9 @@ def rasterize( return output @classmethod - def from_bounds_projected(cls, raster_or_vector: gu.Raster | VectorType, out_crs: CRS | None = None, densify_pts: int = 5000) -> VectorType: + def from_bounds_projected( + cls, raster_or_vector: gu.Raster | VectorType, out_crs: CRS | None = None, densify_pts: int = 5000 + ) -> VectorType: """Create a vector polygon from projected bounds of a raster or vector. :param raster_or_vector: A raster or vector @@ -599,9 +645,9 @@ def from_bounds_projected(cls, raster_or_vector: gu.Raster | VectorType, out_crs df = gpd.GeoDataFrame(geometry=[poly], crs=out_crs) - return cls(df) + return cls(df) # type: ignore - def query(self: VectorType, expression: str, inplace: bool = False) -> VectorType: + def query(self: Vector, expression: str, inplace: bool = False) -> Vector | None: """ Query the Vector dataset with a valid Pandas expression. @@ -612,13 +658,12 @@ def query(self: VectorType, expression: str, inplace: bool = False) -> VectorTyp """ # Modify inplace if wanted and return the self instance. if inplace: - self.ds.query(expression, inplace=True) - return self + self._ds = self.ds.query(expression, inplace=True) + return None # Otherwise, create a new Vector from the queried dataset. - new_vector = self.__new__(type(self)) - new_vector.__init__(self.ds.query(expression)) # type: ignore - return new_vector # type: ignore + new_vector = Vector(self.ds.query(expression)) + return new_vector def proximity( self, diff --git a/tests/test_docs.py b/tests/test_docs.py index 5dc53b1a3..a22073702 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1,7 +1,6 @@ """Functions to test the documentation.""" import os import shutil -import warnings import sphinx.cmd.build From 6ffc72e19f7634a4ba18c3469ca6568222f77529 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:03:19 -0800 Subject: [PATCH 052/184] Make mypy ignore other files than core and tests --- .pre-commit-config.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d78f4af6..16ee23464 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,8 +58,7 @@ repos: ] additional_dependencies: [tokenize-rt==3.2.0] - - + files: ^(geoutils|tests) # Sort imports using isort - repo: https://github.com/PyCQA/isort From da3c2eb1af2cd262edb91939d1cadd4a4c6bc5e8 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:03:43 -0800 Subject: [PATCH 053/184] Fix example --- examples/analysis/point_extraction/reduction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/analysis/point_extraction/reduction.py b/examples/analysis/point_extraction/reduction.py index 0fe084d89..ca62fbb88 100644 --- a/examples/analysis/point_extraction/reduction.py +++ b/examples/analysis/point_extraction/reduction.py @@ -48,8 +48,8 @@ # Replace by Vector fonction once done coords = rast.coords(grid=True) -x_closest = rast.copy(new_array=coords[0]).value_at_coords(x=x_coords, y=y_coords) -y_closest = rast.copy(new_array=coords[1]).value_at_coords(x=x_coords, y=y_coords) +x_closest = rast.copy(new_array=coords[0]).value_at_coords(x=x_coords, y=y_coords).squeeze() +y_closest = rast.copy(new_array=coords[1]).value_at_coords(x=x_coords, y=y_coords).squeeze() from shapely import box geometry = [ From dfaf1b6dde2032741050caeb9373b326bae5a656 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:13:03 -0800 Subject: [PATCH 054/184] Fix diff calculation with self install in dev env --- geoutils/misc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/geoutils/misc.py b/geoutils/misc.py index b27afb7d8..1680f7939 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -94,7 +94,6 @@ def diff_environment_yml(fn_env: str, fn_devenv: str, print_dep: str = "both") - """ Compute the difference between environment.yml and dev-environment.yml for setup of continuous integration, while checking that all the dependencies listed in environment.yml are also in dev-environment.yml - :param fn_env: Filename path to environment.yml :param fn_devenv: Filename path to dev-environment.yml :param print_dep: Whether to print conda differences "conda", pip differences "pip" or both. @@ -115,6 +114,10 @@ def diff_environment_yml(fn_env: str, fn_devenv: str, print_dep: str = "both") - if isinstance(conda_dep_devenv[-1], dict): pip_dep_devenv = conda_dep_devenv.pop()["pip"] + # Remove the package's self install for devs via pip, if it exists + if "-e ./" in pip_dep_devenv: + pip_dep_devenv.pop("-e ./") + # Check if there is a pip dependency in the normal env as well, if yes pop it also if isinstance(conda_dep_env[-1], dict): pip_dep_env = conda_dep_env.pop()["pip"] From 6d9ef61d3d51cba69c4c58e931c92255cb7b923a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:17:50 -0800 Subject: [PATCH 055/184] Now should work --- geoutils/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geoutils/misc.py b/geoutils/misc.py index 1680f7939..972084709 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -116,7 +116,7 @@ def diff_environment_yml(fn_env: str, fn_devenv: str, print_dep: str = "both") - # Remove the package's self install for devs via pip, if it exists if "-e ./" in pip_dep_devenv: - pip_dep_devenv.pop("-e ./") + pip_dep_devenv.remove("-e ./") # Check if there is a pip dependency in the normal env as well, if yes pop it also if isinstance(conda_dep_env[-1], dict): From b6ab9c6bc08fd5f3147174d4986eaaf8c1990df5 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:22:43 -0800 Subject: [PATCH 056/184] What about now? --- geoutils/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geoutils/misc.py b/geoutils/misc.py index 972084709..77fc9db3b 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -135,7 +135,7 @@ def diff_environment_yml(fn_env: str, fn_devenv: str, print_dep: str = "both") - # If there is no pip dependency in env, all the ones of dev-env need to be added during CI else: - diff_pip_dep = list(pip_dep_devenv["pip"]) + diff_pip_dep = pip_dep_devenv # If there is no pip dependency, we ignore this step else: From ebfb6f1cbe4c74056e03f894f6c233d63b994342 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:27:20 -0800 Subject: [PATCH 057/184] Hard reset cache --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 6760b9371..f42104b0a 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -48,7 +48,7 @@ jobs: path: ${{ env.CONDA }}/envs key: conda-${{ matrix.os }}-${{ matrix.python-version }}-${{ steps.get-date.outputs.month }}-${{ hashFiles('dev-environment.yml') }}-${{ env.CACHE_NUMBER }} env: - CACHE_NUMBER: 5 # Increase this value to reset cache if environment.yml has not changed + CACHE_NUMBER: 0 # Increase this value to reset cache if environment.yml has not changed id: cache # The trick below is necessary because the generic environment file does not specify a Python version, and only From aa4878bff6dc21607f063c322872b0be23afbdf5 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:35:32 -0800 Subject: [PATCH 058/184] Try to remove jinja condition now that mamba is solving --- dev-environment.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dev-environment.yml b/dev-environment.yml index 59b1c345d..20b05db62 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -12,8 +12,6 @@ dependencies: # Development-specific - autovizwidget - - graphviz - - jinja2<3.1 - sphinx - myst-nb - sphinx-autodoc-typehints @@ -34,4 +32,5 @@ dependencies: - pip: - sphinx-design - sphinx-book-theme + - graphviz - -e ./ From 6ea16a82f2b1dfa151ee97830d65c5164dd31962 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Feb 2023 17:53:01 -0800 Subject: [PATCH 059/184] Try pip --user option for Windows --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index f42104b0a..11ff952ac 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -79,7 +79,7 @@ jobs: pkgs_pip_dev=`python -c "import geoutils; geoutils.misc.diff_environment_yml('environment.yml', 'dev-environment.yml', 'pip')"` mamba install $pkgs_conda_dev --freeze-installed if [[ "$pkgs_pip_dev" != "None" ]]; then - pip install $pkgs_pip_dev + pip install --user $pkgs_pip_dev fi # Stop the build if there are Python syntax errors or undefined names From 46a1f16c0bc82bbd813544f566506c2c906759c1 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sat, 25 Feb 2023 13:36:29 -0800 Subject: [PATCH 060/184] Array ufunc fixes --- doc/source/about_geoutils.md | 2 +- .../array_numerics/numpy_interfacing.py | 18 ++++----- .../handling/georeferencing/reproj_vector.py | 4 +- geoutils/georaster/raster.py | 10 +++-- tests/test_georaster.py | 40 +++++++++++++++++++ 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 118744680..bf84dcfb3 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -31,7 +31,7 @@ We are working on making features fully consistent for the first long-term relea GeoUtils is built on top of [Rasterio](https://rasterio.readthedocs.io/en/latest/), [GeoPandas](https://geopandas.org/en/stable/docs.html) and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenced operations, and relies on [NumPy](https://numpy.org/doc/stable/), [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: -- A **common and consistent framework** for efficient rasters and vectors handling, +- A **common and consistent framework** for efficient raster and vector handling, - A structure following the **principal of least knowledge**2 to foster accessibility, - A **pythonic arithmetic** and **NumPy interfacing** for robust numerical computing and intuitive analysis. diff --git a/examples/analysis/array_numerics/numpy_interfacing.py b/examples/analysis/array_numerics/numpy_interfacing.py index 7d4a702bb..124c2d5df 100644 --- a/examples/analysis/array_numerics/numpy_interfacing.py +++ b/examples/analysis/array_numerics/numpy_interfacing.py @@ -6,16 +6,16 @@ """ # sphinx_gallery_thumbnail_number = 2 # %% -# We open a raster +# We open a raster. import geoutils as gu - rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) -# %% We plot the original raster. +# %% We plot it. rast.show(cmap="terrain") # %% -# NumPy interfacing allows to use any NumPy function directly on the raster +# +# The NumPy interface allows to use almost any NumPy function directly on the raster. import numpy as np @@ -34,13 +34,13 @@ # .. important:: # For rigorous slope and aspect calculation (matching that of GDAL), **check-out our sister package** `xDEM `_. # -# We can make numpy logical operations to isolate the terrain oriented South and above three thousand meters. The rasters will be cast to a :class:`Mask`. +# We use NumPy logical operations to isolate the terrain oriented South and above three thousand meters. The rasters will be logically cast to a +# :class:`Mask`. -# Not supported yet, fix first -# mask = np.logical_and.reduce((asp > -45, asp < 45, rast > 3000)) -# mask +mask = np.logical_and.reduce((asp > -45, asp < 45, rast > 3000)) +mask # %% # We plot the mask. -# mask.show() +mask.show() diff --git a/examples/handling/georeferencing/reproj_vector.py b/examples/handling/georeferencing/reproj_vector.py index a398a6aa5..566ba7a8c 100644 --- a/examples/handling/georeferencing/reproj_vector.py +++ b/examples/handling/georeferencing/reproj_vector.py @@ -23,7 +23,7 @@ import matplotlib.pyplot as plt rast.show(cmap="Greys_r") -vect.show(ref_crs=rast, fc="none", ec="tab:purple", lw=3) +vect.show(ax="new", fc="none", ec="tab:purple", lw=3) # %% # **First option:** using the raster as a reference to match, we reproject the vector. We simply have to pass the :class:`~geoutils.Raster` as an argument @@ -40,6 +40,6 @@ # **Second option:** we can pass the georeferencing argument ``dst_crs`` to :func:`~geoutils.Vector.reproject` (an EPSG code can be passed directly as # :class:`int`). -# Reproject in UTM zone 45N +# Reproject in UTM zone 45N. vect_reproj = vect.reproject(dst_crs=32645) vect_reproj diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 7459ffa21..470734041 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1441,10 +1441,12 @@ def get_nanarray(self, return_mask: bool = False) -> np.ndarray | tuple[np.ndarr else: return nanarray - def __array__(self) -> np.ndarray: - """Method to cast np.array() or np.asarray() function directly on Raster classes.""" - - return self._data + # This is interfering with __array_ufunc__ and __array_function__, so better to leave out and specify + # behaviour directly in those. + # def __array__(self) -> np.ndarray: + # """Method to cast np.array() or np.asarray() function directly on Raster classes.""" + # + # return self._data def __array_ufunc__( self, diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 05ef4cf3c..af82dcc50 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -3076,14 +3076,18 @@ class TestArrayInterface: np.random.seed(42) arr1 = np.random.randint(min_val, max_val, (height, width), dtype="int32") + np.random.normal(size=(height, width)) arr2 = np.random.randint(min_val, max_val, (height, width), dtype="int32") + np.random.normal(size=(height, width)) + # This third one is to try ufunc methods like reduce() + arr3 = np.random.randint(min_val, max_val, (height, width), dtype="int32") + np.random.normal(size=(height, width)) # Create two random masks mask1 = np.random.randint(0, 2, size=(width, height), dtype=bool) mask2 = np.random.randint(0, 2, size=(width, height), dtype=bool) + mask3 = np.random.randint(0, 2, size=(width, height), dtype=bool) # Assert that there is at least one unmasked value assert np.count_nonzero(~mask1) > 0 assert np.count_nonzero(~mask2) > 0 + assert np.count_nonzero(~mask3) > 0 @pytest.mark.parametrize("ufunc_str", ufuncs_str_1nin_1nout + ufuncs_str_1nin_2nout) # type: ignore @pytest.mark.parametrize( @@ -3326,3 +3330,39 @@ def test_array_functions_2nin( output_ma = arrfunc(rst1.data, rst2.data) assert np.ma.allequal(output_rst, output_ma) + + @pytest.mark.parametrize("method_str", ["reduce"]) # type: ignore + def test_ufunc_methods(self, method_str): + """ + Test that universal function methods all behave properly, don't need to test all + nodatas and dtypes as this was done above. + """ + + ma1 = np.ma.masked_array(data=self.arr1.astype("float32"), mask=self.mask1) + ma2 = np.ma.masked_array(data=self.arr2.astype("float32"), mask=self.mask2) + ma3 = np.ma.masked_array(data=self.arr3.astype("float32"), mask=self.mask3) + + rst1 = gu.Raster.from_array(ma1, transform=self.transform, crs=None, nodata=_default_nodata("float32")) + rst2 = gu.Raster.from_array(ma2, transform=self.transform, crs=None, nodata=_default_nodata("float32")) + rst3 = gu.Raster.from_array(ma3, transform=self.transform, crs=None, nodata=_default_nodata("float32")) + + # Methods reduce, accumulate, reduceat and at only supported for binary function (2nin) + # -- Test 1: -- Try a ufunc with 2nin, 1nout like np.add + ufunc_2nin_1nout = getattr(np.add, method_str) + output_rst = ufunc_2nin_1nout((rst1, rst2, rst3)) + output_ma = ufunc_2nin_1nout((ma1, ma2, ma3)) + + print(np.shape(output_ma)) + print(np.shape(output_rst.data)) + assert np.ma.allequal(output_rst.data, output_ma) + + # Methods reduce only supports function that ouput a single value + # -- Test 2: -- Try a ufunc with 2nin, 2nout: there's only divmod + # ufunc_2nin_2nout = getattr(np.divmod, method_str) + # outputs_rst = ufunc_2nin_2nout((rst1, rst2, rst3)) + # outputs_ma = ufunc_2nin_2nout((ma1, ma2, ma3)) + # + # assert np.ma.allequal(outputs_ma[0], outputs_rst[0].data) and np.ma.allequal( + # outputs_ma[1], outputs_rst[1].data) + + From af3623aaa314b82a082058a6edf50a8e6eba577e Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sat, 25 Feb 2023 16:12:32 -0800 Subject: [PATCH 061/184] Ensure array functions are cast to Raster if they preserve the shape (isfinite, isnan, isclose, etc) --- doc/source/raster_class.md | 1 + geoutils/georaster/raster.py | 22 ++++++++++++++++++++-- tests/test_georaster.py | 19 ++++++++++++++++--- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 18bbe8633..0d95797c3 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -180,6 +180,7 @@ Logical comparison functions will cast to a {class}`~geoutils.Mask`. ```{code-cell} ipython3 # Is the raster close to another within tolerance? + np.isclose(raster, raster+0.05, atol=0.1) ``` diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 470734041..6993987ba 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1550,11 +1550,29 @@ def __array_function__( else: first_arg = args[0].data + # Separate one and two input functions if func.__name__ in _HANDLED_FUNCTIONS_1NIN: - return func(first_arg, *args[1:], **kwargs) # type: ignore + outputs = func(first_arg, *args[1:], **kwargs) # type: ignore else: second_arg = args[1].data - return func(first_arg, second_arg, *args[2:], **kwargs) # type: ignore + outputs = func(first_arg, second_arg, *args[2:], **kwargs) # type: ignore + + # Below, we recast to Raster if the shape was preserved, otherwise return an array + # First, if there are several outputs in a tuple which are arrays + if isinstance(outputs, tuple) and isinstance(outputs[0], np.ndarray): + if all(output.shape == args[0].data.shape for output in outputs): + return (self.from_array(data=output, transform=self.transform, crs=self.crs, nodata=self.nodata) for output in outputs) + else: + return outputs + # Second, if there is a single output which is an array + elif isinstance(outputs, np.ndarray): + if outputs.shape == args[0].data.shape: + return self.from_array(data=outputs, transform=self.transform, crs=self.crs, nodata=self.nodata) + else: + return outputs + # Else, return outputs directly + else: + return outputs # Note the star is needed because of the default argument 'mode' preceding non default arg 'inplace' # Then the final overload must be duplicated diff --git a/tests/test_georaster.py b/tests/test_georaster.py index af82dcc50..85e5311c5 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -3281,8 +3281,14 @@ def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: N if "gradient" in arrfunc_str: assert np.ma.allequal(output_rst[0], output_ma[0]) and np.ma.allequal(output_rst[1], output_ma[1]) # This test is for when the NumPy function reduces the dimension of the array but not completely - elif isinstance(output_rst, np.ndarray): - assert np.ma.allequal(output_rst, output_ma) + elif isinstance(output_ma, np.ndarray): + # When the NumPy function preserves the shape, it returns a Raster + if output_ma.shape == rst.data.shape: + assert isinstance(output_rst, gu.Raster) + assert np.ma.allequal(output_rst.data, output_ma) + # Otherwise, it returns an array + else: + assert np.ma.allequal(output_rst, output_ma) # This test is for when the NumPy function reduces the dimension to a single number else: assert output_rst == output_ma @@ -3326,10 +3332,17 @@ def test_array_functions_2nin( warnings.filterwarnings("ignore", category=RuntimeWarning) + # Compute outputs output_rst = arrfunc(rst1, rst2) output_ma = arrfunc(rst1.data, rst2.data) - assert np.ma.allequal(output_rst, output_ma) + # When the NumPy function preserves the shape, it returns a Raster + if isinstance(output_ma, np.ndarray) and output_ma.shape == rst1.data.shape: + assert isinstance(output_rst, gu.Raster) + assert np.ma.allequal(output_rst.data, output_ma) + # Otherwise, it returns an array + else: + assert np.ma.allequal(output_rst, output_ma) @pytest.mark.parametrize("method_str", ["reduce"]) # type: ignore def test_ufunc_methods(self, method_str): From 2e52fc92653c88b7512906544d5792918dff0426 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sat, 25 Feb 2023 16:14:31 -0800 Subject: [PATCH 062/184] Linting --- examples/analysis/array_numerics/numpy_interfacing.py | 1 + geoutils/georaster/raster.py | 9 ++++++--- tests/test_georaster.py | 6 ++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/analysis/array_numerics/numpy_interfacing.py b/examples/analysis/array_numerics/numpy_interfacing.py index 124c2d5df..1fce6aa72 100644 --- a/examples/analysis/array_numerics/numpy_interfacing.py +++ b/examples/analysis/array_numerics/numpy_interfacing.py @@ -8,6 +8,7 @@ # %% # We open a raster. import geoutils as gu + rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) # %% We plot it. diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 6993987ba..921de13b1 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1552,16 +1552,19 @@ def __array_function__( # Separate one and two input functions if func.__name__ in _HANDLED_FUNCTIONS_1NIN: - outputs = func(first_arg, *args[1:], **kwargs) # type: ignore + outputs = func(first_arg, *args[1:], **kwargs) # type: ignore else: second_arg = args[1].data - outputs = func(first_arg, second_arg, *args[2:], **kwargs) # type: ignore + outputs = func(first_arg, second_arg, *args[2:], **kwargs) # type: ignore # Below, we recast to Raster if the shape was preserved, otherwise return an array # First, if there are several outputs in a tuple which are arrays if isinstance(outputs, tuple) and isinstance(outputs[0], np.ndarray): if all(output.shape == args[0].data.shape for output in outputs): - return (self.from_array(data=output, transform=self.transform, crs=self.crs, nodata=self.nodata) for output in outputs) + return ( + self.from_array(data=output, transform=self.transform, crs=self.crs, nodata=self.nodata) + for output in outputs + ) else: return outputs # Second, if there is a single output which is an array diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 85e5311c5..6c469c830 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -3344,7 +3344,7 @@ def test_array_functions_2nin( else: assert np.ma.allequal(output_rst, output_ma) - @pytest.mark.parametrize("method_str", ["reduce"]) # type: ignore + @pytest.mark.parametrize("method_str", ["reduce"]) # type: ignore def test_ufunc_methods(self, method_str): """ Test that universal function methods all behave properly, don't need to test all @@ -3369,7 +3369,7 @@ def test_ufunc_methods(self, method_str): print(np.shape(output_rst.data)) assert np.ma.allequal(output_rst.data, output_ma) - # Methods reduce only supports function that ouput a single value + # Methods reduce only supports function that output a single value # -- Test 2: -- Try a ufunc with 2nin, 2nout: there's only divmod # ufunc_2nin_2nout = getattr(np.divmod, method_str) # outputs_rst = ufunc_2nin_2nout((rst1, rst2, rst3)) @@ -3377,5 +3377,3 @@ def test_ufunc_methods(self, method_str): # # assert np.ma.allequal(outputs_ma[0], outputs_rst[0].data) and np.ma.allequal( # outputs_ma[1], outputs_rst[1].data) - - From fa33398866641493b2f71cf0f405e449373ff034 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sat, 25 Feb 2023 16:18:09 -0800 Subject: [PATCH 063/184] Fix typo --- doc/source/raster_class.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 0d95797c3..11f5e344b 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -188,7 +188,8 @@ See {ref}`core-array-funcs` for more details. ## Reproject -Reprojecting a {class}`~geoutils.Raster` is done through the{func}`~geoutils.Raster.reproject`, which enforces new {attr}`~geoutils.Raster.transform` and/or +Reprojecting a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.reproject` function, which enforces new {attr}`~geoutils.Raster.transform` +and/or {class}`~geoutils.Raster.crs`. ```{important} From 8efcc32d8bcf6b9703864c4d6fce75fdc525a852 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Sat, 25 Feb 2023 16:33:53 -0800 Subject: [PATCH 064/184] Linting --- doc/source/raster_class.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 11f5e344b..2843681bc 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -188,7 +188,7 @@ See {ref}`core-array-funcs` for more details. ## Reproject -Reprojecting a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.reproject` function, which enforces new {attr}`~geoutils.Raster.transform` +Reprojecting a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.reproject` function, which enforces new {attr}`~geoutils.Raster.transform` and/or {class}`~geoutils.Raster.crs`. From dc57efea2134222ab165aad82057dd0f47f64909 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 28 Feb 2023 15:36:38 -0800 Subject: [PATCH 065/184] Try to hard reset cache to fix Mac tests --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 11ff952ac..4159f2be8 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -48,7 +48,7 @@ jobs: path: ${{ env.CONDA }}/envs key: conda-${{ matrix.os }}-${{ matrix.python-version }}-${{ steps.get-date.outputs.month }}-${{ hashFiles('dev-environment.yml') }}-${{ env.CACHE_NUMBER }} env: - CACHE_NUMBER: 0 # Increase this value to reset cache if environment.yml has not changed + CACHE_NUMBER: 1 # Increase this value to reset cache if environment.yml has not changed id: cache # The trick below is necessary because the generic environment file does not specify a Python version, and only From 02a8393f047f029ae47dbbd8b5171e2d2bf65f2c Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 28 Feb 2023 18:17:17 -0800 Subject: [PATCH 066/184] Update quick start --- doc/source/about_geoutils.md | 5 +- doc/source/quick_start.md | 151 +++++++++++++++++++++++++---------- examples/analysis/README.rst | 2 + examples/handling/README.rst | 2 + examples/io/README.rst | 2 + 5 files changed, 116 insertions(+), 46 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index bf84dcfb3..c89ed148e 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -54,10 +54,11 @@ More on these core features of GeoUtils in the {ref}`quick-start`, or {ref}`core ## Why the need for GeoUtils? Recent community efforts have improved open-source geospatial analysis in Python, allowing to **move away from the low-level functions and -complexity of [GDAL and OGR](https://gdal.org/)**'s Python bindings for raster and vector handling. Those efforts include in particular [Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html). +complexity of [GDAL and OGR](https://gdal.org/)'s Python bindings** for raster and vector handling. Those efforts include in particular +[Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html). However, these new packages still maintain a relatively low-level API to serve all types of geospatial informatics users, **slowing down end-users focusing -on data analysis**. As a result, interfacing between vector data and raster data is delicate and simple higher-level operation (such as +on data analysis**. As a result, basic interfacing between vector data and raster data is not always straightforward and simple higher-level operation (such as reprojection to match a reference) are not always computed consistently in the community. Additionally, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses mostly on reading, projecting and writing, and thus **requires array extraction diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 0eb035f28..43b859066 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -7,10 +7,12 @@ kernelspec: # Quick start -The following presents how to quickly get started with GeoUtils, show-casing examples on different core aspects of the package. +The following presents a descriptive example show-casing all core aspects of GeoUtils. For more details, refer to the {ref}`core-index`, {ref}`rasters-index` or {ref}`vectors-index` pages. +To find an example about a specific functionality, jump to {ref}`quick-gallery`. + ## The core {class}`~geoutils.Raster` and {class}`~geoutils.Vector` classes In GeoUtils, geospatial handling is object-based and revolves around {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. @@ -28,18 +30,25 @@ rast = gu.Raster(filename_rast) vect = gu.Vector(filename_vect) ``` -A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as `.data`, a {class}`~pyproj.crs.CRS` as `.crs`, -an {class}`~affine.Affine` as `.transform`, and a {class}`float` or {class}`int` as `.nodata`. When a file exists on disk, {class}`~geoutils.Raster` is -linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata, and the array at the appropriate time. +A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, a +{class}`~pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, an {class}`~affine.Affine` as {attr}`~geoutils.Raster.transform`, and a {class}`float` or {class} +`int` as {attr}`geoutils.Raster.nodata`. + ```{code-cell} ipython3 -:tags: [hide-output] # The opened raster rast ``` -A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as `.ds`, for which most methods are -wrapped directly into {class}`~geoutils.Vector`. +```{important} +When a file exists on disk, {class}`~geoutils.Raster` is linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata. The array will be +**loaded in-memory implicitly** when {attr}`~geoutils.Raster.data` is required by an operation. + +See {ref}`core-lazy-load` for more details. +``` + +A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`, for which +most methods are wrapped directly into {class}`~geoutils.Vector`. ```{code-cell} ipython3 :tags: [hide-output] @@ -50,37 +59,33 @@ vect All other attributes are derivatives of those main attributes, or of the filename on disk. Attributes of {class}`~geoutils.Raster` and {class}`~geoutils.Vector` update with geospatial operations on themselves. -## Geospatial handling and match-reference -Geospatial operations are based on class methods, such as {func}`geoutils.Raster.crop` or {func}`geoutils.Vector.proximity`. Nearly all of these methods can be -passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a **reference to match** during the operation. A **reference {class}`~geoutils.Vector`** -enforces a matching of `.bounds` and/or `.crs`, while a **reference {class}`~geoutils.Raster`** can also enforce a matching of `.res`, depending on the nature of the operation. +## Handling and match-reference + +In GeoUtils, geospatial handling operations are based on class methods, such as {func}`~geoutils.Raster.crop` or {func}`~geoutils.Raster.reproject`. + +For convenience and consistency, nearly all of these methods can be passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a +**reference to match** during the operation. A **reference {class}`~geoutils.Vector`** enforces a matching of {attr}`~geoutils.Vector.bounds` and/or +{attr}`~geoutils.Vector.crs`, while a **reference {class}`~geoutils.Raster`** can also enforce a matching of {attr}`~geoutils.Raster.res`, depending on the nature of the operation. ```{code-cell} ipython3 +:tags: [hide-output] # Crop raster to vector's extent rast.crop(vect) +# Print info of cropped raster +print(rast.info()) ``` -```{code-cell} ipython3 -# Compute proximity to vector on raster's grid -rast_proximity_to_vec = vect.proximity(rast) -``` - -All methods can be also be passed any number of georeferencing arguments such as `.shape` or `.res`, and will logically deduce others from the input, much -as in [GDAL](https://gdal.org/)'s command line. - - -```{note} -Right now, the array `.data` of `rast` is still not loaded. Applying {func}`~geoutils.Raster.crop` does not yet require loading, -and `rast`'s metadata is sufficient to provide a georeferenced grid for {func}`~geoutils.Vector.proximity`. The array will only be loaded when necessary. +```{margin} +1The names of geospatial handling methods is largely based on [GDAL and OGR](https://gdal.org/)'s, with the notable exception of {func}`~geoutils.Vector.reproject` that better applies to vectors than `warp`. ``` Additionally, in GeoUtils, **methods that apply to the same georeferencing attributes have consistent naming**1 across {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. -```{margin} -1The names of geospatial handling methods is largely based on [GDAL and OGR](https://gdal.org/)'s, with the notable exception of {func}`~geoutils.Vector.reproject` that better applies to vectors than `warp`. -``` +A {func}`~geoutils.Raster.reproject` involves a change in {attr}`~geoutils.Raster.crs` or {attr}`~geoutils.Raster.transform`, while a {func}`~geoutils.Raster.crop` only involves a change +in {attr}`~geoutils.Raster.bounds`. Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, +and the other way around for {func}`~geoutils.Vector.rasterize`. ```{list-table} :widths: 30 30 30 @@ -98,15 +103,54 @@ Additionally, in GeoUtils, **methods that apply to the same georeferencing attri * - Rasterize/Polygonize - {func}`~geoutils.Raster.polygonize` - {func}`~geoutils.Vector.rasterize` - * - Proximity - - {func}`~geoutils.Raster.proximity` - - {func}`~geoutils.Vector.proximity` ``` -A {func}`~geoutils.Raster.reproject` involves a change in `.crs` or `.transform`, while a {func}`~geoutils.Raster.crop` only involves a change in `.bounds`. -Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, and the other way around for {func}`~geoutils.Vector.rasterize`. +All methods can be also be passed any number of georeferencing arguments such as {attr}`~geoutils.Raster.shape` or {attr}`~geoutils.Raster.res`, and will +naturally deduce others from the input {class}`~geoutils.Raster` or {class}`~geoutils.Vector`, much as in [GDAL](https://gdal.org/)'s command line. +## Higher-level analysis tools + +GeoUtils also implements higher-level geospatial analysis tools for both {class}`Rasters` and {class}`Vectors`. For +example, one can compute the distance to a {class}`~geoutils.Vector` geometry, or to target pixels of a {class}`~geoutils.Raster`, using +{func}`~geoutils.Vector.proximity`. + +As with the geospatial handling functions previously listed, many analysis functions can take a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a +**reference to utilize** during the operation. In the case of {func}`~geoutils.Vector.proximity`, passing a {class}`~geoutils.Raster` serves as a reference +for the georeferenced grid on which to compute the distances. + +```{code-cell} ipython3 +# Compute proximity to vector on raster's grid +rast_proximity_to_vec = vect.proximity(rast) +``` + +```{note} +Right now, the array {attr}`~geoutils.Raster.data` of `rast` is still not loaded. Applying {func}`~geoutils.Raster.crop` does not yet require loading, +and `rast`'s metadata is sufficient to provide a georeferenced grid for {func}`~geoutils.Vector.proximity`. The array will only be loaded when necessary. +``` + +## Quick plotting + +To facilitate the analysis process, GeoUtils includes quick plotting tools that support multiple colorbars and implicitly add layers to the current axis. +Those are wrapped from {func}`rasterio.plot.show` and {func}`geopandas.GeoDataFrame.plot`, and relay any argument passed. + +```{seealso} +GeoUtils' plotting tools only aim to get rid off the most common hassles when quickly plotting raster and vector data during analysis. + +For advanced plotting tools to create "publication-quality" figures, see [Cartopy](https://scitools.org.uk/cartopy/docs/latest/) or +[GeoPlot](https://residentmario.github.io/geoplot/index.html). +``` + +The plotting functionality is named {func}`~geoutils.Raster.show` everywhere, for consistency. Here again, a {class}`~geoutils.Raster` or +{class}`~geoutils.Vector` can be passed as a **reference to match** to ensure all data is displayed on the same grid and projection. + +```{code-cell} ipython3 +# Plot proximity to vector +rast_proximity_to_vec = vect.proximity(rast) +rast_proximity_to_vec.show(cbar_title="Distance to glacier outline") +vect.show(rast_proximity_to_vec, fc="none") +``` + ## Pythonic arithmetic and NumPy interface All {class}`~geoutils.Raster` objects support Python arithmetic ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, @@ -119,7 +163,7 @@ rast += 1 ``` Additionally, the class {class}`~geoutils.Raster` possesses a NumPy masked-array interface that allows to apply to it any [NumPy universal function](https://numpy.org/doc/stable/reference/ufuncs.html) and -most other NumPy array functions, while logically casting `dtypes` and respecting `.nodata` values. +most other NumPy array functions, while logically casting {class}`dtypes` and respecting {attr}`~geoutils.Raster.nodata` values. ```{code-cell} ipython3 # Apply a normalization to the raster @@ -144,7 +188,7 @@ Masks can then be used for indexing a {class}`~geoutils.Raster`, which returns a values_aoi = rast[mask_aoi] ``` -Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean `dtypes`. Using {func}`~geoutils.Raster.polygonize` with a +Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to their boolean {class}`dtypes`. Using {func}`~geoutils.Raster.polygonize` with a {class}`~geoutils.Mask` is straightforward, for instance, to retrieve a {class}`~geoutils.Vector` of the area-of-interest: ```{code-cell} ipython3 @@ -152,21 +196,25 @@ Masks also have simplified, overloaded {class}`~geoutils.Raster` methods due to vect_aoi = mask_aoi.polygonize() ``` -## Plotting and saving geospatial data +```{code-cell} ipython3 +# Plot result +rast.show(cmap='Reds', cbar_title='Normalized infrared') +vect_aoi.show(fc='none', ec='k', lw=0.5) +``` +## Saving to file -Finally, GeoUtils includes basic plotting tools wrapping directly {func}`rasterio.plot.show` and {func}`geopandas.GeoDataFrame.plot` for {class}`~geoutils.Raster` and {class}`~geoutils.Vector`, respectively. -The plotting function was renamed {func}`~geoutils.Raster.show` everywhere, for consistency. +Finally, for saving a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` to file, simply call the {func}`~geoutils.Raster.save` function. -For saving, {func}`~geoutils.Raster.save` is used. +```{code-cell} ipython3 +# Save our AOI vector +# vect_aoi.save() +``` ```{code-cell} ipython3 -# Plot result -import matplotlib.pyplot as plt -fig = plt.figure() -ax = plt.gca() -rast.show(ax=ax, cmap='Reds', cbar_title='Normalized infrared') -vect_aoi.ds.plot(ax=ax, fc='none', ec='k', lw=0.5) +:tags: [remove-cell] +import os +# os.remove() ``` ```{admonition} Wrap-up @@ -184,6 +232,21 @@ For a **bonus** example on parsing satellite metadata and DEMs, continue below. Otherwise, for more **hands-on** examples, explore GeoUtils' gallery of examples! ``` +(quick-gallery)= +## More examples + +To dive into more illustrated code, explore our gallery of examples that is composed of: +- An {ref}`examples-io` section on opening, saving, loading, importing and exporting, +- An {ref}`examples-handling` section on geotransformations (crop, reproject) and raster-vector interfacing, +- An {ref}`examples-analysis` section on analysis tools and raster numerics. + +See also the full concatenated list of examples below. + +```{eval-rst} +.. minigallery:: geoutils.Raster + :add-heading: Examples using rasters and vectors +``` + ## **Bonus:** Parsing metadata with {class}`~geoutils.SatelliteImage` In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses @@ -207,7 +270,7 @@ There are many possible subclass to derive from a {class}`~geoutils.Raster`. Her .. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM :top-classes: geoutils.georaster.raster.Raster ``` -```{note} +```{seealso} The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) (and more) to derive topographic attributes (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same results. diff --git a/examples/analysis/README.rst b/examples/analysis/README.rst index 8aa59895e..0424d218b 100644 --- a/examples/analysis/README.rst +++ b/examples/analysis/README.rst @@ -1,3 +1,5 @@ +.. _examples-analysis: + Analysis ======== diff --git a/examples/handling/README.rst b/examples/handling/README.rst index 6a555e5c4..b9ca3e6dc 100644 --- a/examples/handling/README.rst +++ b/examples/handling/README.rst @@ -1,3 +1,5 @@ +.. _examples-handling: + Handling ======== diff --git a/examples/io/README.rst b/examples/io/README.rst index 57008f221..74d899034 100644 --- a/examples/io/README.rst +++ b/examples/io/README.rst @@ -1,3 +1,5 @@ +.. _examples-io: + Input/output ============ From 934b4d23813d9edd18996c77bd517e98be3c8bcd Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 28 Feb 2023 18:20:13 -0800 Subject: [PATCH 067/184] Linting --- doc/source/about_geoutils.md | 2 +- doc/source/quick_start.md | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index c89ed148e..1c84337ce 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -54,7 +54,7 @@ More on these core features of GeoUtils in the {ref}`quick-start`, or {ref}`core ## Why the need for GeoUtils? Recent community efforts have improved open-source geospatial analysis in Python, allowing to **move away from the low-level functions and -complexity of [GDAL and OGR](https://gdal.org/)'s Python bindings** for raster and vector handling. Those efforts include in particular +complexity of [GDAL and OGR](https://gdal.org/)'s Python bindings** for raster and vector handling. Those efforts include in particular [Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html). However, these new packages still maintain a relatively low-level API to serve all types of geospatial informatics users, **slowing down end-users focusing diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 43b859066..a57fd5c08 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -30,9 +30,9 @@ rast = gu.Raster(filename_rast) vect = gu.Vector(filename_vect) ``` -A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, a +A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, a {class}`~pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, an {class}`~affine.Affine` as {attr}`~geoutils.Raster.transform`, and a {class}`float` or {class} -`int` as {attr}`geoutils.Raster.nodata`. +`int` as {attr}`geoutils.Raster.nodata`. ```{code-cell} ipython3 @@ -41,13 +41,13 @@ rast ``` ```{important} -When a file exists on disk, {class}`~geoutils.Raster` is linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata. The array will be +When a file exists on disk, {class}`~geoutils.Raster` is linked to a {class}`rasterio.io.DatasetReader` object for loading the metadata. The array will be **loaded in-memory implicitly** when {attr}`~geoutils.Raster.data` is required by an operation. See {ref}`core-lazy-load` for more details. ``` -A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`, for which +A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`, for which most methods are wrapped directly into {class}`~geoutils.Vector`. ```{code-cell} ipython3 @@ -62,10 +62,10 @@ All other attributes are derivatives of those main attributes, or of the filenam ## Handling and match-reference -In GeoUtils, geospatial handling operations are based on class methods, such as {func}`~geoutils.Raster.crop` or {func}`~geoutils.Raster.reproject`. +In GeoUtils, geospatial handling operations are based on class methods, such as {func}`~geoutils.Raster.crop` or {func}`~geoutils.Raster.reproject`. -For convenience and consistency, nearly all of these methods can be passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a -**reference to match** during the operation. A **reference {class}`~geoutils.Vector`** enforces a matching of {attr}`~geoutils.Vector.bounds` and/or +For convenience and consistency, nearly all of these methods can be passed solely another {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a +**reference to match** during the operation. A **reference {class}`~geoutils.Vector`** enforces a matching of {attr}`~geoutils.Vector.bounds` and/or {attr}`~geoutils.Vector.crs`, while a **reference {class}`~geoutils.Raster`** can also enforce a matching of {attr}`~geoutils.Raster.res`, depending on the nature of the operation. @@ -83,8 +83,8 @@ print(rast.info()) Additionally, in GeoUtils, **methods that apply to the same georeferencing attributes have consistent naming**1 across {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. -A {func}`~geoutils.Raster.reproject` involves a change in {attr}`~geoutils.Raster.crs` or {attr}`~geoutils.Raster.transform`, while a {func}`~geoutils.Raster.crop` only involves a change -in {attr}`~geoutils.Raster.bounds`. Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, +A {func}`~geoutils.Raster.reproject` involves a change in {attr}`~geoutils.Raster.crs` or {attr}`~geoutils.Raster.transform`, while a {func}`~geoutils.Raster.crop` only involves a change +in {attr}`~geoutils.Raster.bounds`. Using {func}`~geoutils.Raster.polygonize` allows to generate a {class}`~geoutils.Vector` from a {class}`~geoutils.Raster`, and the other way around for {func}`~geoutils.Vector.rasterize`. ```{list-table} @@ -105,18 +105,18 @@ and the other way around for {func}`~geoutils.Vector.rasterize`. - {func}`~geoutils.Vector.rasterize` ``` -All methods can be also be passed any number of georeferencing arguments such as {attr}`~geoutils.Raster.shape` or {attr}`~geoutils.Raster.res`, and will +All methods can be also be passed any number of georeferencing arguments such as {attr}`~geoutils.Raster.shape` or {attr}`~geoutils.Raster.res`, and will naturally deduce others from the input {class}`~geoutils.Raster` or {class}`~geoutils.Vector`, much as in [GDAL](https://gdal.org/)'s command line. -## Higher-level analysis tools +## Higher-level analysis tools -GeoUtils also implements higher-level geospatial analysis tools for both {class}`Rasters` and {class}`Vectors`. For -example, one can compute the distance to a {class}`~geoutils.Vector` geometry, or to target pixels of a {class}`~geoutils.Raster`, using +GeoUtils also implements higher-level geospatial analysis tools for both {class}`Rasters` and {class}`Vectors`. For +example, one can compute the distance to a {class}`~geoutils.Vector` geometry, or to target pixels of a {class}`~geoutils.Raster`, using {func}`~geoutils.Vector.proximity`. -As with the geospatial handling functions previously listed, many analysis functions can take a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a -**reference to utilize** during the operation. In the case of {func}`~geoutils.Vector.proximity`, passing a {class}`~geoutils.Raster` serves as a reference +As with the geospatial handling functions previously listed, many analysis functions can take a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a +**reference to utilize** during the operation. In the case of {func}`~geoutils.Vector.proximity`, passing a {class}`~geoutils.Raster` serves as a reference for the georeferenced grid on which to compute the distances. ```{code-cell} ipython3 @@ -131,17 +131,17 @@ and `rast`'s metadata is sufficient to provide a georeferenced grid for {func}`~ ## Quick plotting -To facilitate the analysis process, GeoUtils includes quick plotting tools that support multiple colorbars and implicitly add layers to the current axis. +To facilitate the analysis process, GeoUtils includes quick plotting tools that support multiple colorbars and implicitly add layers to the current axis. Those are wrapped from {func}`rasterio.plot.show` and {func}`geopandas.GeoDataFrame.plot`, and relay any argument passed. ```{seealso} GeoUtils' plotting tools only aim to get rid off the most common hassles when quickly plotting raster and vector data during analysis. -For advanced plotting tools to create "publication-quality" figures, see [Cartopy](https://scitools.org.uk/cartopy/docs/latest/) or +For advanced plotting tools to create "publication-quality" figures, see [Cartopy](https://scitools.org.uk/cartopy/docs/latest/) or [GeoPlot](https://residentmario.github.io/geoplot/index.html). ``` -The plotting functionality is named {func}`~geoutils.Raster.show` everywhere, for consistency. Here again, a {class}`~geoutils.Raster` or +The plotting functionality is named {func}`~geoutils.Raster.show` everywhere, for consistency. Here again, a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` can be passed as a **reference to match** to ensure all data is displayed on the same grid and projection. ```{code-cell} ipython3 From a4d784af05eae97724c3c2da19b67f11211019b1 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 13:09:25 -0800 Subject: [PATCH 068/184] Force reset cache --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 4159f2be8..6a10ee6a6 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -48,7 +48,7 @@ jobs: path: ${{ env.CONDA }}/envs key: conda-${{ matrix.os }}-${{ matrix.python-version }}-${{ steps.get-date.outputs.month }}-${{ hashFiles('dev-environment.yml') }}-${{ env.CACHE_NUMBER }} env: - CACHE_NUMBER: 1 # Increase this value to reset cache if environment.yml has not changed + CACHE_NUMBER: 2 # Increase this value to reset cache if environment.yml has not changed id: cache # The trick below is necessary because the generic environment file does not specify a Python version, and only From 1da32b7df8396e5386c10b3a8a54b6833521e1ee Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 13:51:52 -0800 Subject: [PATCH 069/184] Update with sphinx-book-theme 1.0 release --- doc/source/conf.py | 39 ++++++++++++++++++++++++++++++++++++++- doc/source/index.md | 3 ++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 34168b40b..7641fd17b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -140,10 +140,47 @@ def setup(app): html_theme_options = { "use_sidenotes": True, "repository_url": "https://github.com/GlacioHack/geoutils", + "repository_branch": "main", "use_repository_button": True, - "logo_only": True, + "use_edit_page_button": True, + "use_source_button": True, + "use_issues_button": True, + "use_download_button": True, + #"logo_only": True, + # "icon_links": [ + # { + # "name": "Conda", + # "url": "https://anaconda.org/conda-forge/geoutils", + # "icon": "https://img.shields.io/conda/vn/conda-forge/geoutils.svg", + # "type": "url", + # }, + # { + # "name": "PyPI", + # "url": "https://pypi.org/project/geoutils/0.0.10/", + # "icon": "https://badge.fury.io/py/geoutils.svg", + # "type": "url", + # }, + # { + # "name": "Testing", + # "url": "https://coveralls.io/github/GlacioHack/geoutils?branch=main", + # "icon": "https://coveralls.io/repos/github/GlacioHack/geoutils/badge.svg?branch=main", + # "type": "url", + # }], } +html_context = { + # ... + "default_mode": "auto" +} + +# Add the search bar to be always displayed (not only on top) +html_sidebars = { + "**": ["navbar-logo.html", + "search-field.html", + "sbt-sidebar-nav.html"] +} + + # html_logo = "path/to/myimage.png" html_static_path = ["_static"] diff --git a/doc/source/index.md b/doc/source/index.md index facc74725..1908bda8e 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -11,7 +11,8 @@ title: GeoUtils :columns: 4 ```{image} ./_static/logo_only_v3.svg -:width: 300px + :width: 300px + :class: dark-light ``` ::: From cd8ebd75945eb57286df7c910a52299e9bd535c9 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 13:59:58 -0800 Subject: [PATCH 070/184] Linting --- doc/source/conf.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 7641fd17b..d3cd56a04 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -146,7 +146,7 @@ def setup(app): "use_source_button": True, "use_issues_button": True, "use_download_button": True, - #"logo_only": True, + # "logo_only": True, # "icon_links": [ # { # "name": "Conda", @@ -169,16 +169,12 @@ def setup(app): } html_context = { - # ... - "default_mode": "auto" + # ... + "default_mode": "auto" } # Add the search bar to be always displayed (not only on top) -html_sidebars = { - "**": ["navbar-logo.html", - "search-field.html", - "sbt-sidebar-nav.html"] -} +html_sidebars = {"**": ["navbar-logo.html", "search-field.html", "sbt-sidebar-nav.html"]} # html_logo = "path/to/myimage.png" From c944ff26a2ba6e7fa3deeb00967e82da850f7de9 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 15:23:37 -0800 Subject: [PATCH 071/184] Force cache reset again --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 6a10ee6a6..ad8b0b02a 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -48,7 +48,7 @@ jobs: path: ${{ env.CONDA }}/envs key: conda-${{ matrix.os }}-${{ matrix.python-version }}-${{ steps.get-date.outputs.month }}-${{ hashFiles('dev-environment.yml') }}-${{ env.CACHE_NUMBER }} env: - CACHE_NUMBER: 2 # Increase this value to reset cache if environment.yml has not changed + CACHE_NUMBER: 100 # Increase this value to reset cache if environment.yml has not changed id: cache # The trick below is necessary because the generic environment file does not specify a Python version, and only From 173febe027b9b951c03a5c5f5329b547eee6b633 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 15:37:49 -0800 Subject: [PATCH 072/184] Fix path to doc --- doc/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/conf.py b/doc/source/conf.py index d3cd56a04..7904231af 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -138,6 +138,7 @@ def setup(app): html_title = "GeoUtils" html_theme_options = { + "path_to_docs": "doc/source", "use_sidenotes": True, "repository_url": "https://github.com/GlacioHack/geoutils", "repository_branch": "main", From 3e225bb9301a64ea6af3fd3c24842d4beb2717a7 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 16:11:09 -0800 Subject: [PATCH 073/184] Force cache reset again --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index ad8b0b02a..d5e43ffc7 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -48,7 +48,7 @@ jobs: path: ${{ env.CONDA }}/envs key: conda-${{ matrix.os }}-${{ matrix.python-version }}-${{ steps.get-date.outputs.month }}-${{ hashFiles('dev-environment.yml') }}-${{ env.CACHE_NUMBER }} env: - CACHE_NUMBER: 100 # Increase this value to reset cache if environment.yml has not changed + CACHE_NUMBER: 101 # Increase this value to reset cache if environment.yml has not changed id: cache # The trick below is necessary because the generic environment file does not specify a Python version, and only From a7386444b96721523844d4071041a74d90694f9e Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 16:22:32 -0800 Subject: [PATCH 074/184] Add graphviz to dev environment for inheritance diagrams --- dev-environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-environment.yml b/dev-environment.yml index 20b05db62..88fd4f65f 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -14,6 +14,7 @@ dependencies: - autovizwidget - sphinx - myst-nb + - graphviz - sphinx-autodoc-typehints - sphinxcontrib-programoutput - numpydoc @@ -32,5 +33,4 @@ dependencies: - pip: - sphinx-design - sphinx-book-theme - - graphviz - -e ./ From 66c20f91fc41f3fed633ae9ce1079678bc76eb6f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 1 Mar 2023 16:48:56 -0800 Subject: [PATCH 075/184] Add pygraphviz to dev environment as well... --- dev-environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev-environment.yml b/dev-environment.yml index 88fd4f65f..92c33be83 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -15,6 +15,7 @@ dependencies: - sphinx - myst-nb - graphviz + - python-graphviz - sphinx-autodoc-typehints - sphinxcontrib-programoutput - numpydoc From 6f37c7c14b5044607b448ddd8e3722dff2c86af6 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 2 Mar 2023 11:53:11 -0800 Subject: [PATCH 076/184] Fix incorrect parameter description in reproject --- geoutils/georaster/raster.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 921de13b1..f92d2eba0 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1702,13 +1702,13 @@ def reproject( To reproject a Raster with different source bounds, first run Raster.crop. - :param dst_ref: a reference raster. If set will use the attributes of this + :param dst_ref: A reference raster. If set will use the attributes of this raster for the output grid. Can be provided as Raster/rasterio data set or as path to the file. :param dst_crs: Specify the Coordinate Reference System or EPSG to reproject to. If dst_ref not set, defaults to self.crs. :param dst_size: Raster size to write to (x, y). Do not use with dst_res. :param dst_bounds: a BoundingBox object or a dictionary containing left, bottom, right, top bounds in the - source CRS. + destination CRS. :param dst_res: Pixel size in units of target CRS. Either 1 value or (xres, yres). Do not use with dst_size. :param dst_nodata: nodata value of the destination. If set to None, will use the same as source, and if source is None, will use GDAL's default. From 326772649c5d6a6c9a3c0bb7e61c1acd4a8bcb60 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 2 Mar 2023 13:58:17 -0800 Subject: [PATCH 077/184] Fix operator in value_to_coords --- geoutils/georaster/raster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index f92d2eba0..21306d3d4 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -2372,7 +2372,7 @@ def format_value(value: Any) -> Any: x, y = projtools.reproject_from_latlon((y, x), self.crs) # type: ignore # Convert coordinates to pixel space - rows, cols = rio.transform.rowcol(self.transform, x, y, op=round) + rows, cols = rio.transform.rowcol(self.transform, x, y, op=np.floor) # Loop over all coordinates passed for k in range(len(rows)): # type: ignore From 147363ebe9c3ce53b55a7a7f9407f3219387295a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 2 Mar 2023 14:36:28 -0800 Subject: [PATCH 078/184] Adapt tests to value_to_coords to check all values on pixel are resolved --- geoutils/georaster/raster.py | 5 +-- tests/test_georaster.py | 66 ++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 31 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 21306d3d4..f158ac09c 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -25,6 +25,7 @@ import rasterio.windows from affine import Affine from mpl_toolkits.axes_grid1 import make_axes_locatable +from math import floor from rasterio.crs import CRS from rasterio.enums import Resampling from rasterio.features import shapes @@ -2372,7 +2373,7 @@ def format_value(value: Any) -> Any: x, y = projtools.reproject_from_latlon((y, x), self.crs) # type: ignore # Convert coordinates to pixel space - rows, cols = rio.transform.rowcol(self.transform, x, y, op=np.floor) + rows, cols = rio.transform.rowcol(self.transform, x, y, op=floor) # Loop over all coordinates passed for k in range(len(rows)): # type: ignore @@ -2577,7 +2578,7 @@ def ij2xy(self, i: ArrayLike, j: ArrayLike, offset: str = "ul") -> tuple[np.ndar """ Return x,y coordinates for a given row, column index pair. - Defaults to upper-left, for which this functionis fully reversible with xy2ij. + Defaults to upper-left, for which this function is fully reversible with xy2ij. :param i: Row (i) index of pixel. :param j: Column (j) index of pixel. diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 6c469c830..f500208a5 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -1425,6 +1425,7 @@ def test_xy2ij_and_interp(self) -> None: i, j = r.xy2ij(x, y) assert img[0, int(i), int(j)] == r.interp_points([(x, y)], order=1)[0] + def test_value_at_coords(self) -> None: """ Test that value at coords works as intended @@ -1435,14 +1436,18 @@ def test_value_at_coords(self) -> None: # Open raster r = gu.Raster(self.landsat_b4_crop_path) - # Random test point that raised an error - itest0 = 118 - jtest0 = 450 - xtest0 = 496930 - ytest0 = 3099170 + # A pixel center where all neighbouring coordinates are different: + # array([[[237, 194, 239], + # [250, 173, 164], + # [255, 192, 128]]] + itest0 = 120 + jtest0 = 451 + # This is the center of the pixel + xtest0 = 496975 + ytest0 = 3099095 # Verify coordinates match indexes - x_out, y_out = r.ij2xy(itest0, jtest0, offset="ul") + x_out, y_out = r.ij2xy(itest0, jtest0, offset="center") assert x_out == xtest0 assert y_out == ytest0 @@ -1451,6 +1456,12 @@ def test_value_at_coords(self) -> None: z = r.data.data[0, itest0, jtest0] assert z == z_val + # Check that the value is the same the other 4 corners of the pixel + assert z == r.value_at_coords(xtest0 + 0.49 * r.res[0], ytest0 - 0.49*r.res[1]) + assert z == r.value_at_coords(xtest0 - 0.49 * r.res[0], ytest0 + 0.49*r.res[1]) + assert z == r.value_at_coords(xtest0 - 0.49 * r.res[0], ytest0 - 0.49*r.res[1]) + assert z == r.value_at_coords(xtest0 + 0.49 * r.res[0], ytest0 + 0.49*r.res[1]) + # -- Tests 2: check arguments work as intended -- # 1/ Lat-lon argument check by getting the coordinates of our last test point @@ -1462,8 +1473,8 @@ def test_value_at_coords(self) -> None: # Get the indexes for the multi-band Raster r_multi = gu.Raster(self.landsat_rgb_path) itest, jtest = r_multi.xy2ij(xtest0, ytest0) - itest = itest[0] - jtest = jtest[0] + itest = int(itest[0]) + jtest = int(jtest[0]) # Extract the values z_band1 = r_multi.value_at_coords(xtest0, ytest0, index=1) z_band2 = r_multi.value_at_coords(xtest0, ytest0, index=2) @@ -1525,6 +1536,24 @@ def test_value_at_coords(self) -> None: assert np.array_equal(windows[0], win0, equal_nan=True) assert np.array_equal(windows[1], win1, equal_nan=True) + # -- Tests 5 -- Check image corners and latlon argument + + # Lower right pixel + x, y = [r.bounds.right - r.res[0]/2, r.bounds.bottom + r.res[1]/2] + lat, lon = pt.reproject_to_latlon([x, y], r.crs) + assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -1, -1] + + # One pixel above + x, y = [r.bounds.right - r.res[0]/2, r.bounds.bottom + 3*r.res[1]/2] + lat, lon = pt.reproject_to_latlon([x, y], r.crs) + assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -2, -1] + + # One pixel left + x, y = [r.bounds.right - 3*r.res[0]/2, r.bounds.bottom + r.res[1]/2] + lat, lon = pt.reproject_to_latlon([x, y], r.crs) + assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -1, -2] + + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_set_nodata(self, example: str) -> None: """ @@ -1979,27 +2008,6 @@ def test_coords(self) -> None: assert yy.min() == pytest.approx(img.bounds.top + hy) assert yy.max() == pytest.approx(img.bounds.bottom - hy) - def test_value_at_coords2(self) -> None: - """ - Check that values returned at selected pixels correspond to what is expected, both for original CRS and lat/lon. - """ - img = gu.Raster(self.landsat_b4_path) - - # Lower right pixel - x, y = [img.bounds.right - img.res[0], img.bounds.bottom + img.res[1]] - lat, lon = pt.reproject_to_latlon([x, y], img.crs) - assert img.value_at_coords(x, y) == img.value_at_coords(lon, lat, latlon=True) == img.data[0, -1, -1] - - # One pixel above - x, y = [img.bounds.right - img.res[0], img.bounds.bottom + 2 * img.res[1]] - lat, lon = pt.reproject_to_latlon([x, y], img.crs) - assert img.value_at_coords(x, y) == img.value_at_coords(lon, lat, latlon=True) == img.data[0, -2, -1] - - # One pixel left - x, y = [img.bounds.right - 2 * img.res[0], img.bounds.bottom + img.res[1]] - lat, lon = pt.reproject_to_latlon([x, y], img.crs) - assert img.value_at_coords(x, y) == img.value_at_coords(lon, lat, latlon=True) == img.data[0, -1, -2] - def test_from_array(self) -> None: # Test that from_array works if nothing is changed From 04b1b7d74d044b2ce4a20c85fc07adc749f4f720 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 2 Mar 2023 14:42:29 -0800 Subject: [PATCH 079/184] Fix cache re-loading error by moving all dependencies to conda-forge channel --- dev-environment.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-environment.yml b/dev-environment.yml index 92c33be83..ec323bd75 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -30,8 +30,8 @@ dependencies: - scikit-image - pyyaml - rioxarray + - sphinx-design + - sphinx-book-theme - pip: - - sphinx-design - - sphinx-book-theme - -e ./ From 6ca58a0c4b0ebf5b2c9fe16ee5e6a4054c173cf0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 2 Mar 2023 15:20:44 -0800 Subject: [PATCH 080/184] Fix parameter description --- geoutils/georaster/raster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index f158ac09c..2a4b935b5 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1710,7 +1710,7 @@ def reproject( :param dst_size: Raster size to write to (x, y). Do not use with dst_res. :param dst_bounds: a BoundingBox object or a dictionary containing left, bottom, right, top bounds in the destination CRS. - :param dst_res: Pixel size in units of target CRS. Either 1 value or (xres, yres). Do not use with dst_size. + :param dst_res: Pixel size in units of destination CRS. Either 1 value or (xres, yres). Do not use with dst_size. :param dst_nodata: nodata value of the destination. If set to None, will use the same as source, and if source is None, will use GDAL's default. :param dst_dtype: Set data type of output. From 39126b6e9773dfe361469aa7c59378430410b06d Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 2 Mar 2023 15:21:33 -0800 Subject: [PATCH 081/184] Linting --- geoutils/georaster/raster.py | 5 +++-- tests/test_georaster.py | 16 +++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 2a4b935b5..67aaf07f0 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -10,6 +10,7 @@ from collections import abc from collections.abc import Iterable from contextlib import ExitStack +from math import floor from numbers import Number from typing import IO, Any, Callable, TypeVar, overload @@ -25,7 +26,6 @@ import rasterio.windows from affine import Affine from mpl_toolkits.axes_grid1 import make_axes_locatable -from math import floor from rasterio.crs import CRS from rasterio.enums import Resampling from rasterio.features import shapes @@ -1710,7 +1710,8 @@ def reproject( :param dst_size: Raster size to write to (x, y). Do not use with dst_res. :param dst_bounds: a BoundingBox object or a dictionary containing left, bottom, right, top bounds in the destination CRS. - :param dst_res: Pixel size in units of destination CRS. Either 1 value or (xres, yres). Do not use with dst_size. + :param dst_res: Pixel size in units of destination CRS. Either 1 value or (xres, yres). Do not use with + dst_size. :param dst_nodata: nodata value of the destination. If set to None, will use the same as source, and if source is None, will use GDAL's default. :param dst_dtype: Set data type of output. diff --git a/tests/test_georaster.py b/tests/test_georaster.py index f500208a5..1eaedf52f 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -1425,7 +1425,6 @@ def test_xy2ij_and_interp(self) -> None: i, j = r.xy2ij(x, y) assert img[0, int(i), int(j)] == r.interp_points([(x, y)], order=1)[0] - def test_value_at_coords(self) -> None: """ Test that value at coords works as intended @@ -1457,10 +1456,10 @@ def test_value_at_coords(self) -> None: assert z == z_val # Check that the value is the same the other 4 corners of the pixel - assert z == r.value_at_coords(xtest0 + 0.49 * r.res[0], ytest0 - 0.49*r.res[1]) - assert z == r.value_at_coords(xtest0 - 0.49 * r.res[0], ytest0 + 0.49*r.res[1]) - assert z == r.value_at_coords(xtest0 - 0.49 * r.res[0], ytest0 - 0.49*r.res[1]) - assert z == r.value_at_coords(xtest0 + 0.49 * r.res[0], ytest0 + 0.49*r.res[1]) + assert z == r.value_at_coords(xtest0 + 0.49 * r.res[0], ytest0 - 0.49 * r.res[1]) + assert z == r.value_at_coords(xtest0 - 0.49 * r.res[0], ytest0 + 0.49 * r.res[1]) + assert z == r.value_at_coords(xtest0 - 0.49 * r.res[0], ytest0 - 0.49 * r.res[1]) + assert z == r.value_at_coords(xtest0 + 0.49 * r.res[0], ytest0 + 0.49 * r.res[1]) # -- Tests 2: check arguments work as intended -- @@ -1539,21 +1538,20 @@ def test_value_at_coords(self) -> None: # -- Tests 5 -- Check image corners and latlon argument # Lower right pixel - x, y = [r.bounds.right - r.res[0]/2, r.bounds.bottom + r.res[1]/2] + x, y = [r.bounds.right - r.res[0] / 2, r.bounds.bottom + r.res[1] / 2] lat, lon = pt.reproject_to_latlon([x, y], r.crs) assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -1, -1] # One pixel above - x, y = [r.bounds.right - r.res[0]/2, r.bounds.bottom + 3*r.res[1]/2] + x, y = [r.bounds.right - r.res[0] / 2, r.bounds.bottom + 3 * r.res[1] / 2] lat, lon = pt.reproject_to_latlon([x, y], r.crs) assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -2, -1] # One pixel left - x, y = [r.bounds.right - 3*r.res[0]/2, r.bounds.bottom + r.res[1]/2] + x, y = [r.bounds.right - 3 * r.res[0] / 2, r.bounds.bottom + r.res[1] / 2] lat, lon = pt.reproject_to_latlon([x, y], r.crs) assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -1, -2] - @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_set_nodata(self, example: str) -> None: """ From 8d768ff13e3f6f65ee50cc51bcee09dd50bfd315 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 3 Mar 2023 17:40:06 -0800 Subject: [PATCH 082/184] Clarify conf and modify title --- doc/source/conf.py | 4 +++- doc/source/index.md | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 7904231af..e2894dcd7 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -44,6 +44,7 @@ # For sphinx design to work properly myst_enable_extensions = ["colon_fence"] +# For myst-nb to find the Jupyter kernel (=environment) to run from nb_kernel_rgx_aliases = {".*geoutils.*": "python3"} intersphinx_mapping = { @@ -169,13 +170,14 @@ def setup(app): # }], } +# For dark mode html_context = { # ... "default_mode": "auto" } # Add the search bar to be always displayed (not only on top) -html_sidebars = {"**": ["navbar-logo.html", "search-field.html", "sbt-sidebar-nav.html"]} +# html_sidebars = {"**": ["navbar-logo.html", "search-field.html", "sbt-sidebar-nav.html"]} # html_logo = "path/to/myimage.png" diff --git a/doc/source/index.md b/doc/source/index.md index 1908bda8e..2a2fc9f23 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -19,8 +19,9 @@ title: GeoUtils :::{grid-item} :columns: 8 :class: sd-fs-3 +:child-align: center -GeoUtils is an **accessible**, **efficient** and **reliable** package to analyze geospatial data. +GeoUtils is a Python package for **accessible**, **efficient** and **reliable** geospatial analysis. :::: **Accessible** owing to its convenient object-based structure, intuitive match-reference operations and familiar geospatial dependencies From 7d34e0a4bc1b306592560c98dbb3415395bbc7f0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Mar 2023 17:11:42 -0800 Subject: [PATCH 083/184] Add option as_array to Vector.create_mask() and index and index assignment to raster --- geoutils/georaster/raster.py | 80 ++++++++++++++++++++++++++++++------ geoutils/geovector.py | 38 ++++++++++++++++- tests/test_georaster.py | 50 +++++++++++++++++++++- tests/test_geovector.py | 19 +++++---- 4 files changed, 163 insertions(+), 24 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index bc0dbb4fc..a9a8dc35f 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -694,16 +694,68 @@ def __str__(self) -> str: return str(s) - def __getitem__(self, value: Raster | Vector | list[float] | tuple[float, ...]) -> np.ndarray | Raster: - """Subset the Raster object: calls the crop method with default parameters""" - - # If input is Mask with the same shape and georeferencing, index in 1D - if isinstance(value, Mask) and self.georeferenced_grid_equal(value): - return self.data[value.data] + def __getitem__(self, index: Raster | Vector | np.ndarray | list[float] | tuple[float, ...]) -> np.ndarray | Raster: + """ + Index or subset the raster: + - If a mask of same georeferencing or array of same shape is passed, return the indexed raster array. + - If a raster, vector, list or tuple of bounds is passed, return the cropped raster matching those objects. + """ + + # If input is Mask with the same shape and georeferencing + if isinstance(index, Mask): + if not self.georeferenced_grid_equal(index): + raise ValueError("Indexing a raster with a mask requires the two being on the same georeferenced grid.") + return self.data[:, index.data.squeeze()] + # If input is array with the same shape + elif isinstance(index, np.ndarray): + if np.shape(index) != self.shape: + raise ValueError("Indexing a raster with an array requires the two having the same shape.") + if str(index.dtype) != "bool": + index = index.astype(bool) + warnings.warn(message="Input array was cast to boolean for indexing.", category=UserWarning) + return self.data[:, index] # Otherwise, subset with crop else: - return self.crop(cropGeom=value, inplace=False) + return self.crop(cropGeom=index, inplace=False) + + def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> None: + """ + Index assignment: if a mask of same georeferencing or array of same shape is passed, + assign values of the raster array. + """ + + # First, check index + + # If input is Mask with the same shape and georeferencing + if isinstance(index, Mask): + if not self.georeferenced_grid_equal(index): + raise ValueError("Indexing a raster with a mask requires the two being on the same georeferenced grid.") + + ind = index.data.squeeze() + # If input is array with the same shape + elif isinstance(index, np.ndarray): + if np.shape(index) != self.shape: + raise ValueError("Indexing a raster with an array requires the two having the same shape.") + if str(index.dtype) != "bool": + ind = index.astype(bool) + warnings.warn(message="Input array was cast to boolean for indexing.", category=UserWarning) + else: + ind = index + # Otherwise, raise an error + else: + raise ValueError("Indexing a raster requires a mask of same georeferenced grid, or a boolean array of same shape.") + + # Second, assign, NumPy will raise appropriate errors itself + + # We need to explicitly load here, as we cannot call the data getter/setter directly + if not self.is_loaded: + self.load() + # Assign the values to the index + self._data[:, ind] = assign + + return None + def raster_equal(self, other: object) -> bool: """Check if a Raster masked array's data (including masked values), mask, fill_value and dtype are equal, @@ -727,8 +779,8 @@ def _overloading_check( self: RasterType, other: RasterType | np.ndarray | Number ) -> tuple[np.ma.masked_array, np.ma.masked_array | Number, float | int | list[int] | list[float] | None]: """ - Before any operation overloading, check input data type and return both self and other data as either \ -a np.ndarray or number, converted to the minimum compatible dtype between both datasets. + Before any operation overloading, check input data type and return both self and other data as either + a np.ndarray or number, converted to the minimum compatible dtype between both datasets. Also returns the best compatible nodata value. The nodata value is set in the following order: @@ -1302,7 +1354,7 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: else: self._data = np.ma.masked_array(data=new_data, fill_value=self.nodata) - def set_mask(self, mask: np.ndarray) -> None: + def set_mask(self, mask: np.ndarray | Mask) -> None: """ Mask all pixels of self.data where `mask` is set to True or > 0. @@ -1312,8 +1364,8 @@ def set_mask(self, mask: np.ndarray) -> None: :param mask: The data mask """ # Check that mask is a Numpy array - if not isinstance(mask, np.ndarray): - raise ValueError("mask must be a numpy array.") + if not isinstance(mask, (np.ndarray, Mask)): + raise ValueError("mask must be a numpy array or a Mask.") # Check that new_data has correct shape if self.is_loaded: @@ -1321,6 +1373,10 @@ def set_mask(self, mask: np.ndarray) -> None: else: raise AttributeError("self.data must be loaded first, with e.g. self.load()") + # If the mask is a Mask instance, pass the boolean array + if isinstance(mask, Mask): + mask = mask.data.filled(False) + if mask.shape != orig_shape: # In case first dimension is empty and other dimensions match -> reshape mask if (orig_shape[0] == 1) & (orig_shape[1:] == mask.shape): diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 47024d739..3e6440f34 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -404,6 +404,34 @@ def reproject( return Vector(self.ds.to_crs(crs=dst_crs)) + @overload + def create_mask( + self, + rst: str | gu.Raster | None = None, + crs: CRS | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, + buffer: int | float | np.number = 0, + *, + as_array: Literal[False] = False + ) -> gu.Mask: + ... + + @overload + def create_mask( + self, + rst: str | gu.Raster | None = None, + crs: CRS | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, + buffer: int | float | np.number = 0, + *, + as_array: Literal[True] + ) -> np.ndarray: + ... + def create_mask( self, rst: str | gu.Raster | None = None, @@ -412,7 +440,8 @@ def create_mask( yres: float | None = None, bounds: tuple[float, float, float, float] | None = None, buffer: int | float | np.number = 0, - ) -> gu.Mask: + as_array: bool = False + ) -> gu.Mask | np.ndarray: """ Rasterize the vector features into a boolean mask matching the georeferencing of a raster. @@ -428,6 +457,7 @@ def create_mask( :param bounds: Output raster bounds (left, bottom, right, top). Only if rst is None (Default to self bounds) :param buffer: Size of buffer to be added around the features, in the raster's projection units. If a negative value is set, will erode the features. + :param as_array: Return mask as a boolean array :returns: A Mask object contain a boolean array """ @@ -507,7 +537,11 @@ def create_mask( if rst is not None: mask = mask.reshape((rst.count, rst.height, rst.width)) # type: ignore - return gu.Raster.from_array(data=mask, transform=transform, crs=crs, nodata=None) + # Return output as mask or as array + if as_array: + return mask.squeeze() + else: + return gu.Raster.from_array(data=mask, transform=transform, crs=crs, nodata=None) def rasterize( self, diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 75e0ede76..1a1d5a9a7 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -738,10 +738,56 @@ def test_masking(self, example: str) -> None: with pytest.raises(ValueError, match="mask must be a numpy array"): r.set_mask(1) - test_data = [[landsat_b4_path, everest_outlines_path], [aster_dem_path, aster_outlines_path]] + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore + def test_getitem_setitem(self, example: str) -> None: + """Test the __getitem__ method ([]) for indexing and __setitem__ for index assignment.""" + + # Open a Raster + rst = gu.Raster(example) + + # Create a boolean array of the same shape, and a mask of the same transform/crs + arr = np.random.randint(low=0, high=2, size=rst.shape, dtype=bool) + mask = gu.Mask.from_array(data=arr, transform=rst.transform, crs=rst.crs) + + # Check that indexing works with both of those + vals_arr = rst[arr] + vals_mask = rst[mask] + + # Those indexing operations should yield the same 1D array of values + assert np.array_equal(vals_mask, vals_arr, equal_nan=True) + + # Now, we test index assignment + rst2 = rst.copy() + + # It should work with a number, or a 1D array of the same length as the indexed one + rst[mask] = 1. + rst2[arr] = np.ones(rst2.shape)[arr] + + # The rasters should be the same + assert rst2.raster_equal(rst) + + # Check that errors are raised for both indexing and index assignment + # An error when the shape is wrong + with pytest.raises(ValueError, match="Indexing a raster with an array requires the two having the same shape."): + rst[arr[:-1, :-1]] + rst[arr[:-1, :-1]] = 1 + # A warning when the array type is not boolean + with pytest.warns(UserWarning, match="Input array was cast to boolean for indexing."): + rst[arr.astype("uint8")] + rst[arr.astype("uint8")] = 1 + # An error when the georeferencing of the Mask does not match + mask.shift(1, 1) + with pytest.raises(ValueError, match="Indexing a raster with a mask requires the two being on the same georeferenced grid."): + rst[mask] + rst[mask] = 1 + # For assignment, an error when the input index is neither a Mask or a boolean ndarray + # (indexing attempts to call crop for differently shaped data) + with pytest.raises(ValueError, match="Indexing a raster requires a mask of same georeferenced grid, or a boolean array of same shape."): + rst["lol"] = 1 + test_data = [[landsat_b4_path, everest_outlines_path], [aster_dem_path, aster_outlines_path]] @pytest.mark.parametrize("data", test_data) # type: ignore - def test_crop_and_getitem(self, data: list[str]) -> None: + def test_crop(self, data: list[str]) -> None: """Test for crop method, also called by square brackets through __getitem__""" raster_path, outlines_path = data diff --git a/tests/test_geovector.py b/tests/test_geovector.py index 27f5cb26f..ad629196b 100644 --- a/tests/test_geovector.py +++ b/tests/test_geovector.py @@ -250,7 +250,7 @@ def test_create_mask(self) -> None: """ # First with given res and bounds -> Should be a 21 x 21 array with 0 everywhere except center pixel vector = self.vector.copy() - out_mask = vector.create_mask(xres=1, bounds=(0, 0, 21, 21)).get_nanarray() + out_mask = vector.create_mask(xres=1, bounds=(0, 0, 21, 21), as_array=True) ref_mask = np.zeros((21, 21), dtype="bool") ref_mask[10, 10] = True assert out_mask.shape == (21, 21) @@ -263,7 +263,7 @@ def test_create_mask(self) -> None: # Then with a gu.Raster as reference, single band rst = gu.Raster.from_array(np.zeros((21, 21)), transform=(1.0, 0.0, 0.0, 0.0, -1.0, 21.0), crs="EPSG:4326") - out_mask = vector.create_mask(rst).get_nanarray() + out_mask = vector.create_mask(rst, as_array=True) assert out_mask.shape == (21, 21) # With gu.Raster, 2 bands -> fails... @@ -271,14 +271,14 @@ def test_create_mask(self) -> None: # out_mask = vector.create_mask(rst) # Test that buffer = 0 works - out_mask_buff = vector.create_mask(rst, buffer=0).get_nanarray() + out_mask_buff = vector.create_mask(rst, buffer=0, as_array=True) assert np.all(ref_mask == out_mask_buff) # Test that buffer > 0 works rst = gu.Raster.from_array(np.zeros((21, 21)), transform=(1.0, 0.0, 0.0, 0.0, -1.0, 21.0), crs="EPSG:4326") - out_mask = vector.create_mask(rst).get_nanarray() + out_mask = vector.create_mask(rst, as_array=True) for buffer in np.arange(1, 8): - out_mask_buff = vector.create_mask(rst, buffer=buffer).get_nanarray() + out_mask_buff = vector.create_mask(rst, buffer=buffer, as_array=True) diff = out_mask_buff & ~out_mask assert np.count_nonzero(diff) > 0 # Difference between masks should always be thinner than buffer + 1 @@ -287,9 +287,9 @@ def test_create_mask(self) -> None: # Test that buffer < 0 works vector_5 = self.vector_5 - out_mask = vector_5.create_mask(rst).get_nanarray() + out_mask = vector_5.create_mask(rst, as_array=True) for buffer in np.arange(-1, -3, -1): - out_mask_buff = vector_5.create_mask(rst, buffer=buffer).get_nanarray() + out_mask_buff = vector_5.create_mask(rst, buffer=buffer, as_array=True) diff = ~out_mask_buff & out_mask assert np.count_nonzero(diff) > 0 # Difference between masks should always be thinner than buffer + 1 @@ -297,7 +297,10 @@ def test_create_mask(self) -> None: assert np.count_nonzero(eroded_diff) == 0 # Check that no warning is raised when creating a mask with a xres not multiple of vector bounds - vector.create_mask(xres=1.01) + mask = vector.create_mask(xres=1.01) + + # Check that by default, create_mask returns a Mask + assert isinstance(mask, gu.Mask) # Check that a warning is raised if the bounds were passed specifically by the user with pytest.warns(UserWarning): From 1dc3ef5758b2c1bd3f36290b7c5e7c96bb3122c6 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Mar 2023 18:11:39 -0800 Subject: [PATCH 084/184] Add tests to pass a Mask to set_mask --- geoutils/georaster/raster.py | 11 ++++++----- tests/test_georaster.py | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index a9a8dc35f..2db64dce9 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1376,15 +1376,16 @@ def set_mask(self, mask: np.ndarray | Mask) -> None: # If the mask is a Mask instance, pass the boolean array if isinstance(mask, Mask): mask = mask.data.filled(False) + mask = mask.squeeze() if mask.shape != orig_shape: - # In case first dimension is empty and other dimensions match -> reshape mask - if (orig_shape[0] == 1) & (orig_shape[1:] == mask.shape): - mask = mask.reshape(orig_shape) + # In case first dimension is more than one (several bands) and other dimensions match + if orig_shape[1:] == mask.shape: + self.data[:, mask > 0] = np.ma.masked else: raise ValueError(f"mask must be of the same shape as existing data: {orig_shape}.") - - self.data[mask > 0] = np.ma.masked + else: + self.data[mask > 0] = np.ma.masked def info(self, stats: bool = False) -> str: """ diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 1a1d5a9a7..b77102f37 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -703,9 +703,21 @@ def test_masking(self, example: str) -> None: """ # Test boolean mask r = gu.Raster(example) - mask = r.data.data == np.min(r.data.data) + # We need to know the existing nodata in case they exist, as set_mask only masks new values + orig_mask = r.data.mask.copy().squeeze() + mask = r.data.data == np.nanmin(r.data) r.set_mask(mask) - assert (np.count_nonzero(mask) > 0) & np.array_equal(mask > 0, r.data.mask) + assert (np.count_nonzero(mask) > 0) & np.array_equal(orig_mask | mask > 0, r.data.mask) + + # Test mask object + r2 = gu.Raster(example) + mask2 = r2 == np.nanmin(r2) + r2.set_mask(mask2) + # Indexing at 0 for the mask in case the data has multiple bands + assert (np.count_nonzero(mask2) > 0) & np.array_equal(orig_mask | mask2.data.filled(False).squeeze(), r2.data.mask[0, :, :]) + # The two last masking (array or Mask) should yield the same result when the data is only 2D + if r.count == 1: + assert np.array_equal(r.data.mask, r2.data.mask) # Test non-boolean mask with values > 0 r = gu.Raster(example) From bbad40691ed211ab5134145c680383f28658830b Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Mar 2023 22:31:00 -0800 Subject: [PATCH 085/184] Make nodata values a tuple and not list to be immutable, and thus hashable --- geoutils/georaster/raster.py | 14 +++++++------- tests/test_georaster.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 2db64dce9..c658ae1c6 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -307,7 +307,7 @@ def __init__( self.tags: dict[str, Any] = {} self._data: np.ma.masked_array | None = None - self._nodata: int | float | list[int] | list[float] | None = nodata + self._nodata: int | float | tuple[int] | tuple[float] | None = nodata self._indexes = indexes self._indexes_loaded: int | tuple[int, ...] | None = None self._masked = masked @@ -1117,7 +1117,7 @@ def is_modified(self) -> bool: return self._is_modified @property - def nodata(self) -> int | float | list[int] | list[float] | None: + def nodata(self) -> int | float | tuple[int] | tuple[float] | None: """ Get nodata value. @@ -1126,7 +1126,7 @@ def nodata(self) -> int | float | list[int] | list[float] | None: return self._nodata @nodata.setter - def nodata(self, new_nodata: int | float | list[int] | list[float] | None) -> None: + def nodata(self, new_nodata: int | float | tuple[int] | tuple[float] | None) -> None: """ Set .nodata and update .data by calling set_nodata() with default parameters. @@ -1143,7 +1143,7 @@ def nodata(self, new_nodata: int | float | list[int] | list[float] | None) -> No self.set_nodata(nodata=new_nodata) def set_nodata( - self, nodata: int | float | list[int] | list[float] | None, update_array: bool = True, update_mask: bool = True + self, nodata: int | float | tuple[int] | tuple[float] | None, update_array: bool = True, update_mask: bool = True ) -> None: """ Set a new nodata value for each band. This updates the old nodata into a new nodata value in the metadata, @@ -1175,10 +1175,10 @@ def set_nodata( raise ValueError("Type of nodata not understood, must be list or float or int") elif (isinstance(nodata, (int, float, np.integer, np.floating))) and self.count > 1: - nodata = [nodata] * self.count + nodata = (nodata,) * self.count elif isinstance(nodata, list) and self.count == 1: - nodata = list(nodata)[0] + nodata = tuple(nodata)[0] elif nodata is None: nodata = None @@ -1427,7 +1427,7 @@ def info(self, stats: bool = False) -> str: else: for b in range(self.count): # try to keep with rasterio convention. - as_str.append(f"Band {b + 1}:") + as_str.append(f"Band {b + 1}:\n") as_str.append(f"[MAXIMUM]: {np.nanmax(self.data[b, :, :]):.2f}\n") as_str.append(f"[MINIMUM]: {np.nanmin(self.data[b, :, :]):.2f}\n") as_str.append(f"[MEDIAN]: {np.ma.median(self.data[b, :, :]):.2f}\n") diff --git a/tests/test_georaster.py b/tests/test_georaster.py index b77102f37..6f60def0f 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -124,7 +124,7 @@ def test_init(self, example: str) -> None: assert np.ma.isMaskedArray(gu.Raster(example, masked=True).data) assert np.ma.isMaskedArray(gu.Raster(example, masked=False).data) - @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore def test_info(self, example: str) -> None: """Test that the information summary is consistent with that of rasterio""" From 4ee41e9f85221d48c7e46be3eebeaf86006afd4b Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Mar 2023 22:42:50 -0800 Subject: [PATCH 086/184] Add test for shift --- tests/test_georaster.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 6f60def0f..79542951f 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -970,6 +970,27 @@ def test_crop(self, data: list[str]) -> None: r_cropped3 = r[outlines] assert list(r_cropped3.bounds) == list(new_bounds) + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) # type: ignore + def test_shift(self, example: str) -> None: + """ Tests shift works as intended""" + + r = gu.Raster(example) + + orig_transform = r.transform + orig_bounds = r.bounds + r.shift(xoff=1, yoff=1) + + # Only bounds should change + assert orig_transform.c + 1 == r.transform.c + assert orig_transform.f + 1 == r.transform.f + for attr in ["a", "b", "d", "e"]: + assert getattr(orig_transform, attr) == getattr(r.transform, attr) + + assert orig_bounds.left + 1 == r.bounds.left + assert orig_bounds.right + 1 == r.bounds.right + assert orig_bounds.bottom + 1 == r.bounds.bottom + assert orig_bounds.top + 1 == r.bounds.top + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_reproject(self, example: str) -> None: warnings.simplefilter("error") From 9594393425f441efbd57cde622a0281ffc9dbcdb Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 7 Mar 2023 23:12:39 -0800 Subject: [PATCH 087/184] Add tests for diff_env_yml --- geoutils/misc.py | 14 ++++++++++---- tests/test_misc.py | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/geoutils/misc.py b/geoutils/misc.py index 77fc9db3b..cc0073682 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -90,21 +90,27 @@ def resampling_method_from_str(method_str: str) -> rio.enums.Resampling: return resampling_method -def diff_environment_yml(fn_env: str, fn_devenv: str, print_dep: str = "both") -> None: +def diff_environment_yml(fn_env: str | dict[str, Any], fn_devenv: str | dict[str, Any], print_dep: str = "both", + input_dict: bool = False) -> None: """ Compute the difference between environment.yml and dev-environment.yml for setup of continuous integration, while checking that all the dependencies listed in environment.yml are also in dev-environment.yml :param fn_env: Filename path to environment.yml :param fn_devenv: Filename path to dev-environment.yml :param print_dep: Whether to print conda differences "conda", pip differences "pip" or both. + :param input_dict: Whether to consider the input as a dict (for testing purposes). """ if not _has_yaml: raise ValueError("Test dependency needed. Install 'pyyaml'") - # Load the yml as dictionaries - yaml_env = yaml.safe_load(open(fn_env)) - yaml_devenv = yaml.safe_load(open(fn_devenv)) + if not input_dict: + # Load the yml as dictionaries + yaml_env = yaml.safe_load(open(fn_env)) + yaml_devenv = yaml.safe_load(open(fn_devenv)) + else: + yaml_env = fn_env + yaml_devenv = fn_devenv # Extract the dependencies values conda_dep_env = yaml_env["dependencies"] diff --git a/tests/test_misc.py b/tests/test_misc.py index 4c3431aa0..1d7d5025c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -103,3 +103,22 @@ def useless_func() -> int: else: with pytest.raises(ValueError, match="^" + text + "$"): useless_func() + + def test_diff_environment_yml(self, capsys) -> None: + + # Test with synthetic environment + env = {"dependencies": ["python==3.9", "numpy", "fiona"]} + devenv = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv"]} + + # This should print the difference between the two + geoutils.misc.diff_environment_yml(env, devenv, input_dict=True, print_dep="conda") + + # Capture the stdout and check it is indeed the right diff + captured = capsys.readouterr().out + assert captured == "opencv\n" + + # This should print the difference including pip + geoutils.misc.diff_environment_yml(env, devenv, input_dict=True, print_dep="both") + + captured = capsys.readouterr().out + assert captured == "opencv\nNone\n" From 3224198db0c86e7d7bfff9067c1f5884feb19035 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 11:45:18 -0800 Subject: [PATCH 088/184] Add test for diff_yml --- tests/test_misc.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_misc.py b/tests/test_misc.py index 1d7d5025c..ac6906b23 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -122,3 +122,11 @@ def test_diff_environment_yml(self, capsys) -> None: captured = capsys.readouterr().out assert captured == "opencv\nNone\n" + + env = {"dependencies": ["python==3.9", "numpy", "fiona"]} + devenv = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils"]}]} + + geoutils.misc.diff_environment_yml(env, devenv, input_dict=True, print_dep="both") + captured = capsys.readouterr().out + + assert captured == "opencv\ngeoutils\n" From 3fe5e7aff8b1314ff7cfbdb50e09f4f530b07c99 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 12:08:29 -0800 Subject: [PATCH 089/184] Linting --- geoutils/georaster/raster.py | 48 ++++++++++++++++++++---------------- geoutils/geovector.py | 38 ++++++++++++++-------------- geoutils/misc.py | 10 +++++--- tests/test_georaster.py | 20 ++++++++++----- tests/test_misc.py | 8 +++--- 5 files changed, 70 insertions(+), 54 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index c658ae1c6..51794a37f 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -276,7 +276,7 @@ def __init__( load_data: bool = False, downsample: AnyNumber = 1, masked: bool = True, - nodata: int | float | list[int] | list[float] | None = None, + nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, attrs: list[str] | None = None, ) -> None: """ @@ -307,7 +307,7 @@ def __init__( self.tags: dict[str, Any] = {} self._data: np.ma.masked_array | None = None - self._nodata: int | float | tuple[int] | tuple[float] | None = nodata + self._nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = nodata self._indexes = indexes self._indexes_loaded: int | tuple[int, ...] | None = None self._masked = masked @@ -557,7 +557,7 @@ def from_array( data: np.ndarray | np.ma.masked_array, transform: tuple[float, ...] | Affine, crs: CRS | int | None, - nodata: int | float | list[int] | list[float] | None = None, + nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, ) -> RasterType: """Create a Raster from a numpy array and some geo-referencing information. @@ -721,8 +721,8 @@ def __getitem__(self, index: Raster | Vector | np.ndarray | list[float] | tuple[ def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> None: """ - Index assignment: if a mask of same georeferencing or array of same shape is passed, - assign values of the raster array. + Index assignment: if a mask of same georeferencing or array of same shape is passed, + assign values of the raster array. """ # First, check index @@ -744,7 +744,9 @@ def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> ind = index # Otherwise, raise an error else: - raise ValueError("Indexing a raster requires a mask of same georeferenced grid, or a boolean array of same shape.") + raise ValueError( + "Indexing a raster requires a mask of same georeferenced grid, or a boolean array of same shape." + ) # Second, assign, NumPy will raise appropriate errors itself @@ -752,11 +754,10 @@ def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> if not self.is_loaded: self.load() # Assign the values to the index - self._data[:, ind] = assign + self._data[:, ind] = assign # type: ignore return None - def raster_equal(self, other: object) -> bool: """Check if a Raster masked array's data (including masked values), mask, fill_value and dtype are equal, as well as the Raster's nodata, and georeferencing.""" @@ -777,7 +778,9 @@ def raster_equal(self, other: object) -> bool: def _overloading_check( self: RasterType, other: RasterType | np.ndarray | Number - ) -> tuple[np.ma.masked_array, np.ma.masked_array | Number, float | int | list[int] | list[float] | None]: + ) -> tuple[ + np.ma.masked_array, np.ma.masked_array | Number, float | int | tuple[int, ...] | tuple[float, ...] | None + ]: """ Before any operation overloading, check input data type and return both self and other data as either a np.ndarray or number, converted to the minimum compatible dtype between both datasets. @@ -1117,7 +1120,7 @@ def is_modified(self) -> bool: return self._is_modified @property - def nodata(self) -> int | float | tuple[int] | tuple[float] | None: + def nodata(self) -> int | float | tuple[int, ...] | tuple[float, ...] | None: """ Get nodata value. @@ -1126,7 +1129,7 @@ def nodata(self) -> int | float | tuple[int] | tuple[float] | None: return self._nodata @nodata.setter - def nodata(self, new_nodata: int | float | tuple[int] | tuple[float] | None) -> None: + def nodata(self, new_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None) -> None: """ Set .nodata and update .data by calling set_nodata() with default parameters. @@ -1143,7 +1146,10 @@ def nodata(self, new_nodata: int | float | tuple[int] | tuple[float] | None) -> self.set_nodata(nodata=new_nodata) def set_nodata( - self, nodata: int | float | tuple[int] | tuple[float] | None, update_array: bool = True, update_mask: bool = True + self, + nodata: int | float | tuple[int, ...] | tuple[float, ...] | None, + update_array: bool = True, + update_mask: bool = True, ) -> None: """ Set a new nodata value for each band. This updates the old nodata into a new nodata value in the metadata, @@ -1171,20 +1177,20 @@ def set_nodata( :param update_mask: Update the old mask by unmasking old nodata and masking new nodata (if array is updated, old nodata are changed to new nodata and thus stay masked) """ - if nodata is not None and not isinstance(nodata, (list, int, float, np.integer, np.floating)): - raise ValueError("Type of nodata not understood, must be list or float or int") + if nodata is not None and not isinstance(nodata, (tuple, int, float, np.integer, np.floating)): + raise ValueError("Type of nodata not understood, must be tuple or float or int") elif (isinstance(nodata, (int, float, np.integer, np.floating))) and self.count > 1: nodata = (nodata,) * self.count - elif isinstance(nodata, list) and self.count == 1: - nodata = tuple(nodata)[0] + elif isinstance(nodata, tuple) and self.count == 1: + nodata = nodata[0] elif nodata is None: nodata = None # Check that nodata has same length as number of bands in self - if isinstance(nodata, list): + if isinstance(nodata, tuple): if len(nodata) != self.count: raise ValueError(f"Length of nodata ({len(nodata)}) incompatible with number of bands ({self.count})") # Check that nodata value is compatible with dtype @@ -1742,8 +1748,8 @@ def reproject( dst_size: tuple[int, int] | None = None, dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, dst_res: float | abc.Iterable[float] | None = None, - dst_nodata: int | float | list[int] | list[float] | None = None, - src_nodata: int | float | list[int] | list[float] | None = None, + dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + src_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, dst_dtype: np.dtype | None = None, resampling: Resampling | str = Resampling.bilinear, silent: bool = False, @@ -3084,8 +3090,8 @@ def reproject( dst_size: tuple[int, int] | None = None, dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, dst_res: float | abc.Iterable[float] | None = None, - dst_nodata: int | float | list[int] | list[float] | None = None, - src_nodata: int | float | list[int] | list[float] | None = None, + dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + src_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, dst_dtype: np.dtype | None = None, resampling: Resampling | str = Resampling.bilinear, silent: bool = False, diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 3e6440f34..5899c6763 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -406,29 +406,29 @@ def reproject( @overload def create_mask( - self, - rst: str | gu.Raster | None = None, - crs: CRS | None = None, - xres: float | None = None, - yres: float | None = None, - bounds: tuple[float, float, float, float] | None = None, - buffer: int | float | np.number = 0, - *, - as_array: Literal[False] = False + self, + rst: str | gu.Raster | None = None, + crs: CRS | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, + buffer: int | float | np.number = 0, + *, + as_array: Literal[False] = False, ) -> gu.Mask: ... @overload def create_mask( - self, - rst: str | gu.Raster | None = None, - crs: CRS | None = None, - xres: float | None = None, - yres: float | None = None, - bounds: tuple[float, float, float, float] | None = None, - buffer: int | float | np.number = 0, - *, - as_array: Literal[True] + self, + rst: str | gu.Raster | None = None, + crs: CRS | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, + buffer: int | float | np.number = 0, + *, + as_array: Literal[True], ) -> np.ndarray: ... @@ -440,7 +440,7 @@ def create_mask( yres: float | None = None, bounds: tuple[float, float, float, float] | None = None, buffer: int | float | np.number = 0, - as_array: bool = False + as_array: bool = False, ) -> gu.Mask | np.ndarray: """ Rasterize the vector features into a boolean mask matching the georeferencing of a raster. diff --git a/geoutils/misc.py b/geoutils/misc.py index cc0073682..0f367eff3 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -3,6 +3,7 @@ import functools import warnings +from typing import Any try: import yaml # type: ignore @@ -90,8 +91,9 @@ def resampling_method_from_str(method_str: str) -> rio.enums.Resampling: return resampling_method -def diff_environment_yml(fn_env: str | dict[str, Any], fn_devenv: str | dict[str, Any], print_dep: str = "both", - input_dict: bool = False) -> None: +def diff_environment_yml( + fn_env: str | dict[str, Any], fn_devenv: str | dict[str, Any], print_dep: str = "both", input_dict: bool = False +) -> None: """ Compute the difference between environment.yml and dev-environment.yml for setup of continuous integration, while checking that all the dependencies listed in environment.yml are also in dev-environment.yml @@ -106,8 +108,8 @@ def diff_environment_yml(fn_env: str | dict[str, Any], fn_devenv: str | dict[str if not input_dict: # Load the yml as dictionaries - yaml_env = yaml.safe_load(open(fn_env)) - yaml_devenv = yaml.safe_load(open(fn_devenv)) + yaml_env = yaml.safe_load(open(fn_env)) # type: ignore + yaml_devenv = yaml.safe_load(open(fn_devenv)) # type: ignore else: yaml_env = fn_env yaml_devenv = fn_devenv diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 79542951f..22b2f1b98 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -714,7 +714,9 @@ def test_masking(self, example: str) -> None: mask2 = r2 == np.nanmin(r2) r2.set_mask(mask2) # Indexing at 0 for the mask in case the data has multiple bands - assert (np.count_nonzero(mask2) > 0) & np.array_equal(orig_mask | mask2.data.filled(False).squeeze(), r2.data.mask[0, :, :]) + assert (np.count_nonzero(mask2) > 0) & np.array_equal( + orig_mask | mask2.data.filled(False).squeeze(), r2.data.mask[0, :, :] + ) # The two last masking (array or Mask) should yield the same result when the data is only 2D if r.count == 1: assert np.array_equal(r.data.mask, r2.data.mask) @@ -772,7 +774,7 @@ def test_getitem_setitem(self, example: str) -> None: rst2 = rst.copy() # It should work with a number, or a 1D array of the same length as the indexed one - rst[mask] = 1. + rst[mask] = 1.0 rst2[arr] = np.ones(rst2.shape)[arr] # The rasters should be the same @@ -789,15 +791,21 @@ def test_getitem_setitem(self, example: str) -> None: rst[arr.astype("uint8")] = 1 # An error when the georeferencing of the Mask does not match mask.shift(1, 1) - with pytest.raises(ValueError, match="Indexing a raster with a mask requires the two being on the same georeferenced grid."): + with pytest.raises( + ValueError, match="Indexing a raster with a mask requires the two being on the same georeferenced grid." + ): rst[mask] rst[mask] = 1 # For assignment, an error when the input index is neither a Mask or a boolean ndarray # (indexing attempts to call crop for differently shaped data) - with pytest.raises(ValueError, match="Indexing a raster requires a mask of same georeferenced grid, or a boolean array of same shape."): + with pytest.raises( + ValueError, + match="Indexing a raster requires a mask of same georeferenced grid, or a boolean array of same shape.", + ): rst["lol"] = 1 test_data = [[landsat_b4_path, everest_outlines_path], [aster_dem_path, aster_outlines_path]] + @pytest.mark.parametrize("data", test_data) # type: ignore def test_crop(self, data: list[str]) -> None: """Test for crop method, also called by square brackets through __getitem__""" @@ -970,9 +978,9 @@ def test_crop(self, data: list[str]) -> None: r_cropped3 = r[outlines] assert list(r_cropped3.bounds) == list(new_bounds) - @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) # type: ignore + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) # type: ignore def test_shift(self, example: str) -> None: - """ Tests shift works as intended""" + """Tests shift works as intended""" r = gu.Raster(example) diff --git a/tests/test_misc.py b/tests/test_misc.py index ac6906b23..0e701c733 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -104,7 +104,7 @@ def useless_func() -> int: with pytest.raises(ValueError, match="^" + text + "$"): useless_func() - def test_diff_environment_yml(self, capsys) -> None: + def test_diff_environment_yml(self, capsys) -> None: # type: ignore # Test with synthetic environment env = {"dependencies": ["python==3.9", "numpy", "fiona"]} @@ -123,10 +123,10 @@ def test_diff_environment_yml(self, capsys) -> None: captured = capsys.readouterr().out assert captured == "opencv\nNone\n" - env = {"dependencies": ["python==3.9", "numpy", "fiona"]} - devenv = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils"]}]} + env2 = {"dependencies": ["python==3.9", "numpy", "fiona"]} + devenv2 = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils"]}]} - geoutils.misc.diff_environment_yml(env, devenv, input_dict=True, print_dep="both") + geoutils.misc.diff_environment_yml(env2, devenv2, input_dict=True, print_dep="both") captured = capsys.readouterr().out assert captured == "opencv\ngeoutils\n" From 3611461c92fdb357da349d7bade167de0f6983ac Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 13:05:03 -0800 Subject: [PATCH 090/184] Refactor crop_geom to remove camel-case, add geovector tests on missing coverage --- .../handling/georeferencing/crop_vector.py | 2 +- geoutils/georaster/raster.py | 12 ++-- geoutils/geovector.py | 44 ++++++------- tests/test_georaster.py | 2 +- tests/test_geovector.py | 62 +++++++++++++++++-- 5 files changed, 84 insertions(+), 38 deletions(-) diff --git a/examples/handling/georeferencing/crop_vector.py b/examples/handling/georeferencing/crop_vector.py index fd3cf2290..9340f9525 100644 --- a/examples/handling/georeferencing/crop_vector.py +++ b/examples/handling/georeferencing/crop_vector.py @@ -44,7 +44,7 @@ # simple :class:`tuple` of bounds. bounds = rast.get_bounds_projected(out_crs=vect.crs) -vect.crop(cropGeom=(bounds.left + 0.5 * (bounds.right - bounds.left), bounds.bottom, bounds.right, bounds.top)) +vect.crop(crop_geom=(bounds.left + 0.5 * (bounds.right - bounds.left), bounds.bottom, bounds.right, bounds.top)) rast.show(ax="new", cmap="Greys_r", alpha=0.7) vect.show(ref_crs=rast, fc="none", ec="tab:purple", lw=3) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 51794a37f..ad4e19491 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -717,7 +717,7 @@ def __getitem__(self, index: Raster | Vector | np.ndarray | list[float] | tuple[ # Otherwise, subset with crop else: - return self.crop(cropGeom=index, inplace=False) + return self.crop(crop_geom=index, inplace=False) def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> None: """ @@ -1646,7 +1646,7 @@ def __array_function__( @overload def crop( self: RasterType, - cropGeom: RasterType | Vector | list[float] | tuple[float, ...], + crop_geom: RasterType | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", *, inplace: Literal[True], @@ -3159,7 +3159,7 @@ def crop( def crop( self: Mask, - cropGeom: Mask | Vector | list[float] | tuple[float, ...], + crop_geom: Mask | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", inplace: bool = True, ) -> Mask | None: @@ -3168,17 +3168,17 @@ def crop( if mode == "match_extent": self.data = self.data.astype("float32") if inplace: - super().crop(cropGeom=cropGeom, mode=mode, inplace=inplace) + super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) self.data = self.data.astype(bool) return None else: - output = super().crop(cropGeom=cropGeom, mode=mode, inplace=inplace) + output = super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) output.data = output.data.astype(bool) return output # Otherwise, run a classic crop else: if not inplace: - return super().crop(cropGeom=cropGeom, mode=mode, inplace=inplace) + return super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) else: return None diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 5899c6763..420cc4515 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -56,7 +56,7 @@ def __init__(self, filename: str | pathlib.Path | gpd.GeoDataFrame): self._ds = filename self._name = None else: - raise ValueError("filename argument not recognised.") + raise TypeError("Filename argument should be a string, Path or geopandas.GeoDataFrame.") self._crs = self.ds.crs @@ -110,7 +110,7 @@ def __str__(self) -> str: def __getitem__(self, value: gu.Raster | Vector | list[float] | tuple[float, ...]) -> Vector: """Subset the Raster object: calls the crop method with default parameters""" - return self.crop(cropGeom=value, clip=False, inplace=False) + return self.crop(crop_geom=value, clip=False, inplace=False) def info(self) -> str: """ @@ -310,7 +310,7 @@ def crop( def crop( self: VectorType, - cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + crop_geom: gu.Raster | Vector | list[float] | tuple[float, ...], clip: bool = False, inplace: bool = True, ) -> VectorType | None: @@ -320,20 +320,20 @@ def crop( Reprojection is done on the fly if georeferenced objects have different projections. - :param cropGeom: Geometry to crop vector to, as either a Raster object, a Vector object, or a list of + :param crop_geom: Geometry to crop vector to, as either a Raster object, a Vector object, or a list of coordinates. If cropGeom is a Raster, crop() will crop to the boundary of the raster as returned by Raster.ds.bounds. If cropGeom is a Vector, crop() will crop to the bounding geometry. If cropGeom is a list of coordinates, the order is assumed to be [xmin, ymin, xmax, ymax]. :param clip: Whether to clip the geometry to the given extent (by default keeps all intersecting). :param inplace: Update the vector inplace or return copy. """ - if isinstance(cropGeom, (gu.Raster, Vector)): + if isinstance(crop_geom, (gu.Raster, Vector)): # For another Vector or Raster, we reproject the bounding box in the same CRS as self - xmin, ymin, xmax, ymax = cropGeom.get_bounds_projected(out_crs=self.crs) - elif isinstance(cropGeom, (list, tuple)): - xmin, ymin, xmax, ymax = cropGeom + xmin, ymin, xmax, ymax = crop_geom.get_bounds_projected(out_crs=self.crs) + elif isinstance(crop_geom, (list, tuple)): + xmin, ymin, xmax, ymax = crop_geom else: - raise ValueError("cropGeom must be a Raster, Vector, or list of coordinates.") + raise TypeError("Crop geometry must be a Raster, Vector, or list of coordinates.") # Need to separate the two options, inplace update if inplace: @@ -434,7 +434,7 @@ def create_mask( def create_mask( self, - rst: str | gu.Raster | None = None, + rst: gu.Raster | None = None, crs: CRS | None = None, xres: float | None = None, yres: float | None = None, @@ -450,7 +450,7 @@ def create_mask( Vector features which fall outside the bounds of the raster file are not written to the new mask file. - :param rst: A Raster object or string to filename + :param rst: A raster :param crs: A pyproj or rasterio CRS object (Default to rst.crs if not None then self.crs) :param xres: Output raster spatial resolution in x. Only is rst is None. :param yres: Output raster spatial resolution in y. Only if rst is None. (Default to xres) @@ -461,16 +461,13 @@ def create_mask( :returns: A Mask object contain a boolean array """ - # If input rst is string, open as Raster - if isinstance(rst, str): - rst = gu.Raster(rst) # type: ignore # If no rst given, use provided dimensions if rst is None: # At minimum, xres must be set if xres is None: - raise ValueError("at least rst or xres must be set") + raise ValueError("At least rst or xres must be set.") if yres is None: yres = xres @@ -491,7 +488,7 @@ def create_mask( if width % 1 != 0 or height % 1 != 0: # Only warn if the bounds were provided, and not derived from the vector if not bounds_shp: - warnings.warn("Bounds not a multiple of xres/yres, use rounded bounds") + warnings.warn("Bounds not a multiple of xres/yres, use rounded bounds.") width = int(np.round(width)) height = int(np.round(height)) @@ -507,7 +504,7 @@ def create_mask( crs = rst.crs bounds = rst.bounds else: - raise ValueError("`rst` must be either a str, geoutils.Raster or None") + raise TypeError("Raster must be a geoutils.Raster or None.") # Copying GeoPandas dataframe before applying changes gdf = self.ds.copy() @@ -522,7 +519,7 @@ def create_mask( # Create a buffer around the features if not isinstance(buffer, (int, float, np.number)): - raise ValueError(f"`buffer` must be a number, currently set to {type(buffer)}") + raise TypeError("Buffer must be a number, currently set to {}.".format(type(buffer).__name__)) if buffer != 0: gdf.geometry = [geom.buffer(buffer) for geom in gdf.geometry] elif buffer == 0: @@ -545,8 +542,8 @@ def create_mask( def rasterize( self, - rst: str | gu.Raster | None = None, - crs: CRS | None = None, + rst: gu.Raster | None = None, + crs: CRS | int | None = None, xres: float | None = None, yres: float | None = None, bounds: tuple[float, float, float, float] | None = None, @@ -576,9 +573,6 @@ def rasterize( :returns: Raster or mask containing the burned geometries """ - # If input rst is string, open as Raster - if isinstance(rst, str): - rst = gu.Raster(rst) # type: ignore if (rst is not None) and (crs is not None): raise ValueError("Only one of rst or crs can be provided.") @@ -598,7 +592,7 @@ def rasterize( # At minimum, xres must be set if xres is None: - raise ValueError("at least rst or xres must be set") + raise ValueError("At least rst or xres must be set.") if yres is None: yres = xres @@ -612,7 +606,7 @@ def rasterize( height = abs((top - bottom) / yres) if width % 1 != 0 or height % 1 != 0: - warnings.warn("Bounds not a multiple of xres/yres, use rounded bounds") + warnings.warn("Bounds not a multiple of xres/yres, use rounded bounds.") width = int(np.round(width)) height = int(np.round(height)) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 22b2f1b98..977d8ec37 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -881,7 +881,7 @@ def test_crop(self, data: list[str]) -> None: r_cropped3 = r.crop(r_cropped_reproj, inplace=False) # Original CRS bounds can be deformed during transformation, but result should be equivalent to this - r_cropped4 = r.crop(cropGeom=r_cropped_reproj.get_bounds_projected(out_crs=r.crs), inplace=False) + r_cropped4 = r.crop(crop_geom=r_cropped_reproj.get_bounds_projected(out_crs=r.crs), inplace=False) assert r_cropped3.raster_equal(r_cropped4) # Check with bracket call diff --git a/tests/test_geovector.py b/tests/test_geovector.py index ad629196b..6ddc10306 100644 --- a/tests/test_geovector.py +++ b/tests/test_geovector.py @@ -7,6 +7,7 @@ import numpy as np import pytest from geopandas.testing import assert_geodataframe_equal +import matplotlib.pyplot as plt from scipy.ndimage import binary_erosion from shapely.geometry.linestring import LineString from shapely.geometry.multilinestring import MultiLineString @@ -46,6 +47,10 @@ def test_init(self) -> None: v2 = gu.Vector(gpd.read_file(self.aster_outlines_path)) assert isinstance(v2, gu.Vector) + # Check errors are raised when filename has wrong type + with pytest.raises(TypeError, match="Filename argument should be a string, Path or geopandas.GeoDataFrame."): + gu.Vector(1) # type: ignore + def test_copy(self) -> None: vector2 = self.glacier_outlines.copy() @@ -129,16 +134,38 @@ def test_rasterize_proj(self) -> None: def test_rasterize_unproj(self) -> None: """Test rasterizing an EPSG:3426 dataset into a projection.""" - v = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) - # Use Web Mercator at 30 m. + vct = gu.Vector(self.everest_outlines_path) + rst = gu.Raster(self.landsat_b4_crop_path) + + # Use Web Mercator at 30 m. # Capture the warning on resolution not matching exactly bounds with pytest.warns(UserWarning): - burned = v.rasterize(xres=30, crs=3857) + burned = vct.rasterize(xres=30, crs=3857) assert burned.shape[0] == 1251 assert burned.shape[1] == 1522 + # Typically, rasterize returns a raster + burned_in2_out1 = vct.rasterize(rst=rst, in_value=2, out_value=1) + assert isinstance(burned_in2_out1, gu.Raster) + + # For an in_value of 1 and out_value of 0 (default), it returns a mask + burned_mask = vct.rasterize(rst=rst, in_value=1) + assert isinstance(burned_mask, gu.Mask) + + # Check that rasterizing with in_value=1 is the same as creating a mask + assert burned_mask.raster_equal(vct.create_mask(rst=rst)) + + # The two rasterization should match + assert np.all(burned_in2_out1[burned_mask] == 2) + assert np.all(burned_in2_out1[~burned_mask] == 1) + + # Check that errors are raised + with pytest.raises(ValueError, match="Only one of rst or crs can be provided."): + vct.rasterize(rst=rst, crs=3857) + + test_data = [[landsat_b4_crop_path, everest_outlines_path], [aster_dem_path, aster_outlines_path]] @pytest.mark.parametrize("data", test_data) # type: ignore @@ -155,7 +182,12 @@ def test_crop(self, data: list[str]) -> None: # Crop outlines_new = outlines.copy() - outlines_new.crop(rst) + outlines_new.crop(crop_geom=rst) + + # Crop by passing bounds + outlines_new_bounds = outlines.copy() + outlines_new_bounds.crop(crop_geom=list(rst.bounds)) + assert_geodataframe_equal(outlines_new.ds, outlines_new_bounds.ds) # Check with bracket call outlines_new2 = outlines_new[rst] @@ -179,6 +211,10 @@ def test_crop(self, data: list[str]) -> None: # Check that some features were indeed removed assert np.sum(~np.array(intersects_old)) > 0 + # Check that error is raised when cropGeom argument is unvalid + with pytest.raises(TypeError, match="Crop geometry must be a Raster, Vector, or list of coordinates."): + outlines.crop(1) # type: ignore + def test_proximity(self) -> None: """ The core functionality is already tested against GDAL in test_raster: just verify the vector-specific behaviour. @@ -302,6 +338,18 @@ def test_create_mask(self) -> None: # Check that by default, create_mask returns a Mask assert isinstance(mask, gu.Mask) + # Check that an error is raised if xres is not passed + with pytest.raises(ValueError, match="At least rst or xres must be set."): + vector.create_mask() + + # Check that an error is raised if buffer is the wrong type + with pytest.raises(TypeError, match="Buffer must be a number, currently set to str."): + vector.create_mask(rst, buffer="lol") # type: ignore + + # If the raster has the wrong type + with pytest.raises(TypeError, match="Raster must be a geoutils.Raster or None."): + vector.create_mask("lol") # type: ignore + # Check that a warning is raised if the bounds were passed specifically by the user with pytest.warns(UserWarning): vector.create_mask(xres=1.01, bounds=(0, 0, 21, 21)) @@ -415,7 +463,7 @@ def test_buffer_metric(self) -> None: # And this time, it is the reprojected GeoDataFrame that should almost match (within a tolerance of 10e-06) assert all(direct_gpd_buffer.ds.geom_almost_equals(two_squares_geographic_buffered_reproj.ds)) - def test_buffer_without_overlap(self) -> None: + def test_buffer_without_overlap(self, monkeypatch) -> None: """ Check that non-overlapping buffer feature works. Does not work on simple geometries, so test on MultiPolygon. Yet, very simple geometries yield unexpected results, as is the case for the second test case here. @@ -478,3 +526,7 @@ def test_buffer_without_overlap(self) -> None: mask_buffer = two_squares.create_mask(xres=0.1, bounds=(0, 0, 21, 21), buffer=buffer_size) mask_nobuffer = two_squares.create_mask(xres=0.1, bounds=(0, 0, 21, 21)) assert np.all(mask_nobuffer | mask_nonoverlap == mask_buffer) + + # Check that plotting runs without errors and close it + monkeypatch.setattr(plt, 'show', lambda: None) + two_squares.buffer_without_overlap(buffer_size, plot=True) From 9a61aab5bb00f33b00a26b35e18fcffe25e3b170 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 14:15:23 -0800 Subject: [PATCH 091/184] Fix cropGeom refactoring --- geoutils/georaster/raster.py | 16 +++---- tests/test_georaster.py | 84 ++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index ad4e19491..bc7baf688 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1656,7 +1656,7 @@ def crop( @overload def crop( self: RasterType, - cropGeom: RasterType | Vector | list[float] | tuple[float, ...], + crop_geom: RasterType | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", *, inplace: Literal[False], @@ -1666,7 +1666,7 @@ def crop( @overload def crop( self: RasterType, - cropGeom: RasterType | Vector | list[float] | tuple[float, ...], + crop_geom: RasterType | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", inplace: bool = True, ) -> RasterType | None: @@ -1674,7 +1674,7 @@ def crop( def crop( self: RasterType, - cropGeom: RasterType | Vector | list[float] | tuple[float, ...], + crop_geom: RasterType | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", inplace: bool = True, ) -> RasterType | None: @@ -1683,7 +1683,7 @@ def crop( Reprojection is done on the fly if georeferenced objects have different projections. - :param cropGeom: Geometry to crop raster to, as either a Raster object, a Vector object, or a list of + :param crop_geom: Geometry to crop raster to, as either a Raster object, a Vector object, or a list of coordinates. If cropGeom is a Raster, crop() will crop to the boundary of the raster as returned by Raster.ds.bounds. If cropGeom is a Vector, crop() will crop to the bounding geometry. If cropGeom is a list of coordinates, the order is assumed to be [xmin, ymin, xmax, ymax]. @@ -1699,11 +1699,11 @@ def crop( "match_pixel", ], "mode must be one of 'match_pixel', 'match_extent'" - if isinstance(cropGeom, (Raster, Vector)): + if isinstance(crop_geom, (Raster, Vector)): # For another Vector or Raster, we reproject the bounding box in the same CRS as self - xmin, ymin, xmax, ymax = cropGeom.get_bounds_projected(out_crs=self.crs) - elif isinstance(cropGeom, (list, tuple)): - xmin, ymin, xmax, ymax = cropGeom + xmin, ymin, xmax, ymax = crop_geom.get_bounds_projected(out_crs=self.crs) + elif isinstance(crop_geom, (list, tuple)): + xmin, ymin, xmax, ymax = crop_geom else: raise ValueError("cropGeom must be a Raster, Vector, or list of coordinates.") diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 977d8ec37..37a6b4f6d 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -813,62 +813,62 @@ def test_crop(self, data: list[str]) -> None: raster_path, outlines_path = data r = gu.Raster(raster_path) - # -- Test with cropGeom being a list/tuple -- ## - cropGeom: list[float] = list(r.bounds) + # -- Test with crop_geom being a list/tuple -- ## + crop_geom: list[float] = list(r.bounds) # Test with same bounds -> should be the same # - cropGeom2 = [cropGeom[0], cropGeom[1], cropGeom[2], cropGeom[3]] - r_cropped = r.crop(cropGeom2, inplace=False) + crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3]] + r_cropped = r.crop(crop_geom2, inplace=False) assert r_cropped.raster_equal(r) # Test with bracket call - r_cropped_getitem = r[cropGeom2] + r_cropped_getitem = r[crop_geom2] assert r_cropped_getitem.raster_equal(r_cropped) # - Test cropping each side by a random integer of pixels - # rand_int = np.random.randint(1, min(r.shape) - 1) # Left - cropGeom2 = [cropGeom[0] + rand_int * r.res[0], cropGeom[1], cropGeom[2], cropGeom[3]] - r_cropped = r.crop(cropGeom2, inplace=False) - assert list(r_cropped.bounds) == cropGeom2 + crop_geom2 = [crop_geom[0] + rand_int * r.res[0], crop_geom[1], crop_geom[2], crop_geom[3]] + r_cropped = r.crop(crop_geom2, inplace=False) + assert list(r_cropped.bounds) == crop_geom2 assert np.array_equal(r.data[:, :, rand_int:].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, :, rand_int:].mask, r_cropped.data.mask) # Right - cropGeom2 = [cropGeom[0], cropGeom[1], cropGeom[2] - rand_int * r.res[0], cropGeom[3]] - r_cropped = r.crop(cropGeom2, inplace=False) - assert list(r_cropped.bounds) == cropGeom2 + crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2] - rand_int * r.res[0], crop_geom[3]] + r_cropped = r.crop(crop_geom2, inplace=False) + assert list(r_cropped.bounds) == crop_geom2 assert np.array_equal(r.data[:, :, :-rand_int].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, :, :-rand_int].mask, r_cropped.data.mask) # Bottom - cropGeom2 = [cropGeom[0], cropGeom[1] + rand_int * abs(r.res[1]), cropGeom[2], cropGeom[3]] - r_cropped = r.crop(cropGeom2, inplace=False) - assert list(r_cropped.bounds) == cropGeom2 + crop_geom2 = [crop_geom[0], crop_geom[1] + rand_int * abs(r.res[1]), crop_geom[2], crop_geom[3]] + r_cropped = r.crop(crop_geom2, inplace=False) + assert list(r_cropped.bounds) == crop_geom2 assert np.array_equal(r.data[:, :-rand_int, :].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, :-rand_int, :].mask, r_cropped.data.mask) # Top - cropGeom2 = [cropGeom[0], cropGeom[1], cropGeom[2], cropGeom[3] - rand_int * abs(r.res[1])] - r_cropped = r.crop(cropGeom2, inplace=False) - assert list(r_cropped.bounds) == cropGeom2 + crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3] - rand_int * abs(r.res[1])] + r_cropped = r.crop(crop_geom2, inplace=False) + assert list(r_cropped.bounds) == crop_geom2 assert np.array_equal(r.data[:, rand_int:, :].data, r_cropped.data, equal_nan=True) assert np.array_equal(r.data[:, rand_int:, :].mask, r_cropped.data.mask) # Same but tuple - cropGeom3: tuple[float, float, float, float] = ( - cropGeom[0], - cropGeom[1], - cropGeom[2], - cropGeom[3] - rand_int * r.res[0], + crop_geom3: tuple[float, float, float, float] = ( + crop_geom[0], + crop_geom[1], + crop_geom[2], + crop_geom[3] - rand_int * r.res[0], ) - r_cropped = r.crop(cropGeom3, inplace=False) - assert list(r_cropped.bounds) == list(cropGeom3) + r_cropped = r.crop(crop_geom3, inplace=False) + assert list(r_cropped.bounds) == list(crop_geom3) assert np.array_equal(r.data[:, rand_int:, :].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, rand_int:, :].mask, r_cropped.data.mask) - # -- Test with CropGeom being a Raster -- # + # -- Test with crop_geom being a Raster -- # r_cropped2 = r.crop(r_cropped, inplace=False) assert r_cropped2.raster_equal(r_cropped) @@ -897,29 +897,29 @@ def test_crop(self, data: list[str]) -> None: rand_float = np.random.randint(1, min(r.shape) - 1) + 0.25 # left - cropGeom2 = [cropGeom[0] + rand_float * r.res[0], cropGeom[1], cropGeom[2], cropGeom[3]] - r_cropped = r.crop(cropGeom2, inplace=False) + crop_geom2 = [crop_geom[0] + rand_float * r.res[0], crop_geom[1], crop_geom[2], crop_geom[3]] + r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[1] - (r_cropped.bounds.right - r_cropped.bounds.left) / r.res[0] == int(rand_float) assert np.array_equal(r.data[:, :, int(rand_float) :].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, :, int(rand_float) :].mask, r_cropped.data.mask) # right - cropGeom2 = [cropGeom[0], cropGeom[1], cropGeom[2] - rand_float * r.res[0], cropGeom[3]] - r_cropped = r.crop(cropGeom2, inplace=False) + crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2] - rand_float * r.res[0], crop_geom[3]] + r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[1] - (r_cropped.bounds.right - r_cropped.bounds.left) / r.res[0] == int(rand_float) assert np.array_equal(r.data[:, :, : -int(rand_float)].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, :, : -int(rand_float)].mask, r_cropped.data.mask) # bottom - cropGeom2 = [cropGeom[0], cropGeom[1] + rand_float * abs(r.res[1]), cropGeom[2], cropGeom[3]] - r_cropped = r.crop(cropGeom2, inplace=False) + crop_geom2 = [crop_geom[0], crop_geom[1] + rand_float * abs(r.res[1]), crop_geom[2], crop_geom[3]] + r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[0] - (r_cropped.bounds.top - r_cropped.bounds.bottom) / r.res[1] == int(rand_float) assert np.array_equal(r.data[:, : -int(rand_float), :].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, : -int(rand_float), :].mask, r_cropped.data.mask) # top - cropGeom2 = [cropGeom[0], cropGeom[1], cropGeom[2], cropGeom[3] - rand_float * abs(r.res[1])] - r_cropped = r.crop(cropGeom2, inplace=False) + crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3] - rand_float * abs(r.res[1])] + r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[0] - (r_cropped.bounds.top - r_cropped.bounds.bottom) / r.res[1] == int(rand_float) assert np.array_equal(r.data[:, int(rand_float) :, :].data, r_cropped.data.data, equal_nan=True) assert np.array_equal(r.data[:, int(rand_float) :, :].mask, r_cropped.data.mask) @@ -928,11 +928,11 @@ def test_crop(self, data: list[str]) -> None: # Test all sides at once, with rand_float less than half the smallest extent # The cropped extent should exactly match the requested extent, res will be changed accordingly rand_float = np.random.randint(1, min(r.shape) / 2 - 1) + 0.25 - cropGeom2 = [ - cropGeom[0] + rand_float * r.res[0], - cropGeom[1] + rand_float * abs(r.res[1]), - cropGeom[2] - rand_float * r.res[0], - cropGeom[3] - rand_float * abs(r.res[1]), + crop_geom2 = [ + crop_geom[0] + rand_float * r.res[0], + crop_geom[1] + rand_float * abs(r.res[1]), + crop_geom[2] - rand_float * r.res[0], + crop_geom[3] - rand_float * abs(r.res[1]), ] # Filter warning about dst_nodata not set in reprojection (because match_extent triggers reproject) @@ -940,9 +940,9 @@ def test_crop(self, data: list[str]) -> None: warnings.filterwarnings( "ignore", category=UserWarning, message="For reprojection, dst_nodata must be set.*" ) - r_cropped = r.crop(cropGeom2, inplace=False, mode="match_extent") + r_cropped = r.crop(crop_geom2, inplace=False, mode="match_extent") - assert list(r_cropped.bounds) == cropGeom2 + assert list(r_cropped.bounds) == crop_geom2 # The change in resolution should be less than what would occur with +/- 1 pixel assert np.all( abs(np.array(r.res) - np.array(r_cropped.res)) < np.array(r.res) / np.array(r_cropped.shape)[::-1] @@ -956,7 +956,7 @@ def test_crop(self, data: list[str]) -> None: r_cropped2 = r.crop(r_cropped, inplace=False, mode="match_extent") assert r_cropped2.raster_equal(r_cropped) - # -- Test with CropGeom being a Vector -- # + # -- Test with crop_geom being a Vector -- # outlines = gu.Vector(outlines_path) # First, we reproject manually the outline @@ -1824,7 +1824,7 @@ def test_set_nodata(self, example: str) -> None: # -- Fifth, let's check that errors are raised when they should -- # A ValueError if input nodata is neither a list, tuple, integer, floating - with pytest.raises(ValueError, match="Type of nodata not understood, must be list or float or int"): + with pytest.raises(ValueError, match="Type of nodata not understood, must be tuple or float or int"): r.set_nodata(nodata="this_should_not_work") # type: ignore # A ValueError if nodata value is incompatible with dtype From 62f3430c9ff7249b81597583910de99c2a3d41a5 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 15:06:17 -0800 Subject: [PATCH 092/184] Streamline main description of functions in raster --- geoutils/georaster/raster.py | 307 ++++++++++++++++------------------- tests/test_georaster.py | 6 +- 2 files changed, 147 insertions(+), 166 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index bc7baf688..e9d9c4305 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -224,44 +224,20 @@ def _load_rio( class Raster: """ - Create a Raster object from a rasterio-supported raster dataset. - - If not otherwise specified below, attribute types and contents correspond - to the attributes defined by rasterio. - - Attributes: - filename_or_dataset : str - The path/filename of the loaded, file, only set if a disk-based file is read in. - data : np.array - Loaded image. Dimensions correspond to (count, height, width). - count_loaded : int - Number of bands loaded into .data - indexes_loaded : tuple - The indexes of the bands of the opened dataset loaded into .data. - is_loaded : bool - True if the image data have been loaded into this Raster. - - bounds - - crs - - driver - - dtypes - - height - - name - - nodata - - res - - shape - - transform - - width + The georeferenced raster. + + Main attributes: + data: :class:`np.ndarray` + Data array of the raster, with dimensions corresponding to (count, height, width). + transform: :class:`affine.Affine` + Geotransform of the raster. + crs: :class:`pyproj.crs.CRS` + Coordinate reference system of the raster. + nodata: :class:`int` or :class:`float` + Nodata value of the raster. + + All other attributes are derivatives of those attributes, or read from the file on disk. + See the API for more details. """ def __init__( @@ -280,19 +256,19 @@ def __init__( attrs: list[str] | None = None, ) -> None: """ - Load a rasterio-supported dataset, given a filename. + Instantiate a raster from a filename or rasterio dataset. - :param filename_or_dataset: The filename of the dataset. + :param filename_or_dataset: The filename or dataset. - :param indexes: The band(s) to load into the object. Default is to load all bands. + :param indexes: The band(s) to load into the object. Default loads all bands. - :param load_data: Load the raster data into the object. Default is False. + :param load_data: Whether to load the array during instantiation. Default is False. - :param downsample: Reduce the size of the image loaded by this factor. Default is 1. + :param downsample: Downsample the array once loaded by a round factor. Default is no downsampling. - :param masked: the data is loaded as a masked array, with no data values masked. Default is True. + :param masked: Whether to load the array as a NumPy masked-array, with nodata values masked. Default is True. - :param nodata: nodata to be used (overwrites the metadata). Default is None, i.e. reads from metadata. + :param nodata: The nodata value to be used (overwrites the metadata). Default reads from metadata. :param attrs: Additional attributes from rasterio's DataReader class to add to the Raster object. Default list is set by geoutils.georaster.raster._default_rio_attrs, i.e. @@ -421,14 +397,14 @@ def __init__( @property def count_on_disk(self) -> None | int: - """Return the count of bands on disk if it exists.""" + """The count of bands on disk if it exists.""" if self._disk_shape is not None: return self._disk_shape[0] return None @property def count(self) -> int: - """Return the count of bands loaded in memory if they are, otherwise the one on disk.""" + """The count of bands loaded in memory if they are, otherwise the one on disk.""" if self.is_loaded: return int(self.data.shape[0]) # This can only happen if data is not loaded, with a DatasetReader on disk is open, never returns None @@ -436,21 +412,21 @@ def count(self) -> int: @property def height(self) -> int: - """Return the height of the Raster in pixels.""" + """The height of the raster in pixels.""" if not self.is_loaded: return self._disk_shape[1] # type: ignore return int(self.data.shape[1]) @property def width(self) -> int: - """Return the width of the Raster in pixels.""" + """The width of the raster in pixels.""" if not self.is_loaded: return self._disk_shape[2] # type: ignore return int(self.data.shape[2]) @property def shape(self) -> tuple[int, int]: - """Return a (height, width) tuple of the data shape in pixels.""" + """The shape (i.e., height, width) of the raster in pixels.""" # If a downsampling argument was defined but data not loaded yet if self._out_shape is not None and not self.is_loaded: return self._out_shape[1], self._out_shape[2] @@ -461,36 +437,36 @@ def shape(self) -> tuple[int, int]: @property def res(self) -> tuple[float | int, float | int]: - """Return the X/Y resolution in georeferenced units of the Raster.""" + """The resolution (X, Y) of the raster in georeferenced units.""" return self.transform[0], abs(self.transform[4]) @property def bounds(self) -> rio.coords.BoundingBox: - """Return the bounding coordinates of the Raster.""" + """The bounding coordinates of the raster.""" return rio.coords.BoundingBox(*rio.transform.array_bounds(self.height, self.width, self.transform)) @property def is_loaded(self) -> bool: - """Return False if the data attribute is None, and True if data exists.""" + """Whether the raster array is loaded.""" return self._data is not None @property def dtypes(self) -> tuple[str, ...]: - """Return the string representations of the data types for each band.""" + """The data type for each raster band (string representation).""" if not self.is_loaded and self._disk_dtypes is not None: return self._disk_dtypes return (str(self.data.dtype),) * self.count @property def indexes_on_disk(self) -> None | tuple[int, ...]: - """Return the indexes of bands on disk if it exists.""" + """The indexes of bands on disk if it exists.""" if self._disk_indexes is not None: return self._disk_indexes return None @property def indexes(self) -> tuple[int, ...]: - """Return the indexes of bands loaded in memory if they are, otherwise on disk.""" + """The indexes of bands loaded in memory if they are, otherwise on disk.""" if self._indexes is not None and not self.is_loaded: if isinstance(self._indexes, int): return (self._indexes,) @@ -505,7 +481,7 @@ def indexes(self) -> tuple[int, ...]: def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: """ - Load the data from disk. + Load the raster array from disk. :param kwargs: Optional keyword arguments sent to '_load_rio()'. :param indexes: The band(s) to load. Note that rasterio begins counting at 1, not 0. @@ -559,7 +535,7 @@ def from_array( crs: CRS | int | None, nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, ) -> RasterType: - """Create a Raster from a numpy array and some geo-referencing information. + """Create a raster from a numpy array and the georeferencing information. :param data: data array @@ -604,7 +580,7 @@ def from_array( return cls({"data": data, "transform": transform, "crs": crs, "nodata": nodata}) def to_rio_dataset(self) -> rio.io.DatasetReader: - """Export to a rasterio in-memory dataset.""" + """Export to a Rasterio in-memory dataset.""" # Create handle to new memory file mfh = rio.io.MemoryFile() @@ -628,7 +604,7 @@ def to_rio_dataset(self) -> rio.io.DatasetReader: return mfh.open() def __repr__(self) -> str: - """Convert formal Raster string representation.""" + """Convert to raster string representation.""" # If data not loaded, return and string and avoid calling .data if not self.is_loaded: @@ -659,7 +635,7 @@ def __repr__(self) -> str: # return s def _repr_html_(self) -> str: - """Convert to HTML representation for notebooks and docs""" + """Convert to raster HTML representation for documentation.""" # If data not loaded, return and string and avoid calling .data if not self.is_loaded: @@ -685,7 +661,7 @@ def _repr_html_(self) -> str: return str(s) def __str__(self) -> str: - """Provide simplified string for print() about Raster.""" + """Provide simplified raster string representation for print().""" if not self.is_loaded: s = "not_loaded" @@ -696,7 +672,9 @@ def __str__(self) -> str: def __getitem__(self, index: Raster | Vector | np.ndarray | list[float] | tuple[float, ...]) -> np.ndarray | Raster: """ - Index or subset the raster: + Index or subset the raster. + + Two cases: - If a mask of same georeferencing or array of same shape is passed, return the indexed raster array. - If a raster, vector, list or tuple of bounds is passed, return the cropped raster matching those objects. """ @@ -721,8 +699,10 @@ def __getitem__(self, index: Raster | Vector | np.ndarray | list[float] | tuple[ def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> None: """ - Index assignment: if a mask of same georeferencing or array of same shape is passed, - assign values of the raster array. + Perform index assignment on the raster. + + If a mask of same georeferencing or array of same shape is passed, + it is used as index to assign values to the raster array. """ # First, check index @@ -759,8 +739,13 @@ def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> return None def raster_equal(self, other: object) -> bool: - """Check if a Raster masked array's data (including masked values), mask, fill_value and dtype are equal, - as well as the Raster's nodata, and georeferencing.""" + """ + Check if two rasters are equal. + + This means that are equal: + - The raster's masked array's data (including masked values), mask, fill_value and dtype, + - The raster's transform, crs and nodata values. + """ if not isinstance(other, type(self)): # TODO: Possibly add equals to SatelliteImage? return NotImplemented @@ -866,7 +851,8 @@ def _overloading_check( def __add__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Sum up the data of two rasters or a raster and a numpy array, or a raster and single number. + Sum two rasters, or a raster and a numpy array, or a raster and single number. + If other is a Raster, it must have the same data.shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. @@ -1076,7 +1062,7 @@ def astype(self, dtype: DTypeLike, inplace: Literal[True]) -> None: def astype(self, dtype: DTypeLike, inplace: bool = False) -> Raster | None: """ - Converts the data type of a Raster object. + Convert the data type of a raster. :param dtype: Any numpy dtype or string accepted by numpy.astype :param inplace: Set to True to modify the raster in place. @@ -1103,9 +1089,9 @@ def astype(self, dtype: DTypeLike, inplace: bool = False) -> Raster | None: @property def is_modified(self) -> bool: - """Check whether file has been modified since it was created/opened. + """Whether the array has been modified since it was loaded from disk. - :returns: True if Raster has been modified. + :returns: True if raster has been modified. """ if not self.is_loaded: @@ -1122,7 +1108,7 @@ def is_modified(self) -> bool: @property def nodata(self) -> int | float | tuple[int, ...] | tuple[float, ...] | None: """ - Get nodata value. + Nodata value of the raster. :returns: Nodata value """ @@ -1263,7 +1249,7 @@ def set_nodata( @property def data(self) -> np.ma.masked_array: """ - Get data. + Array of the raster. :returns: data array. @@ -1362,10 +1348,12 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: def set_mask(self, mask: np.ndarray | Mask) -> None: """ - Mask all pixels of self.data where `mask` is set to True or > 0. + Set a mask on the raster array. - Masking is performed in place. - `mask` must have the same shape as loaded data, unless the first dimension is 1, then it is ignored. + All pixels where `mask` is set to True or > 0 will be masked (in addition to previously masked pixels). + + Masking is performed in place. The mask must have the same shape as loaded data, + unless the first dimension is 1, then it is ignored. :param mask: The data mask """ @@ -1395,7 +1383,7 @@ def set_mask(self, mask: np.ndarray | Mask) -> None: def info(self, stats: bool = False) -> str: """ - Returns string of information about the raster (filename, coordinate system, number of columns/rows, etc.). + Summarize information about the raster. :param stats: Add statistics for each band of the dataset (max, min, median, mean, std. dev.). Default is to not calculate statistics. @@ -1444,9 +1432,9 @@ def info(self, stats: bool = False) -> str: def copy(self: RasterType, new_array: np.ndarray | None = None) -> RasterType: """ - Copy the Raster object in memory + Copy the raster in-memory. - :param new_array: New array to use for the copied Raster + :param new_array: New array to use for the copied raster :return: """ @@ -1461,9 +1449,9 @@ def copy(self: RasterType, new_array: np.ndarray | None = None) -> RasterType: def georeferenced_grid_equal(self: RasterType, raster: RasterType) -> bool: """ - Check that grid shape, geotransform and CRS are equal. + Check that raster shape, geotransform and CRS are equal. - :param raster: Another Raster object + :param raster: Another raster object :return: Whether the two objects have the same georeferenced grid """ @@ -1480,11 +1468,13 @@ def get_nanarray(self, return_mask: Literal[True]) -> tuple[np.ndarray, np.ndarr def get_nanarray(self, return_mask: bool = False) -> np.ndarray | tuple[np.ndarray, np.ndarray]: """ - Method to return the squeezed masked array filled with NaNs and associated squeezed mask, both as a copy. + Get NaN array from the raster. + + Optionally, return the mask from the masked array. - :param return_mask: Whether to return the mask of valid data + :param return_mask: Whether to return the mask of valid data. - :returns Array with masked data as NaNs, (Optional) Mask of valid data + :returns Array with masked data as NaNs, (Optional) Mask of valid data. """ # Cast array to float32 is its dtype is integer (cannot be filled with NaNs otherwise) @@ -1679,7 +1669,9 @@ def crop( inplace: bool = True, ) -> RasterType | None: """ - Crop the Raster to a given extent, or bounds of a raster or vector. + Crop the raster to a given extent. + + Match-reference: a reference raster or vector can be passed to match bounds during cropping. Reprojection is done on the fly if georeferenced objects have different projections. @@ -1757,9 +1749,11 @@ def reproject( memory_limit: int = 64, ) -> RasterType: """ - Reproject raster to a specified grid. + Reproject raster to a different geotransform (resolution, bounds) and/or coordinate reference system (CRS). + + Match-reference: a reference raster can be passed to match resolution, bounds and CRS during reprojection. - The output grid can either be given by a reference Raster (using `dst_ref`), + The output grid can either be given by a reference raster (using `dst_ref`), or by manually providing the output CRS (`dst_crs`), dimensions (`dst_size`), resolution (with `dst_size`) and/or bounds (`dst_bounds`). Any resampling algorithm implemented in rasterio can be used. @@ -2006,10 +2000,10 @@ def reproject( def shift(self, xoff: float, yoff: float) -> None: """ - Translate the Raster by a given x,y offset. + Shift the raster by a X/Y offset. - :param xoff: Translation x offset. - :param yoff: Translation y offset. + :param xoff: Translation X offset. + :param yoff: Translation Y offset. """ @@ -2031,7 +2025,8 @@ def save( gcps: list[tuple[float, ...]] | None = None, gcps_crs: CRS | None = None, ) -> None: - """Write the Raster to a geo-referenced file. + """ + Write the raster to file. Given a filename to save the Raster to, create a geo-referenced file on disk which contains the contents of self.data. @@ -2127,7 +2122,8 @@ def save( dst.gcps = (rio_gcps, gcps_crs) def to_xarray(self, name: str | None = None) -> rioxarray.DataArray: - """Convert this Raster into an xarray DataArray using rioxarray. + """ + Convert raster to a xarray.DataArray. This method uses rioxarray to generate a DataArray with associated geo-referencing information. @@ -2150,7 +2146,7 @@ def to_xarray(self, name: str | None = None) -> rioxarray.DataArray: def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coords.BoundingBox: """ - Return self's bounds in the given CRS. + Get raster bounds projected in a specified CRS. :param out_crs: Output CRS :param densify_pts: Maximum points to be added between image corners to account for non linear edges. @@ -2217,7 +2213,8 @@ def show( return_axes: bool = False, **kwargs: Any, ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: - r"""Show/display the raster, with axes in projection of image. + r""" + Plot the raster, with axes in projection of image. This method is a wrapper to rasterio.plot.show. Any \*\*kwargs which you give this method will be passed to it. @@ -2347,13 +2344,11 @@ def value_at_coords( boundless: bool = True, reducer_function: Callable[[np.ndarray], float] = np.ma.mean, ) -> Any: - """ Extract the pixel value(s) at the nearest pixel(s) from the specified coordinates. - - Extract pixel value of each band in dataset at the specified - coordinates. Alternatively, if band is specified, return only that - band's pixel value. + """ + Extract raster values at the nearest pixels from the specified coordinates, + or reduced (e.g., mean of pixels) from a window around the specified coordinates. - Optionally, return mean of pixels within a square window. + By default, samples pixel value of each band. Can be passed a band index to sample from. :param x: x (or longitude) coordinate(s). :param y: y (or latitude) coordinate(s). @@ -2524,13 +2519,13 @@ def format_value(value: Any) -> Any: def coords(self, offset: str = "corner", grid: bool = True) -> tuple[np.ndarray, np.ndarray]: """ - Get x,y coordinates of all pixels in the raster. + Get coordinates (x,y) of all pixels in the raster. :param offset: coordinate type. If 'corner', returns corner coordinates of pixels. If 'center', returns center coordinates. Default is corner. :param grid: Return grid - :returns x,y: numpy arrays corresponding to the x,y coordinates of each pixel. + :returns x,y: Arrays of the (x,y) coordinates. """ assert offset in [ "corner", @@ -2561,7 +2556,7 @@ def xy2ij( shift_area_or_point: bool = False, ) -> tuple[np.ndarray, np.ndarray]: """ - Return row, column indices for a given x,y coordinate pair. + Get indexes (row,column) of coordinates (x,y). Optionally, user can enforce the interpretation of pixel coordinates in self.tags['AREA_OR_POINT'] to ensure that the indexes of points represent the right location. See parameter description of @@ -2574,7 +2569,7 @@ def xy2ij( :param shift_area_or_point: Shifts index to center pixel coordinates if GDAL's AREA_OR_POINT attribute (in self.tags) is "Point", keeps the corner pixel coordinate for "Area". - :returns i, j: indices of x,y in the image. + :returns i, j: indices of (x,y) in the image. """ # Input checks if op not in [np.float32, np.float64, float]: @@ -2642,7 +2637,7 @@ def xy2ij( def ij2xy(self, i: ArrayLike, j: ArrayLike, offset: str = "ul") -> tuple[np.ndarray, np.ndarray]: """ - Return x,y coordinates for a given row, column index pair. + Get coordinates (x,y) of indexes (row,column). Defaults to upper-left, for which this function is fully reversible with xy2ij. @@ -2688,7 +2683,7 @@ def interp_points( ) -> np.ndarray: """ - Interpolate raster values at a given point, or sets of points. + Interpolate raster values at a set of points. Optionally, user can enforce the interpretation of pixel coordinates in self.tags['AREA_OR_POINT'] to ensure that the interpolation of points is done at the right location. See parameter description @@ -2734,40 +2729,10 @@ def interp_points( return rpts - # #TODO: right now it's a loop... could add multiprocessing parallel loop outside, - # # but such a method probably exists already within scipy/other interpolation packages? - # for pt in pts: - # i,j = self.xy2ij(pt[0],pt[1]) - # if self.outside_image(i,j, index=True): - # rpts.append(np.nan) - # continue - # else: - # x = xx[j - nsize:j + nsize + 1] - # y = yy[i - nsize:i + nsize + 1] - # - # #TODO: read only that window? - # z = self.data[band-1, i - nsize:i + nsize + 1, j - nsize:j + nsize + 1] - # if mode in ['linear', 'cubic', 'quintic', 'nearest']: - # X, Y = np.meshgrid(x, y) - # try: - # zint = griddata((X.flatten(), Y.flatten()), z.flatten(), list(pt), method=mode)[0] - # except: - # #TODO: currently fails when dealing with the edges - # print('Interpolation failed for:') - # print(pt) - # print(i,j) - # print(x) - # print(y) - # print(z) - # zint = np.nan - # else: - # zint = np.nanmean(z.flatten()) - # rpts.append(zint) - # rpts = np.array(rpts) def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int | None = None) -> list[Raster]: """ - Split the bands into separate copied rasters. + Split the bands into separate rasters. :param copy: Copy the bands or return slices of the original data. :param subset: Optional. A subset of band indices to extract (from 1 to self.count). Defaults to all. @@ -2826,7 +2791,9 @@ def to_points( self, subset: float | int = 1, as_array: bool = False, pixel_offset: Literal["center", "corner"] = "center" ) -> np.ndarray | Vector: """ - Subset a point cloud of the raster. + Convert raster to points. + + Optionally, randomly subset the raster. If 'subset' is either 1 or is equal to the pixel count, all points are returned in order. If 'subset' is smaller than 1 (for fractions) or the pixel count, a random sample is returned. @@ -2893,43 +2860,43 @@ def to_points( return points def polygonize( - self, in_value: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = "all" + self, target_values: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = "all" ) -> Vector: """ - Return a GeoDataFrame polygonized from a raster. + Polygonize the raster into a vector. - :param in_value: Value or range of values of the raster from which to + :param target_values: Value or range of values of the raster from which to create geometries (Default is 1). If 'all', all unique pixel values of the raster are used. :returns: Vector containing the polygonized geometries. """ # mask a unique value set by a number - if isinstance(in_value, Number): + if isinstance(target_values, Number): - if np.sum(self.data == in_value) == 0: - raise ValueError(f"no pixel with in_value {in_value}") + if np.sum(self.data == target_values) == 0: + raise ValueError(f"no pixel with in_value {target_values}") - bool_msk = np.array(self.data == in_value).astype(np.uint8) + bool_msk = np.array(self.data == target_values).astype(np.uint8) # mask values within boundaries set by a tuple - elif isinstance(in_value, tuple): + elif isinstance(target_values, tuple): - if np.sum((self.data > in_value[0]) & (self.data < in_value[1])) == 0: - raise ValueError(f"no pixel with in_value between {in_value[0]} and {in_value[1]}") + if np.sum((self.data > target_values[0]) & (self.data < target_values[1])) == 0: + raise ValueError(f"no pixel with in_value between {target_values[0]} and {target_values[1]}") - bool_msk = ((self.data > in_value[0]) & (self.data < in_value[1])).astype(np.uint8) + bool_msk = ((self.data > target_values[0]) & (self.data < target_values[1])).astype(np.uint8) # mask specific values set by a sequence - elif isinstance(in_value, list) or isinstance(in_value, np.ndarray): + elif isinstance(target_values, list) or isinstance(target_values, np.ndarray): - if np.sum(np.isin(self.data, in_value)) == 0: - raise ValueError("no pixel with in_value " + ", ".join(map("{}".format, in_value))) + if np.sum(np.isin(self.data, target_values)) == 0: + raise ValueError("no pixel with in_value " + ", ".join(map("{}".format, target_values))) - bool_msk = np.isin(self.data, in_value).astype("uint8") + bool_msk = np.isin(self.data, target_values).astype("uint8") # mask all valid values - elif in_value == "all": + elif target_values == "all": bool_msk = (~self.data.mask).astype("uint8") @@ -2972,8 +2939,7 @@ def proximity( distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", ) -> Raster: """ - Proximity to this Raster's target pixels, or to a Vector's geometry, computed for each cell of this Raster's - grid. + Compute the proximity distance to the raster target pixels, or to a vector geometry on the raster grid. When passing a Vector, by default, the boundary of the geometry will be used. The full geometry can be used by passing "geometry", or any lower dimensional geometry attribute such as "centroid", "envelope" or "convex_hull". @@ -3002,8 +2968,23 @@ def proximity( return self.copy(new_array=proximity) -# Subclass Mask for manipulating boolean Rasters class Mask(Raster): + """ + The georeferenced mask. + + Subclasses :class:`geoutils.Raster`. + + Main attributes: + data: :class:`np.ndarray` + Boolean data array of the mask, with dimensions corresponding to (height, width). + transform: :class:`affine.Affine` + Geotransform of the raster. + crs: :class:`pyproj.crs.CRS` + Coordinate reference system of the raster. + + All other attributes are derivatives of those attributes, or read from the file on disk. + See the API for more details. + """ def __init__( self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], @@ -3037,7 +3018,7 @@ def __init__( self._dtypes = (bool,) def __repr__(self) -> str: - """Convert formal Raster string representation.""" + """Convert mask to string representation.""" # If data not loaded, return and string and avoid calling .data if not self.is_loaded: @@ -3060,7 +3041,7 @@ def __repr__(self) -> str: return str(s) def _repr_html_(self) -> str: - """Convert to HTML representation for notebooks and docs""" + """Convert mask to HTML representation for notebooks and docs""" # If data not loaded, return and string and avoid calling .data if not self.is_loaded: @@ -3183,17 +3164,17 @@ def crop( return None def polygonize( - self, in_value: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = 1 + self, target_values: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = 1 ) -> Vector: # If target values is passed but does not correspond to 0 or 1, raise a warning - if not isinstance(in_value, (int, np.integer, float, np.floating)) or in_value not in [0, 1]: + if not isinstance(target_values, (int, np.integer, float, np.floating)) or target_values not in [0, 1]: warnings.warn("In-value converted to 1 for polygonizing boolean mask.") - in_value = 1 + target_values = 1 # Convert to unsigned integer and pass to parent method self._data = self.data.astype("uint8") - return super().polygonize(in_value=in_value) + return super().polygonize(target_values=target_values) def proximity( self, diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 37a6b4f6d..d75d2548e 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2263,7 +2263,7 @@ def test_polygonize(self, example: str) -> None: pixel_area = np.count_nonzero(img.data == value) * img.res[0] * img.res[1] # Polygonize the raster for this value, and compute the total area - polygonized = img.polygonize(in_value=value) + polygonized = img.polygonize(target_values=value) polygon_area = polygonized.ds.area.sum() # Check that these two areas are approximately equal @@ -2282,11 +2282,11 @@ def test_polygonize(self, example: str) -> None: ) img_dtype = img_dtype.astype(dtype) value = np.unique(img_dtype)[0] - img_dtype.polygonize(in_value=value) + img_dtype.polygonize(target_values=value) # And for a boolean object, such as a mask mask = img > value - mask.polygonize(in_value=1) + mask.polygonize(target_values=1) # Test all options, with both an artificial Raster (that has all target values) and a real Raster @pytest.mark.parametrize("distunits", ["GEO", "PIXEL"]) # type: ignore From 6b715f4ea07f1071df406f241dc3ab8dfbcf2aa5 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 15:23:44 -0800 Subject: [PATCH 093/184] Streamline the function and attribute descriptions in geovector --- geoutils/georaster/raster.py | 17 +++---- geoutils/geovector.py | 97 +++++++++++++++++++++--------------- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index e9d9c4305..4438d2023 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -274,8 +274,6 @@ def __init__( Default list is set by geoutils.georaster.raster._default_rio_attrs, i.e. ['bounds', 'count', 'crs', 'driver', 'dtypes', 'height', 'indexes', 'name', 'nodata', 'res', 'shape', 'transform', 'width'] - if no attrs are specified, these will be added. - - :return: A Raster object """ self.driver: str | None = None self.name: str | None = None @@ -604,7 +602,7 @@ def to_rio_dataset(self) -> rio.io.DatasetReader: return mfh.open() def __repr__(self) -> str: - """Convert to raster string representation.""" + """Convert raster to string representation.""" # If data not loaded, return and string and avoid calling .data if not self.is_loaded: @@ -635,7 +633,7 @@ def __repr__(self) -> str: # return s def _repr_html_(self) -> str: - """Convert to raster HTML representation for documentation.""" + """Convert raster to HTML representation for documentation.""" # If data not loaded, return and string and avoid calling .data if not self.is_loaded: @@ -1753,12 +1751,11 @@ def reproject( Match-reference: a reference raster can be passed to match resolution, bounds and CRS during reprojection. - The output grid can either be given by a reference raster (using `dst_ref`), - or by manually providing the output CRS (`dst_crs`), dimensions (`dst_size`), - resolution (with `dst_size`) and/or bounds (`dst_bounds`). - Any resampling algorithm implemented in rasterio can be used. + Alternatively, the destination resolution, bounds and CRS can be passed individually. - To reproject a Raster with different source bounds, first run Raster.crop. + Any resampling algorithm implemented in Rasterio can be passed as a string. + See https://rasterio.readthedocs.io/en/stable/api/rasterio.enums.html#rasterio.enums.Resampling + for the full list. :param dst_ref: A reference raster. If set will use the attributes of this raster for the output grid. Can be provided as Raster/rasterio data set or as path to the file. @@ -2939,7 +2936,7 @@ def proximity( distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", ) -> Raster: """ - Compute the proximity distance to the raster target pixels, or to a vector geometry on the raster grid. + Compute proximity distances to the raster target pixels, or to a vector geometry on the raster grid. When passing a Vector, by default, the boundary of the geometry will be used. The full geometry can be used by passing "geometry", or any lower dimensional geometry attribute such as "centroid", "envelope" or "convex_hull". diff --git a/geoutils/geovector.py b/geoutils/geovector.py index 420cc4515..a2854c687 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -33,27 +33,36 @@ class Vector: """ - Create a Vector object from a fiona-supported vector dataset. + The georeferenced vector + + Main attributes: + ds: :class:`geopandas.GeoDataFrame` + Geodataframe of the vector. + crs: :class:`pyproj.crs.CRS` + Coordinate reference system of the vector. + bounds: :class:`rio.coords.BoundingBox` + Coordinate bounds of the vector. + + All other attributes are derivatives of those attributes, or read from the file on disk. + See the API for more details. """ - def __init__(self, filename: str | pathlib.Path | gpd.GeoDataFrame): + def __init__(self, filename_or_dataset: str | pathlib.Path | gpd.GeoDataFrame): """ - Load a fiona-supported dataset, given a filename. + Instantiate a vector from a filename or geopandas geodataframe. - :param filename: The filename or GeoDataFrame of the dataset. - - :return: A Vector object + :param filename_or_dataset: The filename or geodataframe. """ - if isinstance(filename, (str, pathlib.Path)): + if isinstance(filename_or_dataset, (str, pathlib.Path)): with warnings.catch_warnings(): # This warning shows up in numpy 1.21 (2021-07-09) warnings.filterwarnings("ignore", ".*attribute.*array_interface.*Polygon.*") - ds = gpd.read_file(filename) + ds = gpd.read_file(filename_or_dataset) self._ds = ds - self._name: str | gpd.GeoDataFrame | None = filename - elif isinstance(filename, gpd.GeoDataFrame): - self._ds = filename + self._name: str | gpd.GeoDataFrame | None = filename_or_dataset + elif isinstance(filename_or_dataset, gpd.GeoDataFrame): + self._ds = filename_or_dataset self._name = None else: raise TypeError("Filename argument should be a string, Path or geopandas.GeoDataFrame.") @@ -61,7 +70,8 @@ def __init__(self, filename: str | pathlib.Path | gpd.GeoDataFrame): self._crs = self.ds.crs def __repr__(self) -> str: - """Representation of vector""" + """Convert vector to string representation.""" + # Get the representation of ds str_ds = "\n ".join(self.__str__().split("\n")) @@ -81,7 +91,7 @@ def __repr__(self) -> str: return s def _repr_html_(self) -> str: - """Representation of vector for html""" + """Convert vector to HTML string representation for documentation.""" str_ds = "\n ".join(self.ds.__str__().split("\n")) @@ -103,18 +113,20 @@ def _repr_html_(self) -> str: return s def __str__(self) -> str: - """Provide string of information about Raster.""" + """Provide simplified vector string representation for print().""" return str(self.ds.__str__()) def __getitem__(self, value: gu.Raster | Vector | list[float] | tuple[float, ...]) -> Vector: - """Subset the Raster object: calls the crop method with default parameters""" + """ + Index or subset the vector. + """ return self.crop(crop_geom=value, clip=False, inplace=False) def info(self) -> str: """ - Returns string of information about the vector (filename, coordinate system, number of layers, features, etc.). + Summarize information about the vector. :returns: text information about Vector attributes. :rtype: str @@ -143,7 +155,8 @@ def show( return_axes: bool = False, **kwargs: Any, ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: - r"""Show/display the vector, with axes in projection of image. + r""" + Plot the vector. This method is a wrapper to geopandas.GeoDataFrame.plot. Any \*\*kwargs which you give this method will be passed to it. @@ -241,23 +254,23 @@ def show( @property def bounds(self) -> rio.coords.BoundingBox: - """Get a bounding box of the total bounds of the Vector.""" + """Bounding box of the vector.""" return rio.coords.BoundingBox(*self.ds.total_bounds) @property def crs(self) -> rio.crs.CRS: - """Ensure CRS cannot be set""" + """Coordinate reference system of the vector.""" self._crs = self.ds.crs return self._crs @property def ds(self) -> gpd.GeoDataFrame: - """Getter method for dataset""" + """Geodataframe of the vector.""" return self._ds @ds.setter def ds(self, new_ds: gpd.GeoDataFrame | gpd.GeoSeries) -> None: - """Setting a new GeoDataFrame""" + """Set a new geodataframe.""" if isinstance(new_ds, gpd.GeoDataFrame): self._ds = new_ds @@ -268,11 +281,11 @@ def ds(self, new_ds: gpd.GeoDataFrame | gpd.GeoSeries) -> None: @property def name(self) -> str | None: - """Filename if exists""" + """Name on disk, if it exists.""" return self._name def copy(self: VectorType) -> VectorType: - """Return a copy of the Vector.""" + """Return a copy of the vector.""" # Utilise the copy method of GeoPandas new_vector = self.__new__(type(self)) new_vector.__init__(self.ds.copy()) # type: ignore @@ -315,8 +328,11 @@ def crop( inplace: bool = True, ) -> VectorType | None: """ - Crop the Vector to given extent, or bounds of a raster or vector. Optionally, clip geometries - to that extent (by default keeps all intersecting). + Crop the vector to given extent. + + Match-reference: a reference raster or vector can be passed to match bounds during cropping. + + Optionally, clip geometries to that extent (by default keeps all intersecting). Reprojection is done on the fly if georeferenced objects have different projections. @@ -355,10 +371,11 @@ def reproject( dst_crs: CRS | str | int | None = None, ) -> Vector: """ - Reproject vector to a specified CRS. + Reproject vector to a specified coordinate reference system. + + Match-reference: a reference raster or vector can be passed to match CRS during reprojection. - The output CRS can either be given by a reference Raster or Vector (using `dst_ref`) or by manually - providing the output CRS (`dst_crs`) + Alternatively, a CRS can be passed in many formats (string, EPSG integer, or CRS). To reproject a Vector with different source bounds, first run Vector.crop(). @@ -443,7 +460,9 @@ def create_mask( as_array: bool = False, ) -> gu.Mask | np.ndarray: """ - Rasterize the vector features into a boolean mask matching the georeferencing of a raster. + Create a mask from the vector features. + + Match-reference: a raster can be passed to match its resolution, bounds and CRS when creating the mask. Alternatively, user can specify a grid to rasterize on using xres, yres, bounds and crs. Only xres is mandatory, by default yres=xres and bounds/crs are set to self's. @@ -551,9 +570,9 @@ def rasterize( out_value: int | float = 0, ) -> gu.Raster | gu.Mask: """ - Return an array with input geometries burned in. + Rasterize vector to a raster or mask, with input geometries burned in. - By default, output raster has the extent/dimensions of the provided raster file. + Match-reference: a raster can be passed to match its resolution, bounds and CRS when rasterizing the vector. Alternatively, user can specify a grid to rasterize on using xres, yres, bounds and crs. Only xres is mandatory, by default yres=xres and bounds/crs are set to self's. @@ -677,7 +696,7 @@ def from_bounds_projected( def query(self: Vector, expression: str, inplace: bool = False) -> Vector | None: """ - Query the Vector dataset with a valid Pandas expression. + Query the vector with a valid Pandas expression. :param expression: A python-like expression to evaluate. Example: "col1 > col2". :param inplace: Whether the query should modify the data in place or return a modified copy. @@ -702,11 +721,11 @@ def proximity( distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", ) -> gu.Raster: """ - Proximity to this Vector's geometry computed for each cell of a Raster grid, or for a grid size with - this Vector's extent. + Compute proximity distances to this vector's geometry. + + Match-reference: a raster can be passed to match its resolution, bounds and CRS for computing proximity distances. - If passed, the Raster georeferenced grid will be used to burn the proximity. If not, a grid size can be - passed to create a georeferenced grid with the bounds and CRS of this Vector. + Alternatively, a grid size can be passed to create a georeferenced grid with the bounds and CRS of this vector. By default, the boundary of the Vector's geometry will be used. The full geometry can be used by passing "geometry", @@ -747,7 +766,7 @@ def proximity( def buffer_metric(self, buffer_size: float) -> Vector: """ - Buffer the vector in a metric system (UTM only). + Buffer the vector features in a local metric system (UTM only). The outlines are projected to a local UTM, then reverted to the original projection after buffering. @@ -788,7 +807,7 @@ def buffer_metric(self, buffer_size: float) -> Vector: def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coords.BoundingBox: """ - Return self's bounds in the given CRS. + Get vector bounds projected in a specified CRS. :param out_crs: Output CRS :param densify_pts: Maximum points to be added between image corners to account for nonlinear edges. @@ -802,7 +821,7 @@ def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coo def buffer_without_overlap(self, buffer_size: int | float, metric: bool = True, plot: bool = False) -> Vector: """ - Returns a Vector object containing self's geometries extended by a buffer, without overlapping each other. + Buffer the vector geometries without overlapping each other. The algorithm is based upon this tutorial: https://statnmap.com/2020-07-31-buffer-area-for-nearest-neighbour/. The buffered polygons are created using Voronoi polygons in order to delineate the "area of influence" From 0d20da0646177808c1a2c19121c2598f48d05b45 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 15:38:32 -0800 Subject: [PATCH 094/184] Linting --- geoutils/georaster/raster.py | 8 ++++---- geoutils/geovector.py | 12 ++++++------ tests/test_geovector.py | 17 ++++++++--------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 4438d2023..a443f1aec 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -2726,7 +2726,6 @@ def interp_points( return rpts - def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int | None = None) -> list[Raster]: """ Split the bands into separate rasters. @@ -2982,6 +2981,7 @@ class Mask(Raster): All other attributes are derivatives of those attributes, or read from the file on disk. See the API for more details. """ + def __init__( self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], @@ -3109,7 +3109,7 @@ def reproject( @overload def crop( self: Mask, - cropGeom: Mask | Vector | list[float] | tuple[float, ...], + crop_geom: Mask | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", *, inplace: Literal[True], @@ -3119,7 +3119,7 @@ def crop( @overload def crop( self: Mask, - cropGeom: Mask | Vector | list[float] | tuple[float, ...], + crop_geom: Mask | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", *, inplace: Literal[False], @@ -3129,7 +3129,7 @@ def crop( @overload def crop( self: Mask, - cropGeom: Mask | Vector | list[float] | tuple[float, ...], + crop_geom: Mask | Vector | list[float] | tuple[float, ...], mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", inplace: bool = True, ) -> Mask | None: diff --git a/geoutils/geovector.py b/geoutils/geovector.py index a2854c687..43dc9d4c2 100644 --- a/geoutils/geovector.py +++ b/geoutils/geovector.py @@ -72,7 +72,6 @@ def __init__(self, filename_or_dataset: str | pathlib.Path | gpd.GeoDataFrame): def __repr__(self) -> str: """Convert vector to string representation.""" - # Get the representation of ds str_ds = "\n ".join(self.__str__().split("\n")) @@ -294,7 +293,7 @@ def copy(self: VectorType) -> VectorType: @overload def crop( self: VectorType, - cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + crop_geom: gu.Raster | Vector | list[float] | tuple[float, ...], clip: bool, *, inplace: Literal[True], @@ -304,7 +303,7 @@ def crop( @overload def crop( self: VectorType, - cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + crop_geom: gu.Raster | Vector | list[float] | tuple[float, ...], clip: bool, *, inplace: Literal[False], @@ -314,7 +313,7 @@ def crop( @overload def crop( self: VectorType, - cropGeom: gu.Raster | Vector | list[float] | tuple[float, ...], + crop_geom: gu.Raster | Vector | list[float] | tuple[float, ...], clip: bool, *, inplace: bool = True, @@ -538,7 +537,7 @@ def create_mask( # Create a buffer around the features if not isinstance(buffer, (int, float, np.number)): - raise TypeError("Buffer must be a number, currently set to {}.".format(type(buffer).__name__)) + raise TypeError(f"Buffer must be a number, currently set to {type(buffer).__name__}.") if buffer != 0: gdf.geometry = [geom.buffer(buffer) for geom in gdf.geometry] elif buffer == 0: @@ -723,7 +722,8 @@ def proximity( """ Compute proximity distances to this vector's geometry. - Match-reference: a raster can be passed to match its resolution, bounds and CRS for computing proximity distances. + Match-reference: a raster can be passed to match its resolution, bounds and CRS for computing + proximity distances. Alternatively, a grid size can be passed to create a georeferenced grid with the bounds and CRS of this vector. diff --git a/tests/test_geovector.py b/tests/test_geovector.py index 6ddc10306..d426741c0 100644 --- a/tests/test_geovector.py +++ b/tests/test_geovector.py @@ -4,10 +4,10 @@ import re import geopandas as gpd +import matplotlib.pyplot as plt import numpy as np import pytest from geopandas.testing import assert_geodataframe_equal -import matplotlib.pyplot as plt from scipy.ndimage import binary_erosion from shapely.geometry.linestring import LineString from shapely.geometry.multilinestring import MultiLineString @@ -49,7 +49,7 @@ def test_init(self) -> None: # Check errors are raised when filename has wrong type with pytest.raises(TypeError, match="Filename argument should be a string, Path or geopandas.GeoDataFrame."): - gu.Vector(1) # type: ignore + gu.Vector(1) # type: ignore def test_copy(self) -> None: @@ -165,7 +165,6 @@ def test_rasterize_unproj(self) -> None: with pytest.raises(ValueError, match="Only one of rst or crs can be provided."): vct.rasterize(rst=rst, crs=3857) - test_data = [[landsat_b4_crop_path, everest_outlines_path], [aster_dem_path, aster_outlines_path]] @pytest.mark.parametrize("data", test_data) # type: ignore @@ -211,9 +210,9 @@ def test_crop(self, data: list[str]) -> None: # Check that some features were indeed removed assert np.sum(~np.array(intersects_old)) > 0 - # Check that error is raised when cropGeom argument is unvalid + # Check that error is raised when cropGeom argument is invalid with pytest.raises(TypeError, match="Crop geometry must be a Raster, Vector, or list of coordinates."): - outlines.crop(1) # type: ignore + outlines.crop(1) # type: ignore def test_proximity(self) -> None: """ @@ -344,11 +343,11 @@ def test_create_mask(self) -> None: # Check that an error is raised if buffer is the wrong type with pytest.raises(TypeError, match="Buffer must be a number, currently set to str."): - vector.create_mask(rst, buffer="lol") # type: ignore + vector.create_mask(rst, buffer="lol") # type: ignore # If the raster has the wrong type with pytest.raises(TypeError, match="Raster must be a geoutils.Raster or None."): - vector.create_mask("lol") # type: ignore + vector.create_mask("lol") # type: ignore # Check that a warning is raised if the bounds were passed specifically by the user with pytest.warns(UserWarning): @@ -463,7 +462,7 @@ def test_buffer_metric(self) -> None: # And this time, it is the reprojected GeoDataFrame that should almost match (within a tolerance of 10e-06) assert all(direct_gpd_buffer.ds.geom_almost_equals(two_squares_geographic_buffered_reproj.ds)) - def test_buffer_without_overlap(self, monkeypatch) -> None: + def test_buffer_without_overlap(self, monkeypatch) -> None: # type: ignore """ Check that non-overlapping buffer feature works. Does not work on simple geometries, so test on MultiPolygon. Yet, very simple geometries yield unexpected results, as is the case for the second test case here. @@ -528,5 +527,5 @@ def test_buffer_without_overlap(self, monkeypatch) -> None: assert np.all(mask_nobuffer | mask_nonoverlap == mask_buffer) # Check that plotting runs without errors and close it - monkeypatch.setattr(plt, 'show', lambda: None) + monkeypatch.setattr(plt, "show", lambda: None) two_squares.buffer_without_overlap(buffer_size, plot=True) From c06afc936e9623388d869710b3464e0ecda15342 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 16:50:41 -0800 Subject: [PATCH 095/184] Add test for to_xarray --- geoutils/georaster/raster.py | 39 +++++++++------- tests/test_georaster.py | 90 +++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 18 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index a443f1aec..b621b7a94 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -44,6 +44,7 @@ from typing_extensions import Literal # type: ignore try: + import xarray as xr import rioxarray _has_rioxarray = True @@ -133,7 +134,7 @@ def _default_nodata(dtype: str | np.dtype | type) -> int: } # Check argument dtype is as expected if not isinstance(dtype, (str, np.dtype, type)): - raise ValueError(f"dtype {dtype} not understood") + raise TypeError(f"dtype {dtype} not understood.") # Convert numpy types to string if isinstance(dtype, type): @@ -146,7 +147,7 @@ def _default_nodata(dtype: str | np.dtype | type) -> int: if dtype in default_nodata_lookup.keys(): return default_nodata_lookup[dtype] else: - raise NotImplementedError(f"No default nodata value set for dtype {dtype}") + raise NotImplementedError(f"No default nodata value set for dtype {dtype}.") # Set default attributes to be kept from rasterio's DatasetReader @@ -357,7 +358,7 @@ def __init__( # Downsampled image size if not isinstance(downsample, (int, float)): - raise ValueError("downsample must be of type int or float") + raise TypeError("downsample must be of type int or float.") if downsample == 1: out_shape = (count, self.height, self.width) else: @@ -387,11 +388,11 @@ def __init__( # Provide a catch in case trying to load from data array elif isinstance(filename_or_dataset, np.ndarray): - raise ValueError("np.array provided as filename. Did you mean to call Raster.from_array(...) instead? ") + raise TypeError("The filename is an array, did you mean to call Raster.from_array(...) instead?") # Don't recognise the input, so stop here. else: - raise ValueError("filename argument not recognised.") + raise TypeError("The filename argument is not recognised, should be a path or a Rasterio dataset.") @property def count_on_disk(self) -> None | int: @@ -488,18 +489,24 @@ def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: :raises AttributeError: If no 'filename' attribute exists. """ if self.is_loaded: - raise ValueError("Data are already loaded") + raise ValueError("Data are already loaded.") if self.filename is None: - raise AttributeError("'filename' is not set") + raise AttributeError("Cannot load as filename is not set anymore. Did you manually update the filename attribute?") # If no index is passed, use all of them if indexes is None: valid_indexes = self.indexes - elif isinstance(indexes, int): - valid_indexes = (indexes,) - else: - valid_indexes = tuple(indexes) + # If a new index was pass, redefine out_shape + elif isinstance(indexes, (int, list)): + # Rewrite properly as a tuple + if isinstance(indexes, int): + valid_indexes = (indexes,) + else: + valid_indexes = tuple(indexes) + # Update out_shape + self._out_shape = (len(valid_indexes), self._out_shape[1], self._out_shape[2]) + # Save which indexes are loaded self._indexes_loaded = valid_indexes @@ -563,7 +570,7 @@ def from_array( if isinstance(transform, tuple): transform = Affine(*transform) else: - raise ValueError("transform argument needs to be Affine or tuple.") + raise TypeError("The transform argument needs to be Affine or tuple.") # Enable shortcut to create CRS from an EPSG ID. if isinstance(crs, int): @@ -2118,7 +2125,7 @@ def save( dst.gcps = (rio_gcps, gcps_crs) - def to_xarray(self, name: str | None = None) -> rioxarray.DataArray: + def to_xarray(self, name: str | None = None) -> xr.DataArray: """ Convert raster to a xarray.DataArray. @@ -2135,11 +2142,11 @@ def to_xarray(self, name: str | None = None) -> rioxarray.DataArray: if not _has_rioxarray: raise ImportError("rioxarray is required for this functionality.") - xr = rioxarray.open_rasterio(self.to_rio_dataset()) + ds = rioxarray.open_rasterio(self.to_rio_dataset()) if name is not None: - xr.name = name + ds.name = name - return xr + return ds def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coords.BoundingBox: """ diff --git a/tests/test_georaster.py b/tests/test_georaster.py index d75d2548e..009c3aa3c 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -14,6 +14,8 @@ import numpy as np import pytest import rasterio as rio +import xarray as xr +import rioxarray as rioxr from pylint import epylint import geoutils as gu @@ -124,6 +126,12 @@ def test_init(self, example: str) -> None: assert np.ma.isMaskedArray(gu.Raster(example, masked=True).data) assert np.ma.isMaskedArray(gu.Raster(example, masked=False).data) + # Check that an error is raised when instantiating with an array + with pytest.raises(TypeError, match="The filename is an array, did you mean to call Raster.from_array(...) instead?"): + gu.Raster(np.ones(size=(1,1))) # type: ignore + with pytest.raises(TypeError, match="The filename argument is not recognised, should be a path or a Rasterio dataset."): + gu.Raster(1) # type: ignore + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore def test_info(self, example: str) -> None: """Test that the information summary is consistent with that of rasterio""" @@ -160,7 +168,7 @@ def test_info(self, example: str) -> None: continue assert line == new_stats.splitlines()[i] - def test_loading(self) -> None: + def test_load(self) -> None: """ Test that loading metadata and data works for all possible cases. """ @@ -170,6 +178,12 @@ def test_loading(self) -> None: assert r_explicit.raster_equal(r_implicit) + # Same for a multi-band raster + r_explicit_rgb = gu.Raster(self.landsat_rgb_path, load_data=True) + r_implicit_rgb = gu.Raster(self.landsat_rgb_path) + + assert r_explicit_rgb.raster_equal(r_implicit_rgb) + # Test 1 - loading metadata only, single band # For the first example with Landsat B4 r = gu.Raster(self.landsat_b4_path) @@ -257,6 +271,33 @@ def test_loading(self) -> None: assert r.indexes_on_disk == (1, 2, 3) assert r.data.shape == (r.count, r.height, r.width) + # Test 7 - load a single band a posteriori calling load() + r = gu.Raster(self.landsat_rgb_path) + r.load(indexes=1) + assert r.count == 1 + assert r.count_on_disk == 3 + assert r.indexes == (1,) + assert r.indexes_on_disk == (1, 2, 3) + assert r.data.shape == (r.count, r.height, r.width) + + # Test 8 - load a list of band a posteriori calling load() + r = gu.Raster(self.landsat_rgb_path) + r.load(indexes=[2, 3]) + assert r.count == 2 + assert r.count_on_disk == 3 + assert r.indexes == (1, 2) + assert r.indexes_on_disk == (1, 2, 3) + assert r.data.shape == (r.count, r.height, r.width) + + # Check that errors are raised when appropriate + with pytest.raises(ValueError, match="Data are already loaded."): + r.load() + with pytest.raises(AttributeError, match="Cannot load as filename is not set anymore. " + "Did you manually update the filename attribute?"): + r = gu.Raster(self.landsat_b4_path) + r.filename = None + r.load() + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_to_rio_dataset(self, example: str): """Test the export to a rasterio dataset""" @@ -277,6 +318,39 @@ def test_to_rio_dataset(self, example: str): assert np.array_equal(rst.data.data, rio_ds.read().data) assert np.array_equal(rst.data.mask, rio_ds.read(masked=True).mask) + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) # type: ignore + def test_to_xarray(self, example: str): + """Test the export to a xarray dataset""" + + # Open raster and export to rio dataset + rst = gu.Raster(example) + ds = rst.to_xarray() + + # Check that the output is indeed a xarray Dataset + assert isinstance(ds, xr.DataArray) + + # Check that all attributes are equal + assert ds.band.size == rst.count + assert ds.x.size == rst.width + assert ds.y.size == rst.height + + # Check that coordinates are shifted by a half pixel + assert ds.x.values[0] == rst.bounds.left + rst.res[0] / 2 + assert ds.y.values[0] == rst.bounds.top - rst.res[1] / 2 + assert ds.x.values[-1] == rst.bounds.right - rst.res[0] / 2 + assert ds.y.values[-1] == rst.bounds.bottom + rst.res[1] / 2 + + # Check that georeferencing attribute are equal + new_trans_order = ["c", "a", "b", "f", "d", "e"] + assert ds.spatial_ref.GeoTransform == " ".join([str(getattr(rst.transform, attr)) for attr in new_trans_order]) + + # Check that CRS are equal + assert ds.spatial_ref.crs_wkt == rst.crs.to_wkt() + + # Check that the arrays are equal in NaN type + assert np.array_equal(rst.data.data, ds.data) + + @pytest.mark.parametrize("nodata_init", [None, "type_default"]) # type: ignore @pytest.mark.parametrize( "dtype", ["uint8", "int8", "uint16", "int16", "uint32", "int32", "float32", "float64", "longdouble"] @@ -516,6 +590,10 @@ def test_downsampling(self) -> None: # One pixel right and down assert r.xy2ij(r.bounds.left + r.res[0], r.bounds.top - r.res[1]) == (1, 1) + # Check that error is raised when downsampling value is not valid + with pytest.raises(TypeError, match="downsample must be of type int or float."): + gu.Raster(self.landsat_b4_path, downsample=[1, 1]) # type: ignore + def test_add_sub(self) -> None: """ Test addition, subtraction and negation on a Raster object. @@ -1901,10 +1979,14 @@ def test_default_nodata(self) -> None: assert _default_nodata(dtype) == -99999 # Check that an error is raised for other types - expected_message = "No default nodata value set for dtype" + expected_message = "No default nodata value set for dtype." with pytest.raises(NotImplementedError, match=expected_message): _default_nodata("bla") + # Check that an error is raised for a wrong type + with pytest.raises(TypeError, match="dtype 1 not understood."): + _default_nodata(1) # type: ignore + def test_astype(self) -> None: warnings.simplefilter("error") @@ -2136,6 +2218,10 @@ def test_from_array(self) -> None: out_img = gu.Raster.from_array(img.data, img.transform, img.crs, nodata=0) assert out_img.data.mask[0, 0, 0] + # Check that error is raised if the transform is not affine + with pytest.raises(TypeError, match="The transform argument needs to be Affine or tuple."): + gu.Raster.from_array(data=img.data, transform="lol", crs=None, nodata=None) # type: ignore + def test_type_hints(self) -> None: """Test that pylint doesn't raise errors on valid code.""" # Create a temporary directory and a temporary filename From a283e8ebebf75cec6cd232b5f9a8ec9fda010f03 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 16:57:50 -0800 Subject: [PATCH 096/184] Linting --- geoutils/georaster/raster.py | 10 ++++++---- tests/test_georaster.py | 26 +++++++++++++++----------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index b621b7a94..74d934ef7 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -44,8 +44,8 @@ from typing_extensions import Literal # type: ignore try: - import xarray as xr import rioxarray + import xarray as xr _has_rioxarray = True except ImportError: @@ -492,7 +492,9 @@ def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: raise ValueError("Data are already loaded.") if self.filename is None: - raise AttributeError("Cannot load as filename is not set anymore. Did you manually update the filename attribute?") + raise AttributeError( + "Cannot load as filename is not set anymore. Did you manually update the filename attribute?" + ) # If no index is passed, use all of them if indexes is None: @@ -505,8 +507,8 @@ def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: else: valid_indexes = tuple(indexes) # Update out_shape - self._out_shape = (len(valid_indexes), self._out_shape[1], self._out_shape[2]) - + if self._out_shape is not None: + self._out_shape = (len(valid_indexes), self._out_shape[1], self._out_shape[2]) # Save which indexes are loaded self._indexes_loaded = valid_indexes diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 009c3aa3c..3a6bfc21c 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -15,7 +15,6 @@ import pytest import rasterio as rio import xarray as xr -import rioxarray as rioxr from pylint import epylint import geoutils as gu @@ -127,10 +126,14 @@ def test_init(self, example: str) -> None: assert np.ma.isMaskedArray(gu.Raster(example, masked=False).data) # Check that an error is raised when instantiating with an array - with pytest.raises(TypeError, match="The filename is an array, did you mean to call Raster.from_array(...) instead?"): - gu.Raster(np.ones(size=(1,1))) # type: ignore - with pytest.raises(TypeError, match="The filename argument is not recognised, should be a path or a Rasterio dataset."): - gu.Raster(1) # type: ignore + with pytest.raises( + TypeError, match="The filename is an array, did you mean to call Raster.from_array(...) instead?" + ): + gu.Raster(np.ones(size=(1, 1))) # type: ignore + with pytest.raises( + TypeError, match="The filename argument is not recognised, should be a path or a Rasterio dataset." + ): + gu.Raster(1) # type: ignore @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore def test_info(self, example: str) -> None: @@ -292,8 +295,10 @@ def test_load(self) -> None: # Check that errors are raised when appropriate with pytest.raises(ValueError, match="Data are already loaded."): r.load() - with pytest.raises(AttributeError, match="Cannot load as filename is not set anymore. " - "Did you manually update the filename attribute?"): + with pytest.raises( + AttributeError, + match="Cannot load as filename is not set anymore. " "Did you manually update the filename attribute?", + ): r = gu.Raster(self.landsat_b4_path) r.filename = None r.load() @@ -350,7 +355,6 @@ def test_to_xarray(self, example: str): # Check that the arrays are equal in NaN type assert np.array_equal(rst.data.data, ds.data) - @pytest.mark.parametrize("nodata_init", [None, "type_default"]) # type: ignore @pytest.mark.parametrize( "dtype", ["uint8", "int8", "uint16", "int16", "uint32", "int32", "float32", "float64", "longdouble"] @@ -592,7 +596,7 @@ def test_downsampling(self) -> None: # Check that error is raised when downsampling value is not valid with pytest.raises(TypeError, match="downsample must be of type int or float."): - gu.Raster(self.landsat_b4_path, downsample=[1, 1]) # type: ignore + gu.Raster(self.landsat_b4_path, downsample=[1, 1]) # type: ignore def test_add_sub(self) -> None: """ @@ -1985,7 +1989,7 @@ def test_default_nodata(self) -> None: # Check that an error is raised for a wrong type with pytest.raises(TypeError, match="dtype 1 not understood."): - _default_nodata(1) # type: ignore + _default_nodata(1) # type: ignore def test_astype(self) -> None: warnings.simplefilter("error") @@ -2220,7 +2224,7 @@ def test_from_array(self) -> None: # Check that error is raised if the transform is not affine with pytest.raises(TypeError, match="The transform argument needs to be Affine or tuple."): - gu.Raster.from_array(data=img.data, transform="lol", crs=None, nodata=None) # type: ignore + gu.Raster.from_array(data=img.data, transform="lol", crs=None, nodata=None) # type: ignore def test_type_hints(self) -> None: """Test that pylint doesn't raise errors on valid code.""" From ece179e394e612afd6142b89f902df0ce625e37c Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 17:00:46 -0800 Subject: [PATCH 097/184] Fix test_init --- tests/test_georaster.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 3a6bfc21c..3d251a712 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -127,9 +127,9 @@ def test_init(self, example: str) -> None: # Check that an error is raised when instantiating with an array with pytest.raises( - TypeError, match="The filename is an array, did you mean to call Raster.from_array(...) instead?" - ): - gu.Raster(np.ones(size=(1, 1))) # type: ignore + TypeError, match=re.escape("The filename is an array, did you mean to call Raster.from_array(...) instead?" + )): + gu.Raster(np.ones(shape=(1, 1))) # type: ignore with pytest.raises( TypeError, match="The filename argument is not recognised, should be a path or a Rasterio dataset." ): From a3bd710a85fcdb58f62a7cab5e1e9dd80c74888e Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 17:01:50 -0800 Subject: [PATCH 098/184] Linting --- tests/test_georaster.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 3d251a712..b397d29ca 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -127,8 +127,8 @@ def test_init(self, example: str) -> None: # Check that an error is raised when instantiating with an array with pytest.raises( - TypeError, match=re.escape("The filename is an array, did you mean to call Raster.from_array(...) instead?" - )): + TypeError, match=re.escape("The filename is an array, did you mean to call Raster.from_array(...) instead?") + ): gu.Raster(np.ones(shape=(1, 1))) # type: ignore with pytest.raises( TypeError, match="The filename argument is not recognised, should be a path or a Rasterio dataset." From 0d3d19d97dbb4f4f3241721a390d9bee2201def9 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 17:43:44 -0800 Subject: [PATCH 099/184] Add tests for diff_yml --- geoutils/misc.py | 7 +++++-- tests/test_georaster.py | 22 ++++++++++++++++++++++ tests/test_misc.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/geoutils/misc.py b/geoutils/misc.py index 0f367eff3..9ddb84b7d 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -4,6 +4,7 @@ import functools import warnings from typing import Any +import copy try: import yaml # type: ignore @@ -111,8 +112,10 @@ def diff_environment_yml( yaml_env = yaml.safe_load(open(fn_env)) # type: ignore yaml_devenv = yaml.safe_load(open(fn_devenv)) # type: ignore else: - yaml_env = fn_env - yaml_devenv = fn_devenv + # We need a copy as we'll pop things out and don't want to affect input + # dict.copy() is shallow and does not work with embedded list in dicts (as is the case here) + yaml_env = copy.deepcopy(fn_env) + yaml_devenv = copy.deepcopy(fn_devenv) # Extract the dependencies values conda_dep_env = yaml_env["dependencies"] diff --git a/tests/test_georaster.py b/tests/test_georaster.py index b397d29ca..68798d345 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -135,6 +135,28 @@ def test_init(self, example: str) -> None: ): gu.Raster(1) # type: ignore + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore + def test_repr_str(self, example: str) -> None: + """Test the representation of a raster works""" + + # For data not loaded by default + r = gu.Raster(example) + + r_repr = r.__repr__() + r_str = r.__str__() + + assert r_str == "not_loaded" + assert r_repr.split("data=")[1][:10] == "not_loaded" + + # With data loaded + r.load() + + r_repr = r.__repr__() + r_str = r.__str__() + + assert r_str == r.data.__str__() + assert r_repr.split("data=")[1][:10] != "not_loaded" + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore def test_info(self, example: str) -> None: """Test that the information summary is consistent with that of rasterio""" diff --git a/tests/test_misc.py b/tests/test_misc.py index 0e701c733..ff2ceaeae 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -130,3 +130,32 @@ def test_diff_environment_yml(self, capsys) -> None: # type: ignore captured = capsys.readouterr().out assert captured == "opencv\ngeoutils\n" + + # This should print only pip + geoutils.misc.diff_environment_yml(env2, devenv2, input_dict=True, print_dep="pip") + captured = capsys.readouterr().out + + assert captured == "geoutils\n" + + # This should raise an error because print_dep is not well defined + with pytest.raises(ValueError, match='The argument "print_dep" can only be "conda", "pip" or "both".'): + geoutils.misc.diff_environment_yml(env2, devenv2, input_dict=True, print_dep="lol") + + # When the dependencies are not defined in dev-env but in env, it should raise an error + # For normal dependencies + env3 = {"dependencies": ["python==3.9", "numpy", "fiona", "lol"]} + devenv3 = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils"]}]} + with pytest.raises(ValueError, match="The following dependencies are listed in env but not dev-env: lol"): + geoutils.misc.diff_environment_yml(env3, devenv3, input_dict=True, print_dep="pip") + + # For pip dependencies + env4 = {"dependencies": ["python==3.9", "numpy", "fiona", {"pip": ["lol"]}]} + devenv4 = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils"]}]} + with pytest.raises(ValueError, match="The following pip dependencies are listed in env but not dev-env: lol"): + geoutils.misc.diff_environment_yml(env4, devenv4, input_dict=True, print_dep="pip") + + + + + + From a77c074a9dc20d672c281a04dde73019f7d64dc4 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 18:43:03 -0800 Subject: [PATCH 100/184] Add tests for mask overloaded functions and logical bitwise operations --- geoutils/georaster/raster.py | 42 ++++++-- tests/test_georaster.py | 187 ++++++++++++++++++++++++++++++++++- tests/test_misc.py | 3 +- 3 files changed, 222 insertions(+), 10 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 74d934ef7..6ae673080 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -3080,7 +3080,7 @@ def reproject( dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, src_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, dst_dtype: np.dtype | None = None, - resampling: Resampling | str = Resampling.bilinear, + resampling: Resampling | str = Resampling.nearest, silent: bool = False, n_threads: int = 0, memory_limit: int = 64, @@ -3090,6 +3090,8 @@ def reproject( if resampling in [Resampling.nearest, "nearest"]: self.data = self.data.astype("uint8") else: + warnings.warn("Reprojecting a mask with a resampling method other than 'nearest', " + "the boolean array will be converted to float during interpolation.") self.data = self.data.astype("float32") # Call Raster.reproject() @@ -3216,26 +3218,50 @@ def proximity( distance_unit=distance_unit, ) - # Logical bitwise operations - def __and__(self: Mask, other: Mask) -> Mask: + + def __and__(self: Mask, other: Mask | np.ndarray) -> Mask: + """Bitwise and between masks, or a mask and an array.""" + self_data, other_data = self._overloading_check(other)[0:2] return self.from_array( - data=(self.data & other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + data=(self_data & other_data), transform=self.transform, crs=self.crs, nodata=self.nodata ) - def __or__(self: Mask, other: Mask) -> Mask: + def __rand__(self: Mask, other: Mask | np.ndarray) -> Mask: + """Bitwise and between masks, or a mask and an array.""" + + return self.__and__(other) + + def __or__(self: Mask, other: Mask | np.ndarray) -> Mask: + """Bitwise or between masks, or a mask and an array.""" + + self_data, other_data = self._overloading_check(other)[0:2] return self.from_array( - data=(self.data | other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + data=(self_data | other_data), transform=self.transform, crs=self.crs, nodata=self.nodata ) - def __xor__(self: Mask, other: Mask) -> Mask: + def __ror__(self: Mask, other: Mask | np.ndarray) -> Mask: + """Bitwise or between masks, or a mask and an array.""" + + return self.__or__(other) + + def __xor__(self: Mask, other: Mask | np.ndarray) -> Mask: + """Bitwise xor between masks, or a mask and an array.""" + + self_data, other_data = self._overloading_check(other)[0:2] return self.from_array( - data=(self.data ^ other.data), transform=self.transform, crs=self.crs, nodata=self.nodata + data=(self_data ^ other_data), transform=self.transform, crs=self.crs, nodata=self.nodata ) + def __rxor__(self: Mask, other: Mask | np.ndarray) -> Mask: + """Bitwise xor between masks, or a mask and an array.""" + + return self.__xor__(other) + def __invert__(self: Mask) -> Mask: + """Bitwise inversion of a mask.""" return self.from_array(data=~self.data, transform=self.transform, crs=self.crs, nodata=self.nodata) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 68798d345..f1e4d9d28 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -145,6 +145,13 @@ def test_repr_str(self, example: str) -> None: r_repr = r.__repr__() r_str = r.__str__() + # Check that the class is printed correctly + assert r_repr[0:6] == "Raster" + + # Check that all main attribute names are printed + attrs_shown = ["data", "transform", "crs", "nodata"] + assert all([attr+"=" in r_repr for attr in attrs_shown]) + assert r_str == "not_loaded" assert r_repr.split("data=")[1][:10] == "not_loaded" @@ -2541,7 +2548,7 @@ def test_to_points(self) -> None: class TestMask: - # Real data + # Paths to example data landsat_b4_path = examples.get_path("everest_landsat_b4") landsat_b4_crop_path = examples.get_path("everest_landsat_b4_cropped") landsat_rgb_path = examples.get_path("everest_landsat_rgb") @@ -2557,6 +2564,11 @@ class TestMask: arr_mask = np.random.randint(0, 2, size=(1, width, height), dtype=bool) mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + # Mask without nodata + mask_landsat_b4 = gu.Raster(landsat_b4_path) > 125 + # Mask with nodata + mask_aster_dem = gu.Raster(aster_dem_path) > 2000 + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore def test_init(self, example: str) -> None: """Test that Mask subclass initialization function as intended.""" @@ -2585,6 +2597,38 @@ def test_init(self, example: str) -> None: mask2 = gu.Mask(mask) assert mask.raster_equal(mask2) + @pytest.mark.parametrize("example", [landsat_b4_path, landsat_rgb_path, aster_dem_path]) # type: ignore + def test_repr_str(self, example: str) -> None: + """Test the representation of a raster works""" + + # For data not loaded by default + r = gu.Mask(example) + + r_repr = r.__repr__() + r_str = r.__str__() + + # Check that the class is printed correctly + assert r_repr[0:6] == "Mask" + + # Check that all main attribute names are printed + attrs_shown = ["data", "transform", "crs"] + assert all([attr + "=" in r_repr for attr in attrs_shown]) + + # Check nodata is removed + assert "nodata=" not in r_repr + + assert r_str == "not_loaded" + assert r_repr.split("data=")[1][:10] == "not_loaded" + + # With data loaded + r.load() + + r_repr = r.__repr__() + r_str = r.__str__() + + assert r_str == r.data.__str__() + assert r_repr.split("data=")[1][:10] != "not_loaded" + def test_from_array(self) -> None: """Test that Raster.__init__ casts to Mask with dict input of from_array() and a boolean data array.""" @@ -2667,6 +2711,121 @@ def test_implicit_logical_casting_real(self, example: str) -> None: assert np.array_equal(mask.data.mask, rst.data.mask) + @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore + def test_reproject(self, mask: gu.Mask) -> None: + + # Test 1: with a classic resampling (bilinear) + + # Reproject mask + mask_reproj = mask.reproject() + + # Check instance is respected + assert isinstance(mask_reproj, gu.Mask) + + # This should be equivalent to converting the array to uint8, reprojecting, converting back + mask_uint8 = mask.astype("uint8") + mask_uint8_reproj = mask_uint8.reproject() + mask_uint8_reproj.data = mask_uint8_reproj.data.astype("bool") + + assert mask_reproj.raster_equal(mask_uint8_reproj) + + # Test 2: should raise a warning when the resampling differs from nearest + + with pytest.warns(UserWarning, match="Reprojecting a mask with a resampling method other than 'nearest', " + "the boolean array will be converted to float during interpolation."): + mask.reproject(resampling="bilinear") + + @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore + def test_crop(self, mask: gu.Mask) -> None: + + + # Test with same bounds -> should be the same # + crop_geom = mask.bounds + mask_cropped = mask.crop(crop_geom, inplace=False) + assert mask_cropped.raster_equal(mask) + + # Check if instance is respected + assert isinstance(mask_cropped, gu.Mask) + + # Test with bracket call + mask_cropped_getitem = mask[crop_geom] + assert mask_cropped_getitem.raster_equal(mask_cropped) + + # - Test cropping each side by a random integer of pixels - # + rand_int = np.random.randint(1, min(mask.shape) - 1) + + # Left + crop_geom2 = [crop_geom[0] + rand_int * mask.res[0], crop_geom[1], crop_geom[2], crop_geom[3]] + mask_cropped = mask.crop(crop_geom2, inplace=False) + assert list(mask_cropped.bounds) == crop_geom2 + assert np.array_equal(mask.data[:, :, rand_int:].data, mask_cropped.data.data, equal_nan=True) + assert np.array_equal(mask.data[:, :, rand_int:].mask, mask_cropped.data.mask) + + # Right + crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2] - rand_int * mask.res[0], crop_geom[3]] + mask_cropped = mask.crop(crop_geom2, inplace=False) + assert list(mask_cropped.bounds) == crop_geom2 + assert np.array_equal(mask.data[:, :, :-rand_int].data, mask_cropped.data.data, equal_nan=True) + assert np.array_equal(mask.data[:, :, :-rand_int].mask, mask_cropped.data.mask) + + # Bottom + crop_geom2 = [crop_geom[0], crop_geom[1] + rand_int * abs(mask.res[1]), crop_geom[2], crop_geom[3]] + mask_cropped = mask.crop(crop_geom2, inplace=False) + assert list(mask_cropped.bounds) == crop_geom2 + assert np.array_equal(mask.data[:, :-rand_int, :].data, mask_cropped.data.data, equal_nan=True) + assert np.array_equal(mask.data[:, :-rand_int, :].mask, mask_cropped.data.mask) + + # Top + crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3] - rand_int * abs(mask.res[1])] + mask_cropped = mask.crop(crop_geom2, inplace=False) + assert list(mask_cropped.bounds) == crop_geom2 + assert np.array_equal(mask.data[:, rand_int:, :].data, mask_cropped.data, equal_nan=True) + assert np.array_equal(mask.data[:, rand_int:, :].mask, mask_cropped.data.mask) + + # Test inplace + mask_orig = mask.copy() + mask.crop(crop_geom2) + assert list(mask.bounds) == crop_geom2 + assert np.array_equal(mask.data[:, rand_int:, :].data, mask_orig.data, equal_nan=True) + assert np.array_equal(mask.data[:, rand_int:, :].mask, mask_orig.data.mask) + + # Run with match_extent, check that inplace or not yields the same result + mask_cropped = mask.crop(crop_geom2, inplace=False, mode="match_extent") + mask_orig.crop(crop_geom2, mode="match_extent") + assert mask_cropped.raster_equal(mask_orig) + + @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore + def test_polygonize(self, mask: gu.Mask) -> None: + + # Run default + vect = mask.polygonize() + + # Check the output is cast into a vector + assert isinstance(vect, gu.Vector) + + # Run with zero as target + vect = mask.polygonize(target_values=0) + assert isinstance(vect, gu.Vector) + + # Check a warning is raised when using a non-boolean value + with pytest.warns(UserWarning, match="In-value converted to 1 for polygonizing boolean mask."): + mask.polygonize(target_values=2) + + + @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore + def test_proximity(self, mask: gu.Mask) -> None: + + # Run default + rast = mask.proximity() + + # Check that output is cast back into a raster + assert isinstance(rast, gu.Raster) + # A mask is a raster, so also need to check this + assert not isinstance(rast, gu.Mask) + + + + class TestArithmetic: """ Test that all arithmetic overloading functions work as expected. @@ -3155,6 +3314,32 @@ def test_ops_logical_implicit(self) -> None: assert (r2 >= array_2d).raster_equal(self.from_array(r2.data >= array_2d[np.newaxis, :, :], rst_ref=r1)) assert (r1 >= floatval).raster_equal(self.from_array(r1.data >= floatval, rst_ref=r1)) + def test_ops_logical_bitwise_implicit(self) -> None: + + # Create two masks + r1 = self.r1 + m1 = self.r1 > 128 + m2 = self.r2 > 128 + array_2d = np.random.randint(1, 255, (self.height, self.width)).astype("uint8") > 128 + + # Bitwise or + assert (m1 | m2).raster_equal(self.from_array(m1.data | m2.data, rst_ref=r1)) + assert (m1 | array_2d).raster_equal(self.from_array(m1.data | array_2d, rst_ref=r1)) + assert (array_2d | m1).raster_equal(self.from_array(array_2d | m1.data, rst_ref=r1)) + + # Bitwise and + assert (m1 & m2).raster_equal(self.from_array(m1.data & m2.data, rst_ref=r1)) + assert (m1 & array_2d).raster_equal(self.from_array(m1.data & array_2d, rst_ref=r1)) + assert (array_2d & m1).raster_equal(self.from_array(array_2d & m1.data, rst_ref=r1)) + + # Bitwise xor + assert (m1 ^ m2).raster_equal(self.from_array(m1.data ^ m2.data, rst_ref=r1)) + assert (m1 ^ array_2d).raster_equal(self.from_array(m1.data ^ array_2d, rst_ref=r1)) + assert (array_2d ^ m1).raster_equal(self.from_array(array_2d ^ m1.data, rst_ref=r1)) + + # Bitwise invert + assert (~m1).raster_equal(self.from_array(~m1.data, rst_ref=r1)) + @pytest.mark.parametrize("op", ops_2args) # type: ignore def test_raise_errors(self, op: str) -> None: """ diff --git a/tests/test_misc.py b/tests/test_misc.py index ff2ceaeae..70a5d31e2 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -124,8 +124,9 @@ def test_diff_environment_yml(self, capsys) -> None: # type: ignore assert captured == "opencv\nNone\n" env2 = {"dependencies": ["python==3.9", "numpy", "fiona"]} - devenv2 = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils"]}]} + devenv2 = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils", "-e ./"]}]} + # The diff function should not account for -e ./ that is the local install for developers geoutils.misc.diff_environment_yml(env2, devenv2, input_dict=True, print_dep="both") captured = capsys.readouterr().out From dc0760ac14545ea0f890d0c9ae2726de2d2f1407 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 18:45:05 -0800 Subject: [PATCH 101/184] Linting --- geoutils/georaster/raster.py | 7 ++++--- geoutils/misc.py | 2 +- tests/test_georaster.py | 16 +++++++--------- tests/test_misc.py | 8 +------- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 6ae673080..3ad0846fa 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -3090,8 +3090,10 @@ def reproject( if resampling in [Resampling.nearest, "nearest"]: self.data = self.data.astype("uint8") else: - warnings.warn("Reprojecting a mask with a resampling method other than 'nearest', " - "the boolean array will be converted to float during interpolation.") + warnings.warn( + "Reprojecting a mask with a resampling method other than 'nearest', " + "the boolean array will be converted to float during interpolation." + ) self.data = self.data.astype("float32") # Call Raster.reproject() @@ -3218,7 +3220,6 @@ def proximity( distance_unit=distance_unit, ) - def __and__(self: Mask, other: Mask | np.ndarray) -> Mask: """Bitwise and between masks, or a mask and an array.""" self_data, other_data = self._overloading_check(other)[0:2] diff --git a/geoutils/misc.py b/geoutils/misc.py index 9ddb84b7d..5202c36f8 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -1,10 +1,10 @@ """Miscellaneous functions, mainly for testing.""" from __future__ import annotations +import copy import functools import warnings from typing import Any -import copy try: import yaml # type: ignore diff --git a/tests/test_georaster.py b/tests/test_georaster.py index f1e4d9d28..8b723ebf2 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -150,7 +150,7 @@ def test_repr_str(self, example: str) -> None: # Check that all main attribute names are printed attrs_shown = ["data", "transform", "crs", "nodata"] - assert all([attr+"=" in r_repr for attr in attrs_shown]) + assert all(attr + "=" in r_repr for attr in attrs_shown) assert r_str == "not_loaded" assert r_repr.split("data=")[1][:10] == "not_loaded" @@ -2612,7 +2612,7 @@ def test_repr_str(self, example: str) -> None: # Check that all main attribute names are printed attrs_shown = ["data", "transform", "crs"] - assert all([attr + "=" in r_repr for attr in attrs_shown]) + assert all(attr + "=" in r_repr for attr in attrs_shown) # Check nodata is removed assert "nodata=" not in r_repr @@ -2710,7 +2710,6 @@ def test_implicit_logical_casting_real(self, example: str) -> None: assert np.array_equal(mask.data.data, rst.data.data >= 1) assert np.array_equal(mask.data.mask, rst.data.mask) - @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_reproject(self, mask: gu.Mask) -> None: @@ -2731,14 +2730,16 @@ def test_reproject(self, mask: gu.Mask) -> None: # Test 2: should raise a warning when the resampling differs from nearest - with pytest.warns(UserWarning, match="Reprojecting a mask with a resampling method other than 'nearest', " - "the boolean array will be converted to float during interpolation."): + with pytest.warns( + UserWarning, + match="Reprojecting a mask with a resampling method other than 'nearest', " + "the boolean array will be converted to float during interpolation.", + ): mask.reproject(resampling="bilinear") @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_crop(self, mask: gu.Mask) -> None: - # Test with same bounds -> should be the same # crop_geom = mask.bounds mask_cropped = mask.crop(crop_geom, inplace=False) @@ -2811,7 +2812,6 @@ def test_polygonize(self, mask: gu.Mask) -> None: with pytest.warns(UserWarning, match="In-value converted to 1 for polygonizing boolean mask."): mask.polygonize(target_values=2) - @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_proximity(self, mask: gu.Mask) -> None: @@ -2824,8 +2824,6 @@ def test_proximity(self, mask: gu.Mask) -> None: assert not isinstance(rast, gu.Mask) - - class TestArithmetic: """ Test that all arithmetic overloading functions work as expected. diff --git a/tests/test_misc.py b/tests/test_misc.py index 70a5d31e2..afb146466 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -150,13 +150,7 @@ def test_diff_environment_yml(self, capsys) -> None: # type: ignore geoutils.misc.diff_environment_yml(env3, devenv3, input_dict=True, print_dep="pip") # For pip dependencies - env4 = {"dependencies": ["python==3.9", "numpy", "fiona", {"pip": ["lol"]}]} + env4 = {"dependencies": ["python==3.9", "numpy", "fiona", {"pip": ["lol"]}]} devenv4 = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv", {"pip": ["geoutils"]}]} with pytest.raises(ValueError, match="The following pip dependencies are listed in env but not dev-env: lol"): geoutils.misc.diff_environment_yml(env4, devenv4, input_dict=True, print_dep="pip") - - - - - - From 6cab5b3a2dbf21d49630792718ea5ffee14d460a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 19:08:09 -0800 Subject: [PATCH 102/184] Fix mask class methods --- geoutils/georaster/raster.py | 26 ++++++++++++++------------ tests/test_georaster.py | 16 ++++++++++------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 3ad0846fa..2913c40d8 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -3088,13 +3088,13 @@ def reproject( # Depending on resampling, adjust to rasterio supported types if resampling in [Resampling.nearest, "nearest"]: - self.data = self.data.astype("uint8") + self._data = self.data.astype("uint8") else: warnings.warn( "Reprojecting a mask with a resampling method other than 'nearest', " "the boolean array will be converted to float during interpolation." ) - self.data = self.data.astype("float32") + self._data = self.data.astype("float32") # Call Raster.reproject() output = super().reproject( @@ -3113,7 +3113,7 @@ def reproject( ) # Transform back to a boolean array - output.data = output.data.astype(bool) + output._data = output.data.astype(bool) return output @@ -3157,20 +3157,22 @@ def crop( # If there is resampling involved during cropping, encapsulate type as in reproject() if mode == "match_extent": - self.data = self.data.astype("float32") - if inplace: - super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) - self.data = self.data.astype(bool) - return None - else: - output = super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) - output.data = output.data.astype(bool) - return output + raise ValueError(NotImplementedError) + # self._data = self.data.astype("float32") + # if inplace: + # super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) + # self._data = self.data.astype(bool) + # return None + # else: + # output = super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) + # output._data = output.data.astype(bool) + # return output # Otherwise, run a classic crop else: if not inplace: return super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) else: + super().crop(crop_geom=crop_geom, mode=mode, inplace=inplace) return None def polygonize( diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 8b723ebf2..cd6323374 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2608,7 +2608,7 @@ def test_repr_str(self, example: str) -> None: r_str = r.__str__() # Check that the class is printed correctly - assert r_repr[0:6] == "Mask" + assert r_repr[0:4] == "Mask" # Check that all main attribute names are printed attrs_shown = ["data", "transform", "crs"] @@ -2787,13 +2787,17 @@ def test_crop(self, mask: gu.Mask) -> None: mask_orig = mask.copy() mask.crop(crop_geom2) assert list(mask.bounds) == crop_geom2 - assert np.array_equal(mask.data[:, rand_int:, :].data, mask_orig.data, equal_nan=True) - assert np.array_equal(mask.data[:, rand_int:, :].mask, mask_orig.data.mask) + assert np.array_equal(mask_orig.data[:, rand_int:, :].data, mask.data, equal_nan=True) + assert np.array_equal(mask_orig.data[:, rand_int:, :].mask, mask.data.mask) # Run with match_extent, check that inplace or not yields the same result - mask_cropped = mask.crop(crop_geom2, inplace=False, mode="match_extent") - mask_orig.crop(crop_geom2, mode="match_extent") - assert mask_cropped.raster_equal(mask_orig) + + # TODO: Pretty sketchy with the current functioning of "match_extent", + # should we just remove it from Raster.crop() ? + + # mask_cropped = mask.crop(crop_geom2, inplace=False, mode="match_extent") + # mask_orig.crop(crop_geom2, mode="match_extent") + # assert mask_cropped.raster_equal(mask_orig) @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_polygonize(self, mask: gu.Mask) -> None: From df823d6f066142656befd40450e9e12d73ea7078 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 8 Mar 2023 19:23:12 -0800 Subject: [PATCH 103/184] Adjust repr test for mask --- tests/test_georaster.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index cd6323374..c0b790641 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2617,17 +2617,7 @@ def test_repr_str(self, example: str) -> None: # Check nodata is removed assert "nodata=" not in r_repr - assert r_str == "not_loaded" - assert r_repr.split("data=")[1][:10] == "not_loaded" - - # With data loaded - r.load() - - r_repr = r.__repr__() - r_str = r.__str__() - assert r_str == r.data.__str__() - assert r_repr.split("data=")[1][:10] != "not_loaded" def test_from_array(self) -> None: """Test that Raster.__init__ casts to Mask with dict input of from_array() and a boolean data array.""" From c9ec2af3912c6aea00e88dc10d0488c8622f82a2 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 15:27:09 -0800 Subject: [PATCH 104/184] Add more tests to spatial tools and georaster --- geoutils/georaster/raster.py | 12 ++-- geoutils/spatial_tools.py | 21 +++--- tests/test_georaster.py | 14 +++- tests/test_spatial_tools.py | 122 +++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 18 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 2913c40d8..580ea9fff 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1742,7 +1742,7 @@ def crop( def reproject( self: RasterType, - dst_ref: RasterType | rio.io.Dataset | str | None = None, + dst_ref: RasterType | str | None = None, dst_crs: CRS | str | int | None = None, dst_size: tuple[int, int] | None = None, dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, @@ -1767,7 +1767,7 @@ def reproject( for the full list. :param dst_ref: A reference raster. If set will use the attributes of this - raster for the output grid. Can be provided as Raster/rasterio data set or as path to the file. + raster for the output grid. :param dst_crs: Specify the Coordinate Reference System or EPSG to reproject to. If dst_ref not set, defaults to self.crs. :param dst_size: Raster size to write to (x, y). Do not use with dst_res. @@ -1845,15 +1845,13 @@ def reproject( # Preferably use Raster instance to avoid rasterio data set to remain open. See PR #45 if isinstance(dst_ref, Raster): ds_ref = dst_ref - elif isinstance(dst_ref, rio.io.MemoryFile) or isinstance(dst_ref, rasterio.io.DatasetReader): - ds_ref = dst_ref elif isinstance(dst_ref, str): if not os.path.exists(dst_ref): raise ValueError("Reference raster does not exist.") ds_ref = Raster(dst_ref, load_data=False) else: raise TypeError( - "Type of dst_ref not understood, must be path to file (str), Raster or rasterio data set." + "Type of dst_ref not understood, must be path to file (str), Raster." ) # Read reprojecting params from ref raster @@ -3303,7 +3301,7 @@ def proximity_from_vector_or_raster( # We create a geodataframe with the geometry type boundary_shp = gpd.GeoDataFrame(geometry=vector.ds.__getattr__(geometry_type), crs=vector.crs) # We mask the pixels that make up the geometry type - mask_boundary = Vector(boundary_shp).create_mask(raster).get_nanarray() + mask_boundary = Vector(boundary_shp).create_mask(raster, as_array=True) else: # We mask target pixels @@ -3334,7 +3332,7 @@ def proximity_from_vector_or_raster( if in_or_out == "both": pass elif in_or_out in ["in", "out"]: - mask_polygon = Vector(vector.ds).create_mask(raster).get_nanarray() + mask_polygon = Vector(vector.ds).create_mask(raster, as_array=True) if in_or_out == "in": proximity[~mask_polygon] = 0 else: diff --git a/geoutils/spatial_tools.py b/geoutils/spatial_tools.py index 1bba8b299..ff133d528 100644 --- a/geoutils/spatial_tools.py +++ b/geoutils/spatial_tools.py @@ -481,26 +481,27 @@ def subdivide_array(shape: tuple[int, ...], count: int) -> np.ndarray: def get_xy_rotated(raster: Raster, along_track_angle: float) -> tuple[np.ndarray, np.ndarray]: """ Rotate x, y axes of image to get along- and cross-track distances. - :param raster: raster to get x,y positions from. - :param along_track_angle: angle by which to rotate axes (degrees) - :returns xxr, yyr: arrays corresponding to along (x) and cross (y) track distances. + :param raster: Raster to get x,y positions from. + :param along_track_angle: Angle by which to rotate axes (degrees) + + :returns xxr, yyr: Arrays corresponding to along (x) and cross (y) track distances. """ myang = np.deg2rad(along_track_angle) - # get grid coordinates + # Get grid coordinates xx, yy = raster.coords(grid=True) xx -= np.min(xx) yy -= np.min(yy) - # get rotated coordinates + # Get rotated coordinates - # for along-track - xxr = np.multiply(xx, np.cos(myang)) + np.multiply(-1 * yy, np.sin(along_track_angle)) - # for cross-track - yyr = np.multiply(xx, np.sin(myang)) + np.multiply(yy, np.cos(along_track_angle)) + # For along-track + xxr = xx * np.cos(myang) - yy * np.sin(myang) + # For cross-track + yyr = xx * np.sin(myang) + yy * np.cos(myang) - # re-initialize coordinate at zero + # Re-initialize coordinate at zero xxr -= np.nanmin(xxr) yyr -= np.nanmin(yyr) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index c0b790641..a81541084 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -1641,7 +1641,13 @@ def test_xy2ij_and_interp(self) -> None: x = 493120.0 y = 3101000.0 i, j = r.xy2ij(x, y) - assert img[0, int(i), int(j)] == r.interp_points([(x, y)], order=1)[0] + val = r.interp_points([(x, y)], order=1)[0] + assert img[0, int(i), int(j)] == val + + # Finally, check that interp convert to latlon + lat, lon = gu.projtools.reproject_to_latlon((x,y), in_crs=r.crs) + val_latlon = r.interp_points([(lat, lon)], order=1, input_latlon=True)[0] + assert val == pytest.approx(val_latlon, abs=0.0001) def test_value_at_coords(self) -> None: """ @@ -2195,6 +2201,12 @@ def test_save(self, example: str) -> None: with pytest.warns(UserWarning): img.save(TemporaryFile()) + # Test with blank argument + img.save(temp_file.name, blank_value=0) + saved = gu.Raster(temp_file.name) + + assert np.array_equal(saved.data.data, np.zeros(np.shape(saved.data))) + # Clean up temporary folder - fails on Windows try: temp_dir.cleanup() diff --git a/tests/test_spatial_tools.py b/tests/test_spatial_tools.py index 73de92d62..81ffe4498 100644 --- a/tests/test_spatial_tools.py +++ b/tests/test_spatial_tools.py @@ -430,3 +430,125 @@ def test_subsample(self, array: np.ndarray) -> None: assert len(indices) == np.ndim(array) assert np.ndim(array[indices]) == 1 assert np.size(array[indices]) == int(np.size(array) * 0.3) + +class TestRasterTools: + + def test_get_valid_extent(self) -> None: + """Check the function to get valid extent.""" + + # Create an artificial array and masked array + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + + # For no unvalid values, the function should return the edges + # For the array + assert (0, 4, 0, 4) == gu.spatial_tools.get_valid_extent(arr) + # For the masked-array + assert (0, 4, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) + + # 1/ First column: + # If we mask it in the masked array + mask_ma[0, :] = np.ma.masked + assert (1, 4, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[0, :] = np.nan + assert (1, 4, 0, 4) == gu.spatial_tools.get_valid_extent(arr) + mask_ma.data[0, :] = np.nan + mask_ma.mask = False + assert (1, 4, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) + + # 2/ First row: + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + # If we mask it in the masked array + mask_ma[:, 0] = np.ma.masked + assert (0, 4, 1, 4) == gu.spatial_tools.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[:, 0] = np.nan + assert (0, 4, 1, 4) == gu.spatial_tools.get_valid_extent(arr) + mask_ma.data[:, 0] = np.nan + mask_ma.mask = False + assert (0, 4, 1, 4) == gu.spatial_tools.get_valid_extent(mask_ma) + + # 3/ Last column: + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + + # If we mask it in the masked array + mask_ma[-1, :] = np.ma.masked + assert (0, 3, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[-1, :] = np.nan + assert (0, 3, 0, 4) == gu.spatial_tools.get_valid_extent(arr) + mask_ma.data[-1, :] = np.nan + mask_ma.mask = False + assert (0, 3, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) + + # 4/ Last row: + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + + # If we mask it in the masked array + mask_ma[:, -1] = np.ma.masked + assert (0, 4, 0, 3) == gu.spatial_tools.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[:, -1] = np.nan + assert (0, 4, 0, 3) == gu.spatial_tools.get_valid_extent(arr) + mask_ma.data[:, -1] = np.nan + mask_ma.mask = False + assert (0, 4, 0, 3) == gu.spatial_tools.get_valid_extent(mask_ma) + + + def test_get_xy_rotated(self): + """Check the function to rotate array.""" + + # Create an artificial raster + width = height = 5 + transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) + r1 = gu.Raster.from_array(np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None) + + # First, we get initial coords + xx, yy = r1.coords(grid=True) + + # Rotating the coordinates 90 degrees should be the same as rotating the array + xx90, yy90 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=90) + assert np.allclose(np.rot90(xx90), xx) + assert np.allclose(np.rot90(yy90), yy) + + # Same for 180 degrees + xx180, yy180 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=180) + assert np.allclose(np.rot90(xx180, k=2), xx) + assert np.allclose(np.rot90(yy180, k=2), yy) + + # Same for 270 degrees + xx270, yy270 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=270) + assert np.allclose(np.rot90(xx270, k=3), xx) + assert np.allclose(np.rot90(yy270, k=3), yy) + + # 360 degrees should get us back on our feet + xx360, yy360 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=360) + assert np.allclose(xx360, xx) + assert np.allclose(yy360, yy) + + # Test that the values make sense for 45 degrees + xx45, yy45 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=45) + # Should have zero on the upper left corner for xx + assert xx45[0, 0] == pytest.approx(0) + # Then a multiple of sqrt2 along each dimension + assert xx45[1, 0] == pytest.approx(xx45[0, 1]) == pytest.approx(0.1 * np.sqrt(2)) + # The lower right corner should have the highest coordinate (0.8) times sqrt(2) + assert xx45[-1, -1] == pytest.approx(np.max(xx) * np.sqrt(2)) + + # Finally, yy should be rotated by 90 + assert np.allclose(np.rot90(xx45), yy45) + + + xx, yy = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=90) From fd9f4867e5bfb035a534cafce8cba5137d13c587 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 16:54:11 -0800 Subject: [PATCH 105/184] Add tests for geoviewer --- geoutils/georaster/raster.py | 2 +- geoutils/geoviewer.py | 24 ++++++++++------------- tests/test_geoviewer.py | 38 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 tests/test_geoviewer.py diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 580ea9fff..8d79e3f05 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -2256,7 +2256,7 @@ def show( if index is None: index = np.arange(1, self.count + 1) elif isinstance(index, int): - if index >= self.count: + if index > self.count: raise ValueError(f"Index must be in range 1-{self.count:d}") pass else: diff --git a/geoutils/geoviewer.py b/geoutils/geoviewer.py index 0d404fd81..db7248e26 100755 --- a/geoutils/geoviewer.py +++ b/geoutils/geoviewer.py @@ -10,7 +10,7 @@ import argparse import sys -from typing import Any +import os import matplotlib.pyplot as plt import numpy as np @@ -117,34 +117,31 @@ def getparser() -> argparse.Namespace: help="True or False, if False then allow dynamic image downscaling, if True, prevent it.", ) - args = parser.parse_args() + return parser - return args - -def main() -> None: +def main(args=None) -> None: # Parse arguments - args = getparser() + parser = getparser() + args = parser.parse_args(args) # Load image metadata # img = Raster(args.filename, load_data=False) - xmin, xmax, ymin, ymax = img.bounds # Resample if image is too large # if ((img.width > args.max_size) or (img.height > args.max_size)) & (not args.noresampl): dfact = max(int(img.width / args.max_size), int(img.height / args.max_size)) print(f"Image will be downsampled by a factor {dfact}.") - new_shape: tuple[Any, ...] | None = (img.count, int(img.height / dfact), int(img.width / dfact)) else: - new_shape = None + dfact = 1 # Read image # - img.load(out_shape=new_shape) + img = Raster(args.filename, downsample=dfact) # Set no data value if args.nodata == "default": - nodata = img.nodata + pass else: try: nodata = float(args.nodata) @@ -201,8 +198,7 @@ def main() -> None: figsize = plt.rcParams["figure.figsize"] else: try: - figsize = tuple(int(arg) for arg in args.figsize) - xfigsize, yfigsize = figsize + figsize = tuple(int(arg) for arg in args.figsize.split(",")) except Exception as exception: print("ERROR: figsize must be a tuple of size 2, currently set to %s" % args.figsize) sys.stderr.write(str(exception)) @@ -225,7 +221,7 @@ def main() -> None: # plot img.show( ax=ax, - band=args.band, + index=args.band, cmap=cmap, interpolation="nearest", vmin=vmin, diff --git a/tests/test_geoviewer.py b/tests/test_geoviewer.py new file mode 100644 index 000000000..8825e4f5c --- /dev/null +++ b/tests/test_geoviewer.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import os +import pytest + +import matplotlib.pyplot as plt +import geoutils.geoviewer as gv +import geoutils as gu + +@pytest.mark.parametrize("filename", [gu.examples.get_path("everest_landsat_b4"), + gu.examples.get_path("exploradores_aster_dem")]) +@pytest.mark.parametrize("option", + (("-cmap", "Reds"), ("-vmin", "-10", "-vmax", "10"), + ("-band", "1"), ("-nocb", ), ("-clabel", "Test"), + ("-figsize", "8,8"), ("-max_size", "1000"), + ("-save", "test.png"), ("-dpi", "300"), + ("-nodata", "99"), ("-noresampl", ))) +def test_geoviewer(capsys, monkeypatch, filename, option): + + # To avoid having the plots popping up during execution + monkeypatch.setattr(plt, "show", lambda: None) + + # To not get exception when testing generic functions such as --help + try: + gv.main([filename, *option]) + except SystemExit: + pass + + # Capture error output (not stdout, just a plot) + output = capsys.readouterr().err + + # No error should be raised + assert output == '' + + # Remove file if it was created + if option[0] == "-save": + if os.path.exists("test.png"): + os.remove("test.png") From 47827d27f385273dd97973f99f65459c5fc798d8 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 17:03:50 -0800 Subject: [PATCH 106/184] Linting --- geoutils/georaster/raster.py | 8 +++----- geoutils/geoviewer.py | 18 +++++++++--------- tests/test_georaster.py | 2 +- tests/test_geoviewer.py | 37 ++++++++++++++++++++++++------------ tests/test_spatial_tools.py | 12 ++++++------ 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 8d79e3f05..f5df0ab30 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -1850,9 +1850,7 @@ def reproject( raise ValueError("Reference raster does not exist.") ds_ref = Raster(dst_ref, load_data=False) else: - raise TypeError( - "Type of dst_ref not understood, must be path to file (str), Raster." - ) + raise TypeError("Type of dst_ref not understood, must be path to file (str), Raster.") # Read reprojecting params from ref raster dst_crs = ds_ref.crs @@ -3070,7 +3068,7 @@ def _repr_html_(self) -> str: def reproject( self: Mask, - dst_ref: RasterType | rio.io.Dataset | str | None = None, + dst_ref: RasterType | str | None = None, dst_crs: CRS | str | int | None = None, dst_size: tuple[int, int] | None = None, dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, @@ -3096,7 +3094,7 @@ def reproject( # Call Raster.reproject() output = super().reproject( - dst_ref=dst_ref, + dst_ref=dst_ref, # type: ignore dst_crs=dst_crs, dst_size=dst_size, dst_bounds=dst_bounds, diff --git a/geoutils/geoviewer.py b/geoutils/geoviewer.py index db7248e26..cf397ead1 100755 --- a/geoutils/geoviewer.py +++ b/geoutils/geoviewer.py @@ -10,7 +10,7 @@ import argparse import sys -import os +from typing import Sequence import matplotlib.pyplot as plt import numpy as np @@ -18,7 +18,7 @@ from geoutils.georaster import Raster -def getparser() -> argparse.Namespace: +def getparser() -> argparse.ArgumentParser: # Set up description parser = argparse.ArgumentParser(description="Visualisation tool for any image supported by GDAL.") @@ -120,23 +120,23 @@ def getparser() -> argparse.Namespace: return parser -def main(args=None) -> None: +def main(test_args: Sequence[str] | None = None) -> None: # Parse arguments parser = getparser() - args = parser.parse_args(args) + args = parser.parse_args(test_args) # type: ignore - # Load image metadata # + # Load image metadata img = Raster(args.filename, load_data=False) - # Resample if image is too large # + # Resample if image is too large if ((img.width > args.max_size) or (img.height > args.max_size)) & (not args.noresampl): dfact = max(int(img.width / args.max_size), int(img.height / args.max_size)) print(f"Image will be downsampled by a factor {dfact}.") else: dfact = 1 - # Read image # + # Read image img = Raster(args.filename, downsample=dfact) # Set no data value @@ -150,7 +150,7 @@ def main(args=None) -> None: img.set_nodata(nodata) - # Set default parameters # + # Set default parameters # vmin if args.vmin is not None: @@ -213,7 +213,7 @@ def main(args=None) -> None: except ValueError: raise ValueError("ERROR: dpi must be an integer, currently set to %s" % args.dpi) - # Plot data # + # Plot data fig = plt.figure(figsize=figsize) ax = fig.add_subplot(111) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index a81541084..099d7c6cc 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -1645,7 +1645,7 @@ def test_xy2ij_and_interp(self) -> None: assert img[0, int(i), int(j)] == val # Finally, check that interp convert to latlon - lat, lon = gu.projtools.reproject_to_latlon((x,y), in_crs=r.crs) + lat, lon = gu.projtools.reproject_to_latlon((x, y), in_crs=r.crs) val_latlon = r.interp_points([(lat, lon)], order=1, input_latlon=True)[0] assert val == pytest.approx(val_latlon, abs=0.0001) diff --git a/tests/test_geoviewer.py b/tests/test_geoviewer.py index 8825e4f5c..0c80e2280 100644 --- a/tests/test_geoviewer.py +++ b/tests/test_geoviewer.py @@ -1,21 +1,34 @@ from __future__ import annotations import os -import pytest import matplotlib.pyplot as plt -import geoutils.geoviewer as gv +import pytest + import geoutils as gu +import geoutils.geoviewer as gv + -@pytest.mark.parametrize("filename", [gu.examples.get_path("everest_landsat_b4"), - gu.examples.get_path("exploradores_aster_dem")]) -@pytest.mark.parametrize("option", - (("-cmap", "Reds"), ("-vmin", "-10", "-vmax", "10"), - ("-band", "1"), ("-nocb", ), ("-clabel", "Test"), - ("-figsize", "8,8"), ("-max_size", "1000"), - ("-save", "test.png"), ("-dpi", "300"), - ("-nodata", "99"), ("-noresampl", ))) -def test_geoviewer(capsys, monkeypatch, filename, option): +@pytest.mark.parametrize( + "filename", [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")] +) # type: ignore +@pytest.mark.parametrize( + "option", + ( + ("-cmap", "Reds"), + ("-vmin", "-10", "-vmax", "10"), + ("-band", "1"), + ("-nocb",), + ("-clabel", "Test"), + ("-figsize", "8,8"), + ("-max_size", "1000"), + ("-save", "test.png"), + ("-dpi", "300"), + ("-nodata", "99"), + ("-noresampl",), + ), +) # type: ignore +def test_geoviewer(capsys, monkeypatch, filename, option): # type: ignore # To avoid having the plots popping up during execution monkeypatch.setattr(plt, "show", lambda: None) @@ -30,7 +43,7 @@ def test_geoviewer(capsys, monkeypatch, filename, option): output = capsys.readouterr().err # No error should be raised - assert output == '' + assert output == "" # Remove file if it was created if option[0] == "-save": diff --git a/tests/test_spatial_tools.py b/tests/test_spatial_tools.py index 81ffe4498..7d0ac3822 100644 --- a/tests/test_spatial_tools.py +++ b/tests/test_spatial_tools.py @@ -431,8 +431,8 @@ def test_subsample(self, array: np.ndarray) -> None: assert np.ndim(array[indices]) == 1 assert np.size(array[indices]) == int(np.size(array) * 0.3) -class TestRasterTools: +class TestRasterTools: def test_get_valid_extent(self) -> None: """Check the function to get valid extent.""" @@ -441,7 +441,7 @@ def test_get_valid_extent(self) -> None: arr_mask = np.zeros(shape=(5, 5), dtype=bool) mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) - # For no unvalid values, the function should return the edges + # For no invalid values, the function should return the edges # For the array assert (0, 4, 0, 4) == gu.spatial_tools.get_valid_extent(arr) # For the masked-array @@ -506,14 +506,15 @@ def test_get_valid_extent(self) -> None: mask_ma.mask = False assert (0, 4, 0, 3) == gu.spatial_tools.get_valid_extent(mask_ma) - - def test_get_xy_rotated(self): + def test_get_xy_rotated(self) -> None: """Check the function to rotate array.""" # Create an artificial raster width = height = 5 transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) - r1 = gu.Raster.from_array(np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None) + r1 = gu.Raster.from_array( + np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None + ) # First, we get initial coords xx, yy = r1.coords(grid=True) @@ -550,5 +551,4 @@ def test_get_xy_rotated(self): # Finally, yy should be rotated by 90 assert np.allclose(np.rot90(xx45), yy45) - xx, yy = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=90) From 2aa785d808c44ee6e19fbd514a066609c6068ae1 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 17:18:27 -0800 Subject: [PATCH 107/184] Add tests in satimg --- tests/test_satimg.py | 60 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/test_satimg.py b/tests/test_satimg.py index 4217555c6..e9c0131b7 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -1,6 +1,7 @@ """ Test functions for SatelliteImage class """ +import datetime import datetime as dt import sys from io import StringIO @@ -220,3 +221,62 @@ def test_sw_tile_naming_parsing(self) -> None: # same here assert gu.georaster.satimg.latlon_to_sw_naming((0, -180)) == "N00W180" assert gu.georaster.satimg.latlon_to_sw_naming((0, 180)) == "N00W180" + + def test_parse_tile_attr_from_name(self) -> None: + """Test the parsing of tile attribute from tile name.""" + + # For ASTER, SRTM, NASADEM: 1x1 tiling globally + y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N01W179", product="SRTMGL1") + + assert y == 1 + assert x == -179 + assert size == (1, 1) + assert epsg == 4326 + + # For TanDEM-X: depends on latitude + # Mid-latitude is 2 x 1 + y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N62E04", product="TDM1") + assert y == 62 + assert x == 4 + assert size == (1, 2) + assert epsg == 4326 + + # Low-latitude is 1 x 1 + y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N52E04", product="TDM1") + assert y == 52 + assert x == 4 + assert size == (1, 1) + assert epsg == 4326 + + # High-latitude is 5 x 1 + y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N82E04", product="TDM1") + assert y == 82 + assert x == 4 + assert size == (1, 4) + assert epsg == 4326 + + def test_parse_landsat(self) -> None: + """Test the parsing of landsat metadata from name.""" + + # Landsat 1 + landsat1 = "LM10170391976031AAA01.tif" + attrs1 = gu.georaster.satimg.parse_landsat(landsat1) + + assert attrs1[0] == "Landsat 1" + assert attrs1[1] == "MSS" + assert attrs1[-1] == datetime.datetime(1976, 1, 31) + + # Landsat 7 example + landsat7 = "LE71400412000304SGS00_B4.tif" + attrs7 = gu.georaster.satimg.parse_landsat(landsat7) + + assert attrs7[0] == "Landsat 7" + assert attrs7[1] == "ETM+" + assert attrs7[-1] == datetime.datetime(2000, 10, 30) + + + + + + + From 531147f0a54e40776ba1231d805f79a0a241c2a5 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 17:18:56 -0800 Subject: [PATCH 108/184] Linting --- tests/test_satimg.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/test_satimg.py b/tests/test_satimg.py index e9c0131b7..a87004ffe 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -266,17 +266,10 @@ def test_parse_landsat(self) -> None: assert attrs1[1] == "MSS" assert attrs1[-1] == datetime.datetime(1976, 1, 31) - # Landsat 7 example + # Landsat 7 example landsat7 = "LE71400412000304SGS00_B4.tif" attrs7 = gu.georaster.satimg.parse_landsat(landsat7) assert attrs7[0] == "Landsat 7" assert attrs7[1] == "ETM+" assert attrs7[-1] == datetime.datetime(2000, 10, 30) - - - - - - - From 080f3c4ab8ddc8a2f8693e7c89c09829571e4707 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 17:40:00 -0800 Subject: [PATCH 109/184] Homogenize errors and add tests for geoviewer --- geoutils/geoviewer.py | 15 +++++---------- tests/test_geoviewer.py | 26 +++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/geoutils/geoviewer.py b/geoutils/geoviewer.py index cf397ead1..9fa491258 100755 --- a/geoutils/geoviewer.py +++ b/geoutils/geoviewer.py @@ -146,7 +146,7 @@ def main(test_args: Sequence[str] | None = None) -> None: try: nodata = float(args.nodata) except ValueError: - raise ValueError("ERROR: nodata must be a float, currently set to %s" % args.nodata) + raise ValueError("Nodata must be a float, currently set to %s" % args.nodata) img.set_nodata(nodata) @@ -188,10 +188,7 @@ def main(test_args: Sequence[str] | None = None) -> None: elif args.cmap in plt.cm.datad.keys(): cmap = args.cmap else: - print("ERROR: cmap set to %s, must be in:" % args.cmap) - for i, elem in enumerate(plt.cm.datad.keys(), 1): - print(str(elem), end="\n" if i % 10 == 0 else ", ") - sys.exit(1) + raise ValueError("Wrong cmap, must be in: {}".format(",".join(str(elem) for elem in plt.cm.datad.keys()))) # Figsize if args.figsize == "default": @@ -199,10 +196,8 @@ def main(test_args: Sequence[str] | None = None) -> None: else: try: figsize = tuple(int(arg) for arg in args.figsize.split(",")) - except Exception as exception: - print("ERROR: figsize must be a tuple of size 2, currently set to %s" % args.figsize) - sys.stderr.write(str(exception)) - sys.exit(1) + except Exception: + raise ValueError("Figsize must be a tuple of size 2, currently set to %s" % args.figsize) # dpi if args.dpi == "default": @@ -211,7 +206,7 @@ def main(test_args: Sequence[str] | None = None) -> None: try: dpi = int(args.dpi) except ValueError: - raise ValueError("ERROR: dpi must be an integer, currently set to %s" % args.dpi) + raise ValueError("dpi must be an integer, currently set to %s" % args.dpi) # Plot data diff --git a/tests/test_geoviewer.py b/tests/test_geoviewer.py index 0c80e2280..8a6321b2f 100644 --- a/tests/test_geoviewer.py +++ b/tests/test_geoviewer.py @@ -28,7 +28,7 @@ ("-noresampl",), ), ) # type: ignore -def test_geoviewer(capsys, monkeypatch, filename, option): # type: ignore +def test_geoviewer_valid(capsys, monkeypatch, filename, option): # type: ignore # To avoid having the plots popping up during execution monkeypatch.setattr(plt, "show", lambda: None) @@ -49,3 +49,27 @@ def test_geoviewer(capsys, monkeypatch, filename, option): # type: ignore if option[0] == "-save": if os.path.exists("test.png"): os.remove("test.png") + +@pytest.mark.parametrize( + "filename", [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")] +) # type: ignore +@pytest.mark.parametrize( + "option", + ( + ("-cmap", "Lols"), + ("-vmin", "lol"), + ("-vmin", "lol2"), + ("-figsize", "blabla"), + ("-dpi", "300.5"), + ("-nodata", "lol"), + ), +) # type: ignore +def test_geoviewer_invalid(capsys, monkeypatch, filename, option): # type: ignore + + # To avoid having the plots popping up during execution + monkeypatch.setattr(plt, "show", lambda: None) + + # To not get exception when testing generic functions such as --help + with pytest.raises(ValueError): + gv.main([filename, *option]) + From 30689c461608c5f5c40d12b0f4a4e6a119ee0113 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 17:43:37 -0800 Subject: [PATCH 110/184] Add random state tests for subsample --- tests/test_spatial_tools.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_spatial_tools.py b/tests/test_spatial_tools.py index 7d0ac3822..9cf1717fb 100644 --- a/tests/test_spatial_tools.py +++ b/tests/test_spatial_tools.py @@ -431,6 +431,16 @@ def test_subsample(self, array: np.ndarray) -> None: assert np.ndim(array[indices]) == 1 assert np.size(array[indices]) == int(np.size(array) * 0.3) + # Check that we can pass an integer to fix the random state + sub42 = gu.spatial_tools.subsample_raster(array, subsample=10, random_state=42) + # Check by passing a generator directly + random_gen = np.random.RandomState(np.random.MT19937(np.random.SeedSequence(42))) + sub42_gen = gu.spatial_tools.subsample_raster(array, subsample=10, random_state=random_gen) + # Both should be equal + assert np.array_equal(sub42, sub42_gen) + + + class TestRasterTools: def test_get_valid_extent(self) -> None: From 02fb399c0ce7a0a22cb1075c492b1ff1351fac6e Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 17:44:11 -0800 Subject: [PATCH 111/184] Linting --- geoutils/geoviewer.py | 1 - tests/test_geoviewer.py | 2 +- tests/test_spatial_tools.py | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/geoutils/geoviewer.py b/geoutils/geoviewer.py index 9fa491258..77be6b445 100755 --- a/geoutils/geoviewer.py +++ b/geoutils/geoviewer.py @@ -9,7 +9,6 @@ from __future__ import annotations import argparse -import sys from typing import Sequence import matplotlib.pyplot as plt diff --git a/tests/test_geoviewer.py b/tests/test_geoviewer.py index 8a6321b2f..25094f75e 100644 --- a/tests/test_geoviewer.py +++ b/tests/test_geoviewer.py @@ -50,6 +50,7 @@ def test_geoviewer_valid(capsys, monkeypatch, filename, option): # type: ignore if os.path.exists("test.png"): os.remove("test.png") + @pytest.mark.parametrize( "filename", [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")] ) # type: ignore @@ -72,4 +73,3 @@ def test_geoviewer_invalid(capsys, monkeypatch, filename, option): # type: igno # To not get exception when testing generic functions such as --help with pytest.raises(ValueError): gv.main([filename, *option]) - diff --git a/tests/test_spatial_tools.py b/tests/test_spatial_tools.py index 9cf1717fb..fffbda6a7 100644 --- a/tests/test_spatial_tools.py +++ b/tests/test_spatial_tools.py @@ -440,8 +440,6 @@ def test_subsample(self, array: np.ndarray) -> None: assert np.array_equal(sub42, sub42_gen) - - class TestRasterTools: def test_get_valid_extent(self) -> None: """Check the function to get valid extent.""" From 14e429d37015df6cdc21e360238cbddf986609a0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 17:58:38 -0800 Subject: [PATCH 112/184] Add coveragerc file to exclude overload and importerrors that do not need to be tested --- .coveragerc | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 000000000..dda948d5c --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[report] +exclude_lines = + pragma: not covered + @overload + except ImportError \ No newline at end of file From 56bb9f10f2495f2b76c1291b51e119f084b14abb Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 18:05:25 -0800 Subject: [PATCH 113/184] Linting --- .coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index dda948d5c..c53e3f29f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,4 +2,4 @@ exclude_lines = pragma: not covered @overload - except ImportError \ No newline at end of file + except ImportError From b561c602a29de163ba575a9a32c90a5451715d71 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 9 Mar 2023 18:20:51 -0800 Subject: [PATCH 114/184] Fix tempfile error on Windows --- tests/test_georaster.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 099d7c6cc..9e65ee8c9 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2202,6 +2202,7 @@ def test_save(self, example: str) -> None: img.save(TemporaryFile()) # Test with blank argument + temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) img.save(temp_file.name, blank_value=0) saved = gu.Raster(temp_file.name) From 02e7bfb416247d9a3f0160ffdeea8f16ce620b29 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 10 Mar 2023 15:01:23 -0800 Subject: [PATCH 115/184] Add tests for coords(), that was malfunctioning --- tests/test_georaster.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 9e65ee8c9..919e30ec1 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2214,32 +2214,42 @@ def test_save(self, example: str) -> None: except (NotADirectoryError, PermissionError): pass - def test_coords(self) -> None: + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) + def test_coords(self, example: str) -> None: img = gu.Raster(self.landsat_b4_path) - xx, yy = img.coords(offset="corner") - assert xx.min() == pytest.approx(img.bounds.left) - assert xx.max() == pytest.approx(img.bounds.right - img.res[0]) + + # With corner argument + xx0, yy0 = img.coords(offset="corner", grid=False) + + assert xx0[0] == pytest.approx(img.bounds.left) + assert xx0[-1] == pytest.approx(img.bounds.right - img.res[0]) if img.res[1] > 0: - assert yy.min() == pytest.approx(img.bounds.bottom) - assert yy.max() == pytest.approx(img.bounds.top - img.res[1]) + assert yy0[0] == pytest.approx(img.bounds.bottom) + assert yy0[-1] == pytest.approx(img.bounds.top - img.res[1]) else: # Currently not covered by test image - assert yy.min() == pytest.approx(img.bounds.top) - assert yy.max() == pytest.approx(img.bounds.bottom + img.res[1]) + assert yy0[0] == pytest.approx(img.bounds.top) + assert yy0[-1] == pytest.approx(img.bounds.bottom + img.res[1]) - xx, yy = img.coords(offset="center") + # With center argument + xx, yy = img.coords(offset="center", grid=False) hx = img.res[0] / 2 hy = img.res[1] / 2 - assert xx.min() == pytest.approx(img.bounds.left + hx) - assert xx.max() == pytest.approx(img.bounds.right - hx) + assert xx[0] == pytest.approx(img.bounds.left + hx) + assert xx[-1] == pytest.approx(img.bounds.right - hx) if img.res[1] > 0: - assert yy.min() == pytest.approx(img.bounds.bottom + hy) - assert yy.max() == pytest.approx(img.bounds.top - hy) + assert yy[0] == pytest.approx(img.bounds.bottom + hy) + assert yy[-1] == pytest.approx(img.bounds.top - hy) else: # Currently not covered by test image - assert yy.min() == pytest.approx(img.bounds.top + hy) - assert yy.max() == pytest.approx(img.bounds.bottom - hy) + assert yy[-1] == pytest.approx(img.bounds.top + hy) + assert yy[0] == pytest.approx(img.bounds.bottom - hy) + + # With grid argument (default argument, repeated here for clarity) + xxgrid, yygrid = img.coords(offset="corner", grid=True) + assert np.array_equal(xxgrid, np.repeat(xx0[np.newaxis, :], img.height, axis=0)) + assert np.array_equal(yygrid, np.flipud(np.repeat(yy0[:, np.newaxis], img.width, axis=1))) def test_from_array(self) -> None: From 6c7d7f25782c7baa8240461bdbb6a59ec5a51719 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Mar 2023 14:27:57 -0700 Subject: [PATCH 116/184] Reorganize spatial tools into georaster/ modules --- geoutils/__init__.py | 1 - geoutils/georaster/__init__.py | 3 + geoutils/georaster/array.py | 108 ++++ .../multiraster.py} | 255 +------- geoutils/georaster/raster.py | 20 +- geoutils/georaster/sampling.py | 154 +++++ tests/test_array.py | 213 +++++++ tests/test_georaster.py | 8 +- tests/test_multiraster.py | 278 +++++++++ tests/test_sampling.py | 87 +++ tests/test_spatial_tools.py | 562 ------------------ 11 files changed, 867 insertions(+), 822 deletions(-) create mode 100644 geoutils/georaster/array.py rename geoutils/{spatial_tools.py => georaster/multiraster.py} (56%) create mode 100644 geoutils/georaster/sampling.py create mode 100644 tests/test_array.py create mode 100644 tests/test_multiraster.py create mode 100644 tests/test_sampling.py delete mode 100644 tests/test_spatial_tools.py diff --git a/geoutils/__init__.py b/geoutils/__init__.py index 897e8c077..b1659e23b 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -2,7 +2,6 @@ GeoUtils is a python package of raster and vector tools. """ -from geoutils import spatial_tools # noqa from geoutils import examples, georaster, geovector, projtools # noqa from geoutils.georaster import Mask, Raster, SatelliteImage # noqa from geoutils.geovector import Vector # noqa diff --git a/geoutils/georaster/__init__.py b/geoutils/georaster/__init__.py index a02755db4..0eee67dbc 100644 --- a/geoutils/georaster/__init__.py +++ b/geoutils/georaster/__init__.py @@ -1,4 +1,7 @@ from geoutils.georaster.raster import Raster, RasterType, Mask # noqa isort:skip from geoutils.georaster.satimg import SatelliteImage # noqa +from geoutils.georaster.array import * # noqa +from geoutils.georaster.sampling import * # noqa +from geoutils.georaster.multiraster import * # noqa __all__ = ["RasterType", "Raster"] diff --git a/geoutils/georaster/array.py b/geoutils/georaster/array.py new file mode 100644 index 000000000..104ba2a56 --- /dev/null +++ b/geoutils/georaster/array.py @@ -0,0 +1,108 @@ +"""Array tools related to rasters.""" + +from __future__ import annotations + +import warnings + +import numpy as np +import geoutils as gu + +def get_mask(array: np.ndarray | np.ma.masked_array) -> np.ndarray: + """ + Return the mask of invalid values, whether array is a ndarray with NaNs or a np.ma.masked_array. + + :param array: Input array. + + :returns invalid_mask: boolean array, True where array is masked or Nan. + """ + mask = (array.mask | ~np.isfinite(array.data)) if isinstance(array, np.ma.masked_array) else ~np.isfinite(array) + return mask.squeeze() + + +def get_array_and_mask( + array: np.ndarray | np.ma.masked_array, check_shape: bool = True, copy: bool = True +) -> tuple[np.ndarray, np.ndarray]: + """ + Return array with masked values set to NaN and the associated mask. + Works whether array is a ndarray with NaNs or a np.ma.masked_array. + + :param array: Input array. + :param check_shape: Validate that the array is either a 1D array, a 2D array or a 3D array of shape (1, rows, cols). + :param copy: Return a copy of 'array'. If False, a view will be attempted (and warn if not possible) + + :returns array_data, invalid_mask: a tuple of ndarrays. First is array with invalid pixels converted to NaN, \ + second is mask of invalid pixels (True if invalid). + """ + # + if isinstance(array, gu.Raster): + array = array.data + + if check_shape: + if len(array.shape) > 2 and array.shape[0] > 1: + raise ValueError( + f"Invalid array shape given: {array.shape}." "Expected 2D array or 3D array where arr.shape[0] == 1" + ) + + # If an occupied mask exists and a view was requested, trigger a warning. + if not copy and np.any(getattr(array, "mask", False)): + warnings.warn("Copying is required to respect the mask. Returning copy. Set 'copy=True' to hide this message.") + copy = True + + # If array is of type integer and has a mask, it needs to be converted to float (to assign nans) + if np.any(getattr(array, "mask", False)) and np.issubdtype(array.dtype, np.integer): # type: ignore + array = array.astype(np.float32) # type: ignore + + # Convert into a regular ndarray (a view or copy depending on the 'copy' argument) + array_data = np.array(array).squeeze() if copy else np.asarray(array).squeeze() + + # Get the mask of invalid pixels and set nans if it is occupied. + invalid_mask = get_mask(array) + if np.any(invalid_mask): + array_data[invalid_mask] = np.nan + + return array_data, invalid_mask + + +def get_valid_extent(array: np.ndarray | np.ma.masked_array) -> tuple[int, ...]: + """ + Return (rowmin, rowmax, colmin, colmax), the first/last row/column of array with valid pixels + """ + if not array.dtype == "bool": + valid_mask = ~get_mask(array) + else: + valid_mask = array + cols_nonzero = np.where(np.count_nonzero(valid_mask, axis=0) > 0)[0] + rows_nonzero = np.where(np.count_nonzero(valid_mask, axis=1) > 0)[0] + return rows_nonzero[0], rows_nonzero[-1], cols_nonzero[0], cols_nonzero[-1] + + +def get_xy_rotated(raster: gu.Raster, along_track_angle: float) -> tuple[np.ndarray, np.ndarray]: + """ + Rotate x, y axes of image to get along- and cross-track distances. + :param raster: Raster to get x,y positions from. + :param along_track_angle: Angle by which to rotate axes (degrees) + + :returns xxr, yyr: Arrays corresponding to along (x) and cross (y) track distances. + """ + + myang = np.deg2rad(along_track_angle) + + # Get grid coordinates + xx, yy = raster.coords(grid=True) + xx -= np.min(xx) + yy -= np.min(yy) + + # Get rotated coordinates + + # For along-track + xxr = xx * np.cos(myang) - yy * np.sin(myang) + # For cross-track + yyr = xx * np.sin(myang) + yy * np.cos(myang) + + # Re-initialize coordinate at zero + xxr -= np.nanmin(xxr) + yyr -= np.nanmin(yyr) + + return xxr, yyr + + diff --git a/geoutils/spatial_tools.py b/geoutils/georaster/multiraster.py similarity index 56% rename from geoutils/spatial_tools.py rename to geoutils/georaster/multiraster.py index ff133d528..9a3e1abc4 100644 --- a/geoutils/spatial_tools.py +++ b/geoutils/georaster/multiraster.py @@ -1,9 +1,4 @@ -"""Basic operations to be run on 2D image arrays - -Optional dependencies: - skimage.transform (@subdivide_array) - -""" +"""Multiple rasters tools.""" from __future__ import annotations import warnings @@ -15,79 +10,11 @@ from tqdm import tqdm import geoutils as gu -from geoutils.georaster import Raster, RasterType +from geoutils.georaster import Raster, RasterType, get_array_and_mask from geoutils.georaster.raster import _default_nodata from geoutils.misc import resampling_method_from_str -def get_mask(array: np.ndarray | np.ma.masked_array) -> np.ndarray: - """ - Return the mask of invalid values, whether array is a ndarray with NaNs or a np.ma.masked_array. - - :param array: Input array. - - :returns invalid_mask: boolean array, True where array is masked or Nan. - """ - mask = (array.mask | ~np.isfinite(array.data)) if isinstance(array, np.ma.masked_array) else ~np.isfinite(array) - return mask.squeeze() - - -def get_array_and_mask( - array: np.ndarray | np.ma.masked_array | RasterType, check_shape: bool = True, copy: bool = True -) -> tuple[np.ndarray, np.ndarray]: - """ - Return array with masked values set to NaN and the associated mask. - Works whether array is a ndarray with NaNs or a np.ma.masked_array. - - :param array: Input array. - :param check_shape: Validate that the array is either a 1D array, a 2D array or a 3D array of shape (1, rows, cols). - :param copy: Return a copy of 'array'. If False, a view will be attempted (and warn if not possible) - - :returns array_data, invalid_mask: a tuple of ndarrays. First is array with invalid pixels converted to NaN, \ - second is mask of invalid pixels (True if invalid). - """ - if isinstance(array, gu.Raster): - array = array.data - - if check_shape: - if len(array.shape) > 2 and array.shape[0] > 1: - raise ValueError( - f"Invalid array shape given: {array.shape}." "Expected 2D array or 3D array where arr.shape[0] == 1" - ) - - # If an occupied mask exists and a view was requested, trigger a warning. - if not copy and np.any(getattr(array, "mask", False)): - warnings.warn("Copying is required to respect the mask. Returning copy. Set 'copy=True' to hide this message.") - copy = True - - # If array is of type integer and has a mask, it needs to be converted to float (to assign nans) - if np.any(getattr(array, "mask", False)) and np.issubdtype(array.dtype, np.integer): # type: ignore - array = array.astype(np.float32) # type: ignore - - # Convert into a regular ndarray (a view or copy depending on the 'copy' argument) - array_data = np.array(array).squeeze() if copy else np.asarray(array).squeeze() - - # Get the mask of invalid pixels and set nans if it is occupied. - invalid_mask = get_mask(array) - if np.any(invalid_mask): - array_data[invalid_mask] = np.nan - - return array_data, invalid_mask - - -def get_valid_extent(array: np.ndarray | np.ma.masked_array) -> tuple[int, ...]: - """ - Return (rowmin, rowmax, colmin, colmax), the first/last row/column of array with valid pixels - """ - if not array.dtype == "bool": - valid_mask = ~get_mask(array) - else: - valid_mask = array - cols_nonzero = np.where(np.count_nonzero(valid_mask, axis=0) > 0)[0] - rows_nonzero = np.where(np.count_nonzero(valid_mask, axis=1) > 0)[0] - return rows_nonzero[0], rows_nonzero[-1], cols_nonzero[0], cols_nonzero[-1] - - def load_multiple_rasters( raster_paths: list[str], crop: bool = True, ref_grid: int | None = None, **kwargs: Any ) -> list[RasterType]: @@ -380,181 +307,3 @@ def merge_rasters( ) return merged_raster - - -def _get_closest_rectangle(size: int) -> tuple[int, int]: - """ - Given a 1D array size, return a rectangular shape that is closest to a cube which the size fits in. - - If 'size' does not have an integer root, a rectangle is returned that is slightly larger than 'size'. - - :examples: - >>> _get_closest_rectangle(4) # size will be 4 - (2, 2) - >>> _get_closest_rectangle(9) # size will be 9 - (3, 3) - >>> _get_closest_rectangle(3) # size will be 4; needs padding afterward. - (2, 2) - >>> _get_closest_rectangle(55) # size will be 56; needs padding afterward. - (7, 8) - >>> _get_closest_rectangle(24) # size will be 25; needs padding afterward - (5, 5) - >>> _get_closest_rectangle(85620) # size will be 85849; needs padding afterward - (293, 293) - >>> _get_closest_rectangle(52011) # size will be 52212; needs padding afterward - (228, 229) - """ - close_cube = int(np.sqrt(size)) - - # If size has an integer root, return the respective cube. - if close_cube**2 == size: - return (close_cube, close_cube) - - # One of these rectangles/cubes will cover all cells, so return the first that does. - potential_rectangles = [(close_cube, close_cube + 1), (close_cube + 1, close_cube + 1)] - - for rectangle in potential_rectangles: - if np.prod(rectangle) >= size: - return rectangle - - raise NotImplementedError(f"Function criteria not met for rectangle of size: {size}") - - -def subdivide_array(shape: tuple[int, ...], count: int) -> np.ndarray: - """ - Create indices for subdivison of an array in a number of blocks. - - If 'count' is divisible by the product of 'shape', the amount of cells in each block will be equal. - If 'count' is not divisible, the amount of cells in each block will be very close to equal. - - :param shape: The shape of a array to be subdivided. - :param count: The amount of subdivisions to make. - - :examples: - >>> subdivide_array((4, 4), 4) - array([[0, 0, 1, 1], - [0, 0, 1, 1], - [2, 2, 3, 3], - [2, 2, 3, 3]]) - - >>> subdivide_array((6, 4), 4) - array([[0, 0, 1, 1], - [0, 0, 1, 1], - [0, 0, 1, 1], - [2, 2, 3, 3], - [2, 2, 3, 3], - [2, 2, 3, 3]]) - - >>> subdivide_array((5, 4), 3) - array([[0, 0, 0, 0], - [0, 0, 0, 0], - [1, 1, 2, 2], - [1, 1, 2, 2], - [1, 1, 2, 2]]) - - :raises ValueError: If the 'shape' size (`np.prod(shape)`) is smallern than 'count' - If the shape is not a 2D shape. - - :returns: An array of shape 'shape' with 'count' unique indices. - """ - try: - import skimage.transform - except ImportError: - raise ImportError("Missing optional dependency, skimage.transform, required by this function.") - - if count > np.prod(shape): - raise ValueError(f"Shape '{shape}' size ({np.prod(shape)}) is smaller than 'count' ({count}).") - - if len(shape) != 2: - raise ValueError(f"Expected a 2D shape, got {len(shape)}D shape: {shape}") - - # Generate a small grid of indices, with the same unique count as 'count' - rect = _get_closest_rectangle(count) - small_indices = np.pad(np.arange(count), np.prod(rect) - count, mode="edge")[: np.prod(rect)].reshape(rect) - - # Upscale the grid to fit the output shape using nearest neighbour scaling. - indices = skimage.transform.resize(small_indices, shape, order=0, preserve_range=True).astype(int) - - return indices.reshape(shape) - - -def get_xy_rotated(raster: Raster, along_track_angle: float) -> tuple[np.ndarray, np.ndarray]: - """ - Rotate x, y axes of image to get along- and cross-track distances. - :param raster: Raster to get x,y positions from. - :param along_track_angle: Angle by which to rotate axes (degrees) - - :returns xxr, yyr: Arrays corresponding to along (x) and cross (y) track distances. - """ - - myang = np.deg2rad(along_track_angle) - - # Get grid coordinates - xx, yy = raster.coords(grid=True) - xx -= np.min(xx) - yy -= np.min(yy) - - # Get rotated coordinates - - # For along-track - xxr = xx * np.cos(myang) - yy * np.sin(myang) - # For cross-track - yyr = xx * np.sin(myang) + yy * np.cos(myang) - - # Re-initialize coordinate at zero - xxr -= np.nanmin(xxr) - yyr -= np.nanmin(yyr) - - return xxr, yyr - - -def subsample_raster( - array: np.ndarray | np.ma.masked_array, - subsample: float | int, - return_indices: bool = False, - random_state: None | np.random.RandomState | np.random.Generator | int = None, -) -> np.ndarray: - """ - Randomly subsample a 1D or 2D array by a subsampling factor, taking only non NaN/masked values. - - :param subsample: If <= 1, will be considered a fraction of valid pixels to extract. - If > 1 will be considered the number of pixels to extract. - :param return_indices: If set to True, will return the extracted indices only. - :param random_state: Random state, or seed number to use for random calculations (for testing) - - :returns: The subsampled array (1D) or the indices to extract (same shape as input array) - """ - # Define state for random subsampling (to fix results during testing) - if random_state is None: - rnd = np.random.default_rng() - elif isinstance(random_state, (np.random.RandomState, np.random.Generator)): - rnd = random_state - else: - rnd = np.random.RandomState(np.random.MT19937(np.random.SeedSequence(random_state))) - - # Get number of points to extract - if (subsample <= 1) & (subsample > 0): - npoints = int(subsample * np.size(array)) - elif subsample > 1: - npoints = int(subsample) - else: - raise ValueError("`subsample` must be > 0") - - # Remove invalid values and flatten array - mask = get_mask(array) # -> need to remove .squeeze in get_mask - valids = np.argwhere(~mask.flatten()).squeeze() - - # Checks that array and npoints are correct - assert np.ndim(valids) == 1, "Something is wrong with array dimension, check input data and shape" - if npoints > np.size(valids): - npoints = np.size(valids) - - # Randomly extract npoints without replacement - indices = rnd.choice(valids, npoints, replace=False) - unraveled_indices = np.unravel_index(indices, array.shape) - - if return_indices: - return unraveled_indices - - else: - return array[unraveled_indices] diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index f5df0ab30..537ef2498 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -20,8 +20,6 @@ import numpy as np import pyproj import rasterio as rio -import rasterio.mask -import rasterio.transform import rasterio.warp import rasterio.windows from affine import Affine @@ -36,6 +34,7 @@ from geoutils._typing import AnyNumber, ArrayLike, DTypeLike from geoutils.geovector import Vector from geoutils.projtools import _get_bounds_projected +from geoutils.georaster.sampling import subsample_array # If python38 or above, Literal is builtin. Otherwise, use typing_extensions try: @@ -2968,6 +2967,23 @@ def proximity( return self.copy(new_array=proximity) + def subsample( + self, + subsample: int | float, + return_indices: bool = False, + random_state: None | np.random.RandomState | np.random.Generator | int = None + ) -> np.ndarray: + """ + Randomly subsample the raster. Only valid values are considered. + + :param subsample: If <= 1, a fraction of the total pixels to extract. If > 1, the number of pixels. + :param return_indices: Whether to return the extracted indices only. + :param random_state: Random state or seed number. + + :return: Array of subsampled valid values, or array of subsampled indices. + """ + + return subsample_array(array=self.data, subsample=subsample, return_indices=return_indices, random_state=random_state) class Mask(Raster): """ diff --git a/geoutils/georaster/sampling.py b/geoutils/georaster/sampling.py new file mode 100644 index 000000000..4972480be --- /dev/null +++ b/geoutils/georaster/sampling.py @@ -0,0 +1,154 @@ +"""Sampling tools for arrays and rasters.""" + +from __future__ import annotations + +import numpy as np +from geoutils.georaster.array import get_mask + +def subsample_array( + array: np.ndarray | np.ma.masked_array, + subsample: float | int, + return_indices: bool = False, + random_state: None | np.random.RandomState | np.random.Generator | int = None, +) -> np.ndarray: + """ + Randomly subsample a 1D or 2D array by a subsampling factor, taking only non NaN/masked values. + + :param array: + :param subsample: If <= 1, will be considered a fraction of valid pixels to extract. + If > 1 will be considered the number of pixels to extract. + :param return_indices: If set to True, will return the extracted indices only. + :param random_state: Random state, or seed number to use for random calculations (for testing) + + :returns: The subsampled array (1D) or the indices to extract (same shape as input array) + """ + # Define state for random subsampling (to fix results during testing) + if random_state is None: + rnd = np.random.default_rng() + elif isinstance(random_state, (np.random.RandomState, np.random.Generator)): + rnd = random_state + else: + rnd = np.random.RandomState(np.random.MT19937(np.random.SeedSequence(random_state))) + + # Get number of points to extract + if (subsample <= 1) & (subsample > 0): + npoints = int(subsample * np.size(array)) + elif subsample > 1: + npoints = int(subsample) + else: + raise ValueError("`subsample` must be > 0") + + # Remove invalid values and flatten array + mask = get_mask(array) # -> need to remove .squeeze in get_mask + valids = np.argwhere(~mask.flatten()).squeeze() + + # Checks that array and npoints are correct + assert np.ndim(valids) == 1, "Something is wrong with array dimension, check input data and shape" + if npoints > np.size(valids): + npoints = np.size(valids) + + # Randomly extract npoints without replacement + indices = rnd.choice(valids, npoints, replace=False) + unraveled_indices = np.unravel_index(indices, array.shape) + + if return_indices: + return unraveled_indices + + else: + return array[unraveled_indices] + + +def _get_closest_rectangle(size: int) -> tuple[int, int]: + """ + Given a 1D array size, return a rectangular shape that is closest to a cube which the size fits in. + + If 'size' does not have an integer root, a rectangle is returned that is slightly larger than 'size'. + + :examples: + >>> _get_closest_rectangle(4) # size will be 4 + (2, 2) + >>> _get_closest_rectangle(9) # size will be 9 + (3, 3) + >>> _get_closest_rectangle(3) # size will be 4; needs padding afterward. + (2, 2) + >>> _get_closest_rectangle(55) # size will be 56; needs padding afterward. + (7, 8) + >>> _get_closest_rectangle(24) # size will be 25; needs padding afterward + (5, 5) + >>> _get_closest_rectangle(85620) # size will be 85849; needs padding afterward + (293, 293) + >>> _get_closest_rectangle(52011) # size will be 52212; needs padding afterward + (228, 229) + """ + close_cube = int(np.sqrt(size)) + + # If size has an integer root, return the respective cube. + if close_cube**2 == size: + return (close_cube, close_cube) + + # One of these rectangles/cubes will cover all cells, so return the first that does. + potential_rectangles = [(close_cube, close_cube + 1), (close_cube + 1, close_cube + 1)] + + for rectangle in potential_rectangles: + if np.prod(rectangle) >= size: + return rectangle + + raise NotImplementedError(f"Function criteria not met for rectangle of size: {size}") + + +def subdivide_array(shape: tuple[int, ...], count: int) -> np.ndarray: + """ + Create indices for subdivison of an array in a number of blocks. + + If 'count' is divisible by the product of 'shape', the amount of cells in each block will be equal. + If 'count' is not divisible, the amount of cells in each block will be very close to equal. + + :param shape: The shape of a array to be subdivided. + :param count: The amount of subdivisions to make. + + :examples: + >>> subdivide_array((4, 4), 4) + array([[0, 0, 1, 1], + [0, 0, 1, 1], + [2, 2, 3, 3], + [2, 2, 3, 3]]) + + >>> subdivide_array((6, 4), 4) + array([[0, 0, 1, 1], + [0, 0, 1, 1], + [0, 0, 1, 1], + [2, 2, 3, 3], + [2, 2, 3, 3], + [2, 2, 3, 3]]) + + >>> subdivide_array((5, 4), 3) + array([[0, 0, 0, 0], + [0, 0, 0, 0], + [1, 1, 2, 2], + [1, 1, 2, 2], + [1, 1, 2, 2]]) + + :raises ValueError: If the 'shape' size (`np.prod(shape)`) is smallern than 'count' + If the shape is not a 2D shape. + + :returns: An array of shape 'shape' with 'count' unique indices. + """ + try: + import skimage.transform + except ImportError: + raise ImportError("Missing optional dependency, skimage.transform, required by this function.") + + if count > np.prod(shape): + raise ValueError(f"Shape '{shape}' size ({np.prod(shape)}) is smaller than 'count' ({count}).") + + if len(shape) != 2: + raise ValueError(f"Expected a 2D shape, got {len(shape)}D shape: {shape}") + + # Generate a small grid of indices, with the same unique count as 'count' + rect = _get_closest_rectangle(count) + small_indices = np.pad(np.arange(count), np.prod(rect) - count, mode="edge")[: np.prod(rect)].reshape(rect) + + # Upscale the grid to fit the output shape using nearest neighbour scaling. + indices = skimage.transform.resize(small_indices, shape, order=0, preserve_range=True).astype(int) + + return indices.reshape(shape) \ No newline at end of file diff --git a/tests/test_array.py b/tests/test_array.py new file mode 100644 index 000000000..058594660 --- /dev/null +++ b/tests/test_array.py @@ -0,0 +1,213 @@ +"""Test array tools.""" +from __future__ import annotations + +import warnings + +import numpy as np +import pytest +import rasterio as rio + +import geoutils as gu + +class TestArray: + + @pytest.mark.parametrize("dtype", ["uint8", "uint16", "int32", "float32", "float16"]) # type: ignore + @pytest.mark.parametrize( + "mask_and_viewable", + [ + (None, True), # An ndarray with no mask should support views + (False, True), # A masked array with an empty mask should support views + ([True, False, False, False], False), # A masked array with an occupied mask should not support views. + ([False, False, False, False], True), # A masked array with an empty occupied mask should support views. + ], + ) # type: ignore + @pytest.mark.parametrize( + "shape_and_check_passes", + [ + ((1, 2, 2), True), # A 3D array with a shape[0] == 1 is okay. + ((2, 1, 2), False), # A 3D array with a shape[0] != 1 is not okay. + ((2, 2), True), # A 2D array is okay. + ((4,), True), # A 1D array is okay. + ], + ) # type: ignore + def test_get_array_and_mask(self, + dtype: str, + mask_and_viewable: tuple[None | bool | list[bool], bool], + shape_and_check_passes: tuple[tuple[int, ...], bool], + ) -> None: + """Validate that the function returns views when expected, and copies otherwise.""" + warnings.simplefilter("error") + + masked_values, view_should_be_possible = mask_and_viewable + shape, check_should_pass = shape_and_check_passes + + # Create an array of the specified dtype + array = np.ones(shape, dtype=dtype) + if masked_values is not None: + if masked_values is False: + array = np.ma.masked_array(array) + else: + array = np.ma.masked_array(array, mask=np.reshape(masked_values, array.shape)) + + # Validate that incorrect shapes raise the correct error. + if not check_should_pass: + with pytest.raises(ValueError, match="Invalid array shape given"): + gu.georaster.get_array_and_mask(array, check_shape=True) + + # Stop the test here as the failure is now validated. + return + + # Get a copy of the array and check its shape (it should always pass at this point) + arr, _ = gu.georaster.get_array_and_mask(array, copy=True, check_shape=True) + + # Validate that the array is a copy + assert not np.shares_memory(arr, array) + + # If it was an integer dtype and it had a mask, validate that the array is now "float32" + if np.issubdtype(dtype, np.integer) and np.any(masked_values or False): + assert arr.dtype == "float32" + + # If there was no mask or the mask was empty, validate that arr and array are equivalent + if not np.any(masked_values or False): + assert np.sum(np.abs(array - arr)) == 0.0 + + with warnings.catch_warnings(record=True) as caught_warnings: + warnings.simplefilter("always") + + # Try to create a view. + arr_view, mask = gu.georaster.get_array_and_mask(array, copy=False) + + # If it should be possible, validate that there were no warnings. + if view_should_be_possible: + assert len(caught_warnings) == 0, (caught_warnings[0].message, array) + # Otherwise, validate that one warning was raised with the correct text. + else: + assert len(caught_warnings) == 1 + assert "Copying is required" in str(caught_warnings[0].message) + + # Validate that the view shares memory if it was possible, or otherwise that it is a copy. + if view_should_be_possible: + assert np.shares_memory(array, arr_view) + else: + assert not np.shares_memory(array, arr_view) + + + def test_get_valid_extent(self) -> None: + """Check the function to get valid extent.""" + + # Create an artificial array and masked array + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + + # For no invalid values, the function should return the edges + # For the array + assert (0, 4, 0, 4) == gu.georaster.get_valid_extent(arr) + # For the masked-array + assert (0, 4, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + + # 1/ First column: + # If we mask it in the masked array + mask_ma[0, :] = np.ma.masked + assert (1, 4, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[0, :] = np.nan + assert (1, 4, 0, 4) == gu.georaster.get_valid_extent(arr) + mask_ma.data[0, :] = np.nan + mask_ma.mask = False + assert (1, 4, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + + # 2/ First row: + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + # If we mask it in the masked array + mask_ma[:, 0] = np.ma.masked + assert (0, 4, 1, 4) == gu.georaster.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[:, 0] = np.nan + assert (0, 4, 1, 4) == gu.georaster.get_valid_extent(arr) + mask_ma.data[:, 0] = np.nan + mask_ma.mask = False + assert (0, 4, 1, 4) == gu.georaster.get_valid_extent(mask_ma) + + # 3/ Last column: + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + + # If we mask it in the masked array + mask_ma[-1, :] = np.ma.masked + assert (0, 3, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[-1, :] = np.nan + assert (0, 3, 0, 4) == gu.georaster.get_valid_extent(arr) + mask_ma.data[-1, :] = np.nan + mask_ma.mask = False + assert (0, 3, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + + # 4/ Last row: + arr = np.ones(shape=(5, 5)) + arr_mask = np.zeros(shape=(5, 5), dtype=bool) + mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) + + # If we mask it in the masked array + mask_ma[:, -1] = np.ma.masked + assert (0, 4, 0, 3) == gu.georaster.get_valid_extent(mask_ma) + + # If we changed the array to NaNs + arr[:, -1] = np.nan + assert (0, 4, 0, 3) == gu.georaster.get_valid_extent(arr) + mask_ma.data[:, -1] = np.nan + mask_ma.mask = False + assert (0, 4, 0, 3) == gu.georaster.get_valid_extent(mask_ma) + + def test_get_xy_rotated(self) -> None: + """Check the function to rotate array.""" + + # Create an artificial raster + width = height = 5 + transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) + r1 = gu.Raster.from_array( + np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None + ) + + # First, we get initial coords + xx, yy = r1.coords(grid=True) + + # Rotating the coordinates 90 degrees should be the same as rotating the array + xx90, yy90 = gu.georaster.get_xy_rotated(r1, along_track_angle=90) + assert np.allclose(np.rot90(xx90), xx) + assert np.allclose(np.rot90(yy90), yy) + + # Same for 180 degrees + xx180, yy180 = gu.georaster.get_xy_rotated(r1, along_track_angle=180) + assert np.allclose(np.rot90(xx180, k=2), xx) + assert np.allclose(np.rot90(yy180, k=2), yy) + + # Same for 270 degrees + xx270, yy270 = gu.georaster.get_xy_rotated(r1, along_track_angle=270) + assert np.allclose(np.rot90(xx270, k=3), xx) + assert np.allclose(np.rot90(yy270, k=3), yy) + + # 360 degrees should get us back on our feet + xx360, yy360 = gu.georaster.get_xy_rotated(r1, along_track_angle=360) + assert np.allclose(xx360, xx) + assert np.allclose(yy360, yy) + + # Test that the values make sense for 45 degrees + xx45, yy45 = gu.georaster.get_xy_rotated(r1, along_track_angle=45) + # Should have zero on the upper left corner for xx + assert xx45[0, 0] == pytest.approx(0) + # Then a multiple of sqrt2 along each dimension + assert xx45[1, 0] == pytest.approx(xx45[0, 1]) == pytest.approx(0.1 * np.sqrt(2)) + # The lower right corner should have the highest coordinate (0.8) times sqrt(2) + assert xx45[-1, -1] == pytest.approx(np.max(xx) * np.sqrt(2)) + + # Finally, yy should be rotated by 90 + assert np.allclose(np.rot90(xx45), yy45) + + xx, yy = gu.georaster.get_xy_rotated(r1, along_track_angle=90) diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 919e30ec1..694278210 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -767,7 +767,7 @@ def test_copy(self, example: str) -> None: # When passing the new array as a NaN ndarray, only the valid data is equal, because masked data is NaN in one # case, and -9999 in the other - r_arr = gu.spatial_tools.get_array_and_mask(r)[0] + r_arr = gu.georaster.get_array_and_mask(r)[0] r2 = r.copy(new_array=r_arr) assert np.ma.allequal(r.data, r2.data) # If a nodata value exists, and we update the NaN pixels to be that nodata value, then the two Rasters should @@ -1122,13 +1122,13 @@ def test_reproject(self, example: str) -> None: r_nodata.set_nodata(None) # Make sure at least one pixel is masked for test 1 - rand_indices = gu.spatial_tools.subsample_raster(r_nodata.data, 10, return_indices=True) + rand_indices = gu.georaster.subsample_array(r_nodata.data, 10, return_indices=True) r_nodata.data[rand_indices] = np.ma.masked assert np.count_nonzero(r_nodata.data.mask) > 0 # make sure at least one pixel is set at default nodata for test default_nodata = _default_nodata(r_nodata.dtypes[0]) - rand_indices = gu.spatial_tools.subsample_raster(r_nodata.data, 10, return_indices=True) + rand_indices = gu.georaster.subsample_array(r_nodata.data, 10, return_indices=True) r_nodata.data[rand_indices] = default_nodata assert np.count_nonzero(r_nodata.data == default_nodata) > 0 @@ -1229,7 +1229,7 @@ def test_reproject(self, example: str) -> None: # Create a raster with (additional) random gaps r_gaps = r.copy() nsamples = 200 - rand_indices = gu.spatial_tools.subsample_raster(r_gaps.data, nsamples, return_indices=True) + rand_indices = gu.georaster.subsample_array(r_gaps.data, nsamples, return_indices=True) r_gaps.data[rand_indices] = np.ma.masked assert np.sum(r_gaps.data.mask) - np.sum(r.data.mask) == nsamples # sanity check diff --git a/tests/test_multiraster.py b/tests/test_multiraster.py new file mode 100644 index 000000000..1f9f9cbfb --- /dev/null +++ b/tests/test_multiraster.py @@ -0,0 +1,278 @@ +""" +Test tools involving multiple rasters. +""" +from __future__ import annotations + +import warnings +from typing import Callable + +import numpy as np +import pytest +import rasterio as rio + +import geoutils as gu +from geoutils import examples +from geoutils.georaster import RasterType + + +class stack_merge_images: + """ + Test cases for stacking and merging images + Split an image with some overlap, then stack/merge it, and validate bounds and shape. + Param `cls` is used to set the type of the output, e.g. gu.Raster (default). + """ + + def __init__(self, image: str, cls: Callable[[str], RasterType] = gu.Raster) -> None: + img = cls(examples.get_path(image)) + self.img = img + + # Find the easting midpoint of the img + x_midpoint = np.mean([img.bounds.right, img.bounds.left]) + x_midpoint -= (x_midpoint - img.bounds.left) % img.res[0] + + # Cut the img into two imgs that slightly overlap each other. + self.img1 = img.copy() + self.img1.crop( + rio.coords.BoundingBox( + right=x_midpoint + img.res[0] * 3, left=img.bounds.left, top=img.bounds.top, bottom=img.bounds.bottom + ) + ) + self.img2 = img.copy() + self.img2.crop( + rio.coords.BoundingBox( + left=x_midpoint - img.res[0] * 3, right=img.bounds.right, top=img.bounds.top, bottom=img.bounds.bottom + ) + ) + + # To check that use_ref_bounds work - create a img that do not cover the whole extent + self.img3 = img.copy() + self.img3.crop( + rio.coords.BoundingBox( + left=x_midpoint - img.res[0] * 3, + right=img.bounds.right - img.res[0] * 2, + top=img.bounds.top, + bottom=img.bounds.bottom, + ) + ) + + +@pytest.fixture +def images_1d(): # type: ignore + return stack_merge_images("everest_landsat_b4") + + +@pytest.fixture +def sat_images(): # type: ignore + return stack_merge_images("everest_landsat_b4", cls=gu.SatelliteImage) + + +@pytest.fixture +def images_3d(): # type: ignore + return stack_merge_images("everest_landsat_rgb") + + +class TestMultiRaster: + + @pytest.mark.parametrize( + "rasters", [pytest.lazy_fixture("images_1d"), pytest.lazy_fixture("sat_images"), pytest.lazy_fixture("images_3d")] + ) # type: ignore + def test_stack_rasters(self, rasters) -> None: # type: ignore + """Test stack_rasters""" + + # Silence the reprojection warning for default nodata value + warnings.filterwarnings("ignore", category=UserWarning, message="New nodata value found in the data array.*") + warnings.filterwarnings("ignore", category=UserWarning, message="For reprojection, dst_nodata must be set.*") + + # Merge the two overlapping DEMs and check that output bounds and shape is correct + if rasters.img1.count > 1: + # Check warning is raised once + with pytest.warns( + expected_warning=UserWarning, + match="Some input Rasters have multiple bands, only their first band will be used.", + ): + stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2]) + # Then ignore the other ones + warnings.filterwarnings( + "ignore", + category=UserWarning, + message="Some input Rasters have multiple bands, only their first band will be used.", + ) + + else: + stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2]) + + assert stacked_img.count == 2 + assert rasters.img.shape == stacked_img.shape + assert type(stacked_img) == gu.Raster # Check output object is always Raster, whatever input was given + assert np.count_nonzero(np.isnan(stacked_img.data)) == 0 # Check no NaNs introduced + + merged_bounds = gu.projtools.merge_bounds( + [rasters.img1.bounds, rasters.img2.bounds], resolution=rasters.img1.res[0] + ) + assert merged_bounds == stacked_img.bounds + + # Check that reference works with input Raster + stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2], reference=rasters.img) + assert rasters.img.bounds == stacked_img.bounds + + # Others than int or gu.Raster should raise a ValueError + try: + stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2], reference="a string") + except ValueError as exception: + if "reference should be" not in str(exception): + raise exception + + # Check that use_ref_bounds works - use a img that do not cover the whole extent + + # This case should not preserve original extent + stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img3]) + assert stacked_img.bounds != rasters.img.bounds + + # This case should preserve original extent + stacked_img2 = gu.georaster.stack_rasters( + [rasters.img1, rasters.img3], reference=rasters.img, use_ref_bounds=True + ) + assert stacked_img2.bounds == rasters.img.bounds + + @pytest.mark.parametrize( + "rasters", [pytest.lazy_fixture("images_1d"), pytest.lazy_fixture("images_3d")] + ) # type: ignore + def test_merge_rasters(self, rasters) -> None: # type: ignore + """Test merge_rasters""" + # Merge the two overlapping DEMs and check that it closely resembles the initial DEM + + # Silence the reprojection warning for default nodata value + warnings.filterwarnings("ignore", category=UserWarning, message="New nodata value found in the data array.*") + warnings.filterwarnings("ignore", category=UserWarning, message="For reprojection, dst_nodata must be set.*") + + # Ignore warning already checked in test_stack_rasters + if rasters.img1.count > 1: + warnings.filterwarnings( + "ignore", + category=UserWarning, + message="Some input Rasters have multiple bands, only their first band will be used.", + ) + + merged_img = gu.georaster.merge_rasters([rasters.img1, rasters.img2], merge_algorithm=np.nanmean) + + assert rasters.img.shape == merged_img.shape + assert rasters.img.bounds == merged_img.bounds + assert np.count_nonzero(np.isnan(merged_img.data)) == 0 # Check no NaNs introduced + + diff = rasters.img.data - merged_img.data + + assert np.abs(np.nanmean(diff)) < 1 + + # Check that reference works + merged_img2 = gu.georaster.merge_rasters([rasters.img1, rasters.img2], reference=rasters.img) + assert merged_img2 == merged_img + + + + # Group rasters for for testing `load_multiple_rasters` + # two overlapping, single band rasters + # two overlapping, 1 and 3 band rasters + # three overlapping rasters + # TODO: add a case with different CRS - issue raised #310 + raster_groups = [ + [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("everest_landsat_b4_cropped")], + [gu.examples.get_path("everest_landsat_rgb"), gu.examples.get_path("everest_landsat_b4_cropped")], + [ + gu.examples.get_path("everest_landsat_b4"), + gu.examples.get_path("everest_landsat_rgb"), + gu.examples.get_path("everest_landsat_b4_cropped"), + ], + ] + + @pytest.mark.parametrize("raster_paths", raster_groups) # type: ignore + def test_load_multiple_overlap(self, raster_paths: list[str]) -> None: + """ + Test load_multiple_rasters functionalities, when rasters overlap -> no warning is raised + """ + # - Test that with crop=False and ref_grid=None, rasters are simply loaded - # + output_rst: list[gu.Raster] = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) + for k, rst in enumerate(output_rst): + assert rst.is_loaded + rst2 = gu.Raster(raster_paths[k]) + assert rst == rst2 + + # - Test that with crop=True and ref_grid=None, rasters are cropped only in area of overlap - # + output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) + ref_crs = gu.Raster(raster_paths[0], load_data=False).crs + + # Save original and new bounds (as polygons) in the reference CRS + orig_poly_bounds = [] + new_poly_bounds = [] + for k, rst in enumerate(output_rst): + assert rst.is_loaded + rst2 = gu.Raster(raster_paths[k], load_data=False) + assert rst.crs == rst2.crs # CRS should not have been changed + orig_poly_bounds.append(gu.projtools.bounds2poly(rst2.bounds, rst.crs, ref_crs)) + new_poly_bounds.append(gu.projtools.bounds2poly(rst.bounds, rst.crs, ref_crs)) + + # Check that the new bounds are contained in the original bounds of all rasters + for poly1 in new_poly_bounds: + for poly2 in orig_poly_bounds: + assert poly2.contains(poly1) + + # - Test that with crop=False and ref_grid=0, rasters all have the same grid as the reference - # + # For the landsat test case, a warning will be raised because nodata is None + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=UserWarning) + output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) + + ref_rst = gu.Raster(raster_paths[0], load_data=False) + for k, rst in enumerate(output_rst): + rst2 = gu.Raster(raster_paths[k], load_data=False) + assert rst.is_loaded + # Georeferences are forced to ref + assert rst.crs == ref_rst.crs + assert rst.shape == ref_rst.shape + assert rst.transform == ref_rst.transform + # Original number of bounds and nodata must be preserved + assert rst.count == rst2.count + if rst2.nodata is not None: # if none, reprojection with set a default value + assert rst.nodata == rst2.nodata + + raster_groups = [ + [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")], + ] + + @pytest.mark.parametrize("raster_paths", raster_groups) # type: ignore + def test_load_multiple_no_overlap(self, raster_paths: list[str]) -> None: + """ + Test load_multiple_rasters functionalities with rasters that do not overlap -> raises warning in certain cases + """ + # - With crop=False and ref_grid=None, rasters are simply loaded - # + output_rst: list[gu.Raster] = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) + for k, rst in enumerate(output_rst): + assert rst.is_loaded + rst2 = gu.Raster(raster_paths[k]) + assert rst == rst2 + + # - With crop=True -> should raise a warning - # + with pytest.warns(UserWarning, match="Intersection is void, returning unloaded rasters."): + output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) + + # - Should work with crop=False and ref_grid=0 - # + # For the landsat test case, a warning will be raised because nodata is None + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=UserWarning) + output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) + + ref_rst = gu.Raster(raster_paths[0], load_data=False) + for k, rst in enumerate(output_rst): + rst2 = gu.Raster(raster_paths[k], load_data=False) + assert rst.is_loaded + # Georeferences are forced to ref + assert rst.crs == ref_rst.crs + assert rst.shape == ref_rst.shape + assert rst.transform == ref_rst.transform + # Original number of bounds and nodata must be preserved + assert rst.count == rst2.count + if rst2.nodata is not None: # if none, reprojection with set a default value + assert rst.nodata == rst2.nodata + # Logically, the non overlapping raster should have only masked values + if k != 0: + assert np.count_nonzero(~rst.data.mask) == 0 + diff --git a/tests/test_sampling.py b/tests/test_sampling.py new file mode 100644 index 000000000..940bf9dd4 --- /dev/null +++ b/tests/test_sampling.py @@ -0,0 +1,87 @@ +"""Test sampling tools for arrays and rasters.""" + +from __future__ import annotations + +import numpy as np +import pytest + +import geoutils as gu + +class TestSubsampling: + """ + Different examples of 1D to 3D arrays with masked values for testing. + """ + + # Case 1 - 1D array, 1 masked value + array1D = np.ma.masked_array(np.arange(10), mask=np.zeros(10)) + array1D.mask[3] = True + assert np.ndim(array1D) == 1 + assert np.count_nonzero(array1D.mask) > 0 + + # Case 2 - 2D array, 1 masked value + array2D = np.ma.masked_array(np.arange(9).reshape((3, 3)), mask=np.zeros((3, 3))) + array2D.mask[0, 1] = True + assert np.ndim(array2D) == 2 + assert np.count_nonzero(array2D.mask) > 0 + + # Case 3 - 3D array, 1 masked value + array3D = np.ma.masked_array(np.arange(9).reshape((1, 3, 3)), mask=np.zeros((1, 3, 3))) + array3D = np.ma.vstack((array3D, array3D + 10)) + array3D.mask[0, 0, 1] = True + assert np.ndim(array3D) == 3 + assert np.count_nonzero(array3D.mask) > 0 + + @pytest.mark.parametrize("array", [array1D, array2D, array3D]) # type: ignore + def test_subsample(self, array: np.ndarray) -> None: + """ + Test gu.georaster.subsample_array. + """ + # Test that subsample > 1 works as expected, i.e. output 1D array, with no masked values, or selected size + for npts in np.arange(2, np.size(array)): + random_values = gu.georaster.subsample_array(array, subsample=npts) + assert np.ndim(random_values) == 1 + assert np.size(random_values) == npts + assert np.count_nonzero(random_values.mask) == 0 + + # Test if subsample > number of valid values => return all + random_values = gu.georaster.subsample_array(array, subsample=np.size(array) + 3) + assert np.all(np.sort(random_values) == array[~array.mask]) + + # Test if subsample = 1 => return all valid values + random_values = gu.georaster.subsample_array(array, subsample=1) + assert np.all(np.sort(random_values) == array[~array.mask]) + + # Test if subsample < 1 + random_values = gu.georaster.subsample_array(array, subsample=0.5) + assert np.size(random_values) == int(np.size(array) * 0.5) + + # Test with optional argument return_indices + indices = gu.georaster.subsample_array(array, subsample=0.3, return_indices=True) + assert np.ndim(indices) == 2 + assert len(indices) == np.ndim(array) + assert np.ndim(array[indices]) == 1 + assert np.size(array[indices]) == int(np.size(array) * 0.3) + + # Check that we can pass an integer to fix the random state + sub42 = gu.georaster.subsample_array(array, subsample=10, random_state=42) + # Check by passing a generator directly + random_gen = np.random.RandomState(np.random.MT19937(np.random.SeedSequence(42))) + sub42_gen = gu.georaster.subsample_array(array, subsample=10, random_state=random_gen) + # Both should be equal + assert np.array_equal(sub42, sub42_gen) + + def test_subdivide_array(self) -> None: + test_shape = (6, 4) + test_count = 4 + subdivision_grid = gu.georaster.subdivide_array(test_shape, test_count) + + assert subdivision_grid.shape == test_shape + assert np.unique(subdivision_grid).size == test_count + + assert np.unique(gu.georaster.subdivide_array((3, 3), 3)).size == 3 + + with pytest.raises(ValueError, match=r"Expected a 2D shape, got 1D shape.*"): + gu.georaster.subdivide_array((5,), 2) + + with pytest.raises(ValueError, match=r"Shape.*smaller than.*"): + gu.georaster.subdivide_array((5, 2), 15) diff --git a/tests/test_spatial_tools.py b/tests/test_spatial_tools.py deleted file mode 100644 index fffbda6a7..000000000 --- a/tests/test_spatial_tools.py +++ /dev/null @@ -1,562 +0,0 @@ -""" -Functions to test the spatial tools. -""" -from __future__ import annotations - -import warnings -from typing import Callable - -import numpy as np -import pytest -import rasterio as rio - -import geoutils as gu -from geoutils import examples -from geoutils.georaster import RasterType - -# Group rasters for for testing `load_multiple_rasters` -# two overlapping, single band rasters -# two overlapping, 1 and 3 band rasters -# three overlapping rasters -# TODO: add a case with different CRS - issue raised #310 -raster_groups = [ - [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("everest_landsat_b4_cropped")], - [gu.examples.get_path("everest_landsat_rgb"), gu.examples.get_path("everest_landsat_b4_cropped")], - [ - gu.examples.get_path("everest_landsat_b4"), - gu.examples.get_path("everest_landsat_rgb"), - gu.examples.get_path("everest_landsat_b4_cropped"), - ], -] - - -@pytest.mark.parametrize("raster_paths", raster_groups) # type: ignore -def test_load_multiple_overlap(raster_paths: list[str]) -> None: - """ - Test load_multiple_rasters functionalities, when rasters overlap -> no warning is raised - """ - # - Test that with crop=False and ref_grid=None, rasters are simply loaded - # - output_rst: list[gu.Raster] = gu.spatial_tools.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) - for k, rst in enumerate(output_rst): - assert rst.is_loaded - rst2 = gu.Raster(raster_paths[k]) - assert rst == rst2 - - # - Test that with crop=True and ref_grid=None, rasters are cropped only in area of overlap - # - output_rst = gu.spatial_tools.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) - ref_crs = gu.Raster(raster_paths[0], load_data=False).crs - - # Save original and new bounds (as polygons) in the reference CRS - orig_poly_bounds = [] - new_poly_bounds = [] - for k, rst in enumerate(output_rst): - assert rst.is_loaded - rst2 = gu.Raster(raster_paths[k], load_data=False) - assert rst.crs == rst2.crs # CRS should not have been changed - orig_poly_bounds.append(gu.projtools.bounds2poly(rst2.bounds, rst.crs, ref_crs)) - new_poly_bounds.append(gu.projtools.bounds2poly(rst.bounds, rst.crs, ref_crs)) - - # Check that the new bounds are contained in the original bounds of all rasters - for poly1 in new_poly_bounds: - for poly2 in orig_poly_bounds: - assert poly2.contains(poly1) - - # - Test that with crop=False and ref_grid=0, rasters all have the same grid as the reference - # - # For the landsat test case, a warning will be raised because nodata is None - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=UserWarning) - output_rst = gu.spatial_tools.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) - - ref_rst = gu.Raster(raster_paths[0], load_data=False) - for k, rst in enumerate(output_rst): - rst2 = gu.Raster(raster_paths[k], load_data=False) - assert rst.is_loaded - # Georeferences are forced to ref - assert rst.crs == ref_rst.crs - assert rst.shape == ref_rst.shape - assert rst.transform == ref_rst.transform - # Original number of bounds and nodata must be preserved - assert rst.count == rst2.count - if rst2.nodata is not None: # if none, reprojection with set a default value - assert rst.nodata == rst2.nodata - - -raster_groups = [ - [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")], -] - - -@pytest.mark.parametrize("raster_paths", raster_groups) # type: ignore -def test_load_multiple_no_overlap(raster_paths: list[str]) -> None: - """ - Test load_multiple_rasters functionalities with rasters that do not overlap -> raises warning in certain cases - """ - # - With crop=False and ref_grid=None, rasters are simply loaded - # - output_rst: list[gu.Raster] = gu.spatial_tools.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) - for k, rst in enumerate(output_rst): - assert rst.is_loaded - rst2 = gu.Raster(raster_paths[k]) - assert rst == rst2 - - # - With crop=True -> should raise a warning - # - with pytest.warns(UserWarning, match="Intersection is void, returning unloaded rasters."): - output_rst = gu.spatial_tools.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) - - # - Should work with crop=False and ref_grid=0 - # - # For the landsat test case, a warning will be raised because nodata is None - with warnings.catch_warnings(): - warnings.simplefilter("ignore", category=UserWarning) - output_rst = gu.spatial_tools.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) - - ref_rst = gu.Raster(raster_paths[0], load_data=False) - for k, rst in enumerate(output_rst): - rst2 = gu.Raster(raster_paths[k], load_data=False) - assert rst.is_loaded - # Georeferences are forced to ref - assert rst.crs == ref_rst.crs - assert rst.shape == ref_rst.shape - assert rst.transform == ref_rst.transform - # Original number of bounds and nodata must be preserved - assert rst.count == rst2.count - if rst2.nodata is not None: # if none, reprojection with set a default value - assert rst.nodata == rst2.nodata - # Logically, the non overlapping raster should have only masked values - if k != 0: - assert np.count_nonzero(~rst.data.mask) == 0 - - -class stack_merge_images: - """ - Test cases for stacking and merging images - Split an image with some overlap, then stack/merge it, and validate bounds and shape. - Param `cls` is used to set the type of the output, e.g. gu.Raster (default). - """ - - def __init__(self, image: str, cls: Callable[[str], RasterType] = gu.Raster) -> None: - img = cls(examples.get_path(image)) - self.img = img - - # Find the easting midpoint of the img - x_midpoint = np.mean([img.bounds.right, img.bounds.left]) - x_midpoint -= (x_midpoint - img.bounds.left) % img.res[0] - - # Cut the img into two imgs that slightly overlap each other. - self.img1 = img.copy() - self.img1.crop( - rio.coords.BoundingBox( - right=x_midpoint + img.res[0] * 3, left=img.bounds.left, top=img.bounds.top, bottom=img.bounds.bottom - ) - ) - self.img2 = img.copy() - self.img2.crop( - rio.coords.BoundingBox( - left=x_midpoint - img.res[0] * 3, right=img.bounds.right, top=img.bounds.top, bottom=img.bounds.bottom - ) - ) - - # To check that use_ref_bounds work - create a img that do not cover the whole extent - self.img3 = img.copy() - self.img3.crop( - rio.coords.BoundingBox( - left=x_midpoint - img.res[0] * 3, - right=img.bounds.right - img.res[0] * 2, - top=img.bounds.top, - bottom=img.bounds.bottom, - ) - ) - - -@pytest.fixture -def images_1d(): # type: ignore - return stack_merge_images("everest_landsat_b4") - - -@pytest.fixture -def sat_images(): # type: ignore - return stack_merge_images("everest_landsat_b4", cls=gu.SatelliteImage) - - -@pytest.fixture -def images_3d(): # type: ignore - return stack_merge_images("everest_landsat_rgb") - - -@pytest.mark.parametrize( - "rasters", [pytest.lazy_fixture("images_1d"), pytest.lazy_fixture("sat_images"), pytest.lazy_fixture("images_3d")] -) # type: ignore -def test_stack_rasters(rasters) -> None: # type: ignore - """Test stack_rasters""" - - # Silence the reprojection warning for default nodata value - warnings.filterwarnings("ignore", category=UserWarning, message="New nodata value found in the data array.*") - warnings.filterwarnings("ignore", category=UserWarning, message="For reprojection, dst_nodata must be set.*") - - # Merge the two overlapping DEMs and check that output bounds and shape is correct - if rasters.img1.count > 1: - # Check warning is raised once - with pytest.warns( - expected_warning=UserWarning, - match="Some input Rasters have multiple bands, only their first band will be used.", - ): - stacked_img = gu.spatial_tools.stack_rasters([rasters.img1, rasters.img2]) - # Then ignore the other ones - warnings.filterwarnings( - "ignore", - category=UserWarning, - message="Some input Rasters have multiple bands, only their first band will be used.", - ) - - else: - stacked_img = gu.spatial_tools.stack_rasters([rasters.img1, rasters.img2]) - - assert stacked_img.count == 2 - assert rasters.img.shape == stacked_img.shape - assert type(stacked_img) == gu.Raster # Check output object is always Raster, whatever input was given - assert np.count_nonzero(np.isnan(stacked_img.data)) == 0 # Check no NaNs introduced - - merged_bounds = gu.projtools.merge_bounds( - [rasters.img1.bounds, rasters.img2.bounds], resolution=rasters.img1.res[0] - ) - assert merged_bounds == stacked_img.bounds - - # Check that reference works with input Raster - stacked_img = gu.spatial_tools.stack_rasters([rasters.img1, rasters.img2], reference=rasters.img) - assert rasters.img.bounds == stacked_img.bounds - - # Others than int or gu.Raster should raise a ValueError - try: - stacked_img = gu.spatial_tools.stack_rasters([rasters.img1, rasters.img2], reference="a string") - except ValueError as exception: - if "reference should be" not in str(exception): - raise exception - - # Check that use_ref_bounds works - use a img that do not cover the whole extent - - # This case should not preserve original extent - stacked_img = gu.spatial_tools.stack_rasters([rasters.img1, rasters.img3]) - assert stacked_img.bounds != rasters.img.bounds - - # This case should preserve original extent - stacked_img2 = gu.spatial_tools.stack_rasters( - [rasters.img1, rasters.img3], reference=rasters.img, use_ref_bounds=True - ) - assert stacked_img2.bounds == rasters.img.bounds - - -@pytest.mark.parametrize( - "rasters", [pytest.lazy_fixture("images_1d"), pytest.lazy_fixture("images_3d")] -) # type: ignore -def test_merge_rasters(rasters) -> None: # type: ignore - """Test merge_rasters""" - # Merge the two overlapping DEMs and check that it closely resembles the initial DEM - - # Silence the reprojection warning for default nodata value - warnings.filterwarnings("ignore", category=UserWarning, message="New nodata value found in the data array.*") - warnings.filterwarnings("ignore", category=UserWarning, message="For reprojection, dst_nodata must be set.*") - - # Ignore warning already checked in test_stack_rasters - if rasters.img1.count > 1: - warnings.filterwarnings( - "ignore", - category=UserWarning, - message="Some input Rasters have multiple bands, only their first band will be used.", - ) - - merged_img = gu.spatial_tools.merge_rasters([rasters.img1, rasters.img2], merge_algorithm=np.nanmean) - - assert rasters.img.shape == merged_img.shape - assert rasters.img.bounds == merged_img.bounds - assert np.count_nonzero(np.isnan(merged_img.data)) == 0 # Check no NaNs introduced - - diff = rasters.img.data - merged_img.data - - assert np.abs(np.nanmean(diff)) < 1 - - # Check that reference works - merged_img2 = gu.spatial_tools.merge_rasters([rasters.img1, rasters.img2], reference=rasters.img) - assert merged_img2 == merged_img - - -def test_subdivide_array() -> None: - - test_shape = (6, 4) - test_count = 4 - subdivision_grid = gu.spatial_tools.subdivide_array(test_shape, test_count) - - assert subdivision_grid.shape == test_shape - assert np.unique(subdivision_grid).size == test_count - - assert np.unique(gu.spatial_tools.subdivide_array((3, 3), 3)).size == 3 - - with pytest.raises(ValueError, match=r"Expected a 2D shape, got 1D shape.*"): - gu.spatial_tools.subdivide_array((5,), 2) - - with pytest.raises(ValueError, match=r"Shape.*smaller than.*"): - gu.spatial_tools.subdivide_array((5, 2), 15) - - -@pytest.mark.parametrize("dtype", ["uint8", "uint16", "int32", "float32", "float16"]) # type: ignore -@pytest.mark.parametrize( - "mask_and_viewable", - [ - (None, True), # An ndarray with no mask should support views - (False, True), # A masked array with an empty mask should support views - ([True, False, False, False], False), # A masked array with an occupied mask should not support views. - ([False, False, False, False], True), # A masked array with an empty occupied mask should support views. - ], -) # type: ignore -@pytest.mark.parametrize( - "shape_and_check_passes", - [ - ((1, 2, 2), True), # A 3D array with a shape[0] == 1 is okay. - ((2, 1, 2), False), # A 3D array with a shape[0] != 1 is not okay. - ((2, 2), True), # A 2D array is okay. - ((4,), True), # A 1D array is okay. - ], -) # type: ignore -def test_get_array_and_mask( - dtype: str, - mask_and_viewable: tuple[None | bool | list[bool], bool], - shape_and_check_passes: tuple[tuple[int, ...], bool], -) -> None: - """Validate that the function returns views when expected, and copies otherwise.""" - warnings.simplefilter("error") - - masked_values, view_should_be_possible = mask_and_viewable - shape, check_should_pass = shape_and_check_passes - - # Create an array of the specified dtype - array = np.ones(shape, dtype=dtype) - if masked_values is not None: - if masked_values is False: - array = np.ma.masked_array(array) - else: - array = np.ma.masked_array(array, mask=np.reshape(masked_values, array.shape)) - - # Validate that incorrect shapes raise the correct error. - if not check_should_pass: - with pytest.raises(ValueError, match="Invalid array shape given"): - gu.spatial_tools.get_array_and_mask(array, check_shape=True) - - # Stop the test here as the failure is now validated. - return - - # Get a copy of the array and check its shape (it should always pass at this point) - arr, _ = gu.spatial_tools.get_array_and_mask(array, copy=True, check_shape=True) - - # Validate that the array is a copy - assert not np.shares_memory(arr, array) - - # If it was an integer dtype and it had a mask, validate that the array is now "float32" - if np.issubdtype(dtype, np.integer) and np.any(masked_values or False): - assert arr.dtype == "float32" - - # If there was no mask or the mask was empty, validate that arr and array are equivalent - if not np.any(masked_values or False): - assert np.sum(np.abs(array - arr)) == 0.0 - - with warnings.catch_warnings(record=True) as caught_warnings: - warnings.simplefilter("always") - - # Try to create a view. - arr_view, mask = gu.spatial_tools.get_array_and_mask(array, copy=False) - - # If it should be possible, validate that there were no warnings. - if view_should_be_possible: - assert len(caught_warnings) == 0, (caught_warnings[0].message, array) - # Otherwise, validate that one warning was raised with the correct text. - else: - assert len(caught_warnings) == 1 - assert "Copying is required" in str(caught_warnings[0].message) - - # Validate that the view shares memory if it was possible, or otherwise that it is a copy. - if view_should_be_possible: - assert np.shares_memory(array, arr_view) - else: - assert not np.shares_memory(array, arr_view) - - -class TestSubsample: - """ - Different examples of 1D to 3D arrays with masked values for testing. - """ - - # Case 1 - 1D array, 1 masked value - array1D = np.ma.masked_array(np.arange(10), mask=np.zeros(10)) - array1D.mask[3] = True - assert np.ndim(array1D) == 1 - assert np.count_nonzero(array1D.mask) > 0 - - # Case 2 - 2D array, 1 masked value - array2D = np.ma.masked_array(np.arange(9).reshape((3, 3)), mask=np.zeros((3, 3))) - array2D.mask[0, 1] = True - assert np.ndim(array2D) == 2 - assert np.count_nonzero(array2D.mask) > 0 - - # Case 3 - 3D array, 1 masked value - array3D = np.ma.masked_array(np.arange(9).reshape((1, 3, 3)), mask=np.zeros((1, 3, 3))) - array3D = np.ma.vstack((array3D, array3D + 10)) - array3D.mask[0, 0, 1] = True - assert np.ndim(array3D) == 3 - assert np.count_nonzero(array3D.mask) > 0 - - @pytest.mark.parametrize("array", [array1D, array2D, array3D]) # type: ignore - def test_subsample(self, array: np.ndarray) -> None: - """ - Test gu.spatial_tools.subsample_raster. - """ - # Test that subsample > 1 works as expected, i.e. output 1D array, with no masked values, or selected size - for npts in np.arange(2, np.size(array)): - random_values = gu.spatial_tools.subsample_raster(array, subsample=npts) - assert np.ndim(random_values) == 1 - assert np.size(random_values) == npts - assert np.count_nonzero(random_values.mask) == 0 - - # Test if subsample > number of valid values => return all - random_values = gu.spatial_tools.subsample_raster(array, subsample=np.size(array) + 3) - assert np.all(np.sort(random_values) == array[~array.mask]) - - # Test if subsample = 1 => return all valid values - random_values = gu.spatial_tools.subsample_raster(array, subsample=1) - assert np.all(np.sort(random_values) == array[~array.mask]) - - # Test if subsample < 1 - random_values = gu.spatial_tools.subsample_raster(array, subsample=0.5) - assert np.size(random_values) == int(np.size(array) * 0.5) - - # Test with optional argument return_indices - indices = gu.spatial_tools.subsample_raster(array, subsample=0.3, return_indices=True) - assert np.ndim(indices) == 2 - assert len(indices) == np.ndim(array) - assert np.ndim(array[indices]) == 1 - assert np.size(array[indices]) == int(np.size(array) * 0.3) - - # Check that we can pass an integer to fix the random state - sub42 = gu.spatial_tools.subsample_raster(array, subsample=10, random_state=42) - # Check by passing a generator directly - random_gen = np.random.RandomState(np.random.MT19937(np.random.SeedSequence(42))) - sub42_gen = gu.spatial_tools.subsample_raster(array, subsample=10, random_state=random_gen) - # Both should be equal - assert np.array_equal(sub42, sub42_gen) - - -class TestRasterTools: - def test_get_valid_extent(self) -> None: - """Check the function to get valid extent.""" - - # Create an artificial array and masked array - arr = np.ones(shape=(5, 5)) - arr_mask = np.zeros(shape=(5, 5), dtype=bool) - mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) - - # For no invalid values, the function should return the edges - # For the array - assert (0, 4, 0, 4) == gu.spatial_tools.get_valid_extent(arr) - # For the masked-array - assert (0, 4, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) - - # 1/ First column: - # If we mask it in the masked array - mask_ma[0, :] = np.ma.masked - assert (1, 4, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) - - # If we changed the array to NaNs - arr[0, :] = np.nan - assert (1, 4, 0, 4) == gu.spatial_tools.get_valid_extent(arr) - mask_ma.data[0, :] = np.nan - mask_ma.mask = False - assert (1, 4, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) - - # 2/ First row: - arr = np.ones(shape=(5, 5)) - arr_mask = np.zeros(shape=(5, 5), dtype=bool) - mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) - # If we mask it in the masked array - mask_ma[:, 0] = np.ma.masked - assert (0, 4, 1, 4) == gu.spatial_tools.get_valid_extent(mask_ma) - - # If we changed the array to NaNs - arr[:, 0] = np.nan - assert (0, 4, 1, 4) == gu.spatial_tools.get_valid_extent(arr) - mask_ma.data[:, 0] = np.nan - mask_ma.mask = False - assert (0, 4, 1, 4) == gu.spatial_tools.get_valid_extent(mask_ma) - - # 3/ Last column: - arr = np.ones(shape=(5, 5)) - arr_mask = np.zeros(shape=(5, 5), dtype=bool) - mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) - - # If we mask it in the masked array - mask_ma[-1, :] = np.ma.masked - assert (0, 3, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) - - # If we changed the array to NaNs - arr[-1, :] = np.nan - assert (0, 3, 0, 4) == gu.spatial_tools.get_valid_extent(arr) - mask_ma.data[-1, :] = np.nan - mask_ma.mask = False - assert (0, 3, 0, 4) == gu.spatial_tools.get_valid_extent(mask_ma) - - # 4/ Last row: - arr = np.ones(shape=(5, 5)) - arr_mask = np.zeros(shape=(5, 5), dtype=bool) - mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) - - # If we mask it in the masked array - mask_ma[:, -1] = np.ma.masked - assert (0, 4, 0, 3) == gu.spatial_tools.get_valid_extent(mask_ma) - - # If we changed the array to NaNs - arr[:, -1] = np.nan - assert (0, 4, 0, 3) == gu.spatial_tools.get_valid_extent(arr) - mask_ma.data[:, -1] = np.nan - mask_ma.mask = False - assert (0, 4, 0, 3) == gu.spatial_tools.get_valid_extent(mask_ma) - - def test_get_xy_rotated(self) -> None: - """Check the function to rotate array.""" - - # Create an artificial raster - width = height = 5 - transform = rio.transform.from_bounds(0, 0, 1, 1, width, height) - r1 = gu.Raster.from_array( - np.random.randint(1, 255, (height, width), dtype="uint8"), transform=transform, crs=None - ) - - # First, we get initial coords - xx, yy = r1.coords(grid=True) - - # Rotating the coordinates 90 degrees should be the same as rotating the array - xx90, yy90 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=90) - assert np.allclose(np.rot90(xx90), xx) - assert np.allclose(np.rot90(yy90), yy) - - # Same for 180 degrees - xx180, yy180 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=180) - assert np.allclose(np.rot90(xx180, k=2), xx) - assert np.allclose(np.rot90(yy180, k=2), yy) - - # Same for 270 degrees - xx270, yy270 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=270) - assert np.allclose(np.rot90(xx270, k=3), xx) - assert np.allclose(np.rot90(yy270, k=3), yy) - - # 360 degrees should get us back on our feet - xx360, yy360 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=360) - assert np.allclose(xx360, xx) - assert np.allclose(yy360, yy) - - # Test that the values make sense for 45 degrees - xx45, yy45 = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=45) - # Should have zero on the upper left corner for xx - assert xx45[0, 0] == pytest.approx(0) - # Then a multiple of sqrt2 along each dimension - assert xx45[1, 0] == pytest.approx(xx45[0, 1]) == pytest.approx(0.1 * np.sqrt(2)) - # The lower right corner should have the highest coordinate (0.8) times sqrt(2) - assert xx45[-1, -1] == pytest.approx(np.max(xx) * np.sqrt(2)) - - # Finally, yy should be rotated by 90 - assert np.allclose(np.rot90(xx45), yy45) - - xx, yy = gu.spatial_tools.get_xy_rotated(r1, along_track_angle=90) From fc4590369f7c58aa7506100139b9b5034b719613 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Mar 2023 14:56:52 -0700 Subject: [PATCH 117/184] Linting --- geoutils/georaster/__init__.py | 6 +++--- geoutils/georaster/array.py | 4 ++-- geoutils/georaster/raster.py | 11 +++++++---- geoutils/georaster/sampling.py | 4 +++- tests/test_array.py | 12 ++++++------ tests/test_georaster.py | 2 +- tests/test_multiraster.py | 11 ++++------- tests/test_sampling.py | 1 + 8 files changed, 27 insertions(+), 24 deletions(-) diff --git a/geoutils/georaster/__init__.py b/geoutils/georaster/__init__.py index 0eee67dbc..87ac7f63e 100644 --- a/geoutils/georaster/__init__.py +++ b/geoutils/georaster/__init__.py @@ -1,7 +1,7 @@ from geoutils.georaster.raster import Raster, RasterType, Mask # noqa isort:skip +from geoutils.georaster.array import * # noqa +from geoutils.georaster.multiraster import * # noqa +from geoutils.georaster.sampling import * # noqa from geoutils.georaster.satimg import SatelliteImage # noqa -from geoutils.georaster.array import * # noqa -from geoutils.georaster.sampling import * # noqa -from geoutils.georaster.multiraster import * # noqa __all__ = ["RasterType", "Raster"] diff --git a/geoutils/georaster/array.py b/geoutils/georaster/array.py index 104ba2a56..2a64ed3e1 100644 --- a/geoutils/georaster/array.py +++ b/geoutils/georaster/array.py @@ -5,8 +5,10 @@ import warnings import numpy as np + import geoutils as gu + def get_mask(array: np.ndarray | np.ma.masked_array) -> np.ndarray: """ Return the mask of invalid values, whether array is a ndarray with NaNs or a np.ma.masked_array. @@ -104,5 +106,3 @@ def get_xy_rotated(raster: gu.Raster, along_track_angle: float) -> tuple[np.ndar yyr -= np.nanmin(yyr) return xxr, yyr - - diff --git a/geoutils/georaster/raster.py b/geoutils/georaster/raster.py index 537ef2498..d13a74cb6 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/georaster/raster.py @@ -32,9 +32,9 @@ import geoutils.geovector as gv from geoutils._typing import AnyNumber, ArrayLike, DTypeLike +from geoutils.georaster.sampling import subsample_array from geoutils.geovector import Vector from geoutils.projtools import _get_bounds_projected -from geoutils.georaster.sampling import subsample_array # If python38 or above, Literal is builtin. Otherwise, use typing_extensions try: @@ -2971,8 +2971,8 @@ def subsample( self, subsample: int | float, return_indices: bool = False, - random_state: None | np.random.RandomState | np.random.Generator | int = None - ) -> np.ndarray: + random_state: None | np.random.RandomState | np.random.Generator | int = None, + ) -> np.ndarray: """ Randomly subsample the raster. Only valid values are considered. @@ -2983,7 +2983,10 @@ def subsample( :return: Array of subsampled valid values, or array of subsampled indices. """ - return subsample_array(array=self.data, subsample=subsample, return_indices=return_indices, random_state=random_state) + return subsample_array( + array=self.data, subsample=subsample, return_indices=return_indices, random_state=random_state + ) + class Mask(Raster): """ diff --git a/geoutils/georaster/sampling.py b/geoutils/georaster/sampling.py index 4972480be..8c76e7b23 100644 --- a/geoutils/georaster/sampling.py +++ b/geoutils/georaster/sampling.py @@ -3,8 +3,10 @@ from __future__ import annotations import numpy as np + from geoutils.georaster.array import get_mask + def subsample_array( array: np.ndarray | np.ma.masked_array, subsample: float | int, @@ -151,4 +153,4 @@ def subdivide_array(shape: tuple[int, ...], count: int) -> np.ndarray: # Upscale the grid to fit the output shape using nearest neighbour scaling. indices = skimage.transform.resize(small_indices, shape, order=0, preserve_range=True).astype(int) - return indices.reshape(shape) \ No newline at end of file + return indices.reshape(shape) diff --git a/tests/test_array.py b/tests/test_array.py index 058594660..b2f3a1d01 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -9,8 +9,8 @@ import geoutils as gu -class TestArray: +class TestArray: @pytest.mark.parametrize("dtype", ["uint8", "uint16", "int32", "float32", "float16"]) # type: ignore @pytest.mark.parametrize( "mask_and_viewable", @@ -30,10 +30,11 @@ class TestArray: ((4,), True), # A 1D array is okay. ], ) # type: ignore - def test_get_array_and_mask(self, - dtype: str, - mask_and_viewable: tuple[None | bool | list[bool], bool], - shape_and_check_passes: tuple[tuple[int, ...], bool], + def test_get_array_and_mask( + self, + dtype: str, + mask_and_viewable: tuple[None | bool | list[bool], bool], + shape_and_check_passes: tuple[tuple[int, ...], bool], ) -> None: """Validate that the function returns views when expected, and copies otherwise.""" warnings.simplefilter("error") @@ -91,7 +92,6 @@ def test_get_array_and_mask(self, else: assert not np.shares_memory(array, arr_view) - def test_get_valid_extent(self) -> None: """Check the function to get valid extent.""" diff --git a/tests/test_georaster.py b/tests/test_georaster.py index 694278210..cca0240bc 100644 --- a/tests/test_georaster.py +++ b/tests/test_georaster.py @@ -2214,7 +2214,7 @@ def test_save(self, example: str) -> None: except (NotADirectoryError, PermissionError): pass - @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) # type: ignore def test_coords(self, example: str) -> None: img = gu.Raster(self.landsat_b4_path) diff --git a/tests/test_multiraster.py b/tests/test_multiraster.py index 1f9f9cbfb..bf723febd 100644 --- a/tests/test_multiraster.py +++ b/tests/test_multiraster.py @@ -72,9 +72,9 @@ def images_3d(): # type: ignore class TestMultiRaster: - @pytest.mark.parametrize( - "rasters", [pytest.lazy_fixture("images_1d"), pytest.lazy_fixture("sat_images"), pytest.lazy_fixture("images_3d")] + "rasters", + [pytest.lazy_fixture("images_1d"), pytest.lazy_fixture("sat_images"), pytest.lazy_fixture("images_3d")], ) # type: ignore def test_stack_rasters(self, rasters) -> None: # type: ignore """Test stack_rasters""" @@ -87,8 +87,8 @@ def test_stack_rasters(self, rasters) -> None: # type: ignore if rasters.img1.count > 1: # Check warning is raised once with pytest.warns( - expected_warning=UserWarning, - match="Some input Rasters have multiple bands, only their first band will be used.", + expected_warning=UserWarning, + match="Some input Rasters have multiple bands, only their first band will be used.", ): stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2]) # Then ignore the other ones @@ -167,8 +167,6 @@ def test_merge_rasters(self, rasters) -> None: # type: ignore merged_img2 = gu.georaster.merge_rasters([rasters.img1, rasters.img2], reference=rasters.img) assert merged_img2 == merged_img - - # Group rasters for for testing `load_multiple_rasters` # two overlapping, single band rasters # two overlapping, 1 and 3 band rasters @@ -275,4 +273,3 @@ def test_load_multiple_no_overlap(self, raster_paths: list[str]) -> None: # Logically, the non overlapping raster should have only masked values if k != 0: assert np.count_nonzero(~rst.data.mask) == 0 - diff --git a/tests/test_sampling.py b/tests/test_sampling.py index 940bf9dd4..39fc2df6e 100644 --- a/tests/test_sampling.py +++ b/tests/test_sampling.py @@ -7,6 +7,7 @@ import geoutils as gu + class TestSubsampling: """ Different examples of 1D to 3D arrays with masked values for testing. From f52609c0bd4e6c45aff92f50be4f6763df80caa3 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Mar 2023 15:35:25 -0700 Subject: [PATCH 118/184] Rename modules georaster and geovector into raster and vector (geo is already in geoutils) --- .pre-commit-config.yaml | 2 +- README.md | 4 +- {geoutils => bin}/geoviewer.py | 4 +- doc/source/conf.py | 8 ++-- doc/source/core_array_funcs.md | 2 +- doc/source/core_inheritance.md | 4 +- doc/source/quick_start.md | 4 +- doc/source/satimg_class.md | 2 +- geoutils/__init__.py | 6 +-- geoutils/georaster/__init__.py | 7 --- geoutils/raster/__init__.py | 7 +++ geoutils/{georaster => raster}/array.py | 0 geoutils/{georaster => raster}/multiraster.py | 6 +-- geoutils/{georaster => raster}/raster.py | 10 ++-- geoutils/{georaster => raster}/sampling.py | 2 +- geoutils/{georaster => raster}/satimg.py | 2 +- geoutils/{geovector.py => vector.py} | 2 +- setup.py | 7 ++- tests/test_array.py | 46 +++++++++---------- tests/test_geoviewer.py | 9 ++-- tests/test_multiraster.py | 30 ++++++------ tests/{test_georaster.py => test_raster.py} | 16 +++---- tests/test_sampling.py | 24 +++++----- tests/test_satimg.py | 38 +++++++-------- tests/{test_geovector.py => test_vector.py} | 18 ++++---- 25 files changed, 131 insertions(+), 129 deletions(-) rename {geoutils => bin}/geoviewer.py (98%) delete mode 100644 geoutils/georaster/__init__.py create mode 100644 geoutils/raster/__init__.py rename geoutils/{georaster => raster}/array.py (100%) rename geoutils/{georaster => raster}/multiraster.py (98%) rename geoutils/{georaster => raster}/raster.py (99%) rename geoutils/{georaster => raster}/sampling.py (99%) rename geoutils/{georaster => raster}/satimg.py (99%) rename geoutils/{geovector.py => vector.py} (99%) rename tests/{test_georaster.py => test_raster.py} (99%) rename tests/{test_geovector.py => test_vector.py} (97%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 16ee23464..24e4f8c0e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: types_or: [python, rst, markdown] files: ^(geoutils|doc|tests)/ - # Replace relative imports (e.g. 'from . import georaster' -> 'from geoutils import georaster') + # Replace relative imports (e.g. 'from . import raster' -> 'from geoutils import raster') - repo: https://github.com/MarcoGorelli/absolufy-imports rev: v0.3.1 hooks: diff --git a/README.md b/README.md index 87589d328..1dc549829 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ See the full documentation at https://geoutils.readthedocs.io. ## Structure GeoUtils is composed of three libraries: -- `georaster.py` to handle raster data set. In particular, a Raster class to load a raster file along with metadata. -- `geovector.py` to handle vector data set. In particular, a Vector class to load a raster file along with metadata. +- `raster.py` to handle raster data set. In particular, a Raster class to load a raster file along with metadata. +- `vector.py` to handle vector data set. In particular, a Vector class to load a raster file along with metadata. - `projtools.py` with various tools around projections. diff --git a/geoutils/geoviewer.py b/bin/geoviewer.py similarity index 98% rename from geoutils/geoviewer.py rename to bin/geoviewer.py index 77be6b445..60b58e500 100755 --- a/geoutils/geoviewer.py +++ b/bin/geoviewer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python """ -geoutils.geoviewer provides a toolset for plotting raster and vector data +Geoviewer provides a command line tool for plotting raster and vector data. TO DO: - change so that only needed band is loaded @@ -14,7 +14,7 @@ import matplotlib.pyplot as plt import numpy as np -from geoutils.georaster import Raster +from geoutils.raster import Raster def getparser() -> argparse.ArgumentParser: diff --git a/doc/source/conf.py b/doc/source/conf.py index e2894dcd7..076ec2729 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -91,10 +91,10 @@ # To avoid long path names in inheritance diagrams inheritance_alias = { - "geoutils.georaster.raster.Raster": "geoutils.Raster", - "geoutils.georaster.raster.Mask": "geoutils.Mask", - "geoutils.georaster.satimg.SatelliteImage": "geoutils.SatelliteImage", - "geoutils.geovector.Vector": "geoutils.Vector", + "geoutils.raster.raster.Raster": "geoutils.Raster", + "geoutils.raster.raster.Mask": "geoutils.Mask", + "geoutils.raster.satimg.SatelliteImage": "geoutils.SatelliteImage", + "geoutils.vector.Vector": "geoutils.Vector", "xdem.dem.DEM": "xdem.DEM", } diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index 05dfa9a8a..df97d5499 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -100,7 +100,7 @@ np.count_nonzero(raster, axis=2) Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), [masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). -A full list of supported array function is available in {attr}`geoutils.georaster.raster.handled_array_funcs`. +A full list of supported array function is available in {attr}`geoutils.raster.raster.handled_array_funcs`. ## Respecting masked values diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 43a4819a4..2f9383507 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -18,8 +18,8 @@ Below is a diagram showing current {class}`~geoutils.Raster` inheritance, which for analyzing digital elevation models. ```{eval-rst} -.. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM - :top-classes: geoutils.georaster.raster.Raster +.. inheritance-diagram:: geoutils.raster.raster geoutils.raster.satimg xdem.dem.DEM + :top-classes: geoutils.raster.raster.Raster ``` ```{note} diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index a57fd5c08..ff36f9b94 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -267,8 +267,8 @@ There are many possible subclass to derive from a {class}`~geoutils.Raster`. Her [xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: ```{eval-rst} -.. inheritance-diagram:: geoutils.georaster.raster geoutils.georaster.satimg xdem.dem.DEM - :top-classes: geoutils.georaster.raster.Raster +.. inheritance-diagram:: geoutils.raster.raster geoutils.raster.satimg xdem.dem.DEM + :top-classes: geoutils.raster.raster.Raster ``` ```{seealso} The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md index 42a90aefe..aa6f41a30 100644 --- a/doc/source/satimg_class.md +++ b/doc/source/satimg_class.md @@ -34,7 +34,7 @@ Right now are supported: The {class}`~geoutils.SatelliteImage.datetime` is always parsed or deduced. -For tiled products such as SRTM, the tile naming is also retrieved, which can be converted to geographic extent with {func}`geoutils.georaster.satimg.parse_tile_attr_from_name`. +For tiled products such as SRTM, the tile naming is also retrieved, which can be converted to geographic extent with {func}`geoutils.raster.satimg.parse_tile_attr_from_name`. ```{code-cell} ipython3 import geoutils as gu diff --git a/geoutils/__init__.py b/geoutils/__init__.py index b1659e23b..36652a7ef 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -2,9 +2,9 @@ GeoUtils is a python package of raster and vector tools. """ -from geoutils import examples, georaster, geovector, projtools # noqa -from geoutils.georaster import Mask, Raster, SatelliteImage # noqa -from geoutils.geovector import Vector # noqa +from geoutils import examples, raster, vector, projtools # noqa +from geoutils.raster import Mask, Raster, SatelliteImage # noqa +from geoutils.vector import Vector # noqa try: from geoutils.version import version as __version__ # noqa diff --git a/geoutils/georaster/__init__.py b/geoutils/georaster/__init__.py deleted file mode 100644 index 87ac7f63e..000000000 --- a/geoutils/georaster/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -from geoutils.georaster.raster import Raster, RasterType, Mask # noqa isort:skip -from geoutils.georaster.array import * # noqa -from geoutils.georaster.multiraster import * # noqa -from geoutils.georaster.sampling import * # noqa -from geoutils.georaster.satimg import SatelliteImage # noqa - -__all__ = ["RasterType", "Raster"] diff --git a/geoutils/raster/__init__.py b/geoutils/raster/__init__.py new file mode 100644 index 000000000..aceb2c43b --- /dev/null +++ b/geoutils/raster/__init__.py @@ -0,0 +1,7 @@ +from geoutils.raster.raster import Raster, RasterType, Mask # noqa isort:skip +from geoutils.raster.array import * # noqa +from geoutils.raster.multiraster import * # noqa +from geoutils.raster.sampling import * # noqa +from geoutils.raster.satimg import SatelliteImage # noqa + +__all__ = ["RasterType", "Raster"] diff --git a/geoutils/georaster/array.py b/geoutils/raster/array.py similarity index 100% rename from geoutils/georaster/array.py rename to geoutils/raster/array.py diff --git a/geoutils/georaster/multiraster.py b/geoutils/raster/multiraster.py similarity index 98% rename from geoutils/georaster/multiraster.py rename to geoutils/raster/multiraster.py index 9a3e1abc4..09eed1631 100644 --- a/geoutils/georaster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -10,8 +10,8 @@ from tqdm import tqdm import geoutils as gu -from geoutils.georaster import Raster, RasterType, get_array_and_mask -from geoutils.georaster.raster import _default_nodata +from geoutils.raster import Raster, RasterType, get_array_and_mask +from geoutils.raster.raster import _default_nodata from geoutils.misc import resampling_method_from_str @@ -175,7 +175,7 @@ def stack_rasters( raster.load() raster.is_loaded = False - nodata = reference_raster.nodata or gu.georaster.raster._default_nodata(reference_raster.data.dtype) + nodata = reference_raster.nodata or gu.raster.raster._default_nodata(reference_raster.data.dtype) # Reproject to reference grid reprojected_raster = raster.reproject( dst_bounds=dst_bounds, diff --git a/geoutils/georaster/raster.py b/geoutils/raster/raster.py similarity index 99% rename from geoutils/georaster/raster.py rename to geoutils/raster/raster.py index d13a74cb6..c8bc8818c 100644 --- a/geoutils/georaster/raster.py +++ b/geoutils/raster/raster.py @@ -1,5 +1,5 @@ """ -geoutils.georaster provides a toolset for working with raster data. +geoutils.raster provides a toolset for working with raster data. """ from __future__ import annotations @@ -30,10 +30,10 @@ from rasterio.plot import show as rshow from scipy.ndimage import distance_transform_edt, map_coordinates -import geoutils.geovector as gv +import geoutils.vector as gv from geoutils._typing import AnyNumber, ArrayLike, DTypeLike -from geoutils.georaster.sampling import subsample_array -from geoutils.geovector import Vector +from geoutils.raster.sampling import subsample_array +from geoutils.vector import Vector from geoutils.projtools import _get_bounds_projected # If python38 or above, Literal is builtin. Otherwise, use typing_extensions @@ -271,7 +271,7 @@ def __init__( :param nodata: The nodata value to be used (overwrites the metadata). Default reads from metadata. :param attrs: Additional attributes from rasterio's DataReader class to add to the Raster object. - Default list is set by geoutils.georaster.raster._default_rio_attrs, i.e. + Default list is set by geoutils.raster.raster._default_rio_attrs, i.e. ['bounds', 'count', 'crs', 'driver', 'dtypes', 'height', 'indexes', 'name', 'nodata', 'res', 'shape', 'transform', 'width'] - if no attrs are specified, these will be added. """ diff --git a/geoutils/georaster/sampling.py b/geoutils/raster/sampling.py similarity index 99% rename from geoutils/georaster/sampling.py rename to geoutils/raster/sampling.py index 8c76e7b23..62cfcaa30 100644 --- a/geoutils/georaster/sampling.py +++ b/geoutils/raster/sampling.py @@ -4,7 +4,7 @@ import numpy as np -from geoutils.georaster.array import get_mask +from geoutils.raster.array import get_mask def subsample_array( diff --git a/geoutils/georaster/satimg.py b/geoutils/raster/satimg.py similarity index 99% rename from geoutils/georaster/satimg.py rename to geoutils/raster/satimg.py index 0bb0dd301..051da34f3 100644 --- a/geoutils/georaster/satimg.py +++ b/geoutils/raster/satimg.py @@ -13,7 +13,7 @@ import numpy as np import rasterio as rio -from geoutils.georaster import Raster, RasterType +from geoutils.raster import Raster, RasterType lsat_sensor = {"C": "OLI/TIRS", "E": "ETM+", "T": "TM", "M": "MSS", "O": "OLI", "TI": "TIRS"} diff --git a/geoutils/geovector.py b/geoutils/vector.py similarity index 99% rename from geoutils/geovector.py rename to geoutils/vector.py index 43dc9d4c2..54b03ce80 100644 --- a/geoutils/geovector.py +++ b/geoutils/vector.py @@ -758,7 +758,7 @@ def proximity( raster = gu.Raster.from_array(data=np.zeros((1000, 1000)), transform=transform, crs=self.crs) - proximity = gu.georaster.raster.proximity_from_vector_or_raster( + proximity = gu.raster.raster.proximity_from_vector_or_raster( raster=raster, vector=self, geometry_type=geometry_type, in_or_out=in_or_out, distance_unit=distance_unit ) diff --git a/setup.py b/setup.py index 97933d356..8910350a1 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -from glob import glob from os import path from typing import Optional @@ -35,13 +34,13 @@ def write_version_py(filename: Optional[str] = None) -> None: setup( name="geoutils", version=FULLVERSION, - description="Tools for working with geospatial data", + description="Analysis and handling of georeferenced rasters and vectors", long_description=long_description, long_description_content_type="text/markdown", url="https://www.github.com/GlacioHack/geoutils/", author="The GlacioHack Team", license="BSD-3", - packages=["geoutils", "geoutils.georaster"], + packages=["geoutils", "geoutils.raster"], python_requires=">=3.8", install_requires=[ "rasterio", @@ -53,7 +52,7 @@ def write_version_py(filename: Optional[str] = None) -> None: "tqdm", ], extras_require={"rioxarray": ["rioxarray"]}, - scripts=["geoutils/geoviewer.py"], + scripts=["bin/geoviewer.py"], zip_safe=False, classifiers=[ "Programming Language :: Python :: 3", diff --git a/tests/test_array.py b/tests/test_array.py index b2f3a1d01..286bfd3fa 100644 --- a/tests/test_array.py +++ b/tests/test_array.py @@ -53,13 +53,13 @@ def test_get_array_and_mask( # Validate that incorrect shapes raise the correct error. if not check_should_pass: with pytest.raises(ValueError, match="Invalid array shape given"): - gu.georaster.get_array_and_mask(array, check_shape=True) + gu.raster.get_array_and_mask(array, check_shape=True) # Stop the test here as the failure is now validated. return # Get a copy of the array and check its shape (it should always pass at this point) - arr, _ = gu.georaster.get_array_and_mask(array, copy=True, check_shape=True) + arr, _ = gu.raster.get_array_and_mask(array, copy=True, check_shape=True) # Validate that the array is a copy assert not np.shares_memory(arr, array) @@ -76,7 +76,7 @@ def test_get_array_and_mask( warnings.simplefilter("always") # Try to create a view. - arr_view, mask = gu.georaster.get_array_and_mask(array, copy=False) + arr_view, mask = gu.raster.get_array_and_mask(array, copy=False) # If it should be possible, validate that there were no warnings. if view_should_be_possible: @@ -102,21 +102,21 @@ def test_get_valid_extent(self) -> None: # For no invalid values, the function should return the edges # For the array - assert (0, 4, 0, 4) == gu.georaster.get_valid_extent(arr) + assert (0, 4, 0, 4) == gu.raster.get_valid_extent(arr) # For the masked-array - assert (0, 4, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + assert (0, 4, 0, 4) == gu.raster.get_valid_extent(mask_ma) # 1/ First column: # If we mask it in the masked array mask_ma[0, :] = np.ma.masked - assert (1, 4, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + assert (1, 4, 0, 4) == gu.raster.get_valid_extent(mask_ma) # If we changed the array to NaNs arr[0, :] = np.nan - assert (1, 4, 0, 4) == gu.georaster.get_valid_extent(arr) + assert (1, 4, 0, 4) == gu.raster.get_valid_extent(arr) mask_ma.data[0, :] = np.nan mask_ma.mask = False - assert (1, 4, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + assert (1, 4, 0, 4) == gu.raster.get_valid_extent(mask_ma) # 2/ First row: arr = np.ones(shape=(5, 5)) @@ -124,14 +124,14 @@ def test_get_valid_extent(self) -> None: mask_ma = np.ma.masked_array(data=arr, mask=arr_mask) # If we mask it in the masked array mask_ma[:, 0] = np.ma.masked - assert (0, 4, 1, 4) == gu.georaster.get_valid_extent(mask_ma) + assert (0, 4, 1, 4) == gu.raster.get_valid_extent(mask_ma) # If we changed the array to NaNs arr[:, 0] = np.nan - assert (0, 4, 1, 4) == gu.georaster.get_valid_extent(arr) + assert (0, 4, 1, 4) == gu.raster.get_valid_extent(arr) mask_ma.data[:, 0] = np.nan mask_ma.mask = False - assert (0, 4, 1, 4) == gu.georaster.get_valid_extent(mask_ma) + assert (0, 4, 1, 4) == gu.raster.get_valid_extent(mask_ma) # 3/ Last column: arr = np.ones(shape=(5, 5)) @@ -140,14 +140,14 @@ def test_get_valid_extent(self) -> None: # If we mask it in the masked array mask_ma[-1, :] = np.ma.masked - assert (0, 3, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + assert (0, 3, 0, 4) == gu.raster.get_valid_extent(mask_ma) # If we changed the array to NaNs arr[-1, :] = np.nan - assert (0, 3, 0, 4) == gu.georaster.get_valid_extent(arr) + assert (0, 3, 0, 4) == gu.raster.get_valid_extent(arr) mask_ma.data[-1, :] = np.nan mask_ma.mask = False - assert (0, 3, 0, 4) == gu.georaster.get_valid_extent(mask_ma) + assert (0, 3, 0, 4) == gu.raster.get_valid_extent(mask_ma) # 4/ Last row: arr = np.ones(shape=(5, 5)) @@ -156,14 +156,14 @@ def test_get_valid_extent(self) -> None: # If we mask it in the masked array mask_ma[:, -1] = np.ma.masked - assert (0, 4, 0, 3) == gu.georaster.get_valid_extent(mask_ma) + assert (0, 4, 0, 3) == gu.raster.get_valid_extent(mask_ma) # If we changed the array to NaNs arr[:, -1] = np.nan - assert (0, 4, 0, 3) == gu.georaster.get_valid_extent(arr) + assert (0, 4, 0, 3) == gu.raster.get_valid_extent(arr) mask_ma.data[:, -1] = np.nan mask_ma.mask = False - assert (0, 4, 0, 3) == gu.georaster.get_valid_extent(mask_ma) + assert (0, 4, 0, 3) == gu.raster.get_valid_extent(mask_ma) def test_get_xy_rotated(self) -> None: """Check the function to rotate array.""" @@ -179,27 +179,27 @@ def test_get_xy_rotated(self) -> None: xx, yy = r1.coords(grid=True) # Rotating the coordinates 90 degrees should be the same as rotating the array - xx90, yy90 = gu.georaster.get_xy_rotated(r1, along_track_angle=90) + xx90, yy90 = gu.raster.get_xy_rotated(r1, along_track_angle=90) assert np.allclose(np.rot90(xx90), xx) assert np.allclose(np.rot90(yy90), yy) # Same for 180 degrees - xx180, yy180 = gu.georaster.get_xy_rotated(r1, along_track_angle=180) + xx180, yy180 = gu.raster.get_xy_rotated(r1, along_track_angle=180) assert np.allclose(np.rot90(xx180, k=2), xx) assert np.allclose(np.rot90(yy180, k=2), yy) # Same for 270 degrees - xx270, yy270 = gu.georaster.get_xy_rotated(r1, along_track_angle=270) + xx270, yy270 = gu.raster.get_xy_rotated(r1, along_track_angle=270) assert np.allclose(np.rot90(xx270, k=3), xx) assert np.allclose(np.rot90(yy270, k=3), yy) # 360 degrees should get us back on our feet - xx360, yy360 = gu.georaster.get_xy_rotated(r1, along_track_angle=360) + xx360, yy360 = gu.raster.get_xy_rotated(r1, along_track_angle=360) assert np.allclose(xx360, xx) assert np.allclose(yy360, yy) # Test that the values make sense for 45 degrees - xx45, yy45 = gu.georaster.get_xy_rotated(r1, along_track_angle=45) + xx45, yy45 = gu.raster.get_xy_rotated(r1, along_track_angle=45) # Should have zero on the upper left corner for xx assert xx45[0, 0] == pytest.approx(0) # Then a multiple of sqrt2 along each dimension @@ -210,4 +210,4 @@ def test_get_xy_rotated(self) -> None: # Finally, yy should be rotated by 90 assert np.allclose(np.rot90(xx45), yy45) - xx, yy = gu.georaster.get_xy_rotated(r1, along_track_angle=90) + xx, yy = gu.raster.get_xy_rotated(r1, along_track_angle=90) diff --git a/tests/test_geoviewer.py b/tests/test_geoviewer.py index 25094f75e..0c07207ab 100644 --- a/tests/test_geoviewer.py +++ b/tests/test_geoviewer.py @@ -1,13 +1,16 @@ from __future__ import annotations import os +import sys import matplotlib.pyplot as plt import pytest import geoutils as gu -import geoutils.geoviewer as gv +# Import executable just for the test +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "bin/geoviewer.py"))) +import geoviewer @pytest.mark.parametrize( "filename", [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")] @@ -35,7 +38,7 @@ def test_geoviewer_valid(capsys, monkeypatch, filename, option): # type: ignore # To not get exception when testing generic functions such as --help try: - gv.main([filename, *option]) + geoviewer.main([filename, *option]) except SystemExit: pass @@ -72,4 +75,4 @@ def test_geoviewer_invalid(capsys, monkeypatch, filename, option): # type: igno # To not get exception when testing generic functions such as --help with pytest.raises(ValueError): - gv.main([filename, *option]) + geoviewer.main([filename, *option]) diff --git a/tests/test_multiraster.py b/tests/test_multiraster.py index bf723febd..7845c4464 100644 --- a/tests/test_multiraster.py +++ b/tests/test_multiraster.py @@ -12,7 +12,7 @@ import geoutils as gu from geoutils import examples -from geoutils.georaster import RasterType +from geoutils.raster import RasterType class stack_merge_images: @@ -90,7 +90,7 @@ def test_stack_rasters(self, rasters) -> None: # type: ignore expected_warning=UserWarning, match="Some input Rasters have multiple bands, only their first band will be used.", ): - stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2]) + stacked_img = gu.raster.stack_rasters([rasters.img1, rasters.img2]) # Then ignore the other ones warnings.filterwarnings( "ignore", @@ -99,7 +99,7 @@ def test_stack_rasters(self, rasters) -> None: # type: ignore ) else: - stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2]) + stacked_img = gu.raster.stack_rasters([rasters.img1, rasters.img2]) assert stacked_img.count == 2 assert rasters.img.shape == stacked_img.shape @@ -112,12 +112,12 @@ def test_stack_rasters(self, rasters) -> None: # type: ignore assert merged_bounds == stacked_img.bounds # Check that reference works with input Raster - stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2], reference=rasters.img) + stacked_img = gu.raster.stack_rasters([rasters.img1, rasters.img2], reference=rasters.img) assert rasters.img.bounds == stacked_img.bounds # Others than int or gu.Raster should raise a ValueError try: - stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img2], reference="a string") + stacked_img = gu.raster.stack_rasters([rasters.img1, rasters.img2], reference="a string") except ValueError as exception: if "reference should be" not in str(exception): raise exception @@ -125,11 +125,11 @@ def test_stack_rasters(self, rasters) -> None: # type: ignore # Check that use_ref_bounds works - use a img that do not cover the whole extent # This case should not preserve original extent - stacked_img = gu.georaster.stack_rasters([rasters.img1, rasters.img3]) + stacked_img = gu.raster.stack_rasters([rasters.img1, rasters.img3]) assert stacked_img.bounds != rasters.img.bounds # This case should preserve original extent - stacked_img2 = gu.georaster.stack_rasters( + stacked_img2 = gu.raster.stack_rasters( [rasters.img1, rasters.img3], reference=rasters.img, use_ref_bounds=True ) assert stacked_img2.bounds == rasters.img.bounds @@ -153,7 +153,7 @@ def test_merge_rasters(self, rasters) -> None: # type: ignore message="Some input Rasters have multiple bands, only their first band will be used.", ) - merged_img = gu.georaster.merge_rasters([rasters.img1, rasters.img2], merge_algorithm=np.nanmean) + merged_img = gu.raster.merge_rasters([rasters.img1, rasters.img2], merge_algorithm=np.nanmean) assert rasters.img.shape == merged_img.shape assert rasters.img.bounds == merged_img.bounds @@ -164,7 +164,7 @@ def test_merge_rasters(self, rasters) -> None: # type: ignore assert np.abs(np.nanmean(diff)) < 1 # Check that reference works - merged_img2 = gu.georaster.merge_rasters([rasters.img1, rasters.img2], reference=rasters.img) + merged_img2 = gu.raster.merge_rasters([rasters.img1, rasters.img2], reference=rasters.img) assert merged_img2 == merged_img # Group rasters for for testing `load_multiple_rasters` @@ -188,14 +188,14 @@ def test_load_multiple_overlap(self, raster_paths: list[str]) -> None: Test load_multiple_rasters functionalities, when rasters overlap -> no warning is raised """ # - Test that with crop=False and ref_grid=None, rasters are simply loaded - # - output_rst: list[gu.Raster] = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) + output_rst: list[gu.Raster] = gu.raster.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) for k, rst in enumerate(output_rst): assert rst.is_loaded rst2 = gu.Raster(raster_paths[k]) assert rst == rst2 # - Test that with crop=True and ref_grid=None, rasters are cropped only in area of overlap - # - output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) + output_rst = gu.raster.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) ref_crs = gu.Raster(raster_paths[0], load_data=False).crs # Save original and new bounds (as polygons) in the reference CRS @@ -217,7 +217,7 @@ def test_load_multiple_overlap(self, raster_paths: list[str]) -> None: # For the landsat test case, a warning will be raised because nodata is None with warnings.catch_warnings(): warnings.simplefilter("ignore", category=UserWarning) - output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) + output_rst = gu.raster.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) ref_rst = gu.Raster(raster_paths[0], load_data=False) for k, rst in enumerate(output_rst): @@ -242,7 +242,7 @@ def test_load_multiple_no_overlap(self, raster_paths: list[str]) -> None: Test load_multiple_rasters functionalities with rasters that do not overlap -> raises warning in certain cases """ # - With crop=False and ref_grid=None, rasters are simply loaded - # - output_rst: list[gu.Raster] = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) + output_rst: list[gu.Raster] = gu.raster.load_multiple_rasters(raster_paths, crop=False, ref_grid=None) for k, rst in enumerate(output_rst): assert rst.is_loaded rst2 = gu.Raster(raster_paths[k]) @@ -250,13 +250,13 @@ def test_load_multiple_no_overlap(self, raster_paths: list[str]) -> None: # - With crop=True -> should raise a warning - # with pytest.warns(UserWarning, match="Intersection is void, returning unloaded rasters."): - output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) + output_rst = gu.raster.load_multiple_rasters(raster_paths, crop=True, ref_grid=None) # - Should work with crop=False and ref_grid=0 - # # For the landsat test case, a warning will be raised because nodata is None with warnings.catch_warnings(): warnings.simplefilter("ignore", category=UserWarning) - output_rst = gu.georaster.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) + output_rst = gu.raster.load_multiple_rasters(raster_paths, crop=False, ref_grid=0) ref_rst = gu.Raster(raster_paths[0], load_data=False) for k, rst in enumerate(output_rst): diff --git a/tests/test_georaster.py b/tests/test_raster.py similarity index 99% rename from tests/test_georaster.py rename to tests/test_raster.py index cca0240bc..78895441d 100644 --- a/tests/test_georaster.py +++ b/tests/test_raster.py @@ -1,5 +1,5 @@ """ -Test functions for georaster +Test functions for raster """ from __future__ import annotations @@ -20,7 +20,7 @@ import geoutils as gu import geoutils.projtools as pt from geoutils import examples -from geoutils.georaster.raster import _default_nodata, _default_rio_attrs +from geoutils.raster.raster import _default_nodata, _default_rio_attrs from geoutils.misc import resampling_method_from_str from geoutils.projtools import reproject_to_latlon @@ -767,7 +767,7 @@ def test_copy(self, example: str) -> None: # When passing the new array as a NaN ndarray, only the valid data is equal, because masked data is NaN in one # case, and -9999 in the other - r_arr = gu.georaster.get_array_and_mask(r)[0] + r_arr = gu.raster.get_array_and_mask(r)[0] r2 = r.copy(new_array=r_arr) assert np.ma.allequal(r.data, r2.data) # If a nodata value exists, and we update the NaN pixels to be that nodata value, then the two Rasters should @@ -1122,13 +1122,13 @@ def test_reproject(self, example: str) -> None: r_nodata.set_nodata(None) # Make sure at least one pixel is masked for test 1 - rand_indices = gu.georaster.subsample_array(r_nodata.data, 10, return_indices=True) + rand_indices = gu.raster.subsample_array(r_nodata.data, 10, return_indices=True) r_nodata.data[rand_indices] = np.ma.masked assert np.count_nonzero(r_nodata.data.mask) > 0 # make sure at least one pixel is set at default nodata for test default_nodata = _default_nodata(r_nodata.dtypes[0]) - rand_indices = gu.georaster.subsample_array(r_nodata.data, 10, return_indices=True) + rand_indices = gu.raster.subsample_array(r_nodata.data, 10, return_indices=True) r_nodata.data[rand_indices] = default_nodata assert np.count_nonzero(r_nodata.data == default_nodata) > 0 @@ -1229,7 +1229,7 @@ def test_reproject(self, example: str) -> None: # Create a raster with (additional) random gaps r_gaps = r.copy() nsamples = 200 - rand_indices = gu.georaster.subsample_array(r_gaps.data, nsamples, return_indices=True) + rand_indices = gu.raster.subsample_array(r_gaps.data, nsamples, return_indices=True) r_gaps.data[rand_indices] = np.ma.masked assert np.sum(r_gaps.data.mask) - np.sum(r.data.mask) == nsamples # sanity check @@ -3475,8 +3475,8 @@ class TestArrayInterface: # Most other math functions are already universal functions # Separate between two lists (single input and double input) for testing - handled_functions_2in = gu.georaster.raster._HANDLED_FUNCTIONS_2NIN - handled_functions_1in = gu.georaster.raster._HANDLED_FUNCTIONS_1NIN + handled_functions_2in = gu.raster.raster._HANDLED_FUNCTIONS_2NIN + handled_functions_1in = gu.raster.raster._HANDLED_FUNCTIONS_1NIN # Details below: # NaN functions: [f for f in np.lib.nanfunctions.__all__] diff --git a/tests/test_sampling.py b/tests/test_sampling.py index 39fc2df6e..041b8d16a 100644 --- a/tests/test_sampling.py +++ b/tests/test_sampling.py @@ -35,54 +35,54 @@ class TestSubsampling: @pytest.mark.parametrize("array", [array1D, array2D, array3D]) # type: ignore def test_subsample(self, array: np.ndarray) -> None: """ - Test gu.georaster.subsample_array. + Test gu.raster.subsample_array. """ # Test that subsample > 1 works as expected, i.e. output 1D array, with no masked values, or selected size for npts in np.arange(2, np.size(array)): - random_values = gu.georaster.subsample_array(array, subsample=npts) + random_values = gu.raster.subsample_array(array, subsample=npts) assert np.ndim(random_values) == 1 assert np.size(random_values) == npts assert np.count_nonzero(random_values.mask) == 0 # Test if subsample > number of valid values => return all - random_values = gu.georaster.subsample_array(array, subsample=np.size(array) + 3) + random_values = gu.raster.subsample_array(array, subsample=np.size(array) + 3) assert np.all(np.sort(random_values) == array[~array.mask]) # Test if subsample = 1 => return all valid values - random_values = gu.georaster.subsample_array(array, subsample=1) + random_values = gu.raster.subsample_array(array, subsample=1) assert np.all(np.sort(random_values) == array[~array.mask]) # Test if subsample < 1 - random_values = gu.georaster.subsample_array(array, subsample=0.5) + random_values = gu.raster.subsample_array(array, subsample=0.5) assert np.size(random_values) == int(np.size(array) * 0.5) # Test with optional argument return_indices - indices = gu.georaster.subsample_array(array, subsample=0.3, return_indices=True) + indices = gu.raster.subsample_array(array, subsample=0.3, return_indices=True) assert np.ndim(indices) == 2 assert len(indices) == np.ndim(array) assert np.ndim(array[indices]) == 1 assert np.size(array[indices]) == int(np.size(array) * 0.3) # Check that we can pass an integer to fix the random state - sub42 = gu.georaster.subsample_array(array, subsample=10, random_state=42) + sub42 = gu.raster.subsample_array(array, subsample=10, random_state=42) # Check by passing a generator directly random_gen = np.random.RandomState(np.random.MT19937(np.random.SeedSequence(42))) - sub42_gen = gu.georaster.subsample_array(array, subsample=10, random_state=random_gen) + sub42_gen = gu.raster.subsample_array(array, subsample=10, random_state=random_gen) # Both should be equal assert np.array_equal(sub42, sub42_gen) def test_subdivide_array(self) -> None: test_shape = (6, 4) test_count = 4 - subdivision_grid = gu.georaster.subdivide_array(test_shape, test_count) + subdivision_grid = gu.raster.subdivide_array(test_shape, test_count) assert subdivision_grid.shape == test_shape assert np.unique(subdivision_grid).size == test_count - assert np.unique(gu.georaster.subdivide_array((3, 3), 3)).size == 3 + assert np.unique(gu.raster.subdivide_array((3, 3), 3)).size == 3 with pytest.raises(ValueError, match=r"Expected a 2D shape, got 1D shape.*"): - gu.georaster.subdivide_array((5,), 2) + gu.raster.subdivide_array((5,), 2) with pytest.raises(ValueError, match=r"Shape.*smaller than.*"): - gu.georaster.subdivide_array((5, 2), 15) + gu.raster.subdivide_array((5, 2), 15) diff --git a/tests/test_satimg.py b/tests/test_satimg.py index a87004ffe..ca779aa77 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -89,7 +89,7 @@ def test_add_sub(self) -> None: np.random.randint(0, 255, (height, width), dtype="uint8"), transform=transform, crs=None ) - # Check that output type is same - other tests are in test_georaster.py + # Check that output type is same - other tests are in test_raster.py sat_out = -satimg1 assert isinstance(sat_out, gu.SatelliteImage) @@ -119,7 +119,7 @@ def test_copy(self, example: str) -> None: assert isinstance(r2, gu.SatelliteImage) # check all immutable attributes are equal - georaster_attrs = [ + raster_attrs = [ "bounds", "count", "crs", @@ -134,8 +134,8 @@ def test_copy(self, example: str) -> None: ] satimg_attrs = ["satellite", "sensor", "product", "version", "tile_name", "datetime"] # using list directly available in Class - attrs = georaster_attrs + satimg_attrs - all_attrs = attrs + gu.georaster.satimg.satimg_attrs + attrs = raster_attrs + satimg_attrs + all_attrs = attrs + gu.raster.satimg.satimg_attrs for attr in all_attrs: assert r.__getattribute__(attr) == r2.__getattribute__(attr) @@ -192,7 +192,7 @@ def test_filename_parsing(self) -> None: ] for names in copied_names: - attrs = gu.georaster.satimg.parse_metadata_from_fn(names) + attrs = gu.raster.satimg.parse_metadata_from_fn(names) i = copied_names.index(names) assert satellites[i] == attrs[0] assert sensors[i] == attrs[1] @@ -207,26 +207,26 @@ def test_sw_tile_naming_parsing(self) -> None: test_latlon = [(14, -65), (-14, 65), (14, -65), (14, -65), (14, -65), (0, 0)] for tile in test_tiles: - assert gu.georaster.satimg.sw_naming_to_latlon(tile)[0] == test_latlon[test_tiles.index(tile)][0] - assert gu.georaster.satimg.sw_naming_to_latlon(tile)[1] == test_latlon[test_tiles.index(tile)][1] + assert gu.raster.satimg.sw_naming_to_latlon(tile)[0] == test_latlon[test_tiles.index(tile)][0] + assert gu.raster.satimg.sw_naming_to_latlon(tile)[1] == test_latlon[test_tiles.index(tile)][1] for latlon in test_latlon: - assert gu.georaster.satimg.latlon_to_sw_naming(latlon) == test_tiles[test_latlon.index(latlon)] + assert gu.raster.satimg.latlon_to_sw_naming(latlon) == test_tiles[test_latlon.index(latlon)] # check possible exceptions, rounded lat/lon belong to their southwest border - assert gu.georaster.satimg.latlon_to_sw_naming((0, 0)) == "N00E000" + assert gu.raster.satimg.latlon_to_sw_naming((0, 0)) == "N00E000" # those are the same point, should give same naming - assert gu.georaster.satimg.latlon_to_sw_naming((-90, 0)) == "S90E000" - assert gu.georaster.satimg.latlon_to_sw_naming((90, 0)) == "S90E000" + assert gu.raster.satimg.latlon_to_sw_naming((-90, 0)) == "S90E000" + assert gu.raster.satimg.latlon_to_sw_naming((90, 0)) == "S90E000" # same here - assert gu.georaster.satimg.latlon_to_sw_naming((0, -180)) == "N00W180" - assert gu.georaster.satimg.latlon_to_sw_naming((0, 180)) == "N00W180" + assert gu.raster.satimg.latlon_to_sw_naming((0, -180)) == "N00W180" + assert gu.raster.satimg.latlon_to_sw_naming((0, 180)) == "N00W180" def test_parse_tile_attr_from_name(self) -> None: """Test the parsing of tile attribute from tile name.""" # For ASTER, SRTM, NASADEM: 1x1 tiling globally - y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N01W179", product="SRTMGL1") + y, x, size, epsg = gu.raster.satimg.parse_tile_attr_from_name(tile_name="N01W179", product="SRTMGL1") assert y == 1 assert x == -179 @@ -235,21 +235,21 @@ def test_parse_tile_attr_from_name(self) -> None: # For TanDEM-X: depends on latitude # Mid-latitude is 2 x 1 - y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N62E04", product="TDM1") + y, x, size, epsg = gu.raster.satimg.parse_tile_attr_from_name(tile_name="N62E04", product="TDM1") assert y == 62 assert x == 4 assert size == (1, 2) assert epsg == 4326 # Low-latitude is 1 x 1 - y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N52E04", product="TDM1") + y, x, size, epsg = gu.raster.satimg.parse_tile_attr_from_name(tile_name="N52E04", product="TDM1") assert y == 52 assert x == 4 assert size == (1, 1) assert epsg == 4326 # High-latitude is 5 x 1 - y, x, size, epsg = gu.georaster.satimg.parse_tile_attr_from_name(tile_name="N82E04", product="TDM1") + y, x, size, epsg = gu.raster.satimg.parse_tile_attr_from_name(tile_name="N82E04", product="TDM1") assert y == 82 assert x == 4 assert size == (1, 4) @@ -260,7 +260,7 @@ def test_parse_landsat(self) -> None: # Landsat 1 landsat1 = "LM10170391976031AAA01.tif" - attrs1 = gu.georaster.satimg.parse_landsat(landsat1) + attrs1 = gu.raster.satimg.parse_landsat(landsat1) assert attrs1[0] == "Landsat 1" assert attrs1[1] == "MSS" @@ -268,7 +268,7 @@ def test_parse_landsat(self) -> None: # Landsat 7 example landsat7 = "LE71400412000304SGS00_B4.tif" - attrs7 = gu.georaster.satimg.parse_landsat(landsat7) + attrs7 = gu.raster.satimg.parse_landsat(landsat7) assert attrs7[0] == "Landsat 7" assert attrs7[1] == "ETM+" diff --git a/tests/test_geovector.py b/tests/test_vector.py similarity index 97% rename from tests/test_geovector.py rename to tests/test_vector.py index d426741c0..2001bb02f 100644 --- a/tests/test_geovector.py +++ b/tests/test_vector.py @@ -217,7 +217,7 @@ def test_crop(self, data: list[str]) -> None: def test_proximity(self) -> None: """ The core functionality is already tested against GDAL in test_raster: just verify the vector-specific behaviour. - #TODO: add an artificial test as well (mirroring TODO in test_georaster) + #TODO: add an artificial test as well (mirroring TODO in test_raster) """ vector = gu.Vector(self.everest_outlines_path) @@ -358,37 +358,37 @@ def test_extract_vertices(self) -> None: Test that extract_vertices works with simple geometries. """ # Polygons - vertices = gu.geovector.extract_vertices(self.vector.ds) + vertices = gu.vector.extract_vertices(self.vector.ds) assert len(vertices) == 1 assert vertices == [[(10.0, 10.0), (11.0, 10.0), (11.0, 11.0), (10.0, 11.0), (10.0, 10.0)]] # MultiPolygons - vertices = gu.geovector.extract_vertices(self.vector_multipoly.ds) + vertices = gu.vector.extract_vertices(self.vector_multipoly.ds) assert len(vertices) == 2 assert vertices[0] == [(10.0, 10.0), (11.0, 10.0), (11.0, 11.0), (10.0, 11.0), (10.0, 10.0)] assert vertices[1] == [(5.0, 5.0), (6.0, 5.0), (6.0, 6.0), (5.0, 6.0), (5.0, 5.0)] # LineString - vertices = gu.geovector.extract_vertices(self.vector_lines.ds) + vertices = gu.vector.extract_vertices(self.vector_lines.ds) assert len(vertices) == 1 assert vertices == [[(10.0, 10.0), (11.0, 10.0), (11.0, 11.0)]] # MultiLineString - vertices = gu.geovector.extract_vertices(self.vector_multilines.ds) + vertices = gu.vector.extract_vertices(self.vector_multilines.ds) assert len(vertices) == 2 assert vertices[0] == [(10.0, 10.0), (11.0, 10.0), (11.0, 11.0)] assert vertices[1] == [(5.0, 5.0), (6.0, 5.0), (6.0, 6.0)] def test_generate_voronoi(self) -> None: """ - Check that geovector.generate_voronoi_polygons works on a simple Polygon. + Check that vector.generate_voronoi_polygons works on a simple Polygon. Does not work with simple shapes as squares or triangles as the diagram is infinite. For now, test on a set of two squares. """ # Check with a multipolygon - voronoi = gu.geovector.generate_voronoi_polygons(self.vector_multipoly.ds) + voronoi = gu.vector.generate_voronoi_polygons(self.vector_multipoly.ds) assert len(voronoi) == 2 - vertices = gu.geovector.extract_vertices(voronoi) + vertices = gu.vector.extract_vertices(voronoi) assert vertices == [ [(5.5, 10.5), (10.5, 10.5), (10.5, 5.5), (5.5, 10.5)], [(5.5, 10.5), (10.5, 5.5), (5.5, 5.5), (5.5, 10.5)], @@ -397,7 +397,7 @@ def test_generate_voronoi(self) -> None: # Check that it fails with proper error for too simple geometries expected_message = "Invalid geometry, cannot generate finite Voronoi polygons" with pytest.raises(ValueError, match=expected_message): - voronoi = gu.geovector.generate_voronoi_polygons(self.vector.ds) + voronoi = gu.vector.generate_voronoi_polygons(self.vector.ds) def test_buffer_metric(self) -> None: """Check that metric buffering works""" From 56dfce42b1716065bf6469a11ef47b2d6d5f1cd9 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Mar 2023 15:37:22 -0700 Subject: [PATCH 119/184] Linting --- geoutils/__init__.py | 2 +- geoutils/raster/multiraster.py | 2 +- geoutils/raster/raster.py | 2 +- tests/test_geoviewer.py | 7 +++++-- tests/test_multiraster.py | 4 +--- tests/test_raster.py | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/geoutils/__init__.py b/geoutils/__init__.py index 36652a7ef..d99922258 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -2,7 +2,7 @@ GeoUtils is a python package of raster and vector tools. """ -from geoutils import examples, raster, vector, projtools # noqa +from geoutils import examples, projtools, raster, vector # noqa from geoutils.raster import Mask, Raster, SatelliteImage # noqa from geoutils.vector import Vector # noqa diff --git a/geoutils/raster/multiraster.py b/geoutils/raster/multiraster.py index 09eed1631..d6d3afb38 100644 --- a/geoutils/raster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -10,9 +10,9 @@ from tqdm import tqdm import geoutils as gu +from geoutils.misc import resampling_method_from_str from geoutils.raster import Raster, RasterType, get_array_and_mask from geoutils.raster.raster import _default_nodata -from geoutils.misc import resampling_method_from_str def load_multiple_rasters( diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index c8bc8818c..27551f757 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -32,9 +32,9 @@ import geoutils.vector as gv from geoutils._typing import AnyNumber, ArrayLike, DTypeLike +from geoutils.projtools import _get_bounds_projected from geoutils.raster.sampling import subsample_array from geoutils.vector import Vector -from geoutils.projtools import _get_bounds_projected # If python38 or above, Literal is builtin. Otherwise, use typing_extensions try: diff --git a/tests/test_geoviewer.py b/tests/test_geoviewer.py index 0c07207ab..8ca1dd186 100644 --- a/tests/test_geoviewer.py +++ b/tests/test_geoviewer.py @@ -1,3 +1,5 @@ +"""Test for geoviewer executable.""" + from __future__ import annotations import os @@ -8,9 +10,10 @@ import geoutils as gu -# Import executable just for the test +# Add geoviewer path temporarily sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "bin/geoviewer.py"))) -import geoviewer +import geoviewer # noqa + @pytest.mark.parametrize( "filename", [gu.examples.get_path("everest_landsat_b4"), gu.examples.get_path("exploradores_aster_dem")] diff --git a/tests/test_multiraster.py b/tests/test_multiraster.py index 7845c4464..7822b5390 100644 --- a/tests/test_multiraster.py +++ b/tests/test_multiraster.py @@ -129,9 +129,7 @@ def test_stack_rasters(self, rasters) -> None: # type: ignore assert stacked_img.bounds != rasters.img.bounds # This case should preserve original extent - stacked_img2 = gu.raster.stack_rasters( - [rasters.img1, rasters.img3], reference=rasters.img, use_ref_bounds=True - ) + stacked_img2 = gu.raster.stack_rasters([rasters.img1, rasters.img3], reference=rasters.img, use_ref_bounds=True) assert stacked_img2.bounds == rasters.img.bounds @pytest.mark.parametrize( diff --git a/tests/test_raster.py b/tests/test_raster.py index 78895441d..d4018e72b 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -20,9 +20,9 @@ import geoutils as gu import geoutils.projtools as pt from geoutils import examples -from geoutils.raster.raster import _default_nodata, _default_rio_attrs from geoutils.misc import resampling_method_from_str from geoutils.projtools import reproject_to_latlon +from geoutils.raster.raster import _default_nodata, _default_rio_attrs DO_PLOT = False From 07ab0fde5883b16378e12f766f0a8ccd69ffd790 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Mar 2023 16:56:43 -0700 Subject: [PATCH 120/184] Homogenize syntax in docstrings --- geoutils/raster/raster.py | 364 +++++++++++++++++++++----------------- geoutils/vector.py | 61 ++++--- 2 files changed, 234 insertions(+), 191 deletions(-) diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index 27551f757..01fd3cbe7 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -176,19 +176,19 @@ def _load_rio( **kwargs: Any, ) -> np.ma.masked_array: r""" - Load specific bands of the dataset, using rasterio.read(). + Load specific bands of the dataset, using :func:`rasterio.read`. - Ensure that self.data.ndim = 3 for ease of use (needed e.g. in show) + Ensure that ``self.data.ndim=3`` for ease of use (needed e.g. in show). - :param dataset: The dataset to read (opened with "rio.open(filename)") - :param indexes: The band(s) to load. Note that rasterio begins counting at 1, not 0. - :param masked: Should the mask be read (if any exists), and/or should the nodata be used to mask values + :param dataset: Dataset to read (opened with :func:`rasterio.open`). + :param indexes: Band(s) to load. Note that rasterio begins counting at 1, not 0. + :param masked: Whether the mask should be read (if any exists) to use the nodata to mask values. :param transform: Create a window from the given transform (to read only parts of the raster) - :param shape: The expected shape of the read ndarray. Must be given together with the 'transform' argument. + :param shape: Expected shape of the read ndarray. Must be given together with the `transform` argument. - :raises ValueError: If only one of 'transform' and 'shape' are given. + :raises ValueError: If only one of ``transform`` and ``shape`` are given. - :returns: A numpy array if masked == False or a masked_array + :returns: An unmasked array if ``masked`` is ``False``, or a masked array otherwise. \*\*kwargs: any additional arguments to rasterio.io.DatasetReader.read. Useful ones are: @@ -258,9 +258,9 @@ def __init__( """ Instantiate a raster from a filename or rasterio dataset. - :param filename_or_dataset: The filename or dataset. + :param filename_or_dataset: Path to file or Rasterio dataset. - :param indexes: The band(s) to load into the object. Default loads all bands. + :param indexes: Band(s) to load into the object. Default loads all bands. :param load_data: Whether to load the array during instantiation. Default is False. @@ -268,15 +268,15 @@ def __init__( :param masked: Whether to load the array as a NumPy masked-array, with nodata values masked. Default is True. - :param nodata: The nodata value to be used (overwrites the metadata). Default reads from metadata. + :param nodata: Nodata value to be used (overwrites the metadata). Default reads from metadata. - :param attrs: Additional attributes from rasterio's DataReader class to add to the Raster object. + :param attrs: Additional attributes from Rasterio's DataReader class to add to the Raster object. Default list is set by geoutils.raster.raster._default_rio_attrs, i.e. ['bounds', 'count', 'crs', 'driver', 'dtypes', 'height', 'indexes', 'name', 'nodata', 'res', 'shape', 'transform', 'width'] - if no attrs are specified, these will be added. """ - self.driver: str | None = None - self.name: str | None = None + self._driver: str | None = None + self._name: str | None = None self.filename: str | None = None self.tags: dict[str, Any] = {} @@ -395,14 +395,14 @@ def __init__( @property def count_on_disk(self) -> None | int: - """The count of bands on disk if it exists.""" + """Count of bands on disk if it exists.""" if self._disk_shape is not None: return self._disk_shape[0] return None @property def count(self) -> int: - """The count of bands loaded in memory if they are, otherwise the one on disk.""" + """Count of bands loaded in memory if they are, otherwise the one on disk.""" if self.is_loaded: return int(self.data.shape[0]) # This can only happen if data is not loaded, with a DatasetReader on disk is open, never returns None @@ -410,21 +410,21 @@ def count(self) -> int: @property def height(self) -> int: - """The height of the raster in pixels.""" + """Height of the raster in pixels.""" if not self.is_loaded: return self._disk_shape[1] # type: ignore return int(self.data.shape[1]) @property def width(self) -> int: - """The width of the raster in pixels.""" + """Width of the raster in pixels.""" if not self.is_loaded: return self._disk_shape[2] # type: ignore return int(self.data.shape[2]) @property def shape(self) -> tuple[int, int]: - """The shape (i.e., height, width) of the raster in pixels.""" + """Shape (i.e., height, width) of the raster in pixels.""" # If a downsampling argument was defined but data not loaded yet if self._out_shape is not None and not self.is_loaded: return self._out_shape[1], self._out_shape[2] @@ -435,12 +435,12 @@ def shape(self) -> tuple[int, int]: @property def res(self) -> tuple[float | int, float | int]: - """The resolution (X, Y) of the raster in georeferenced units.""" + """Resolution (X, Y) of the raster in georeferenced units.""" return self.transform[0], abs(self.transform[4]) @property def bounds(self) -> rio.coords.BoundingBox: - """The bounding coordinates of the raster.""" + """Bounding coordinates of the raster.""" return rio.coords.BoundingBox(*rio.transform.array_bounds(self.height, self.width, self.transform)) @property @@ -450,21 +450,21 @@ def is_loaded(self) -> bool: @property def dtypes(self) -> tuple[str, ...]: - """The data type for each raster band (string representation).""" + """Data type for each raster band (string representation).""" if not self.is_loaded and self._disk_dtypes is not None: return self._disk_dtypes return (str(self.data.dtype),) * self.count @property def indexes_on_disk(self) -> None | tuple[int, ...]: - """The indexes of bands on disk if it exists.""" + """Indexes of bands on disk if it exists.""" if self._disk_indexes is not None: return self._disk_indexes return None @property def indexes(self) -> tuple[int, ...]: - """The indexes of bands loaded in memory if they are, otherwise on disk.""" + """Indexes of bands loaded in memory if they are, otherwise on disk.""" if self._indexes is not None and not self.is_loaded: if isinstance(self._indexes, int): return (self._indexes,) @@ -477,12 +477,22 @@ def indexes(self) -> tuple[int, ...]: return tuple(range(1, self.count + 1)) return self.indexes_on_disk # type: ignore + @property + def name(self) -> str | None: + """Name of the file on disk, if it exists.""" + return self._name + + @property + def driver(self) -> str | None: + """Driver used to read a file on disk.""" + return self._driver + def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: """ Load the raster array from disk. :param kwargs: Optional keyword arguments sent to '_load_rio()'. - :param indexes: The band(s) to load. Note that rasterio begins counting at 1, not 0. + :param indexes: Band(s) to load. Note that rasterio begins counting at 1, not 0. :raises ValueError: If the data are already loaded. :raises AttributeError: If no 'filename' attribute exists. @@ -543,20 +553,15 @@ def from_array( ) -> RasterType: """Create a raster from a numpy array and the georeferencing information. - :param data: data array - - :param transform: the 2-D affine transform for the image mapping. - Either a tuple(x_res, 0.0, top_left_x, 0.0, y_res, top_left_y) or - an affine.Affine object. - - :param crs: Coordinate Reference System for image. Either a rasterio CRS, - or the EPSG integer. + :param data: Input array. + :param transform: Affine 2D transform. Either a tuple(x_res, 0.0, top_left_x, + 0.0, y_res, top_left_y) or an affine.Affine object. + :param crs: Coordinate reference system. Either a rasterio CRS, + or an EPSG integer. - :param nodata: nodata value - - - :returns: A Raster object containing the provided data. + :param nodata: Nodata value. + :returns: Raster created from the provided array and georeferencing. Example: @@ -859,7 +864,7 @@ def __add__(self: RasterType, other: RasterType | np.ndarray | Number) -> Raster """ Sum two rasters, or a raster and a numpy array, or a raster and single number. - If other is a Raster, it must have the same data.shape, transform and crs as self. + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -876,17 +881,27 @@ def __add__(self: RasterType, other: RasterType | np.ndarray | Number) -> Raster def __radd__(self: RasterType, other: np.ndarray | Number) -> RasterType: """ - Addition overloading when other is first item in the operation (e.g. 1 + rst). + Sum two rasters, or a raster and a numpy array, or a raster and single number. + + For when other is first item in the operation (e.g. 1 + rst). """ return self.__add__(other) def __neg__(self: RasterType) -> RasterType: - """Return self with self.data set to -self.data""" + """ + Take the raster negation. + + Returns a raster with -self.data. + """ return self.from_array(-self.data, self.transform, self.crs, nodata=self.nodata) def __sub__(self, other: Raster | np.ndarray | Number) -> Raster: """ - Subtract two rasters. Both rasters must have the same data.shape, transform and crs. + Subtract two rasters, or a raster and a numpy array, or a raster and single number. + + If other is a Raster, it must have the same shape, transform and crs as self. + If other is a np.ndarray, it must have the same shape. + Otherwise, other must be a single number. """ self_data, other_data, nodata = self._overloading_check(other) out_data = self_data - other_data @@ -894,7 +909,9 @@ def __sub__(self, other: Raster | np.ndarray | Number) -> Raster: def __rsub__(self: RasterType, other: np.ndarray | Number) -> RasterType: """ - Subtraction overloading when other is first item in the operation (e.g. 1 - rst). + Subtract two rasters, or a raster and a numpy array, or a raster and single number. + + For when other is first item in the operation (e.g. 1 - rst). """ self_data, other_data, nodata = self._overloading_check(other) out_data = other_data - self_data @@ -902,8 +919,9 @@ def __rsub__(self: RasterType, other: np.ndarray | Number) -> RasterType: def __mul__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Multiply the data of two rasters or a raster and a numpy array, or a raster and single number. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Multiply two rasters, or a raster and a numpy array, or a raster and single number. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -914,14 +932,17 @@ def __mul__(self: RasterType, other: RasterType | np.ndarray | Number) -> Raster def __rmul__(self: RasterType, other: np.ndarray | Number) -> RasterType: """ - Multiplication overloading when other is first item in the operation (e.g. 2 * rst). + Multiply two rasters, or a raster and a numpy array, or a raster and single number. + + For when other is first item in the operation (e.g. 2 * rst). """ return self.__mul__(other) def __truediv__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - True division of the data of two rasters or a raster and a numpy array, or a raster and single number. - If other is a Raster, it must have the same data.shape, transform and crs as self. + True division of two rasters, or a raster and a numpy array, or a raster and single number. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -932,7 +953,9 @@ def __truediv__(self: RasterType, other: RasterType | np.ndarray | Number) -> Ra def __rtruediv__(self: RasterType, other: np.ndarray | Number) -> RasterType: """ - True division overloading when other is first item in the operation (e.g. 1/rst). + True division of two rasters, or a raster and a numpy array, or a raster and single number. + + For when other is first item in the operation (e.g. 1/rst). """ self_data, other_data, nodata = self._overloading_check(other) out_data = other_data / self_data @@ -941,8 +964,9 @@ def __rtruediv__(self: RasterType, other: np.ndarray | Number) -> RasterType: def __floordiv__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Floor division of the data of two rasters or a raster and a numpy array, or a raster and single number. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Floor division of two rasters, or a raster and a numpy array, or a raster and single number. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -953,7 +977,9 @@ def __floordiv__(self: RasterType, other: RasterType | np.ndarray | Number) -> R def __rfloordiv__(self: RasterType, other: np.ndarray | Number) -> RasterType: """ - Floor division overloading when other is first item in the operation (e.g. 1/rst). + Floor division of two rasters, or a raster and a numpy array, or a raster and single number. + + For when other is first item in the operation (e.g. 1/rst). """ self_data, other_data, nodata = self._overloading_check(other) out_data = other_data // self_data @@ -962,8 +988,9 @@ def __rfloordiv__(self: RasterType, other: np.ndarray | Number) -> RasterType: def __mod__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Modulo of the data of two rasters or a raster and a numpy array, or a raster and single number. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Modulo of two rasters, or a raster and a numpy array, or a raster and single number. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -974,7 +1001,7 @@ def __mod__(self: RasterType, other: RasterType | np.ndarray | Number) -> Raster def __pow__(self: RasterType, power: int | float) -> RasterType: """ - Calculate the power of self.data and returns a Raster. + Power of a raster to a number. """ # Check that input is a number if not isinstance(power, Number): @@ -988,8 +1015,11 @@ def __pow__(self: RasterType, power: int | float) -> RasterType: def __eq__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: # type: ignore """ - Element-wise equality cast into a Mask subclass. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Element-wise equality of two rasters, or a raster and a numpy array, or a raster and single number. + + This operation casts the result into a Mask. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -1000,8 +1030,11 @@ def __eq__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterT def __ne__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: # type: ignore """ - Element-wise equality cast into a Mask subclass. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Element-wise negation of two rasters, or a raster and a numpy array, or a raster and single number. + + This operation casts the result into a Mask. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -1012,8 +1045,12 @@ def __ne__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterT def __lt__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Element-wise equality cast into a Mask subclass. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Element-wise lower than comparison of two rasters, or a raster and a numpy array, + or a raster and single number. + + This operation casts the result into a Mask. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -1024,8 +1061,12 @@ def __lt__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterT def __le__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Element-wise equality cast into a Mask subclass. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Element-wise lower or equal comparison of two rasters, or a raster and a numpy array, + or a raster and single number. + + This operation casts the result into a Mask. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -1036,8 +1077,12 @@ def __le__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterT def __gt__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Element-wise equality cast into a Mask subclass. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Element-wise greater than comparison of two rasters, or a raster and a numpy array, + or a raster and single number. + + This operation casts the result into a Mask. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -1048,8 +1093,12 @@ def __gt__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterT def __ge__(self: RasterType, other: RasterType | np.ndarray | Number) -> RasterType: """ - Element-wise equality cast into a Mask subclass. - If other is a Raster, it must have the same data.shape, transform and crs as self. + Element-wise greater or equal comparison of two rasters, or a raster and a numpy array, + or a raster and single number. + + This operation casts the result into a Mask. + + If other is a Raster, it must have the same shape, transform and crs as self. If other is a np.ndarray, it must have the same shape. Otherwise, other must be a single number. """ @@ -1068,12 +1117,12 @@ def astype(self, dtype: DTypeLike, inplace: Literal[True]) -> None: def astype(self, dtype: DTypeLike, inplace: bool = False) -> Raster | None: """ - Convert the data type of a raster. + Convert data type of the raster. - :param dtype: Any numpy dtype or string accepted by numpy.astype - :param inplace: Set to True to modify the raster in place. + :param dtype: Any numpy dtype or string accepted by numpy.astype. + :param inplace: Whether to modify the raster in-place. - :returns: the output Raster with dtype changed. + :returns: Raster with updated dtype. """ # Check that dtype is supported by rasterio if not rio.dtypes.check_dtype(dtype): @@ -1132,7 +1181,7 @@ def nodata(self, new_nodata: int | float | tuple[int, ...] | tuple[float, ...] | To set nodata for more complex cases (e.g., redefining a wrong nodata that has a valid value in the array), call the function set_nodata() directly to set the arguments update_array and update_mask adequately. - :param new_nodata: New nodata to assign to this instance of Raster + :param new_nodata: New nodata to assign to this instance of Raster. """ self.set_nodata(nodata=new_nodata) @@ -1164,10 +1213,10 @@ def set_nodata( If None is passed as nodata, only the metadata is updated and the mask of oldnodata unset. - :param nodata: Nodata values - :param update_array: Update the old nodata values into new nodata values in the data array + :param nodata: Nodata values. + :param update_array: Update the old nodata values into new nodata values in the data array. :param update_mask: Update the old mask by unmasking old nodata and masking new nodata (if array is updated, - old nodata are changed to new nodata and thus stay masked) + old nodata are changed to new nodata and thus stay masked). """ if nodata is not None and not isinstance(nodata, (tuple, int, float, np.integer, np.floating)): raise ValueError("Type of nodata not understood, must be tuple or float or int") @@ -1257,7 +1306,7 @@ def data(self) -> np.ma.masked_array: """ Array of the raster. - :returns: data array. + :returns: Raster array. """ if not self.is_loaded: @@ -1279,7 +1328,7 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: 5. Masks non-finite values that are unmasked, whether the input is a classic array or a masked_array. Note that these values are not overwritten and can still be accessed in .data.data. - :param new_data: New data to assign to this instance of Raster + :param new_data: New data to assign to this instance of Raster. """ # Check that new_data is a Numpy array @@ -1361,7 +1410,7 @@ def set_mask(self, mask: np.ndarray | Mask) -> None: Masking is performed in place. The mask must have the same shape as loaded data, unless the first dimension is 1, then it is ignored. - :param mask: The data mask + :param mask: The raster array mask. """ # Check that mask is a Numpy array if not isinstance(mask, (np.ndarray, Mask)): @@ -1440,9 +1489,9 @@ def copy(self: RasterType, new_array: np.ndarray | None = None) -> RasterType: """ Copy the raster in-memory. - :param new_array: New array to use for the copied raster + :param new_array: New array to use in the copied raster. - :return: + :return: Copy of the raster. """ if new_array is not None: data = new_array @@ -1457,9 +1506,9 @@ def georeferenced_grid_equal(self: RasterType, raster: RasterType) -> bool: """ Check that raster shape, geotransform and CRS are equal. - :param raster: Another raster object + :param raster: Another raster. - :return: Whether the two objects have the same georeferenced grid + :return: Whether the two objects have the same georeferenced grid. """ return all([self.shape == raster.shape, self.transform == raster.transform, self.crs == raster.crs]) @@ -1677,20 +1726,19 @@ def crop( """ Crop the raster to a given extent. - Match-reference: a reference raster or vector can be passed to match bounds during cropping. + **Match-reference:** a reference raster or vector can be passed to match bounds during cropping. Reprojection is done on the fly if georeferenced objects have different projections. - :param crop_geom: Geometry to crop raster to, as either a Raster object, a Vector object, or a list of - coordinates. If cropGeom is a Raster, crop() will crop to the boundary of the raster as returned by - Raster.ds.bounds. If cropGeom is a Vector, crop() will crop to the bounding geometry. If cropGeom is a + :param crop_geom: Geometry to crop raster to. Can use either a raster or vector as match-reference, or a list of + coordinates. If ``crop_geom`` is a raster or vector, will crop to the bounds. If ``crop_geom`` is a list of coordinates, the order is assumed to be [xmin, ymin, xmax, ymax]. - :param mode: one of 'match_pixel' (default) or 'match_extent'. 'match_pixel' will preserve the original pixel - resolution, cropping to the extent that most closely aligns with the current coordinates. 'match_extent' + :param mode: Either ``"match_pixel"`` (default) or ``"match_extent"``. ``'match_pixel'`` will preserve the original pixel + resolution, cropping to the extent that most closely aligns with the current coordinates. ``'match_extent'`` will match the extent exactly, adjusting the pixel resolution to fit the extent. - :param inplace: Update the raster inplace or return copy. + :param inplace: Whether to update the raster in-place. - :returns: None if inplace=True and a new Raster if inplace=False + :returns: None for in-place cropping (defaults) or a new raster otherwise. """ assert mode in [ "match_extent", @@ -1757,33 +1805,33 @@ def reproject( """ Reproject raster to a different geotransform (resolution, bounds) and/or coordinate reference system (CRS). - Match-reference: a reference raster can be passed to match resolution, bounds and CRS during reprojection. + **Match-reference**: a reference raster can be passed to match resolution, bounds and CRS during reprojection. Alternatively, the destination resolution, bounds and CRS can be passed individually. Any resampling algorithm implemented in Rasterio can be passed as a string. - See https://rasterio.readthedocs.io/en/stable/api/rasterio.enums.html#rasterio.enums.Resampling - for the full list. - - :param dst_ref: A reference raster. If set will use the attributes of this - raster for the output grid. - :param dst_crs: Specify the Coordinate Reference System or EPSG to reproject to. If dst_ref not set, - defaults to self.crs. - :param dst_size: Raster size to write to (x, y). Do not use with dst_res. - :param dst_bounds: a BoundingBox object or a dictionary containing left, bottom, right, top bounds in the - destination CRS. - :param dst_res: Pixel size in units of destination CRS. Either 1 value or (xres, yres). Do not use with - dst_size. - :param dst_nodata: nodata value of the destination. If set to None, will use the same as source, - and if source is None, will use GDAL's default. - :param dst_dtype: Set data type of output. - :param src_nodata: nodata value of the source. If set to None, will read from the metadata. - :param resampling: A rasterio Resampling method - :param silent: If True, will not print warning statements - :param n_threads: The number of worker threads. Defaults to (os.cpu_count() - 1). - :param memory_limit: The warp operation memory limit in MB. Larger values may perform better. - - :returns: Reprojected raster + + + :param dst_ref: Reference raster to match resolution, bounds and CRS. + :param dst_crs: Destination coordinate reference system as a string or EPSG. If ``dst_ref`` not set, + defaults to this raster's CRS. + :param dst_size: Destination size as (x, y). Do not use with ``dst_res``. + :param dst_bounds: Destination bounds as a Rasterio bounding box, or a dictionary containing left, bottom, + right, top bounds in the destination CRS. + :param dst_res: Destination resolution (pixel size) in units of destination CRS. Single value or (xres, yres). + Do not use with ``dst_size``. + :param dst_nodata: Destination nodata value. If set to ``None``, will use the same as source. If source does + not exist, will use GDAL's default. + :param dst_dtype: Destination data type of array. + :param src_nodata: Force a source nodata value (read from the metadata by default). + :param resampling: A Rasterio resampling method, can be passed as a string. + See https://rasterio.readthedocs.io/en/stable/api/rasterio.enums.html#rasterio.enums.Resampling + for the full list. + :param silent: Whether to print warning statements. + :param n_threads: Number of threads. Defaults to (os.cpu_count() - 1). + :param memory_limit: Memory limit in MB for warp operations. Larger values may perform better. + + :returns: Reprojected raster. """ @@ -2001,10 +2049,10 @@ def reproject( def shift(self, xoff: float, yoff: float) -> None: """ - Shift the raster by a X/Y offset. + Shift the raster by a (x,y) offset. - :param xoff: Translation X offset. - :param yoff: Translation Y offset. + :param xoff: Translation x offset. + :param yoff: Translation y offset. """ @@ -2037,20 +2085,19 @@ def save( pixel instead. :param filename: Filename to write the file to. - :param driver: the 'GDAL' driver to use to write the file as. - :param dtype: Data Type to write the image as (defaults to dtype of image data) - :param nodata: nodata value to be used. - :param compress: Compression type. Defaults to 'deflate' (equal to GDALs: COMPRESS=DEFLATE) + :param driver: Driver to write file with. + :param dtype: Data type to write the image as (defaults to dtype of image data). + :param nodata: Force a nodata value to be used (default to that of raster). + :param compress: Compression type. Defaults to 'deflate' (equal to GDALs: COMPRESS=DEFLATE). :param tiled: Whether to write blocks in tiles instead of strips. Improves read performance on large files, - but increases file size. - :param blank_value: Use to write an image out with every pixel's value + but increases file size. + :param blank_value: Use to write an image out with every pixel's value. corresponding to this value, instead of writing the image data to disk. :param co_opts: GDAL creation options provided as a dictionary, - e.g. {'TILED':'YES', 'COMPRESS':'LZW'} - :param metadata: pairs of metadata key, value - :param gcps: list of gcps, each gcp being [row, col, x, y, (z)] - :param gcps_crs: the CRS of the GCPS (Default is None) - + e.g. {'TILED':'YES', 'COMPRESS':'LZW'}. + :param metadata: Pairs of metadata key, value. + :param gcps: List of gcps, each gcp being [row, col, x, y, (z)]. + :param gcps_crs: CRS of the GCPS. :returns: None. """ @@ -2132,7 +2179,7 @@ def to_xarray(self, name: str | None = None) -> xr.DataArray: See the documentation of rioxarray and xarray for more information on the methods and attributes of the resulting DataArray. - :param name: Set the name of the DataArray. + :param name: Name attribute for the DataArray. :returns: xarray DataArray """ @@ -2149,7 +2196,7 @@ def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coo """ Get raster bounds projected in a specified CRS. - :param out_crs: Output CRS + :param out_crs: Output CRS. :param densify_pts: Maximum points to be added between image corners to account for non linear edges. Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. @@ -2220,7 +2267,7 @@ def show( This method is a wrapper to rasterio.plot.show. Any \*\*kwargs which you give this method will be passed to it. - :param index: Which band to plot, from 1 to self.count (default is all). + :param index: Band to plot, from 1 to self.count (default is all). :param cmap: The figure's colormap. Default is plt.rcParams['image.cmap']. :param vmin: Colorbar minimum value. Default is data min. :param vmax: Colorbar maximum value. Default is data max. @@ -2231,7 +2278,7 @@ def show( If "new", will create a new axis. :param return_axes: Whether to return axes. - :returns: None, or (ax, caxes) if return_axes is True + :returns: None, or (ax, caxes) if return_axes is True. You can also pass in \*\*kwargs to be used by the underlying imshow or @@ -2341,9 +2388,9 @@ def value_at_coords( index: int | None = None, masked: bool = False, window: int | None = None, + reducer_function: Callable[[np.ndarray], float] = np.ma.mean, return_window: bool = False, boundless: bool = True, - reducer_function: Callable[[np.ndarray], float] = np.ma.mean, ) -> Any: """ Extract raster values at the nearest pixels from the specified coordinates, @@ -2351,24 +2398,18 @@ def value_at_coords( By default, samples pixel value of each band. Can be passed a band index to sample from. - :param x: x (or longitude) coordinate(s). - :param y: y (or latitude) coordinate(s). - :param latlon: Set to True if coordinates provided as longitude/latitude. - :param index: The band number to extract from (from 1 to self.count). - :param masked: If `masked` is `True` the return value will be a masked - array. Otherwise (the default) the return value will be a - regular array. - :param window: expand area around coordinate to dimensions - window * window. window must be odd. - :param return_window: If True when window=int, returns (mean,array) - where array is the dataset extracted via the specified window size. - :param boundless: If `True`, windows that extend beyond the dataset's extent - are permitted and partially or completely filled arrays (with self.nodata) will - be returned as appropriate. - :param reducer_function: a function to apply to the values in window. - - :returns: When called on a Raster or with a specific band set, return value of pixel. - :returns: If multiple band Raster and the band is not specified, a + :param x: X (or longitude) coordinate(s). + :param y: Y (or latitude) coordinate(s). + :param latlon: Whether coordinates are provided as longitude-latitude. + :param index: Band number to extract from (from 1 to self.count). + :param masked: Whether to return a masked array, or classic array. + :param window: Window size to read around coordinates. Must be odd. + :param reducer_function: Reducer function to apply to the values in window (defaults to np.mean). + :param return_window: Whether to return the windows (in addition to the reduced value). + :param boundless: Whether to allow windows that extend beyond the extent. + + :returns: When called on a raster or with a specific band set, return value of pixel. + :returns: If multiple band raster and the band is not specified, a dictionary containing the value of the pixel in each band. :returns: In addition, if return_window=True, return tuple of (values, arrays) @@ -2566,11 +2607,11 @@ def xy2ij( :param x: X coordinates. :param y: Y coordinates. :param op: Operator to compute index. - :param precision: Precision passed to rio.Dataset.index. + :param precision: Precision passed to :func:`rasterio.transform.rowcol`. :param shift_area_or_point: Shifts index to center pixel coordinates if GDAL's AREA_OR_POINT attribute (in self.tags) is "Point", keeps the corner pixel coordinate for "Area". - :returns i, j: indices of (x,y) in the image. + :returns i, j: Indices of (x,y) in the image. """ # Input checks if op not in [np.float32, np.float64, float]: @@ -2659,9 +2700,9 @@ def outside_image(self, xi: ArrayLike, yj: ArrayLike, index: bool = True) -> boo :param xi: Indices (or coordinates) of x direction to check. :param yj: Indices (or coordinates) of y direction to check. - :param index: Interpret ij as raster indices (default is True). If False, assumes ij is coordinates. + :param index: Interpret ij as raster indices (default is ``True``). If False, assumes ij is coordinates. - :returns is_outside: True if ij is outside the image. + :returns is_outside: ``True`` if ij is outside the image. """ if not index: xi, xj = self.xy2ij(xi, yj) @@ -2866,7 +2907,7 @@ def polygonize( Polygonize the raster into a vector. :param target_values: Value or range of values of the raster from which to - create geometries (Default is 1). If 'all', all unique pixel values of the raster are used. + create geometries (defaults to 'all', for which all unique pixel values of the raster are used). :returns: Vector containing the polygonized geometries. """ @@ -2941,19 +2982,22 @@ def proximity( """ Compute proximity distances to the raster target pixels, or to a vector geometry on the raster grid. - When passing a Vector, by default, the boundary of the geometry will be used. The full geometry can be used by + **Match-reference**: a raster can be passed to match its resolution, bounds and CRS for computing + proximity distances. + + When passing a vector, by default, the boundary of the geometry will be used. The full geometry can be used by passing "geometry", or any lower dimensional geometry attribute such as "centroid", "envelope" or "convex_hull". See all geometry attributes in the Shapely documentation at https://shapely.readthedocs.io/. :param vector: Vector for which to compute the proximity to geometry, - if not provided computed on this Raster target pixels. - :param target_values: (Only with Raster) List of target values to use for the proximity, + if not provided computed on this raster target pixels. + :param target_values: (Only with raster) List of target values to use for the proximity, defaults to all non-zero values. - :param geometry_type: (Only with a Vector) Type of geometry to use for the proximity, defaults to 'boundary'. - :param in_or_out: (Only with a Vector) Compute proximity only 'in' or 'out'-side the geometry, or 'both'. + :param geometry_type: (Only with a vector) Type of geometry to use for the proximity, defaults to 'boundary'. + :param in_or_out: (Only with a vector) Compute proximity only 'in' or 'out'-side the geometry, or 'both'. :param distance_unit: Distance unit, either 'georeferenced' or 'pixel'. - :return: Proximity raster. + :return: Proximity distances raster. """ proximity = proximity_from_vector_or_raster( diff --git a/geoutils/vector.py b/geoutils/vector.py index 54b03ce80..ac80e6ca7 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -49,9 +49,9 @@ class Vector: def __init__(self, filename_or_dataset: str | pathlib.Path | gpd.GeoDataFrame): """ - Instantiate a vector from a filename or geopandas geodataframe. + Instantiate a vector from a filename or GeoPandas geodataframe. - :param filename_or_dataset: The filename or geodataframe. + :param filename_or_dataset: Path to file or GeoPandas geodataframe. """ if isinstance(filename_or_dataset, (str, pathlib.Path)): @@ -127,8 +127,7 @@ def info(self) -> str: """ Summarize information about the vector. - :returns: text information about Vector attributes. - :rtype: str + :returns: Unformation about vector attributes. """ as_str = [ # 'Driver: {} \n'.format(self.driver), f"Filename: {self.name} \n", @@ -160,8 +159,8 @@ def show( This method is a wrapper to geopandas.GeoDataFrame.plot. Any \*\*kwargs which you give this method will be passed to it. - :param ref_crs: The CRS to match when plotting. - :param cmap: The colormap. Default is plt.rcParams['image.cmap']. + :param ref_crs: Coordinate reference system to match when plotting. + :param cmap: Colormap to use. Default is plt.rcParams['image.cmap']. :param vmin: Colorbar minimum value. Default is data min. :param vmax: Colorbar maximum value. Default is data max. :param alpha: Transparency of raster and colorbar. @@ -329,18 +328,17 @@ def crop( """ Crop the vector to given extent. - Match-reference: a reference raster or vector can be passed to match bounds during cropping. + **Match-reference:** a reference raster or vector can be passed to match bounds during cropping. Optionally, clip geometries to that extent (by default keeps all intersecting). Reprojection is done on the fly if georeferenced objects have different projections. :param crop_geom: Geometry to crop vector to, as either a Raster object, a Vector object, or a list of - coordinates. If cropGeom is a Raster, crop() will crop to the boundary of the raster as returned by - Raster.ds.bounds. If cropGeom is a Vector, crop() will crop to the bounding geometry. If cropGeom is a + coordinates. If ``crop_geom`` is a raster or a vector, will crop to the bounds. If ``crop_geom`` is a list of coordinates, the order is assumed to be [xmin, ymin, xmax, ymax]. :param clip: Whether to clip the geometry to the given extent (by default keeps all intersecting). - :param inplace: Update the vector inplace or return copy. + :param inplace: Update the vector in-place or return copy. """ if isinstance(crop_geom, (gu.Raster, Vector)): # For another Vector or Raster, we reproject the bounding box in the same CRS as self @@ -372,18 +370,18 @@ def reproject( """ Reproject vector to a specified coordinate reference system. - Match-reference: a reference raster or vector can be passed to match CRS during reprojection. + **Match-reference:** a reference raster or vector can be passed to match CRS during reprojection. Alternatively, a CRS can be passed in many formats (string, EPSG integer, or CRS). To reproject a Vector with different source bounds, first run Vector.crop(). :param dst_ref: A reference raster or vector whose CRS to use as a reference for reprojection. - Can be provided as Raster, Vector, rasterio dataset, geopandas dataframe, or path to the file. + Can be provided as a raster, vector, Rasterio dataset, GeoPandas dataframe, or path to the file. :param dst_crs: Specify the Coordinate Reference System or EPSG to reproject to. If dst_ref not set, defaults to self.crs. - :returns: Reprojected vector + :returns: Reprojected vector. """ # Check that either dst_ref or dst_crs is provided @@ -461,14 +459,14 @@ def create_mask( """ Create a mask from the vector features. - Match-reference: a raster can be passed to match its resolution, bounds and CRS when creating the mask. + **Match-reference:** a raster can be passed to match its resolution, bounds and CRS when creating the mask. Alternatively, user can specify a grid to rasterize on using xres, yres, bounds and crs. Only xres is mandatory, by default yres=xres and bounds/crs are set to self's. Vector features which fall outside the bounds of the raster file are not written to the new mask file. - :param rst: A raster + :param rst: Reference raster to match during rasterization. :param crs: A pyproj or rasterio CRS object (Default to rst.crs if not None then self.crs) :param xres: Output raster spatial resolution in x. Only is rst is None. :param yres: Output raster spatial resolution in y. Only if rst is None. (Default to xres) @@ -571,25 +569,26 @@ def rasterize( """ Rasterize vector to a raster or mask, with input geometries burned in. - Match-reference: a raster can be passed to match its resolution, bounds and CRS when rasterizing the vector. + **Match-reference:** a raster can be passed to match its resolution, bounds and CRS when rasterizing the vector. + Alternatively, user can specify a grid to rasterize on using xres, yres, bounds and crs. Only xres is mandatory, by default yres=xres and bounds/crs are set to self's. Burn value is set by user and can be either a single number, or an iterable of same length as self.ds. Default is an index from 1 to len(self.ds). - :param rst: A raster to be used as reference for the output grid - :param crs: A pyproj or rasterio CRS object (Default to rst.crs if not None then self.crs) + :param rst: Reference raster to match during rasterization. + :param crs: Coordinate reference system as string or EPSG code (Default to rst.crs if not None then self.crs). :param xres: Output raster spatial resolution in x. Only if rst is None. Must be in units of crs, if set. :param yres: Output raster spatial resolution in y. Only if rst is None. - Must be in units of crs, if set. (Default to xres) - :param bounds: Output raster bounds (left, bottom, right, top). Only if rst is None + Must be in units of crs, if set. (Default to xres). + :param bounds: Output raster bounds (left, bottom, right, top). Only if rst is None. Must be in same system as crs, if set. (Default to self bounds). - :param in_value: Value(s) to be burned inside the polygons (Default is self.ds.index + 1) - :param out_value: Value to be burned outside the polygons (Default is 0) + :param in_value: Value(s) to be burned inside the polygons (Default is self.ds.index + 1). + :param out_value: Value to be burned outside the polygons (Default is 0). - :returns: Raster or mask containing the burned geometries + :returns: Raster or mask containing the burned geometries. """ if (rst is not None) and (crs is not None): @@ -722,7 +721,7 @@ def proximity( """ Compute proximity distances to this vector's geometry. - Match-reference: a raster can be passed to match its resolution, bounds and CRS for computing + **Match-reference**: a raster can be passed to match its resolution, bounds and CRS for computing proximity distances. Alternatively, a grid size can be passed to create a georeferenced grid with the bounds and CRS of this vector. @@ -770,9 +769,9 @@ def buffer_metric(self, buffer_size: float) -> Vector: The outlines are projected to a local UTM, then reverted to the original projection after buffering. - :param buffer_size: Buffering distance in meters + :param buffer_size: Buffering distance in meters. - :return: Buffered shapefile + :return: Buffered shapefile. """ from geoutils.projtools import latlon_to_utm, utm_to_epsg @@ -809,7 +808,7 @@ def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coo """ Get vector bounds projected in a specified CRS. - :param out_crs: Output CRS + :param out_crs: Output CRS. :param densify_pts: Maximum points to be added between image corners to account for nonlinear edges. Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. """ @@ -834,8 +833,8 @@ def buffer_without_overlap(self, buffer_size: int | float, metric: bool = True, It could be implemented in GeoPandas in the future: https://github.com/geopandas/geopandas/issues/2015. :param buffer_size: Buffer size in self's coordinate system units. - :param metric: Whether to perform the buffering in a local metric system (default: True). - :param plot: Set to True to show intermediate plots, useful for understanding or debugging. + :param metric: Whether to perform the buffering in a local metric system (defaults to ``True``). + :param plot: Whether to show intermediate plots. :returns: A Vector containing the buffered geometries. @@ -942,8 +941,8 @@ def extract_vertices(gdf: gpd.GeoDataFrame) -> list[list[tuple[float, float]]]: :param gdf: The GeoDataFrame from which the vertices need to be extracted. - :returns: A list containing a list of (x, y) positions of the vertices. The length of the primary list is equal \ - to the number of geometries inside gdf, and length of each sublist is the number of vertices in the geometry. + :returns: A list containing a list of (x, y) positions of the vertices. The length of the primary list is equal + to the number of geometries inside gdf, and length of each sublist is the number of vertices in the geometry. """ vertices = [] # Loop on all geometries within gdf From e0fd5a5c63ace77f3f0c0f43487bd40efa7e4f39 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 13 Mar 2023 16:58:12 -0700 Subject: [PATCH 121/184] Linting --- geoutils/raster/raster.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index 01fd3cbe7..f230402f9 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -335,8 +335,8 @@ def __init__( self.transform = ds.transform self.crs = ds.crs self._nodata = ds.nodata - self.name = ds.name - self.driver = ds.driver + self._name = ds.name + self._driver = ds.driver self.tags.update(ds.tags()) self._disk_shape = (ds.count, ds.height, ds.width) @@ -1733,7 +1733,7 @@ def crop( :param crop_geom: Geometry to crop raster to. Can use either a raster or vector as match-reference, or a list of coordinates. If ``crop_geom`` is a raster or vector, will crop to the bounds. If ``crop_geom`` is a list of coordinates, the order is assumed to be [xmin, ymin, xmax, ymax]. - :param mode: Either ``"match_pixel"`` (default) or ``"match_extent"``. ``'match_pixel'`` will preserve the original pixel + :param mode: Whether to match within pixels or exact extent. ``'match_pixel'`` will preserve the original pixel resolution, cropping to the extent that most closely aligns with the current coordinates. ``'match_extent'`` will match the extent exactly, adjusting the pixel resolution to fit the extent. :param inplace: Whether to update the raster in-place. From d66c72d607e41b64847f3b6dee83eb928ca1d563 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 14 Mar 2023 17:31:10 -0700 Subject: [PATCH 122/184] Reset cache for pip setup of new geoviewer --- .github/workflows/python-app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index d5e43ffc7..b433a4b8a 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -48,7 +48,7 @@ jobs: path: ${{ env.CONDA }}/envs key: conda-${{ matrix.os }}-${{ matrix.python-version }}-${{ steps.get-date.outputs.month }}-${{ hashFiles('dev-environment.yml') }}-${{ env.CACHE_NUMBER }} env: - CACHE_NUMBER: 101 # Increase this value to reset cache if environment.yml has not changed + CACHE_NUMBER: 102 # Increase this value to reset cache if environment.yml has not changed id: cache # The trick below is necessary because the generic environment file does not specify a Python version, and only From 7aedc987393b43708364bc5309ba3ca6873deb6f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 16 Mar 2023 15:38:11 -0700 Subject: [PATCH 123/184] Try to remove graphviz --- dev-environment.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev-environment.yml b/dev-environment.yml index ec323bd75..cbed28090 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -14,8 +14,6 @@ dependencies: - autovizwidget - sphinx - myst-nb - - graphviz - - python-graphviz - sphinx-autodoc-typehints - sphinxcontrib-programoutput - numpydoc From 02026243361e59b832c59e41a12f1cfc56e3c4e6 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Mar 2023 18:22:59 -0700 Subject: [PATCH 124/184] Incremental commit on wrapping GeoPandas API --- .pre-commit-config.yaml | 1 + bin/geoviewer.py | 2 +- doc/source/api.md | 132 ++++++++- doc/source/conf.py | 4 +- geoutils/misc.py | 56 +++- geoutils/projtools.py | 6 +- geoutils/raster/multiraster.py | 2 +- geoutils/raster/raster.py | 125 ++++----- geoutils/raster/sampling.py | 2 +- geoutils/raster/satimg.py | 22 +- geoutils/vector.py | 492 ++++++++++++++++++++++++++++++--- tests/test_raster.py | 2 +- tests/test_vector.py | 206 +++++++++++++- 13 files changed, 922 insertions(+), 130 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 24e4f8c0e..dc0f3c23b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,6 +49,7 @@ repos: - id: mypy args: [ --strict, + --implicit-optional, --ignore-missing-imports, # Don't warn about stubs since pre-commit runs in a limited env --allow-untyped-calls, # Dynamic function/method calls are okay. Untyped function definitions are not okay. --show-error-codes, diff --git a/bin/geoviewer.py b/bin/geoviewer.py index 60b58e500..f925d7c31 100755 --- a/bin/geoviewer.py +++ b/bin/geoviewer.py @@ -119,7 +119,7 @@ def getparser() -> argparse.ArgumentParser: return parser -def main(test_args: Sequence[str] | None = None) -> None: +def main(test_args: Sequence[str] = None) -> None: # Parse arguments parser = getparser() diff --git a/doc/source/api.md b/doc/source/api.md index 743207566..e74d6c622 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -1,7 +1,7 @@ (api)= # API reference -This page provides an auto-generated summary of GeoUtils’ API. +This page provides a summary of GeoUtils’ API. For more details and examples, refer to the relevant chapters in the main part of the documentation. @@ -247,12 +247,13 @@ And reverse operations. Mask.proximity ``` -## Vectors +## Vector ```{eval-rst} .. minigallery:: geoutils.Vector :add-heading: ``` + ### Opening a file ```{eval-rst} @@ -313,3 +314,130 @@ And reverse operations. :toctree: gen_modules/ Vector.get_bounds_projected +``` + +### Indexing and assignment + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.__getitem__ + Vector.__setitem__ +``` + +### From Shapely and GeoPandas + +#### Geometric attributes and methods + +This first category of attributes and methods return a geometric output, converted to a {class}`~geoutils.Vector` by default. + +Otherwise, calling the method from {attr}`Vector.ds`, they return a {class}`geopandas.GeoSeries` or {class}`geopandas.GeoDataFrame` as in GeoPandas. + +**Attributes:** + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.boundary + Vector.unary_union + Vector.centroid + Vector.convex_hull + Vector.envelope + Vector.exterior +``` + + +**Methods:** + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.representative_point + Vector.normalize + Vector.make_valid + Vector.difference + Vector.symmetric_difference + Vector.union + Vector.intersection + Vector.clip_by_rect + Vector.buffer + Vector.simplify + Vector.affine_transform + Vector.translate + Vector.rotate + Vector.scale + Vector.skew + Vector.interpolate + Vector.merge + Vector.dissolve + Vector.explode + Vector.sjoin + Vector.sjoin_nearest + Vector.overlay +``` + +#### Non-geometric per-feature attributes and methods + +This second category of attributes and methods return a non-geometric output with same length as the number of features. They are thus appended in the +dataframe of the current {class}`~geoutils.Vector` by default, using as column name the name of the operation (e.g., "area", "contains" or "intersects"). + +Otherwise, calling the method from {attr}`Vector.ds`, they return a {class}`pandas.Series` as in GeoPandas. + +**Attributes:** + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.area + Vector.length + Vector.interiors + Vector.geom_type + Vector.geom_bounds + Vector.is_empty + Vector.is_ring + Vector.is_simple + Vector.has_z +``` + +**Methods:** + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.contains + Vector.equals + Vector.geom_equals + Vector.geom_almost_equals + Vector.crosses + Vector.disjoint + Vector.intersects + Vector.overlaps + Vector.touches + Vector.within + Vector.covers + Vector.covered_by + Vector.distance + Vector.relate + Vector.project +``` + +#### Other attributes and methods + + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.has_sindex + Vector.sindex +``` + +```{seealso} +The methods above are described in [GeoPandas GeoSeries's API](https://geopandas.org/en/stable/docs/reference/geoseries.html) and [Shapely object's +documentation](https://shapely.readthedocs.io/en/stable/properties.html). +``` \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index 076ec2729..b580c18f0 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -53,10 +53,12 @@ "numpy": ("https://numpy.org/doc/stable", None), "matplotlib": ("https://matplotlib.org/stable", None), "pyproj": ("https://pyproj4.github.io/pyproj/stable", None), - "geopandas": ("https://geopandas.org/en/stable", None), + "geopandas": ("https://geopandas.org/en/stable/", None), + "shapely": ("https://shapely.readthedocs.io/en/stable/", None), "xarray": ("https://docs.xarray.dev/en/stable/", None), "xdem": ("https://xdem.readthedocs.io/en/latest", None), "rioxarray": ("https://corteva.github.io/rioxarray/stable/", None), + "pandas": ("https://pandas.pydata.org/docs/", None), } sphinx_gallery_conf = { diff --git a/geoutils/misc.py b/geoutils/misc.py index 5202c36f8..34fe7ab28 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -4,7 +4,8 @@ import copy import functools import warnings -from typing import Any +from typing import Any, Union, Callable +from textwrap import dedent try: import yaml # type: ignore @@ -19,7 +20,7 @@ import geoutils -def deprecate(removal_version: str | None = None, details: str | None = None): # type: ignore +def deprecate(removal_version: str = None, details: str = None): # type: ignore """ Trigger a DeprecationWarning for the decorated function. @@ -75,6 +76,57 @@ def new_func(*args, **kwargs): # type: ignore return deprecator_func +def copy_doc(old_class: object, new_class_name: str, origin_class: object = None, replace_return_series_statement: bool = False) -> Callable: + """ + A decorator to copy docstring from a class to another class while replacing the docstring. + ---------- + **params + The classes used to reformat docstring template. + """ + + def decorator(decorated: Callable) -> Callable: + + # Get name of decorated object + # If object is a property, get name through fget + try: + decorated_name = decorated.fget.__name__ + # Otherwise, directly with the name attribute + except AttributeError: + decorated_name = decorated.__name__ + + # Get parent doc + old_class_doc = getattr(old_class, decorated_name).__doc__ + # Remove examples if there are any + doc_descript = old_class_doc.split("\n\n")[0] + + # Remove duplicate white spaces while keeping lines + doc_descript = "\n".join([" ".join(spl.split()) for spl in doc_descript.splitlines()]) + # Replace old class output name by the new class output name + replaced_descript = doc_descript.replace(old_class.__name__, new_class_name) + + # Replace "Return a Series" statement by "Append a Series to Vector" if it exists + if replace_return_series_statement: + if replaced_descript[0:20] == "Returns a ``Series``": + replaced_descript = replaced_descript.replace("Returns a ``Series``", "Returns or appends to ``Vector`` a ``Series``") + else: + replaced_descript += " Can be appended to ``Vector``." + + # Add an intersphinx link to the doc of the previous class + if origin_class is None: + orig_class = old_class + else: + orig_class = origin_class + # Get module and old class names + orig_module_name = orig_class.__module__.split(".")[0] + old_class_name = orig_class.__name__ + add_link_to_old_class = "\n\nSee more details at :func:`" + orig_module_name + "." + old_class_name + "." + decorated_name + "`." + + decorated.__doc__ = replaced_descript + add_link_to_old_class + + return decorated + + return decorator + def resampling_method_from_str(method_str: str) -> rio.enums.Resampling: """Get a rasterio resampling method from a string representation, e.g. "cubic_spline".""" diff --git a/geoutils/projtools.py b/geoutils/projtools.py index 2ead03962..be152ed1a 100644 --- a/geoutils/projtools.py +++ b/geoutils/projtools.py @@ -65,8 +65,8 @@ def utm_to_epsg(utm: str) -> int: def bounds2poly( boundsGeom: list[float] | rio.io.DatasetReader, - in_crs: CRS | None = None, - out_crs: CRS | None = None, + in_crs: CRS = None, + out_crs: CRS = None, ) -> Polygon: """ Converts self's bounds into a shapely Polygon. Optionally, returns it into a different CRS. @@ -108,7 +108,7 @@ def merge_bounds( bounds_list: abc.Iterable[ list[float] | tuple[float] | rio.coords.BoundingBox | rio.io.DatasetReader | gpd.GeoDataFrame ], - resolution: float | None = None, + resolution: float = None, merging_algorithm: str = "union", return_rio_bbox: bool = False, ) -> tuple[float, ...] | rio.coords.BoundingBox: diff --git a/geoutils/raster/multiraster.py b/geoutils/raster/multiraster.py index d6d3afb38..62c8c0bce 100644 --- a/geoutils/raster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -16,7 +16,7 @@ def load_multiple_rasters( - raster_paths: list[str], crop: bool = True, ref_grid: int | None = None, **kwargs: Any + raster_paths: list[str], crop: bool = True, ref_grid: int = None, **kwargs: Any ) -> list[RasterType]: """ Function to load multiple rasters at once in a memory efficient way. diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index f230402f9..e2ac852ff 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -169,10 +169,10 @@ def _default_nodata(dtype: str | np.dtype | type) -> int: def _load_rio( dataset: rio.io.DatasetReader, - indexes: int | tuple[int, ...] | None = None, + indexes: int | tuple[int, ...] = None, masked: bool = False, - transform: Affine | None = None, - shape: tuple[int, int] | None = None, + transform: Affine = None, + shape: tuple[int, int] = None, **kwargs: Any, ) -> np.ma.masked_array: r""" @@ -248,12 +248,12 @@ def __init__( | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], - indexes: None | int | list[int] = None, + indexes: int | list[int] = None, load_data: bool = False, downsample: AnyNumber = 1, masked: bool = True, - nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, - attrs: list[str] | None = None, + nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, + attrs: list[str] = None, ) -> None: """ Instantiate a raster from a filename or rasterio dataset. @@ -275,22 +275,22 @@ def __init__( ['bounds', 'count', 'crs', 'driver', 'dtypes', 'height', 'indexes', 'name', 'nodata', 'res', 'shape', 'transform', 'width'] - if no attrs are specified, these will be added. """ - self._driver: str | None = None - self._name: str | None = None - self.filename: str | None = None + self._driver: str = None + self._name: str = None + self.filename: str = None self.tags: dict[str, Any] = {} - self._data: np.ma.masked_array | None = None + self._data: np.ma.masked_array = None self._nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = nodata self._indexes = indexes - self._indexes_loaded: int | tuple[int, ...] | None = None + self._indexes_loaded: int | tuple[int, ...] = None self._masked = masked - self._out_shape: tuple[int, int, int] | None = None - self._disk_hash: int | None = None + self._out_shape: tuple[int, int, int] = None + self._disk_hash: int = None self._is_modified = True - self._disk_shape: tuple[int, int, int] | None = None - self._disk_indexes: tuple[int] | None = None - self._disk_dtypes: tuple[str] | None = None + self._disk_shape: tuple[int, int, int] = None + self._disk_indexes: tuple[int] = None + self._disk_dtypes: tuple[str] = None # This is for Raster.from_array to work. if isinstance(filename_or_dataset, dict): @@ -487,7 +487,7 @@ def driver(self) -> str | None: """Driver used to read a file on disk.""" return self._driver - def load(self, indexes: None | int | list[int] = None, **kwargs: Any) -> None: + def load(self, indexes: int | list[int] = None, **kwargs: Any) -> None: """ Load the raster array from disk. @@ -549,7 +549,7 @@ def from_array( data: np.ndarray | np.ma.masked_array, transform: tuple[float, ...] | Affine, crs: CRS | int | None, - nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, ) -> RasterType: """Create a raster from a numpy array and the georeferencing information. @@ -1485,7 +1485,7 @@ def info(self, stats: bool = False) -> str: return "".join(as_str) - def copy(self: RasterType, new_array: np.ndarray | None = None) -> RasterType: + def copy(self: RasterType, new_array: np.ndarray = None) -> RasterType: """ Copy the raster in-memory. @@ -1789,14 +1789,14 @@ def crop( def reproject( self: RasterType, - dst_ref: RasterType | str | None = None, - dst_crs: CRS | str | int | None = None, - dst_size: tuple[int, int] | None = None, - dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, - dst_res: float | abc.Iterable[float] | None = None, - dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, - src_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, - dst_dtype: np.dtype | None = None, + dst_ref: RasterType | str = None, + dst_crs: CRS | str | int = None, + dst_size: tuple[int, int] = None, + dst_bounds: dict[str, float] | rio.coords.BoundingBox = None, + dst_res: float | abc.Iterable[float] = None, + dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, + src_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, + dst_dtype: np.dtype = None, resampling: Resampling | str = Resampling.bilinear, silent: bool = False, n_threads: int = 0, @@ -2064,22 +2064,19 @@ def save( self, filename: str | pathlib.Path | IO[bytes], driver: str = "GTiff", - dtype: DTypeLike | None = None, - nodata: AnyNumber | None = None, + dtype: DTypeLike = None, + nodata: AnyNumber = None, compress: str = "deflate", tiled: bool = False, - blank_value: None | int | float = None, - co_opts: dict[str, str] | None = None, - metadata: dict[str, Any] | None = None, - gcps: list[tuple[float, ...]] | None = None, - gcps_crs: CRS | None = None, + blank_value: int | float = None, + co_opts: dict[str, str] = None, + metadata: dict[str, Any] = None, + gcps: list[tuple[float, ...]] = None, + gcps_crs: CRS = None, ) -> None: """ Write the raster to file. - Given a filename to save the Raster to, create a geo-referenced file - on disk which contains the contents of self.data. - If blank_value is set to an integer or float, then instead of writing the contents of self.data to disk, write this provided value to every pixel instead. @@ -2169,7 +2166,7 @@ def save( dst.gcps = (rio_gcps, gcps_crs) - def to_xarray(self, name: str | None = None) -> xr.DataArray: + def to_xarray(self, name: str = None) -> xr.DataArray: """ Convert raster to a xarray.DataArray. @@ -2250,14 +2247,14 @@ def intersection(self, rst: str | Raster, match_ref: bool = True) -> tuple[float def show( self, - index: int | None = None, - cmap: matplotlib.colors.Colormap | str | None = None, - vmin: float | int | None = None, - vmax: float | int | None = None, - alpha: float | int | None = None, - cbar_title: str | None = None, + index: int = None, + cmap: matplotlib.colors.Colormap | str = None, + vmin: float | int = None, + vmax: float | int = None, + alpha: float | int = None, + cbar_title: str = None, add_cbar: bool = True, - ax: matplotlib.axes.Axes | Literal["new"] | None = None, + ax: matplotlib.axes.Axes | Literal["new"] = None, return_axes: bool = False, **kwargs: Any, ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: @@ -2385,9 +2382,9 @@ def value_at_coords( x: float | ArrayLike, y: float | ArrayLike, latlon: bool = False, - index: int | None = None, + index: int = None, masked: bool = False, - window: int | None = None, + window: int = None, reducer_function: Callable[[np.ndarray], float] = np.ma.mean, return_window: bool = False, boundless: bool = True, @@ -2594,7 +2591,7 @@ def xy2ij( x: ArrayLike, y: ArrayLike, op: type = np.float32, - precision: float | None = None, + precision: float = None, shift_area_or_point: bool = False, ) -> tuple[np.ndarray, np.ndarray]: """ @@ -2771,7 +2768,7 @@ def interp_points( return rpts - def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int | None = None) -> list[Raster]: + def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int = None) -> list[Raster]: """ Split the bands into separate rasters. @@ -2973,8 +2970,8 @@ def polygonize( def proximity( self, - vector: Vector | None = None, - target_values: list[float] | None = None, + vector: Vector = None, + target_values: list[float] = None, geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", @@ -3015,7 +3012,7 @@ def subsample( self, subsample: int | float, return_indices: bool = False, - random_state: None | np.random.RandomState | np.random.Generator | int = None, + random_state: np.random.RandomState | np.random.Generator | int = None, ) -> np.ndarray: """ Randomly subsample the raster. Only valid values are considered. @@ -3131,14 +3128,14 @@ def _repr_html_(self) -> str: def reproject( self: Mask, - dst_ref: RasterType | str | None = None, - dst_crs: CRS | str | int | None = None, - dst_size: tuple[int, int] | None = None, - dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, - dst_res: float | abc.Iterable[float] | None = None, - dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, - src_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, - dst_dtype: np.dtype | None = None, + dst_ref: RasterType | str = None, + dst_crs: CRS | str | int = None, + dst_size: tuple[int, int] = None, + dst_bounds: dict[str, float] | rio.coords.BoundingBox = None, + dst_res: float | abc.Iterable[float] = None, + dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, + src_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, + dst_dtype: np.dtype = None, resampling: Resampling | str = Resampling.nearest, silent: bool = False, n_threads: int = 0, @@ -3249,8 +3246,8 @@ def polygonize( def proximity( self, - vector: Vector | None = None, - target_values: list[float] | None = None, + vector: Vector = None, + target_values: list[float] = None, geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", @@ -3335,8 +3332,8 @@ def __invert__(self: Mask) -> Mask: def proximity_from_vector_or_raster( raster: Raster, - vector: Vector | None = None, - target_values: list[float] | None = None, + vector: Vector = None, + target_values: list[float] = None, geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", diff --git a/geoutils/raster/sampling.py b/geoutils/raster/sampling.py index 62cfcaa30..2db9942d6 100644 --- a/geoutils/raster/sampling.py +++ b/geoutils/raster/sampling.py @@ -11,7 +11,7 @@ def subsample_array( array: np.ndarray | np.ma.masked_array, subsample: float | int, return_indices: bool = False, - random_state: None | np.random.RandomState | np.random.Generator | int = None, + random_state: np.random.RandomState | np.random.Generator | int = None, ) -> np.ndarray: """ Randomly subsample a 1D or 2D array by a subsampling factor, taking only non NaN/masked values. diff --git a/geoutils/raster/satimg.py b/geoutils/raster/satimg.py index 051da34f3..301d7766f 100644 --- a/geoutils/raster/satimg.py +++ b/geoutils/raster/satimg.py @@ -119,7 +119,7 @@ def parse_metadata_from_fn(fname: str) -> list[Any]: return list(attrs) -def parse_tile_attr_from_name(tile_name: str, product: str | None = None) -> tuple[float, float, tuple[int, int], int]: +def parse_tile_attr_from_name(tile_name: str, product: str = None) -> tuple[float, float, tuple[int, int], int]: """ Convert tile naming to metadata coordinates based on sensor and product by default the SRTMGL1 1x1° tile naming convention to lat, lon (originally SRTMGL1) @@ -256,18 +256,18 @@ class SatelliteImage(Raster): # type: ignore def __init__( self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile, - attrs: list[str] | None = None, + attrs: list[str] = None, load_data: bool = True, - indexes: int | list[int] | None = None, + indexes: int | list[int] = None, read_from_fn: bool = True, - datetime: dt.datetime | None = None, - tile_name: str | None = None, - satellite: str | None = None, - sensor: str | None = None, - product: str | None = None, - version: str | None = None, + datetime: dt.datetime = None, + tile_name: str = None, + satellite: str = None, + sensor: str = None, + product: str = None, + version: str = None, read_from_meta: bool = True, - fn_meta: str | None = None, + fn_meta: str = None, silent: bool = True, ) -> None: @@ -438,7 +438,7 @@ def __parse_metadata_from_file(self, fn_meta: str | None) -> None: return None - def copy(self, new_array: np.ndarray | None = None) -> SatelliteImage: + def copy(self, new_array: np.ndarray = None) -> SatelliteImage: new_satimg = super().copy(new_array=new_array) # type: ignore # all objects here are immutable so no need for a copy method (string and datetime) # satimg_attrs = ['satellite', 'sensor', 'product', 'version', 'tile_name', 'datetime'] #taken outside of class diff --git a/geoutils/vector.py b/geoutils/vector.py index ac80e6ca7..0b1faf9af 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -11,7 +11,9 @@ from typing import Any, Literal, TypeVar, overload import fiona +import pandas as pd import geopandas as gpd +from geopandas.testing import assert_geodataframe_equal import matplotlib import matplotlib.pyplot as plt import numpy as np @@ -25,6 +27,7 @@ from shapely.geometry.polygon import Polygon import geoutils as gu +from geoutils.misc import copy_doc from geoutils.projtools import _get_bounds_projected, bounds2poly # This is a generic Vector-type (if subclasses are made, this will change appropriately) @@ -67,8 +70,6 @@ def __init__(self, filename_or_dataset: str | pathlib.Path | gpd.GeoDataFrame): else: raise TypeError("Filename argument should be a string, Path or geopandas.GeoDataFrame.") - self._crs = self.ds.crs - def __repr__(self) -> str: """Convert vector to string representation.""" @@ -116,12 +117,13 @@ def __str__(self) -> str: return str(self.ds.__str__()) - def __getitem__(self, value: gu.Raster | Vector | list[float] | tuple[float, ...]) -> Vector: - """ - Index or subset the vector. - """ - return self.crop(crop_geom=value, clip=False, inplace=False) + def __setattr__(self, attr, val): + # have to special case geometry b/c pandas tries to use as column... + if attr == "geometry": + object.__setattr__(self, attr, val) + else: + super().__setattr__(attr, val) def info(self) -> str: """ @@ -142,14 +144,14 @@ def info(self) -> str: def show( self, - ref_crs: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | CRS | int | None = None, - cmap: matplotlib.colors.Colormap | str | None = None, - vmin: float | int | None = None, - vmax: float | int | None = None, - alpha: float | int | None = None, - cbar_title: str | None = None, + ref_crs: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | CRS | int = None, + cmap: matplotlib.colors.Colormap | str = None, + vmin: float | int = None, + vmax: float | int = None, + alpha: float | int = None, + cbar_title: str = None, add_cbar: bool = False, - ax: matplotlib.axes.Axes | None | Literal["new"] = None, + ax: matplotlib.axes.Axes | Literal["new"] = None, return_axes: bool = False, **kwargs: Any, ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: @@ -250,16 +252,407 @@ def show( else: return None + def save( + self, + filename: str | pathlib.Path , + driver: str = None, + schema: dict = None, + index: bool = None, + **kwargs: Any + ) -> None: + """ + Write the vector to file. + + This function is a simple wrapper of :func:`geopandas.GeoDataFrame.to_file`. See there for details. + + :param filename: Filename to write the file to. + :param driver: Driver to write file with. + :param schema: Dictionary passed to Fiona to better control how the file is written. + :param index: Whether to write the index or not. + + :returns: None. + """ + + self.ds.to_file(filename=filename, driver=driver, schema=schema, index=index, **kwargs) + + ######################################################################################################################## + # Overriden and wrapped methods from GeoPandas and Pandas API to logically cast the output for minimalistic manipulation + ######################################################################################################################## + def _override_gdf_output(self, + other: gpd.GeoDataFrame | gpd.GeoSeries | shapely.Geometry | pd.Series | Any) -> Vector | pd.Series: + """Parse outputs of GeoPandas functions to facilitate object manipulation.""" + + # Raise error if output is not treated separately, should appear in tests + if not isinstance(other, (gpd.GeoDataFrame, gpd.GeoDataFrame, pd.Series, shapely.Geometry)): + raise ValueError("Not implemented. This error should only be raised in tests.") + + # If a GeoDataFrame is the output, return it + if isinstance(other, gpd.GeoDataFrame): + return Vector(other) + # If a GeoSeries is the output, re-encapsulate in a GeoDataFrame and return it + elif isinstance(other, gpd.GeoSeries): + return Vector(gpd.GeoDataFrame(geometry=other)) + # If a Shapely Geometry is the output, re-encapsulate in a GeoDataFrame and return it + elif isinstance(other, shapely.Geometry): + return Vector(gpd.GeoDataFrame({"geometry": [other]}, crs=self.crs)) + # If a DataFrame is the output, append it to that of the GeoDataFrame + elif isinstance(other, pd.Series): + return other + + # ----------------------------------------------- + # GeoPandasBase - Attributes that return a Series + # ----------------------------------------------- + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def area(self) -> pd.Series: + return self._override_gdf_output(self.ds.area) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def length(self) -> pd.Series: + return self._override_gdf_output(self.ds.length) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def interiors(self) -> pd.Series: + return self._override_gdf_output(self.ds.interiors) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def geom_type(self) -> pd.Series: + return self._override_gdf_output(self.ds.geom_type) + + # Exception ! bounds is renamed geom_bounds to make Raster and Vector "bounds" the same "total_bounds" + @property + def geom_bounds(self): + """Returns or appends to ``Vector`` a ``Series`` with the bounds of each geometry feature.""" + return self.ds.bounds + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def is_empty(self) -> pd.Series: + return self._override_gdf_output(self.ds.is_empty) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def is_ring(self) -> pd.Series: + return self._override_gdf_output(self.ds.is_ring) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def is_simple(self) -> pd.Series: + return self._override_gdf_output(self.ds.is_simple) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @property + def has_z(self) -> bool: + return self.ds.has_z + + # -------------------------------------------------- + # GeoPandasBase - Attributes that return a GeoSeries + # -------------------------------------------------- + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def boundary(self) -> Vector: + + return self._override_gdf_output(self.ds.boundary) + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def unary_union(self) -> Vector: + + return self._override_gdf_output(self.ds.unary_union) + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def centroid(self) -> Vector: + + return self._override_gdf_output(self.ds.centroid) + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def convex_hull(self) -> Vector: + + return self._override_gdf_output(self.ds.centroid) + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def envelope(self) -> Vector: + + return self._override_gdf_output(self.ds.envelope) + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def exterior(self) -> Vector: + + return self._override_gdf_output(self.ds.exterior) + + # --------------------------------------------------------------------------------- + # GeoPandasBase - Attributes that return a specific value (not Series or GeoSeries) + # --------------------------------------------------------------------------------- + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def has_sindex(self) -> bool: + return self.ds.has_sindex + + @copy_doc(gpd.GeoSeries, "Vector") + @property + def sindex(self) -> bool: + return self.ds.sindex + + @property + def total_bounds(self) -> rio.coords.BoundingBox: + """Total bounds of the vector.""" + return self.ds.total_bounds + + # Exception ! Vector.bounds corresponds to the total_bounds @property def bounds(self) -> rio.coords.BoundingBox: - """Bounding box of the vector.""" + """Total bounding box of the vector.""" return rio.coords.BoundingBox(*self.ds.total_bounds) + # -------------------------------------------- + # GeoPandasBase - Methods that return a Series + # -------------------------------------------- + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def contains(self, other: gu.Vector, align: bool = True) -> Vector: + + return self._override_gdf_output(self.ds.contains(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def geom_equals(self, other: gu.Vector, align: bool = True) -> Vector: + + return self._override_gdf_output(self.ds.geom_equals(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def geom_almost_equals(self, other: gu.Vector, decimal: int = 6, align: bool = True) -> Vector: + + return self._override_gdf_output(self.ds.geom_almost_equals(other=other.ds, decimal=decimal, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def geom_equals_exact(self, other: gu.Vector, tolerance: float, align: bool=True,) -> Vector: + + return self._override_gdf_output(self.ds.geom_equals_exact(other=other.ds, tolerance=tolerance, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def crosses(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.crosses(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def disjoint(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.disjoint(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def intersects(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.intersects(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def overlaps(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.overlaps(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def touches(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.touches(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def within(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.within(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def covers(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.covers(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def covered_by(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.covered_by(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def distance(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.distance(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def relate(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.relate(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + def project(self, other: gu.Vector, normalized: bool = False, align: bool = True) -> Vector: + + return self._override_gdf_output(self.ds.project(other=other.ds, normalized=normalized, align=align)) + + # ----------------------------------------------- + # GeoPandasBase - Methods that return a GeoSeries + # ----------------------------------------------- + + @copy_doc(gpd.GeoSeries, "Vector") + def representative_point(self) -> Vector: + + return self._override_gdf_output(self.ds.representative_point()) + + @copy_doc(gpd.GeoSeries, "Vector") + def normalize(self) -> Vector: + + return self._override_gdf_output(self.ds.normalize()) + + @copy_doc(gpd.GeoSeries, "Vector") + def make_valid(self) -> Vector: + + return self._override_gdf_output(self.ds.make_valid()) + + @copy_doc(gpd.GeoSeries, "Vector") + def difference(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.difference(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector") + def symmetric_difference(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.symmetric_difference(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector") + def union(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.union(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector") + def intersection(self, other: gu.Vector, align: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.intersection(other=other.ds, align=align)) + + @copy_doc(gpd.GeoSeries, "Vector") + def clip_by_rect(self, xmin: float, ymin: float, xmax: float, ymax: float) -> Vector: + + return self._override_gdf_output(self.ds.clip_by_rect(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax)) + + @copy_doc(gpd.GeoSeries, "Vector") + def buffer(self, distance: float, resolution: int=16, **kwargs) -> Vector: + + return self._override_gdf_output(self.ds.buffer(distance=distance, resolution=resolution, **kwargs)) + + @copy_doc(gpd.GeoSeries, "Vector") + def simplify(self, *args: Any, **kwargs: Any) -> Vector: + + return self._override_gdf_output(self.ds.simplify(*args, **kwargs)) + + @copy_doc(gpd.GeoSeries, "Vector") + def affine_transform(self, matrix: tuple[float, ...]) -> Vector: + + return self._override_gdf_output(self.ds.affine_transform(matrix=matrix)) + + @copy_doc(gpd.GeoSeries, "Vector") + def translate(self, xoff: float = 0.0, yoff: float = 0.0, zoff: float = 0.0) -> Vector: + + return self._override_gdf_output(self.ds.translate(xoff=xoff, yoff=yoff, zoff=zoff)) + + @copy_doc(gpd.GeoSeries, "Vector") + def rotate(self, angle: float, origin: str = "center", use_radians: bool = False) -> Vector: + + return self._override_gdf_output(self.ds.rotate(angle=angle, origin=origin, use_radians=use_radians)) + + @copy_doc(gpd.GeoSeries, "Vector") + def scale(self, xfact: float = 1.0, yfact: float = 1.0, zfact: float = 1.0, origin: str = "center") -> Vector: + + return self._override_gdf_output(self.ds.scale(xfact=xfact, yfact=yfact, zfact=zfact, origin=origin)) + + @copy_doc(gpd.GeoSeries, "Vector") + def skew(self, xs: float=0.0, ys: float=0.0, origin: str = "center", use_radians: bool =False) -> Vector: + + return self._override_gdf_output(self.ds.skew(xs=xs, ys=ys, origin=origin, use_radians=use_radians)) + + @copy_doc(gpd.GeoSeries, "Vector") + def interpolate(self, distance: float, normalized: bool=False) -> Vector: + + return self._override_gdf_output(self.ds.interpolate(distance=distance, normalized=normalized)) + + + # ---------------------------------------------- + # GeoDataFrame - Methods that return a GeoSeries + # ---------------------------------------------- + + def __getitem__(self, key: gu.Raster | Vector | list[float] | tuple[float, ...] | Any) -> Vector: + """ + Index the geodataframe or crop the vector. + + If a raster, vector or tuple is passed, crops to its bounds. + Otherwise, indexes the geodataframe. + """ + + if isinstance(key, (gu.Raster, Vector)): + return self.crop(crop_geom=key, clip=False, inplace=False) + else: + return self._override_gdf_output(self.ds.__getitem__(key)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def __setitem__(self, key: Any, value: Any) -> None: + + self.ds.__setitem__(key, value) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def dissolve(self, by: Any=None, aggfunc: Any = "first", as_index: bool=True, level: Any=None, sort: bool=True, observed: bool=False, dropna: bool=True) -> Vector: + + return self._override_gdf_output(self.ds.dissolve(by=by, aggfunc=aggfunc, as_index=as_index, level=level, sort=sort, observed=observed, dropna=dropna)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def explode(self, column: str = None, ignore_index: bool = False, index_parts: bool = None, **kwargs: Any) -> Vector: + + return self._override_gdf_output(self.ds.explode(column=column, ignore_index=ignore_index, index_parts=index_parts, **kwargs)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def sjoin(self, df: Vector | gpd.GeoDataFrame, *args: Any, **kwargs: Any) -> Vector: + + # Ensure input is a geodataframe + if isinstance(df, gu.Vector): + gdf = df.ds + else: + gdf = df + + return self._override_gdf_output(self.ds.sjoin(df=gdf, *args, **kwargs)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def sjoin_nearest(self, right: Vector | gpd.GeoDataFrame, how: str = "inner", max_distance: float = None, lsuffix: str = "left", rsuffix: str = "right", + distance_col: str = None) -> Vector: + + # Ensure input is a geodataframe + if isinstance(right, gu.Vector): + gdf = right.ds + else: + gdf = right + + return self._override_gdf_output(self.ds.sjoin_nearest(right=gdf, how=how, max_distance=max_distance, lsuffix=lsuffix, rsuffix=rsuffix, + distance_col=distance_col)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def overlay(self, right: Vector | gpd.GeoDataFrame, how: str = "intersection", keep_geom_type: bool = None, make_valid: bool = True) -> Vector: + + # Ensure input is a geodataframe + if isinstance(right, gu.Vector): + gdf = right.ds + else: + gdf = right + + return self._override_gdf_output(self.ds.overlay(right=gdf, how=how, keep_geom_type=keep_geom_type, make_valid=make_valid)) + + # -------------------------------- + # End of GeoPandas functionalities + # -------------------------------- + @property def crs(self) -> rio.crs.CRS: """Coordinate reference system of the vector.""" - self._crs = self.ds.crs - return self._crs + return self.ds.crs @property def ds(self) -> gpd.GeoDataFrame: @@ -277,11 +670,26 @@ def ds(self, new_ds: gpd.GeoDataFrame | gpd.GeoSeries) -> None: else: raise ValueError("The dataset of a vector must be set with a GeoSeries or a GeoDataFrame.") + def vector_equal(self, other: gu.Vector) -> bool: + """Check if two vectors are equal.""" + + return assert_geodataframe_equal(self.ds, other.ds) + @property def name(self) -> str | None: """Name on disk, if it exists.""" return self._name + @property + def geometry(self) -> gpd.GeoSeries: + + return self.ds.geometry + + @property + def index(self): + + return self.ds.index + def copy(self: VectorType) -> VectorType: """Return a copy of the vector.""" # Utilise the copy method of GeoPandas @@ -364,8 +772,8 @@ def crop( def reproject( self: Vector, - dst_ref: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | None = None, - dst_crs: CRS | str | int | None = None, + dst_ref: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str = None, + dst_crs: CRS | str | int = None, ) -> Vector: """ Reproject vector to a specified coordinate reference system. @@ -421,11 +829,11 @@ def reproject( @overload def create_mask( self, - rst: str | gu.Raster | None = None, - crs: CRS | None = None, - xres: float | None = None, - yres: float | None = None, - bounds: tuple[float, float, float, float] | None = None, + rst: str | gu.Raster = None, + crs: CRS = None, + xres: float = None, + yres: float = None, + bounds: tuple[float, float, float, float] = None, buffer: int | float | np.number = 0, *, as_array: Literal[False] = False, @@ -435,11 +843,11 @@ def create_mask( @overload def create_mask( self, - rst: str | gu.Raster | None = None, - crs: CRS | None = None, - xres: float | None = None, - yres: float | None = None, - bounds: tuple[float, float, float, float] | None = None, + rst: str | gu.Raster = None, + crs: CRS = None, + xres: float = None, + yres: float = None, + bounds: tuple[float, float, float, float] = None, buffer: int | float | np.number = 0, *, as_array: Literal[True], @@ -448,11 +856,11 @@ def create_mask( def create_mask( self, - rst: gu.Raster | None = None, - crs: CRS | None = None, - xres: float | None = None, - yres: float | None = None, - bounds: tuple[float, float, float, float] | None = None, + rst: gu.Raster = None, + crs: CRS = None, + xres: float = None, + yres: float = None, + bounds: tuple[float, float, float, float] = None, buffer: int | float | np.number = 0, as_array: bool = False, ) -> gu.Mask | np.ndarray: @@ -558,12 +966,12 @@ def create_mask( def rasterize( self, - rst: gu.Raster | None = None, - crs: CRS | int | None = None, - xres: float | None = None, - yres: float | None = None, - bounds: tuple[float, float, float, float] | None = None, - in_value: int | float | abc.Iterable[int | float] | None = None, + rst: gu.Raster = None, + crs: CRS | int = None, + xres: float = None, + yres: float = None, + bounds: tuple[float, float, float, float] = None, + in_value: int | float | abc.Iterable[int | float] = None, out_value: int | float = 0, ) -> gu.Raster | gu.Mask: """ @@ -673,7 +1081,7 @@ def rasterize( @classmethod def from_bounds_projected( - cls, raster_or_vector: gu.Raster | VectorType, out_crs: CRS | None = None, densify_pts: int = 5000 + cls, raster_or_vector: gu.Raster | VectorType, out_crs: CRS = None, densify_pts: int = 5000 ) -> VectorType: """Create a vector polygon from projected bounds of a raster or vector. @@ -712,7 +1120,7 @@ def query(self: Vector, expression: str, inplace: bool = False) -> Vector | None def proximity( self, - raster: gu.Raster | None = None, + raster: gu.Raster = None, grid_size: tuple[int, int] = (1000, 1000), geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", diff --git a/tests/test_raster.py b/tests/test_raster.py index d4018e72b..e54ce6101 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -3169,7 +3169,7 @@ def from_array( cls: type[TestArithmetic], data: np.ndarray | np.ma.masked_array, rst_ref: gu.RasterType, - nodata: int | float | list[int] | list[float] | None = None, + nodata: int | float | list[int] | list[float] = None, ) -> gu.Raster: """ Generate a Raster from numpy array, with set georeferencing. Used for testing only. diff --git a/tests/test_vector.py b/tests/test_vector.py index 2001bb02f..b1e448092 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -1,18 +1,25 @@ from __future__ import annotations +import os.path import pathlib import re +import inspect +import tempfile +from tempfile import NamedTemporaryFile import geopandas as gpd +import geopandas.base import matplotlib.pyplot as plt import numpy as np import pytest -from geopandas.testing import assert_geodataframe_equal +import shapely +from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal from scipy.ndimage import binary_erosion from shapely.geometry.linestring import LineString from shapely.geometry.multilinestring import MultiLineString from shapely.geometry.multipolygon import MultiPolygon from shapely.geometry.polygon import Polygon +from pandas.testing import assert_series_equal import geoutils as gu @@ -69,6 +76,25 @@ def test_query(self) -> None: assert vector2.ds.shape[0] < self.glacier_outlines.ds.shape[0] + def test_save(self) -> None: + """Test the save wrapper for GeoDataFrame.to_file().""" + + vector = gu.Vector(self.aster_outlines_path) + + # Create a temporary file in a temporary directory + temp_dir = tempfile.TemporaryDirectory() + temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name, suffix=".gpkg") + os.remove(temp_file.name) + + # Save and check the file exists + vector.save(temp_file.name, overwrite=True) + assert os.path.exists(temp_file.name) + + # Open and check the object is the same + vector_save = gu.Vector(temp_file.name) + assert_geodataframe_equal(vector_save, vector) + + def test_bounds(self) -> None: bounds = self.glacier_outlines.bounds @@ -529,3 +555,181 @@ def test_buffer_without_overlap(self, monkeypatch) -> None: # type: ignore # Check that plotting runs without errors and close it monkeypatch.setattr(plt, "show", lambda: None) two_squares.buffer_without_overlap(buffer_size, plot=True) + + +class TestGeoPandasMethods: + + # Use two synthetic vectors + poly = Polygon([(10, 10), (11, 10), (11, 11), (10, 11)]) + gdf1 = gpd.GeoDataFrame({"geometry": [poly]}, crs="EPSG:4326") + synthvec1 = gu.Vector(gdf1) + + # Create a synthetic LineString geometry + lines = LineString([(10, 10), (10.5, 10.5), (11, 11)]) + gdf2 = gpd.GeoDataFrame({"geometry": [lines]}, crs="EPSG:4326") + synthvec2 = gu.Vector(gdf2) + + # Use two real-life vectors + realvec1 = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) + realvec2 = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + + # Properties and methods derived from Shapely or GeoPandas + # List of properties and methods with non-geometric output that are implemented in GeoUtils + nongeo_properties = ["area", "length", "interiors", "geom_type", "is_empty", "is_ring", "is_simple"] + nongeo_methods = ["contains", "geom_equals", "geom_almost_equals", "crosses", "disjoint", "intersects", + "overlaps", "touches", "within", "covers", "covered_by", "distance", "relate", "project"] + + # List of properties and methods with geometric output that are implemented in GeoUtils + geo_properties = ["boundary", "unary_union", "centroid", "convex_hull", "envelope", "exterior"] + geo_methods = ["representative_point", "normalize", "make_valid", "difference", "symmetric_difference", "union", + "intersection", "clip_by_rect", "buffer", "simplify", "affine_transform", "translate", "rotate", + "scale", "skew", "interpolate", "dissolve", "explode", "sjoin", "sjoin_nearest", "overlay"] + + # List of other properties and methods + other = ["has_sindex", "sindex"] + all_declared = nongeo_methods + nongeo_properties + geo_methods + geo_properties + other + + # Get all GeoPandasBase public methods with some exceptions + geobase_methods = gpd.base.GeoPandasBase.__dict__.copy() + # exceptions_geobase = ["plot", "explore", "equals"] + # geobase_methods = {key: geobase_methods[key] for key in geobase_methods if key[0] != "_" and key not in exceptions_geobase} + + # Get all GeoDataFrame public methods with some exceptions + gdf_methods = gpd.GeoDataFrame.__dict__.copy() + # exceptions_gdf = ["total_bounds"] + # gdf_methods = {key: gdf_methods[key] for key in gdf_methods if key[0] != "_" and key not in exceptions_gdf} + + def test_overriden_funcs_exist(self) -> None: + """Check that all methods listed above exist in Vector.""" + + # Check that all methods declared in the class above exist in Vector + vector_methods = gu.Vector.__dict__ + + assert all(method in vector_methods.keys() for method in self.all_declared) + + def test_geopandas_coverage(self) -> None: + """Check that all existing methods of GeoPandas are overriden, with a couple exceptions.""" + + # Merge the two + all_methods = self.geobase_methods.copy() + all_methods.update(self.gdf_methods) + + # Check that all methods declared in the class above are covered in Vector + list_missing = [method for method in all_methods.keys() if method not in self.all_declared] + try: + assert len(list_missing) == 0 + except AssertionError: + print('Missing methods from GeoPandas:') + print(list_missing) + + @pytest.mark.parametrize("method", nongeo_methods + geo_methods) # type: ignore + def test_overriden_funcs_args(self, method: str) -> None: + """Check that all methods overriden have the same arguments as in GeoPandas.""" + + # Get GeoPandas class where the methods live + if method in self.geobase_methods.keys(): + upstream_class = gpd.base.GeoPandasBase + elif method in self.gdf_methods.keys(): + upstream_class = gpd.GeoDataFrame + else: + raise ValueError("Method did not belong to GeoDataFrame or GeoPandasBase class.") + + # Get a full argument inspection object for each class + argspec_upstream = inspect.getfullargspec(getattr(upstream_class, method)) + argspec_geoutils = inspect.getfullargspec(getattr(gu.Vector, method)) + + # Check that all positional arguments are the same + assert argspec_upstream.args == argspec_geoutils.args + # Check that the *args and **kwargs argument are declared consistently + assert argspec_upstream.varargs == argspec_geoutils.varargs + assert argspec_upstream.varkw == argspec_geoutils.varkw + # Check that default argument values are the same + assert argspec_upstream.defaults == argspec_geoutils.defaults + + @pytest.mark.parametrize("vector", [synthvec1, synthvec2, realvec1, realvec2]) # type: ignore + @pytest.mark.parametrize("method", nongeo_properties) # type: ignore + def test_nongeo_properties(self, vector: gu.Vector, method: str) -> None: + """Check non-geometric properties are consistent with GeoPandas.""" + + # Get method for each class + output_geoutils = getattr(vector, method) + output_geopandas = getattr(vector.ds, method) + + # Assert equality + assert_series_equal(output_geoutils, output_geopandas) + + @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore + @pytest.mark.parametrize("vector2", [synthvec2, realvec2]) # type: ignore + @pytest.mark.parametrize("method", nongeo_methods) # type: ignore + def test_nongeo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) -> None: + """ + Check non-geometric methods are consistent with GeoPandas. + All these methods require two inputs ("other", "df", or "right" argument). + """ + + # Get method for each class + output_geoutils = getattr(vector1, method)(vector2) + output_geopandas = getattr(vector1.ds, method)(vector2.ds) + + # Assert equality + assert_series_equal(output_geoutils, output_geopandas) + + @pytest.mark.parametrize("vector", [synthvec1, synthvec2, realvec1, realvec2]) # type: ignore + @pytest.mark.parametrize("method", geo_properties) # type: ignore + def test_geo_properties(self, vector: gu.Vector, method: str) -> None: + """Check geometric properties are consistent with GeoPandas.""" + + # Get method for each class + output_geoutils = getattr(vector, method) + output_geopandas = getattr(vector.ds, method) + + # Assert output types + assert isinstance(output_geoutils, gu.Vector) + assert isinstance(output_geopandas, (gpd.GeoSeries, gpd.GeoDataFrame, shapely.Geometry)) + + # Separate cases depending on GeoPandas' output + if isinstance(output_geopandas, gpd.GeoSeries): + # Assert geoseries equality + assert_geoseries_equal(output_geoutils.ds.geometry, output_geopandas) + elif isinstance(output_geopandas, shapely.Geometry): + assert_geodataframe_equal(output_geoutils.ds, gpd.GeoDataFrame({"geometry": [output_geopandas]}, crs=vector.crs)) + else: + assert_geodataframe_equal(output_geoutils.ds, output_geopandas) + + + specific_method_args = {"buffer": {"distance": 1}, "clip_by_rect": {"xmin": 10.5, "ymin": 10.5, "xmax": 11, "ymax": 11}, + "affine_transform": {"matrix": [0, 0, 0, 0, 1, 1]}, "translate": {"xoff": 1, "yoff": 1, "zoff": 0}, + "rotate": {"angle": 90}, "scale": {"xfact": 1.1, "yfact": 1.1, "zfact": 1.1, "origin": "center"}, + "skew": {"xs": 1.1, "ys": 1.1}, "interpolate": {"distance": 1}, "simplify": {"tolerance": 1}} + + @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore + @pytest.mark.parametrize("vector2", [synthvec2, realvec2]) # type: ignore + @pytest.mark.parametrize("method", geo_methods) # type: ignore + def test_geo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) -> None: + """Check geometric properties are consistent with GeoPandas.""" + + # Methods that require two inputs + if method in ["difference", "symmetric_difference", "union", "intersection", "sjoin", "sjoin_nearest", "overlay"]: + output_geoutils = getattr(vector1, method)(vector2) + output_geopandas = getattr(vector1.ds, method)(vector2.ds) + # Methods that require zero input + elif method in ["representative_point", "normalize", "make_valid", "dissolve", "explode"]: + output_geoutils = getattr(vector1, method)() + output_geopandas = getattr(vector1.ds, method)() + elif method in self.specific_method_args.keys(): + output_geoutils = getattr(vector1, method)(**self.specific_method_args[method]) + output_geopandas = getattr(vector1.ds, method)(**self.specific_method_args[method]) + else: + raise ValueError("The method '{}' is not covered by this test.".format(method)) + + # Assert output types + assert isinstance(output_geoutils, gu.Vector) + assert isinstance(output_geopandas, (gpd.GeoSeries, gpd.GeoDataFrame)) + + # Separate cases depending on GeoPandas' output + if isinstance(output_geopandas, gpd.GeoSeries): + # Assert geoseries equality + assert_geoseries_equal(output_geoutils.ds.geometry, output_geopandas) + else: + # Or assert geodataframe equality + assert_geodataframe_equal(output_geoutils.ds, output_geopandas) \ No newline at end of file From bb94f02281f578d6008b49bbfea1274cd0093c8a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Mar 2023 22:11:48 -0700 Subject: [PATCH 125/184] Finalize tests for GeoPandas interface --- geoutils/vector.py | 31 +++++++++++++++++-------------- tests/test_vector.py | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/geoutils/vector.py b/geoutils/vector.py index 0b1faf9af..27dece8d9 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -375,7 +375,7 @@ def centroid(self) -> Vector: @property def convex_hull(self) -> Vector: - return self._override_gdf_output(self.ds.centroid) + return self._override_gdf_output(self.ds.convex_hull) @copy_doc(gpd.GeoSeries, "Vector") @property @@ -483,15 +483,17 @@ def distance(self, other: gu.Vector, align: bool=True) -> Vector: return self._override_gdf_output(self.ds.distance(other=other.ds, align=align)) - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def relate(self, other: gu.Vector, align: bool=True) -> Vector: - - return self._override_gdf_output(self.ds.relate(other=other.ds, align=align)) - - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def project(self, other: gu.Vector, normalized: bool = False, align: bool = True) -> Vector: - - return self._override_gdf_output(self.ds.project(other=other.ds, normalized=normalized, align=align)) + # Method that exists in GeoPandasBase but not exposed in GeoSeries yet + # @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + # def relate(self, other: gu.Vector, align: bool=True) -> Vector: + # + # return self._override_gdf_output(self.ds.relate(other=other.ds, align=align)) + # + # Method that exists in GeoPandasBase but not exposed in GeoSeries yet + # @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + # def project(self, other: gu.Vector, normalized: bool = False, align: bool = True) -> Vector: + # + # return self._override_gdf_output(self.ds.project(other=other.ds.geometry, normalized=normalized, align=align)) # ----------------------------------------------- # GeoPandasBase - Methods that return a GeoSeries @@ -572,10 +574,11 @@ def skew(self, xs: float=0.0, ys: float=0.0, origin: str = "center", use_radians return self._override_gdf_output(self.ds.skew(xs=xs, ys=ys, origin=origin, use_radians=use_radians)) - @copy_doc(gpd.GeoSeries, "Vector") - def interpolate(self, distance: float, normalized: bool=False) -> Vector: - - return self._override_gdf_output(self.ds.interpolate(distance=distance, normalized=normalized)) + # Method that exists in GeoPandasBase but not exposed in GeoSeries yet + # @copy_doc(gpd.GeoSeries, "Vector") + # def interpolate(self, distance: float, normalized: bool=False) -> Vector: + # + # return self._override_gdf_output(self.ds.interpolate(distance=distance, normalized=normalized)) # ---------------------------------------------- diff --git a/tests/test_vector.py b/tests/test_vector.py index b1e448092..91a49ed6b 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -6,6 +6,7 @@ import inspect import tempfile from tempfile import NamedTemporaryFile +import warnings import geopandas as gpd import geopandas.base @@ -92,7 +93,7 @@ def test_save(self) -> None: # Open and check the object is the same vector_save = gu.Vector(temp_file.name) - assert_geodataframe_equal(vector_save, vector) + vector_save.vector_equal(vector) def test_bounds(self) -> None: @@ -577,13 +578,13 @@ class TestGeoPandasMethods: # List of properties and methods with non-geometric output that are implemented in GeoUtils nongeo_properties = ["area", "length", "interiors", "geom_type", "is_empty", "is_ring", "is_simple"] nongeo_methods = ["contains", "geom_equals", "geom_almost_equals", "crosses", "disjoint", "intersects", - "overlaps", "touches", "within", "covers", "covered_by", "distance", "relate", "project"] + "overlaps", "touches", "within", "covers", "covered_by", "distance"] # List of properties and methods with geometric output that are implemented in GeoUtils geo_properties = ["boundary", "unary_union", "centroid", "convex_hull", "envelope", "exterior"] geo_methods = ["representative_point", "normalize", "make_valid", "difference", "symmetric_difference", "union", "intersection", "clip_by_rect", "buffer", "simplify", "affine_transform", "translate", "rotate", - "scale", "skew", "interpolate", "dissolve", "explode", "sjoin", "sjoin_nearest", "overlay"] + "scale", "skew", "dissolve", "explode", "sjoin", "sjoin_nearest", "overlay"] # List of other properties and methods other = ["has_sindex", "sindex"] @@ -651,6 +652,10 @@ def test_overriden_funcs_args(self, method: str) -> None: def test_nongeo_properties(self, vector: gu.Vector, method: str) -> None: """Check non-geometric properties are consistent with GeoPandas.""" + # Remove warnings about operations in a non-projected system, and future changes + warnings.simplefilter("ignore", category=UserWarning) + warnings.simplefilter("ignore", category=FutureWarning) + # Get method for each class output_geoutils = getattr(vector, method) output_geopandas = getattr(vector.ds, method) @@ -667,6 +672,10 @@ def test_nongeo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: st All these methods require two inputs ("other", "df", or "right" argument). """ + # Remove warnings about operations in a non-projected system, and future changes + warnings.simplefilter("ignore", category=UserWarning) + warnings.simplefilter("ignore", category=FutureWarning) + # Get method for each class output_geoutils = getattr(vector1, method)(vector2) output_geopandas = getattr(vector1.ds, method)(vector2.ds) @@ -679,6 +688,10 @@ def test_nongeo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: st def test_geo_properties(self, vector: gu.Vector, method: str) -> None: """Check geometric properties are consistent with GeoPandas.""" + # Remove warnings about operations in a non-projected system, and future changes + warnings.simplefilter("ignore", category=UserWarning) + warnings.simplefilter("ignore", category=FutureWarning) + # Get method for each class output_geoutils = getattr(vector, method) output_geopandas = getattr(vector.ds, method) @@ -698,9 +711,9 @@ def test_geo_properties(self, vector: gu.Vector, method: str) -> None: specific_method_args = {"buffer": {"distance": 1}, "clip_by_rect": {"xmin": 10.5, "ymin": 10.5, "xmax": 11, "ymax": 11}, - "affine_transform": {"matrix": [0, 0, 0, 0, 1, 1]}, "translate": {"xoff": 1, "yoff": 1, "zoff": 0}, + "affine_transform": {"matrix": [1, 1, 1, 1, 1, 1]}, "translate": {"xoff": 1, "yoff": 1, "zoff": 0}, "rotate": {"angle": 90}, "scale": {"xfact": 1.1, "yfact": 1.1, "zfact": 1.1, "origin": "center"}, - "skew": {"xs": 1.1, "ys": 1.1}, "interpolate": {"distance": 1}, "simplify": {"tolerance": 1}} + "skew": {"xs": 1.1, "ys": 1.1}, "interpolate": {"distance": 1}, "simplify": {"tolerance": 0.1}} @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore @pytest.mark.parametrize("vector2", [synthvec2, realvec2]) # type: ignore @@ -708,6 +721,10 @@ def test_geo_properties(self, vector: gu.Vector, method: str) -> None: def test_geo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) -> None: """Check geometric properties are consistent with GeoPandas.""" + # Remove warnings about operations in a non-projected system, and future changes + warnings.simplefilter("ignore", category=UserWarning) + warnings.simplefilter("ignore", category=FutureWarning) + # Methods that require two inputs if method in ["difference", "symmetric_difference", "union", "intersection", "sjoin", "sjoin_nearest", "overlay"]: output_geoutils = getattr(vector1, method)(vector2) @@ -726,10 +743,13 @@ def test_geo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) assert isinstance(output_geoutils, gu.Vector) assert isinstance(output_geopandas, (gpd.GeoSeries, gpd.GeoDataFrame)) - # Separate cases depending on GeoPandas' output - if isinstance(output_geopandas, gpd.GeoSeries): - # Assert geoseries equality + # Separate cases depending on GeoPandas' output, and nature of the function + # Simplify is a special case that can make geometries unvalid, so adjust test + if method == "simplify": + assert_geoseries_equal(output_geopandas.make_valid(), output_geoutils.ds.geometry.make_valid()) + # For geoseries output, check equality of it + elif isinstance(output_geopandas, gpd.GeoSeries): assert_geoseries_equal(output_geoutils.ds.geometry, output_geopandas) + # For geodataframe output, check equality else: - # Or assert geodataframe equality assert_geodataframe_equal(output_geoutils.ds, output_geopandas) \ No newline at end of file From f30fafac393221f70e464025eea2735b66baf059 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Mar 2023 23:01:04 -0700 Subject: [PATCH 126/184] Linting --- bin/geoviewer.py | 2 - doc/source/api.md | 6 +- doc/source/conf.py | 1 + geoutils/misc.py | 24 ++- geoutils/projtools.py | 7 +- geoutils/raster/multiraster.py | 4 +- geoutils/raster/raster.py | 143 +++++++--------- geoutils/raster/sampling.py | 2 +- geoutils/raster/satimg.py | 29 ++-- geoutils/vector.py | 290 ++++++++++++++++----------------- tests/test_geoviewer.py | 2 - tests/test_misc.py | 1 - tests/test_projtools.py | 2 - tests/test_raster.py | 23 +-- tests/test_satimg.py | 2 - tests/test_vector.py | 123 +++++++++----- 16 files changed, 320 insertions(+), 341 deletions(-) diff --git a/bin/geoviewer.py b/bin/geoviewer.py index f925d7c31..9a7ffc98d 100755 --- a/bin/geoviewer.py +++ b/bin/geoviewer.py @@ -18,7 +18,6 @@ def getparser() -> argparse.ArgumentParser: - # Set up description parser = argparse.ArgumentParser(description="Visualisation tool for any image supported by GDAL.") @@ -120,7 +119,6 @@ def getparser() -> argparse.ArgumentParser: def main(test_args: Sequence[str] = None) -> None: - # Parse arguments parser = getparser() args = parser.parse_args(test_args) # type: ignore diff --git a/doc/source/api.md b/doc/source/api.md index e74d6c622..3124faea7 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -381,7 +381,7 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r #### Non-geometric per-feature attributes and methods -This second category of attributes and methods return a non-geometric output with same length as the number of features. They are thus appended in the +This second category of attributes and methods return a non-geometric output with same length as the number of features. They are thus appended in the dataframe of the current {class}`~geoutils.Vector` by default, using as column name the name of the operation (e.g., "area", "contains" or "intersects"). Otherwise, calling the method from {attr}`Vector.ds`, they return a {class}`pandas.Series` as in GeoPandas. @@ -438,6 +438,6 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r ``` ```{seealso} -The methods above are described in [GeoPandas GeoSeries's API](https://geopandas.org/en/stable/docs/reference/geoseries.html) and [Shapely object's +The methods above are described in [GeoPandas GeoSeries's API](https://geopandas.org/en/stable/docs/reference/geoseries.html) and [Shapely object's documentation](https://shapely.readthedocs.io/en/stable/properties.html). -``` \ No newline at end of file +``` diff --git a/doc/source/conf.py b/doc/source/conf.py index b580c18f0..c92b33180 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -123,6 +123,7 @@ # "special-members": "__init__", # } + # To ignore warnings due to having myst-nb reading the .ipynb created by sphinx-gallery # Should eventually be fixed, see: https://github.com/executablebooks/MyST-NB/issues/363 def setup(app): diff --git a/geoutils/misc.py b/geoutils/misc.py index 34fe7ab28..50453755c 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -4,8 +4,7 @@ import copy import functools import warnings -from typing import Any, Union, Callable -from textwrap import dedent +from typing import Any, Callable try: import yaml # type: ignore @@ -20,7 +19,7 @@ import geoutils -def deprecate(removal_version: str = None, details: str = None): # type: ignore +def deprecate(removal_version: str | None = None, details: str | None = None): # type: ignore """ Trigger a DeprecationWarning for the decorated function. @@ -76,7 +75,13 @@ def new_func(*args, **kwargs): # type: ignore return deprecator_func -def copy_doc(old_class: object, new_class_name: str, origin_class: object = None, replace_return_series_statement: bool = False) -> Callable: + +def copy_doc( + old_class: object, + new_class_name: str, + origin_class: object | None = None, + replace_return_series_statement: bool = False, +) -> Callable: # type: ignore """ A decorator to copy docstring from a class to another class while replacing the docstring. ---------- @@ -84,8 +89,7 @@ def copy_doc(old_class: object, new_class_name: str, origin_class: object = None The classes used to reformat docstring template. """ - def decorator(decorated: Callable) -> Callable: - + def decorator(decorated: Callable) -> Callable: # type: ignore # Get name of decorated object # If object is a property, get name through fget try: @@ -107,7 +111,9 @@ def decorator(decorated: Callable) -> Callable: # Replace "Return a Series" statement by "Append a Series to Vector" if it exists if replace_return_series_statement: if replaced_descript[0:20] == "Returns a ``Series``": - replaced_descript = replaced_descript.replace("Returns a ``Series``", "Returns or appends to ``Vector`` a ``Series``") + replaced_descript = replaced_descript.replace( + "Returns a ``Series``", "Returns or appends to ``Vector`` a ``Series``" + ) else: replaced_descript += " Can be appended to ``Vector``." @@ -119,7 +125,9 @@ def decorator(decorated: Callable) -> Callable: # Get module and old class names orig_module_name = orig_class.__module__.split(".")[0] old_class_name = orig_class.__name__ - add_link_to_old_class = "\n\nSee more details at :func:`" + orig_module_name + "." + old_class_name + "." + decorated_name + "`." + add_link_to_old_class = ( + "\n\nSee more details at :func:`" + orig_module_name + "." + old_class_name + "." + decorated_name + "`." + ) decorated.__doc__ = replaced_descript + add_link_to_old_class diff --git a/geoutils/projtools.py b/geoutils/projtools.py index be152ed1a..d45dbbd46 100644 --- a/geoutils/projtools.py +++ b/geoutils/projtools.py @@ -65,8 +65,8 @@ def utm_to_epsg(utm: str) -> int: def bounds2poly( boundsGeom: list[float] | rio.io.DatasetReader, - in_crs: CRS = None, - out_crs: CRS = None, + in_crs: CRS | None = None, + out_crs: CRS | None = None, ) -> Polygon: """ Converts self's bounds into a shapely Polygon. Optionally, returns it into a different CRS. @@ -108,7 +108,7 @@ def merge_bounds( bounds_list: abc.Iterable[ list[float] | tuple[float] | rio.coords.BoundingBox | rio.io.DatasetReader | gpd.GeoDataFrame ], - resolution: float = None, + resolution: float | None = None, merging_algorithm: str = "union", return_rio_bbox: bool = False, ) -> tuple[float, ...] | rio.coords.BoundingBox: @@ -287,7 +287,6 @@ def compare_proj(proj1: CRS, proj2: CRS) -> bool: def _get_bounds_projected( bounds: rio.coords.BoundingBox, in_crs: CRS, out_crs: CRS, densify_pts: int = 5000 ) -> rio.coords.BoundingBox: - # Calculate new bounds left, bottom, right, top = bounds new_bounds = rio.warp.transform_bounds(in_crs, out_crs, left, bottom, right, top, densify_pts) diff --git a/geoutils/raster/multiraster.py b/geoutils/raster/multiraster.py index 62c8c0bce..385315a6c 100644 --- a/geoutils/raster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -16,7 +16,7 @@ def load_multiple_rasters( - raster_paths: list[str], crop: bool = True, ref_grid: int = None, **kwargs: Any + raster_paths: list[str], crop: bool = True, ref_grid: int | None = None, **kwargs: Any ) -> list[RasterType]: """ Function to load multiple rasters at once in a memory efficient way. @@ -80,7 +80,6 @@ def load_multiple_rasters( # Optionally, reproject all rasters to the reference grid if reproject: - ref_rst = output_rst[ref_grid] # Set output bounds - intersection if crop is True, otherwise use that of ref_grid @@ -169,7 +168,6 @@ def stack_rasters( data: list[np.ndarray] = [] for raster in tqdm(rasters, disable=not progress): - # Check that data is loaded, otherwise temporarily load it if not raster.is_loaded: raster.load() diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index e2ac852ff..369bc4405 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -169,10 +169,10 @@ def _default_nodata(dtype: str | np.dtype | type) -> int: def _load_rio( dataset: rio.io.DatasetReader, - indexes: int | tuple[int, ...] = None, + indexes: int | tuple[int, ...] | None = None, masked: bool = False, - transform: Affine = None, - shape: tuple[int, int] = None, + transform: Affine | None = None, + shape: tuple[int, int] | None = None, **kwargs: Any, ) -> np.ma.masked_array: r""" @@ -248,12 +248,12 @@ def __init__( | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], - indexes: int | list[int] = None, + indexes: int | list[int] | None = None, load_data: bool = False, downsample: AnyNumber = 1, masked: bool = True, - nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, - attrs: list[str] = None, + nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + attrs: list[str] | None = None, ) -> None: """ Instantiate a raster from a filename or rasterio dataset. @@ -275,22 +275,22 @@ def __init__( ['bounds', 'count', 'crs', 'driver', 'dtypes', 'height', 'indexes', 'name', 'nodata', 'res', 'shape', 'transform', 'width'] - if no attrs are specified, these will be added. """ - self._driver: str = None - self._name: str = None - self.filename: str = None + self._driver: str | None = None + self._name: str | None = None + self.filename: str | None = None self.tags: dict[str, Any] = {} - self._data: np.ma.masked_array = None + self._data: np.ma.masked_array | None = None self._nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = nodata self._indexes = indexes - self._indexes_loaded: int | tuple[int, ...] = None + self._indexes_loaded: int | tuple[int, ...] | None = None self._masked = masked - self._out_shape: tuple[int, int, int] = None - self._disk_hash: int = None + self._out_shape: tuple[int, int, int] | None = None + self._disk_hash: int | None = None self._is_modified = True - self._disk_shape: tuple[int, int, int] = None - self._disk_indexes: tuple[int] = None - self._disk_dtypes: tuple[str] = None + self._disk_shape: tuple[int, int, int] | None = None + self._disk_indexes: tuple[int] | None = None + self._disk_dtypes: tuple[str] | None = None # This is for Raster.from_array to work. if isinstance(filename_or_dataset, dict): @@ -312,7 +312,6 @@ def __init__( return # Image is a file on disk. elif isinstance(filename_or_dataset, (str, pathlib.Path, rio.io.DatasetReader, rio.io.MemoryFile)): - # ExitStack is used instead of "with rio.open(filename_or_dataset) as ds:". # This is because we might not actually want to open it like that, so this is equivalent # to the pseudocode: @@ -487,7 +486,7 @@ def driver(self) -> str | None: """Driver used to read a file on disk.""" return self._driver - def load(self, indexes: int | list[int] = None, **kwargs: Any) -> None: + def load(self, indexes: int | list[int] | None = None, **kwargs: Any) -> None: """ Load the raster array from disk. @@ -549,7 +548,7 @@ def from_array( data: np.ndarray | np.ma.masked_array, transform: tuple[float, ...] | Affine, crs: CRS | int | None, - nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, + nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, ) -> RasterType: """Create a raster from a numpy array and the georeferencing information. @@ -1244,13 +1243,11 @@ def set_nodata( # If we update mask or array, get the masked array if update_array or update_mask: - # Extract the data variable, so the self.data property doesn't have to be called a bunch of times imgdata = self.data # Loop through the bands for i, new_nodata in enumerate(nodata if isinstance(nodata, Iterable) else [nodata]): - # Get the index of old nodatas index_old_nodatas = imgdata.data[i, :, :] == self.nodata @@ -1485,7 +1482,7 @@ def info(self, stats: bool = False) -> str: return "".join(as_str) - def copy(self: RasterType, new_array: np.ndarray = None) -> RasterType: + def copy(self: RasterType, new_array: np.ndarray | None = None) -> RasterType: """ Copy the raster in-memory. @@ -1789,14 +1786,14 @@ def crop( def reproject( self: RasterType, - dst_ref: RasterType | str = None, - dst_crs: CRS | str | int = None, - dst_size: tuple[int, int] = None, - dst_bounds: dict[str, float] | rio.coords.BoundingBox = None, - dst_res: float | abc.Iterable[float] = None, - dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, - src_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, - dst_dtype: np.dtype = None, + dst_ref: RasterType | str | None = None, + dst_crs: CRS | str | int | None = None, + dst_size: tuple[int, int] | None = None, + dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, + dst_res: float | abc.Iterable[float] | None = None, + dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + src_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + dst_dtype: np.dtype | None = None, resampling: Resampling | str = Resampling.bilinear, silent: bool = False, n_threads: int = 0, @@ -1887,7 +1884,6 @@ def reproject( dst_transform = None # Case a raster is provided as reference if dst_ref is not None: - # Check that dst_ref type is either str, Raster or rasterio data set # Preferably use Raster instance to avoid rasterio data set to remain open. See PR #45 if isinstance(dst_ref, Raster): @@ -1930,7 +1926,6 @@ def reproject( # Let rasterio determine the maximum bounds of the new raster. reproj_kwargs.update({"dst_resolution": dst_res}) else: - # Bounds specified. First check if xres and yres are different. if isinstance(dst_res, tuple): xres = dst_res[0] @@ -1961,7 +1956,6 @@ def reproject( # If dst_bounds is set, will enforce dst_bounds if dst_bounds is not None: - if dst_size is None: # Calculate new raster size which ensures that pixels resolution is as close as possible to original # Raster size is increased by up to one pixel if needed @@ -2012,7 +2006,6 @@ def reproject( # If data is loaded, reproject the numpy array directly if self.is_loaded: - # All masked values must be set to a nodata value for rasterio's reproject to work properly # TODO: another option is to apply rio.warp.reproject to the mask to identify invalid pixels if src_nodata is None and np.sum(self.data.mask) > 0: @@ -2064,15 +2057,15 @@ def save( self, filename: str | pathlib.Path | IO[bytes], driver: str = "GTiff", - dtype: DTypeLike = None, - nodata: AnyNumber = None, + dtype: DTypeLike | None = None, + nodata: AnyNumber | None = None, compress: str = "deflate", tiled: bool = False, - blank_value: int | float = None, - co_opts: dict[str, str] = None, - metadata: dict[str, Any] = None, - gcps: list[tuple[float, ...]] = None, - gcps_crs: CRS = None, + blank_value: int | float | None = None, + co_opts: dict[str, str] | None = None, + metadata: dict[str, Any] | None = None, + gcps: list[tuple[float, ...]] | None = None, + gcps_crs: CRS | None = None, ) -> None: """ Write the raster to file. @@ -2123,7 +2116,6 @@ def save( # if masked array, save with masked values replaced by nodata if isinstance(save_data, np.ma.masked_array): - # In this case, nodata=None is not compatible, so revert to default values, only if masked values exist if (nodata is None) & (np.count_nonzero(save_data.mask) > 0): nodata = _default_nodata(save_data.dtype) @@ -2145,7 +2137,6 @@ def save( tiled=tiled, **co_opts, ) as dst: - dst.write(save_data) # Add metadata (tags in rio) @@ -2166,7 +2157,7 @@ def save( dst.gcps = (rio_gcps, gcps_crs) - def to_xarray(self, name: str = None) -> xr.DataArray: + def to_xarray(self, name: str | None = None) -> xr.DataArray: """ Convert raster to a xarray.DataArray. @@ -2247,14 +2238,14 @@ def intersection(self, rst: str | Raster, match_ref: bool = True) -> tuple[float def show( self, - index: int = None, - cmap: matplotlib.colors.Colormap | str = None, - vmin: float | int = None, - vmax: float | int = None, - alpha: float | int = None, - cbar_title: str = None, + index: int | None = None, + cmap: matplotlib.colors.Colormap | str | None = None, + vmin: float | int | None = None, + vmax: float | int | None = None, + alpha: float | int | None = None, + cbar_title: str | None = None, add_cbar: bool = True, - ax: matplotlib.axes.Axes | Literal["new"] = None, + ax: matplotlib.axes.Axes | Literal["new"] | None = None, return_axes: bool = False, **kwargs: Any, ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: @@ -2382,9 +2373,9 @@ def value_at_coords( x: float | ArrayLike, y: float | ArrayLike, latlon: bool = False, - index: int = None, + index: int | None = None, masked: bool = False, - window: int = None, + window: int | None = None, reducer_function: Callable[[np.ndarray], float] = np.ma.mean, return_window: bool = False, boundless: bool = True, @@ -2477,7 +2468,6 @@ def format_value(value: Any) -> Any: # Loop over all coordinates passed for k in range(len(rows)): # type: ignore - value: float | dict[int, float] | tuple[float | dict[int, float] | tuple[list[float], np.ndarray] | Any] row = rows[k] # type: ignore @@ -2591,7 +2581,7 @@ def xy2ij( x: ArrayLike, y: ArrayLike, op: type = np.float32, - precision: float = None, + precision: float | None = None, shift_area_or_point: bool = False, ) -> tuple[np.ndarray, np.ndarray]: """ @@ -2720,7 +2710,6 @@ def interp_points( shift_area_or_point: bool = False, **kwargs: Any, ) -> np.ndarray: - """ Interpolate raster values at a set of points. @@ -2768,7 +2757,7 @@ def interp_points( return rpts - def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int = None) -> list[Raster]: + def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int | None = None) -> list[Raster]: """ Split the bands into separate rasters. @@ -2911,7 +2900,6 @@ def polygonize( # mask a unique value set by a number if isinstance(target_values, Number): - if np.sum(self.data == target_values) == 0: raise ValueError(f"no pixel with in_value {target_values}") @@ -2919,7 +2907,6 @@ def polygonize( # mask values within boundaries set by a tuple elif isinstance(target_values, tuple): - if np.sum((self.data > target_values[0]) & (self.data < target_values[1])) == 0: raise ValueError(f"no pixel with in_value between {target_values[0]} and {target_values[1]}") @@ -2927,7 +2914,6 @@ def polygonize( # mask specific values set by a sequence elif isinstance(target_values, list) or isinstance(target_values, np.ndarray): - if np.sum(np.isin(self.data, target_values)) == 0: raise ValueError("no pixel with in_value " + ", ".join(map("{}".format, target_values))) @@ -2935,11 +2921,9 @@ def polygonize( # mask all valid values elif target_values == "all": - bool_msk = (~self.data.mask).astype("uint8") else: - raise ValueError("in_value must be a number, a tuple or a sequence") # GeoPandas.from_features() only supports certain dtypes, we find the best common dtype to optimize memory usage @@ -2970,8 +2954,8 @@ def polygonize( def proximity( self, - vector: Vector = None, - target_values: list[float] = None, + vector: Vector | None = None, + target_values: list[float] | None = None, geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", @@ -3012,7 +2996,7 @@ def subsample( self, subsample: int | float, return_indices: bool = False, - random_state: np.random.RandomState | np.random.Generator | int = None, + random_state: np.random.RandomState | np.random.Generator | int | None = None, ) -> np.ndarray: """ Randomly subsample the raster. Only valid values are considered. @@ -3052,7 +3036,6 @@ def __init__( filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile | dict[str, Any], **kwargs: Any, ) -> None: - # If a Mask is passed, simply point back to Mask if isinstance(filename_or_dataset, Mask): for key in filename_or_dataset.__dict__: @@ -3128,20 +3111,19 @@ def _repr_html_(self) -> str: def reproject( self: Mask, - dst_ref: RasterType | str = None, - dst_crs: CRS | str | int = None, - dst_size: tuple[int, int] = None, - dst_bounds: dict[str, float] | rio.coords.BoundingBox = None, - dst_res: float | abc.Iterable[float] = None, - dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, - src_nodata: int | float | tuple[int, ...] | tuple[float, ...] = None, - dst_dtype: np.dtype = None, + dst_ref: RasterType | str | None = None, + dst_crs: CRS | str | int | None = None, + dst_size: tuple[int, int] | None = None, + dst_bounds: dict[str, float] | rio.coords.BoundingBox | None = None, + dst_res: float | abc.Iterable[float] | None = None, + dst_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + src_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + dst_dtype: np.dtype | None = None, resampling: Resampling | str = Resampling.nearest, silent: bool = False, n_threads: int = 0, memory_limit: int = 64, ) -> Mask: - # Depending on resampling, adjust to rasterio supported types if resampling in [Resampling.nearest, "nearest"]: self._data = self.data.astype("uint8") @@ -3210,7 +3192,6 @@ def crop( mode: Literal["match_pixel"] | Literal["match_extent"] = "match_pixel", inplace: bool = True, ) -> Mask | None: - # If there is resampling involved during cropping, encapsulate type as in reproject() if mode == "match_extent": raise ValueError(NotImplementedError) @@ -3234,7 +3215,6 @@ def crop( def polygonize( self, target_values: Number | tuple[Number, Number] | list[Number] | np.ndarray | Literal["all"] = 1 ) -> Vector: - # If target values is passed but does not correspond to 0 or 1, raise a warning if not isinstance(target_values, (int, np.integer, float, np.floating)) or target_values not in [0, 1]: warnings.warn("In-value converted to 1 for polygonizing boolean mask.") @@ -3246,13 +3226,12 @@ def polygonize( def proximity( self, - vector: Vector = None, - target_values: list[float] = None, + vector: Vector | None = None, + target_values: list[float] | None = None, geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", ) -> Raster: - # By default, target True values of the mask if vector is None and target_values is None: target_values = [1] @@ -3332,8 +3311,8 @@ def __invert__(self: Mask) -> Mask: def proximity_from_vector_or_raster( raster: Raster, - vector: Vector = None, - target_values: list[float] = None, + vector: Vector | None = None, + target_values: list[float] | None = None, geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", distance_unit: Literal["pixel"] | Literal["georeferenced"] = "georeferenced", diff --git a/geoutils/raster/sampling.py b/geoutils/raster/sampling.py index 2db9942d6..74477f3bd 100644 --- a/geoutils/raster/sampling.py +++ b/geoutils/raster/sampling.py @@ -11,7 +11,7 @@ def subsample_array( array: np.ndarray | np.ma.masked_array, subsample: float | int, return_indices: bool = False, - random_state: np.random.RandomState | np.random.Generator | int = None, + random_state: np.random.RandomState | np.random.Generator | int | None = None, ) -> np.ndarray: """ Randomly subsample a 1D or 2D array by a subsampling factor, taking only non NaN/masked values. diff --git a/geoutils/raster/satimg.py b/geoutils/raster/satimg.py index 301d7766f..5aa916334 100644 --- a/geoutils/raster/satimg.py +++ b/geoutils/raster/satimg.py @@ -42,12 +42,10 @@ def parse_landsat(gname: str) -> list[Any]: def parse_metadata_from_fn(fname: str) -> list[Any]: - bname = os.path.splitext(os.path.basename(fname))[0] # assumes that the filename has a form XX_YY.ext if "_" in bname: - spl = bname.split("_") # attrs corresponds to: satellite, sensor, product, version, tile_name, datetime @@ -119,7 +117,7 @@ def parse_metadata_from_fn(fname: str) -> list[Any]: return list(attrs) -def parse_tile_attr_from_name(tile_name: str, product: str = None) -> tuple[float, float, tuple[int, int], int]: +def parse_tile_attr_from_name(tile_name: str, product: str | None = None) -> tuple[float, float, tuple[int, int], int]: """ Convert tile naming to metadata coordinates based on sensor and product by default the SRTMGL1 1x1° tile naming convention to lat, lon (originally SRTMGL1) @@ -150,7 +148,6 @@ def parse_tile_attr_from_name(tile_name: str, product: str = None) -> tuple[floa def sw_naming_to_latlon(tile_name: str) -> tuple[float, float]: - """ Get latitude and longitude corresponding to southwestern corner of tile naming (originally SRTMGL1 convention) @@ -250,27 +247,25 @@ def latlon_to_sw_naming( class SatelliteImage(Raster): # type: ignore - date: None | dt.datetime def __init__( self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile, - attrs: list[str] = None, + attrs: list[str] | None = None, load_data: bool = True, - indexes: int | list[int] = None, + indexes: int | list[int] | None = None, read_from_fn: bool = True, - datetime: dt.datetime = None, - tile_name: str = None, - satellite: str = None, - sensor: str = None, - product: str = None, - version: str = None, + datetime: dt.datetime | None = None, + tile_name: str | None = None, + satellite: str | None = None, + sensor: str | None = None, + product: str | None = None, + version: str | None = None, read_from_meta: bool = True, - fn_meta: str = None, + fn_meta: str | None = None, silent: bool = True, ) -> None: - """ Load satellite data through the Raster class and parse additional attributes from filename or metadata. @@ -322,7 +317,6 @@ def __init__( self.__get_date() def __get_date(self) -> dt.datetime | None: # type: ignore - """ Get date from datetime :return: @@ -333,7 +327,6 @@ def __get_date(self) -> dt.datetime | None: # type: ignore self.date = None def __parse_metadata_from_fn(self, silent: bool = False) -> None: - """ Attempts to pull metadata (e.g., sensor, date information) from fname, setting sensor, satellite, tile, datetime, and date attributes. @@ -438,7 +431,7 @@ def __parse_metadata_from_file(self, fn_meta: str | None) -> None: return None - def copy(self, new_array: np.ndarray = None) -> SatelliteImage: + def copy(self, new_array: np.ndarray | None = None) -> SatelliteImage: new_satimg = super().copy(new_array=new_array) # type: ignore # all objects here are immutable so no need for a copy method (string and datetime) # satimg_attrs = ['satellite', 'sensor', 'product', 'version', 'tile_name', 'datetime'] #taken outside of class diff --git a/geoutils/vector.py b/geoutils/vector.py index 27dece8d9..be2dba661 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -11,15 +11,15 @@ from typing import Any, Literal, TypeVar, overload import fiona -import pandas as pd import geopandas as gpd -from geopandas.testing import assert_geodataframe_equal import matplotlib import matplotlib.pyplot as plt import numpy as np +import pandas as pd import rasterio as rio import rasterio.errors import shapely +from geopandas.testing import assert_geodataframe_equal from mpl_toolkits.axes_grid1 import make_axes_locatable from rasterio import features, warp from rasterio.crs import CRS @@ -117,14 +117,6 @@ def __str__(self) -> str: return str(self.ds.__str__()) - - def __setattr__(self, attr, val): - # have to special case geometry b/c pandas tries to use as column... - if attr == "geometry": - object.__setattr__(self, attr, val) - else: - super().__setattr__(attr, val) - def info(self) -> str: """ Summarize information about the vector. @@ -144,14 +136,14 @@ def info(self) -> str: def show( self, - ref_crs: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | CRS | int = None, - cmap: matplotlib.colors.Colormap | str = None, - vmin: float | int = None, - vmax: float | int = None, - alpha: float | int = None, - cbar_title: str = None, + ref_crs: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | CRS | int | None = None, + cmap: matplotlib.colors.Colormap | str | None = None, + vmin: float | int | None = None, + vmax: float | int | None = None, + alpha: float | int | None = None, + cbar_title: str | None = None, add_cbar: bool = False, - ax: matplotlib.axes.Axes | Literal["new"] = None, + ax: matplotlib.axes.Axes | Literal["new"] | None = None, return_axes: bool = False, **kwargs: Any, ) -> None | tuple[matplotlib.axes.Axes, matplotlib.colors.Colormap]: @@ -254,11 +246,11 @@ def show( def save( self, - filename: str | pathlib.Path , - driver: str = None, - schema: dict = None, - index: bool = None, - **kwargs: Any + filename: str | pathlib.Path, + driver: str | None = None, + schema: dict[str, Any] | None = None, + index: bool | None = None, + **kwargs: Any, ) -> None: """ Write the vector to file. @@ -275,11 +267,13 @@ def save( self.ds.to_file(filename=filename, driver=driver, schema=schema, index=index, **kwargs) - ######################################################################################################################## - # Overriden and wrapped methods from GeoPandas and Pandas API to logically cast the output for minimalistic manipulation - ######################################################################################################################## - def _override_gdf_output(self, - other: gpd.GeoDataFrame | gpd.GeoSeries | shapely.Geometry | pd.Series | Any) -> Vector | pd.Series: + ############################################################################ + # Overridden and wrapped methods from GeoPandas API to logically cast outputs + ############################################################################ + + def _override_gdf_output( + self, other: gpd.GeoDataFrame | gpd.GeoSeries | shapely.Geometry | pd.Series | Any + ) -> Vector | pd.Series: """Parse outputs of GeoPandas functions to facilitate object manipulation.""" # Raise error if output is not treated separately, should appear in tests @@ -295,110 +289,104 @@ def _override_gdf_output(self, # If a Shapely Geometry is the output, re-encapsulate in a GeoDataFrame and return it elif isinstance(other, shapely.Geometry): return Vector(gpd.GeoDataFrame({"geometry": [other]}, crs=self.crs)) - # If a DataFrame is the output, append it to that of the GeoDataFrame - elif isinstance(other, pd.Series): + # If a Pandas Series is the output, append it to that of the GeoDataFrame + else: return other # ----------------------------------------------- # GeoPandasBase - Attributes that return a Series # ----------------------------------------------- - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def area(self) -> pd.Series: return self._override_gdf_output(self.ds.area) - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def length(self) -> pd.Series: return self._override_gdf_output(self.ds.length) - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def interiors(self) -> pd.Series: return self._override_gdf_output(self.ds.interiors) - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def geom_type(self) -> pd.Series: return self._override_gdf_output(self.ds.geom_type) # Exception ! bounds is renamed geom_bounds to make Raster and Vector "bounds" the same "total_bounds" @property - def geom_bounds(self): + def geom_bounds(self) -> pd.Series: """Returns or appends to ``Vector`` a ``Series`` with the bounds of each geometry feature.""" return self.ds.bounds - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def is_empty(self) -> pd.Series: return self._override_gdf_output(self.ds.is_empty) - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def is_ring(self) -> pd.Series: return self._override_gdf_output(self.ds.is_ring) - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def is_simple(self) -> pd.Series: return self._override_gdf_output(self.ds.is_simple) - @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property - def has_z(self) -> bool: + def has_z(self) -> pd.Series: return self.ds.has_z # -------------------------------------------------- # GeoPandasBase - Attributes that return a GeoSeries # -------------------------------------------------- - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def boundary(self) -> Vector: - return self._override_gdf_output(self.ds.boundary) - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def unary_union(self) -> Vector: - return self._override_gdf_output(self.ds.unary_union) - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def centroid(self) -> Vector: - return self._override_gdf_output(self.ds.centroid) - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def convex_hull(self) -> Vector: - return self._override_gdf_output(self.ds.convex_hull) - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def envelope(self) -> Vector: - return self._override_gdf_output(self.ds.envelope) - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def exterior(self) -> Vector: - return self._override_gdf_output(self.ds.exterior) # --------------------------------------------------------------------------------- # GeoPandasBase - Attributes that return a specific value (not Series or GeoSeries) # --------------------------------------------------------------------------------- - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def has_sindex(self) -> bool: return self.ds.has_sindex - @copy_doc(gpd.GeoSeries, "Vector") + @copy_doc(gpd.GeoSeries, "Vector") # type: ignore @property def sindex(self) -> bool: return self.ds.sindex @@ -419,68 +407,60 @@ def bounds(self) -> rio.coords.BoundingBox: # -------------------------------------------- @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def contains(self, other: gu.Vector, align: bool = True) -> Vector: - + def contains(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.contains(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def geom_equals(self, other: gu.Vector, align: bool = True) -> Vector: - + def geom_equals(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.geom_equals(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def geom_almost_equals(self, other: gu.Vector, decimal: int = 6, align: bool = True) -> Vector: - + def geom_almost_equals(self, other: gu.Vector, decimal: int = 6, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.geom_almost_equals(other=other.ds, decimal=decimal, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def geom_equals_exact(self, other: gu.Vector, tolerance: float, align: bool=True,) -> Vector: - + def geom_equals_exact( + self, + other: gu.Vector, + tolerance: float, + align: bool = True, + ) -> pd.Series: return self._override_gdf_output(self.ds.geom_equals_exact(other=other.ds, tolerance=tolerance, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def crosses(self, other: gu.Vector, align: bool=True) -> Vector: - + def crosses(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.crosses(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def disjoint(self, other: gu.Vector, align: bool=True) -> Vector: - + def disjoint(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.disjoint(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def intersects(self, other: gu.Vector, align: bool=True) -> Vector: - + def intersects(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.intersects(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def overlaps(self, other: gu.Vector, align: bool=True) -> Vector: - + def overlaps(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.overlaps(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def touches(self, other: gu.Vector, align: bool=True) -> Vector: - + def touches(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.touches(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def within(self, other: gu.Vector, align: bool=True) -> Vector: - + def within(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.within(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def covers(self, other: gu.Vector, align: bool=True) -> Vector: - + def covers(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.covers(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def covered_by(self, other: gu.Vector, align: bool=True) -> Vector: - + def covered_by(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.covered_by(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) - def distance(self, other: gu.Vector, align: bool=True) -> Vector: - + def distance(self, other: gu.Vector, align: bool = True) -> pd.Series: return self._override_gdf_output(self.ds.distance(other=other.ds, align=align)) # Method that exists in GeoPandasBase but not exposed in GeoSeries yet @@ -501,77 +481,62 @@ def distance(self, other: gu.Vector, align: bool=True) -> Vector: @copy_doc(gpd.GeoSeries, "Vector") def representative_point(self) -> Vector: - return self._override_gdf_output(self.ds.representative_point()) @copy_doc(gpd.GeoSeries, "Vector") def normalize(self) -> Vector: - return self._override_gdf_output(self.ds.normalize()) @copy_doc(gpd.GeoSeries, "Vector") def make_valid(self) -> Vector: - return self._override_gdf_output(self.ds.make_valid()) @copy_doc(gpd.GeoSeries, "Vector") - def difference(self, other: gu.Vector, align: bool=True) -> Vector: - + def difference(self, other: gu.Vector, align: bool = True) -> Vector: return self._override_gdf_output(self.ds.difference(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector") - def symmetric_difference(self, other: gu.Vector, align: bool=True) -> Vector: - + def symmetric_difference(self, other: gu.Vector, align: bool = True) -> Vector: return self._override_gdf_output(self.ds.symmetric_difference(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector") - def union(self, other: gu.Vector, align: bool=True) -> Vector: - + def union(self, other: gu.Vector, align: bool = True) -> Vector: return self._override_gdf_output(self.ds.union(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector") - def intersection(self, other: gu.Vector, align: bool=True) -> Vector: - + def intersection(self, other: gu.Vector, align: bool = True) -> Vector: return self._override_gdf_output(self.ds.intersection(other=other.ds, align=align)) @copy_doc(gpd.GeoSeries, "Vector") def clip_by_rect(self, xmin: float, ymin: float, xmax: float, ymax: float) -> Vector: - return self._override_gdf_output(self.ds.clip_by_rect(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax)) @copy_doc(gpd.GeoSeries, "Vector") - def buffer(self, distance: float, resolution: int=16, **kwargs) -> Vector: - + def buffer(self, distance: float, resolution: int = 16, **kwargs: Any) -> Vector: return self._override_gdf_output(self.ds.buffer(distance=distance, resolution=resolution, **kwargs)) @copy_doc(gpd.GeoSeries, "Vector") def simplify(self, *args: Any, **kwargs: Any) -> Vector: - return self._override_gdf_output(self.ds.simplify(*args, **kwargs)) @copy_doc(gpd.GeoSeries, "Vector") def affine_transform(self, matrix: tuple[float, ...]) -> Vector: - return self._override_gdf_output(self.ds.affine_transform(matrix=matrix)) @copy_doc(gpd.GeoSeries, "Vector") def translate(self, xoff: float = 0.0, yoff: float = 0.0, zoff: float = 0.0) -> Vector: - return self._override_gdf_output(self.ds.translate(xoff=xoff, yoff=yoff, zoff=zoff)) @copy_doc(gpd.GeoSeries, "Vector") def rotate(self, angle: float, origin: str = "center", use_radians: bool = False) -> Vector: - return self._override_gdf_output(self.ds.rotate(angle=angle, origin=origin, use_radians=use_radians)) @copy_doc(gpd.GeoSeries, "Vector") def scale(self, xfact: float = 1.0, yfact: float = 1.0, zfact: float = 1.0, origin: str = "center") -> Vector: - return self._override_gdf_output(self.ds.scale(xfact=xfact, yfact=yfact, zfact=zfact, origin=origin)) @copy_doc(gpd.GeoSeries, "Vector") - def skew(self, xs: float=0.0, ys: float=0.0, origin: str = "center", use_radians: bool =False) -> Vector: - + def skew(self, xs: float = 0.0, ys: float = 0.0, origin: str = "center", use_radians: bool = False) -> Vector: return self._override_gdf_output(self.ds.skew(xs=xs, ys=ys, origin=origin, use_radians=use_radians)) # Method that exists in GeoPandasBase but not exposed in GeoSeries yet @@ -580,7 +545,6 @@ def skew(self, xs: float=0.0, ys: float=0.0, origin: str = "center", use_radians # # return self._override_gdf_output(self.ds.interpolate(distance=distance, normalized=normalized)) - # ---------------------------------------------- # GeoDataFrame - Methods that return a GeoSeries # ---------------------------------------------- @@ -600,22 +564,35 @@ def __getitem__(self, key: gu.Raster | Vector | list[float] | tuple[float, ...] @copy_doc(gpd.GeoDataFrame, "Vector") def __setitem__(self, key: Any, value: Any) -> None: - self.ds.__setitem__(key, value) @copy_doc(gpd.GeoDataFrame, "Vector") - def dissolve(self, by: Any=None, aggfunc: Any = "first", as_index: bool=True, level: Any=None, sort: bool=True, observed: bool=False, dropna: bool=True) -> Vector: - - return self._override_gdf_output(self.ds.dissolve(by=by, aggfunc=aggfunc, as_index=as_index, level=level, sort=sort, observed=observed, dropna=dropna)) + def dissolve( + self, + by: Any = None, + aggfunc: Any = "first", + as_index: bool = True, + level: Any = None, + sort: bool = True, + observed: bool = False, + dropna: bool = True, + ) -> Vector: + return self._override_gdf_output( + self.ds.dissolve( + by=by, aggfunc=aggfunc, as_index=as_index, level=level, sort=sort, observed=observed, dropna=dropna + ) + ) @copy_doc(gpd.GeoDataFrame, "Vector") - def explode(self, column: str = None, ignore_index: bool = False, index_parts: bool = None, **kwargs: Any) -> Vector: - - return self._override_gdf_output(self.ds.explode(column=column, ignore_index=ignore_index, index_parts=index_parts, **kwargs)) + def explode( + self, column: str | None = None, ignore_index: bool = False, index_parts: bool | None = None, **kwargs: Any + ) -> Vector: + return self._override_gdf_output( + self.ds.explode(column=column, ignore_index=ignore_index, index_parts=index_parts, **kwargs) + ) @copy_doc(gpd.GeoDataFrame, "Vector") def sjoin(self, df: Vector | gpd.GeoDataFrame, *args: Any, **kwargs: Any) -> Vector: - # Ensure input is a geodataframe if isinstance(df, gu.Vector): gdf = df.ds @@ -625,28 +602,49 @@ def sjoin(self, df: Vector | gpd.GeoDataFrame, *args: Any, **kwargs: Any) -> Vec return self._override_gdf_output(self.ds.sjoin(df=gdf, *args, **kwargs)) @copy_doc(gpd.GeoDataFrame, "Vector") - def sjoin_nearest(self, right: Vector | gpd.GeoDataFrame, how: str = "inner", max_distance: float = None, lsuffix: str = "left", rsuffix: str = "right", - distance_col: str = None) -> Vector: - + def sjoin_nearest( + self, + right: Vector | gpd.GeoDataFrame, + how: str = "inner", + max_distance: float | None = None, + lsuffix: str = "left", + rsuffix: str = "right", + distance_col: str | None = None, + ) -> Vector: # Ensure input is a geodataframe if isinstance(right, gu.Vector): gdf = right.ds else: gdf = right - return self._override_gdf_output(self.ds.sjoin_nearest(right=gdf, how=how, max_distance=max_distance, lsuffix=lsuffix, rsuffix=rsuffix, - distance_col=distance_col)) + return self._override_gdf_output( + self.ds.sjoin_nearest( + right=gdf, + how=how, + max_distance=max_distance, + lsuffix=lsuffix, + rsuffix=rsuffix, + distance_col=distance_col, + ) + ) @copy_doc(gpd.GeoDataFrame, "Vector") - def overlay(self, right: Vector | gpd.GeoDataFrame, how: str = "intersection", keep_geom_type: bool = None, make_valid: bool = True) -> Vector: - + def overlay( + self, + right: Vector | gpd.GeoDataFrame, + how: str = "intersection", + keep_geom_type: bool | None = None, + make_valid: bool = True, + ) -> Vector: # Ensure input is a geodataframe if isinstance(right, gu.Vector): gdf = right.ds else: gdf = right - return self._override_gdf_output(self.ds.overlay(right=gdf, how=how, keep_geom_type=keep_geom_type, make_valid=make_valid)) + return self._override_gdf_output( + self.ds.overlay(right=gdf, how=how, keep_geom_type=keep_geom_type, make_valid=make_valid) + ) # -------------------------------- # End of GeoPandas functionalities @@ -685,12 +683,10 @@ def name(self) -> str | None: @property def geometry(self) -> gpd.GeoSeries: - return self.ds.geometry @property - def index(self): - + def index(self) -> pd.Index: return self.ds.index def copy(self: VectorType) -> VectorType: @@ -775,8 +771,8 @@ def crop( def reproject( self: Vector, - dst_ref: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str = None, - dst_crs: CRS | str | int = None, + dst_ref: gu.Raster | rio.io.DatasetReader | VectorType | gpd.GeoDataFrame | str | None = None, + dst_crs: CRS | str | int | None = None, ) -> Vector: """ Reproject vector to a specified coordinate reference system. @@ -801,7 +797,6 @@ def reproject( # Case a raster or vector is provided as reference if dst_ref is not None: - # Check that dst_ref type is either str, Raster or rasterio data set # Preferably use Raster instance to avoid rasterio data set to remain open. See PR #45 if isinstance(dst_ref, (gu.Raster, gu.Vector)): @@ -832,11 +827,11 @@ def reproject( @overload def create_mask( self, - rst: str | gu.Raster = None, - crs: CRS = None, - xres: float = None, - yres: float = None, - bounds: tuple[float, float, float, float] = None, + rst: str | gu.Raster | None = None, + crs: CRS | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, buffer: int | float | np.number = 0, *, as_array: Literal[False] = False, @@ -846,11 +841,11 @@ def create_mask( @overload def create_mask( self, - rst: str | gu.Raster = None, - crs: CRS = None, - xres: float = None, - yres: float = None, - bounds: tuple[float, float, float, float] = None, + rst: str | gu.Raster | None = None, + crs: CRS | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, buffer: int | float | np.number = 0, *, as_array: Literal[True], @@ -859,11 +854,11 @@ def create_mask( def create_mask( self, - rst: gu.Raster = None, - crs: CRS = None, - xres: float = None, - yres: float = None, - bounds: tuple[float, float, float, float] = None, + rst: gu.Raster | None = None, + crs: CRS | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, buffer: int | float | np.number = 0, as_array: bool = False, ) -> gu.Mask | np.ndarray: @@ -891,7 +886,6 @@ def create_mask( # If no rst given, use provided dimensions if rst is None: - # At minimum, xres must be set if xres is None: raise ValueError("At least rst or xres must be set.") @@ -969,12 +963,12 @@ def create_mask( def rasterize( self, - rst: gu.Raster = None, - crs: CRS | int = None, - xres: float = None, - yres: float = None, - bounds: tuple[float, float, float, float] = None, - in_value: int | float | abc.Iterable[int | float] = None, + rst: gu.Raster | None = None, + crs: CRS | int | None = None, + xres: float | None = None, + yres: float | None = None, + bounds: tuple[float, float, float, float] | None = None, + in_value: int | float | abc.Iterable[int | float] | None = None, out_value: int | float = 0, ) -> gu.Raster | gu.Mask: """ @@ -1017,7 +1011,6 @@ def rasterize( # If no rst given, now use provided dimensions if rst is None: - # At minimum, xres must be set if xres is None: raise ValueError("At least rst or xres must be set.") @@ -1084,7 +1077,7 @@ def rasterize( @classmethod def from_bounds_projected( - cls, raster_or_vector: gu.Raster | VectorType, out_crs: CRS = None, densify_pts: int = 5000 + cls, raster_or_vector: gu.Raster | VectorType, out_crs: CRS | None = None, densify_pts: int = 5000 ) -> VectorType: """Create a vector polygon from projected bounds of a raster or vector. @@ -1123,7 +1116,7 @@ def query(self: Vector, expression: str, inplace: bool = False) -> Vector | None def proximity( self, - raster: gu.Raster = None, + raster: gu.Raster | None = None, grid_size: tuple[int, int] = (1000, 1000), geometry_type: str = "boundary", in_or_out: Literal["in"] | Literal["out"] | Literal["both"] = "both", @@ -1154,7 +1147,6 @@ def proximity( # 0/ If no Raster is passed, create one on the Vector bounds of size 1000 x 1000 if raster is None: - # TODO: this bit of code is common in several vector functions (rasterize, etc): move out as common code? # By default, use self's bounds if self.bounds is None: diff --git a/tests/test_geoviewer.py b/tests/test_geoviewer.py index 8ca1dd186..9ef2d7eb5 100644 --- a/tests/test_geoviewer.py +++ b/tests/test_geoviewer.py @@ -35,7 +35,6 @@ ), ) # type: ignore def test_geoviewer_valid(capsys, monkeypatch, filename, option): # type: ignore - # To avoid having the plots popping up during execution monkeypatch.setattr(plt, "show", lambda: None) @@ -72,7 +71,6 @@ def test_geoviewer_valid(capsys, monkeypatch, filename, option): # type: ignore ), ) # type: ignore def test_geoviewer_invalid(capsys, monkeypatch, filename, option): # type: ignore - # To avoid having the plots popping up during execution monkeypatch.setattr(plt, "show", lambda: None) diff --git a/tests/test_misc.py b/tests/test_misc.py index afb146466..cccb6280a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -105,7 +105,6 @@ def useless_func() -> int: useless_func() def test_diff_environment_yml(self, capsys) -> None: # type: ignore - # Test with synthetic environment env = {"dependencies": ["python==3.9", "numpy", "fiona"]} devenv = {"dependencies": ["python==3.9", "numpy", "fiona", "opencv"]} diff --git a/tests/test_projtools.py b/tests/test_projtools.py index 7572a0706..3e4c9904a 100644 --- a/tests/test_projtools.py +++ b/tests/test_projtools.py @@ -11,14 +11,12 @@ class TestProjTools: - landsat_b4_path = examples.get_path("everest_landsat_b4") landsat_b4_crop_path = examples.get_path("everest_landsat_b4_cropped") landsat_rgb_path = examples.get_path("everest_landsat_rgb") aster_dem_path = examples.get_path("exploradores_aster_dem") def test_latlon_to_utm(self) -> None: - # First: Check errors are raised when format is invalid # If format is invalid diff --git a/tests/test_raster.py b/tests/test_raster.py index e54ce6101..f3ca6d155 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -65,7 +65,6 @@ def run_gdal_proximity( class TestRaster: - landsat_b4_path = examples.get_path("everest_landsat_b4") landsat_b4_crop_path = examples.get_path("everest_landsat_b4_cropped") landsat_rgb_path = examples.get_path("everest_landsat_rgb") @@ -2108,7 +2107,6 @@ def test_show_cbar(self, example, figsize) -> None: assert h == pytest.approx(h_cbar) def test_show(self) -> None: - # Read single band raster and RGB raster img = gu.Raster(self.landsat_b4_path) img_RGB = gu.Raster(self.landsat_rgb_path) @@ -2153,7 +2151,6 @@ def test_show(self) -> None: @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_save(self, example: str) -> None: - # Read single band raster img = gu.Raster(example) @@ -2216,7 +2213,6 @@ def test_save(self, example: str) -> None: @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) # type: ignore def test_coords(self, example: str) -> None: - img = gu.Raster(self.landsat_b4_path) # With corner argument @@ -2252,7 +2248,6 @@ def test_coords(self, example: str) -> None: assert np.array_equal(yygrid, np.flipud(np.repeat(yy0[:, np.newaxis], img.width, axis=1))) def test_from_array(self) -> None: - # Test that from_array works if nothing is changed # -> most tests already performed in test_copy, no need for more img = gu.Raster(self.landsat_b4_path) @@ -2318,7 +2313,6 @@ def test_type_hints(self) -> None: assert bad_lint not in lint_string, f"`{bad_lint}` contained in the lint_string" def test_split_bands(self) -> None: - img = gu.Raster(self.landsat_rgb_path) red, green, blue = img.split_bands(copy=False) @@ -2466,7 +2460,6 @@ def test_proximity_against_gdal(self, distunits: str, target_values: list[float] # It looks like GDAL might not have the right value, # so this particular case is treated differently in tests if target_values is not None and target_values[0] == 112 and raster.filename is not None: - # Get index and number of not almost equal point (tolerance of 10-4) ind_not_almost_equal = np.abs(gdal_proximity - geoutils_proximity) > 1e-04 nb_not_almost_equal = np.count_nonzero(ind_not_almost_equal) @@ -2485,7 +2478,6 @@ def test_proximity_against_gdal(self, distunits: str, target_values: list[float] # For debugging except Exception as exception: - import matplotlib.pyplot as plt # Plotting the xdem and GDAL attributes for comparison (plotting "diff" can also help debug) @@ -2725,7 +2717,6 @@ def test_implicit_logical_casting_real(self, example: str) -> None: @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_reproject(self, mask: gu.Mask) -> None: - # Test 1: with a classic resampling (bilinear) # Reproject mask @@ -2752,7 +2743,6 @@ def test_reproject(self, mask: gu.Mask) -> None: @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_crop(self, mask: gu.Mask) -> None: - # Test with same bounds -> should be the same # crop_geom = mask.bounds mask_cropped = mask.crop(crop_geom, inplace=False) @@ -2814,7 +2804,6 @@ def test_crop(self, mask: gu.Mask) -> None: @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_polygonize(self, mask: gu.Mask) -> None: - # Run default vect = mask.polygonize() @@ -2831,7 +2820,6 @@ def test_polygonize(self, mask: gu.Mask) -> None: @pytest.mark.parametrize("mask", [mask_landsat_b4, mask_aster_dem]) # type: ignore def test_proximity(self, mask: gu.Mask) -> None: - # Run default rast = mask.proximity() @@ -3169,7 +3157,7 @@ def from_array( cls: type[TestArithmetic], data: np.ndarray | np.ma.masked_array, rst_ref: gu.RasterType, - nodata: int | float | list[int] | list[float] = None, + nodata: int | float | list[int] | list[float] | None = None, ) -> gu.Raster: """ Generate a Raster from numpy array, with set georeferencing. Used for testing only. @@ -3330,7 +3318,6 @@ def test_ops_logical_implicit(self) -> None: assert (r1 >= floatval).raster_equal(self.from_array(r1.data >= floatval, rst_ref=r1)) def test_ops_logical_bitwise_implicit(self) -> None: - # Create two masks r1 = self.r1 m1 = self.r1 > 128 @@ -3380,7 +3367,6 @@ def test_raise_errors(self, op: str) -> None: @pytest.mark.parametrize("power", [2, 3.14, -1]) # type: ignore def test_power(self, power: float | int) -> None: - if power > 0: # Integers to negative integer powers are not allowed. assert self.r1**power == self.from_array(self.r1.data**power, rst_ref=self.r1) assert self.r1_f32**power == self.from_array(self.r1_f32.data**power, rst_ref=self.r1_f32) @@ -3540,12 +3526,10 @@ def test_array_ufunc_1nin_1nout(self, ufunc_str: str, nodata_init: None | str, d # Catch warnings with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=RuntimeWarning) # Check if our input dtype is possible on this ufunc, if yes check that outputs are identical if com_dtype in (str(np.dtype(t[0])) for t in ufunc.types): - # For a single output if ufunc.nout == 1: assert np.ma.allequal(ufunc(rst.data), ufunc(rst).data) @@ -3612,16 +3596,13 @@ def test_array_ufunc_2nin_1nout( # Catch warnings with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=RuntimeWarning) warnings.filterwarnings("ignore", category=UserWarning) # Check if both our input dtypes are possible on this ufunc, if yes check that outputs are identical if com_dtype_tuple in ((str(np.dtype(t[0])), str(np.dtype(t[1]))) for t in ufunc.types): - # For a single output if ufunc.nout == 1: - # There exists a single exception due to negative integers as exponent of integers in "power" if ufunc_str == "power" and "int" in dtype1 and "int" in dtype2 and np.min(rst2.data) < 0: with pytest.raises(ValueError, match="Integers to negative integer powers are not allowed."): @@ -3676,7 +3657,6 @@ def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: N # Catch warnings with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=RuntimeWarning) # Pass an argument for functions that require it (nanpercentile, percentile, quantile and nanquantile) and @@ -3754,7 +3734,6 @@ def test_array_functions_2nin( # Catch warnings with warnings.catch_warnings(): - warnings.filterwarnings("ignore", category=RuntimeWarning) # Compute outputs diff --git a/tests/test_satimg.py b/tests/test_satimg.py index ca779aa77..53b34c1f3 100644 --- a/tests/test_satimg.py +++ b/tests/test_satimg.py @@ -17,7 +17,6 @@ class TestSatelliteImage: - landsat_b4 = examples.get_path("everest_landsat_b4") aster_dem = examples.get_path("exploradores_aster_dem") @@ -201,7 +200,6 @@ def test_filename_parsing(self) -> None: assert datetimes[i] == attrs[5] def test_sw_tile_naming_parsing(self) -> None: - # normal examples test_tiles = ["N14W065", "S14E065", "N014W065", "W065N014", "W065N14", "N00E000"] test_latlon = [(14, -65), (-14, 65), (14, -65), (14, -65), (14, -65), (0, 0)] diff --git a/tests/test_vector.py b/tests/test_vector.py index 91a49ed6b..3a2c987a9 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -1,12 +1,12 @@ from __future__ import annotations +import inspect import os.path import pathlib import re -import inspect import tempfile -from tempfile import NamedTemporaryFile import warnings +from tempfile import NamedTemporaryFile import geopandas as gpd import geopandas.base @@ -15,12 +15,12 @@ import pytest import shapely from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal +from pandas.testing import assert_series_equal from scipy.ndimage import binary_erosion from shapely.geometry.linestring import LineString from shapely.geometry.multilinestring import MultiLineString from shapely.geometry.multipolygon import MultiPolygon from shapely.geometry.polygon import Polygon -from pandas.testing import assert_series_equal import geoutils as gu @@ -28,7 +28,6 @@ class TestVector: - landsat_b4_crop_path = gu.examples.get_path("everest_landsat_b4_cropped") everest_outlines_path = gu.examples.get_path("everest_rgi_outlines") aster_dem_path = gu.examples.get_path("exploradores_aster_dem") @@ -60,7 +59,6 @@ def test_init(self) -> None: gu.Vector(1) # type: ignore def test_copy(self) -> None: - vector2 = self.glacier_outlines.copy() assert vector2 is not self.glacier_outlines @@ -70,7 +68,6 @@ def test_copy(self) -> None: assert vector2.ds.shape[0] < self.glacier_outlines.ds.shape[0] def test_query(self) -> None: - vector2 = self.glacier_outlines.query("NAME == 'Ayerbreen'") assert vector2 is not self.glacier_outlines @@ -95,9 +92,7 @@ def test_save(self) -> None: vector_save = gu.Vector(temp_file.name) vector_save.vector_equal(vector) - def test_bounds(self) -> None: - bounds = self.glacier_outlines.bounds assert bounds.left < bounds.right @@ -151,7 +146,6 @@ def test_reproject(self) -> None: v0.reproject(dst_ref=10) # type: ignore def test_rasterize_proj(self) -> None: - # Capture the warning on resolution not matching exactly bounds with pytest.warns(UserWarning): burned = self.glacier_outlines.rasterize(xres=3000) @@ -196,7 +190,6 @@ def test_rasterize_unproj(self) -> None: @pytest.mark.parametrize("data", test_data) # type: ignore def test_crop(self, data: list[str]) -> None: - # Load data raster_path, outlines_path = data rst = gu.Raster(raster_path) @@ -275,7 +268,6 @@ def test_proximity(self) -> None: class TestSynthetic: - # Create a synthetic vector file with a square of size 1, started at position (10, 10) poly1 = Polygon([(10, 10), (11, 10), (11, 11), (10, 11)]) gdf = gpd.GeoDataFrame({"geometry": [poly1]}, crs="EPSG:4326") @@ -559,7 +551,6 @@ def test_buffer_without_overlap(self, monkeypatch) -> None: # type: ignore class TestGeoPandasMethods: - # Use two synthetic vectors poly = Polygon([(10, 10), (11, 10), (11, 11), (10, 11)]) gdf1 = gpd.GeoDataFrame({"geometry": [poly]}, crs="EPSG:4326") @@ -577,14 +568,45 @@ class TestGeoPandasMethods: # Properties and methods derived from Shapely or GeoPandas # List of properties and methods with non-geometric output that are implemented in GeoUtils nongeo_properties = ["area", "length", "interiors", "geom_type", "is_empty", "is_ring", "is_simple"] - nongeo_methods = ["contains", "geom_equals", "geom_almost_equals", "crosses", "disjoint", "intersects", - "overlaps", "touches", "within", "covers", "covered_by", "distance"] + nongeo_methods = [ + "contains", + "geom_equals", + "geom_almost_equals", + "crosses", + "disjoint", + "intersects", + "overlaps", + "touches", + "within", + "covers", + "covered_by", + "distance", + ] # List of properties and methods with geometric output that are implemented in GeoUtils geo_properties = ["boundary", "unary_union", "centroid", "convex_hull", "envelope", "exterior"] - geo_methods = ["representative_point", "normalize", "make_valid", "difference", "symmetric_difference", "union", - "intersection", "clip_by_rect", "buffer", "simplify", "affine_transform", "translate", "rotate", - "scale", "skew", "dissolve", "explode", "sjoin", "sjoin_nearest", "overlay"] + geo_methods = [ + "representative_point", + "normalize", + "make_valid", + "difference", + "symmetric_difference", + "union", + "intersection", + "clip_by_rect", + "buffer", + "simplify", + "affine_transform", + "translate", + "rotate", + "scale", + "skew", + "dissolve", + "explode", + "sjoin", + "sjoin_nearest", + "overlay", + ] # List of other properties and methods other = ["has_sindex", "sindex"] @@ -593,14 +615,15 @@ class TestGeoPandasMethods: # Get all GeoPandasBase public methods with some exceptions geobase_methods = gpd.base.GeoPandasBase.__dict__.copy() # exceptions_geobase = ["plot", "explore", "equals"] - # geobase_methods = {key: geobase_methods[key] for key in geobase_methods if key[0] != "_" and key not in exceptions_geobase} + # geobase_methods = {key: geobase_methods[key] for key in geobase_methods if key[0] != "_" + # and key not in exceptions_geobase} # Get all GeoDataFrame public methods with some exceptions gdf_methods = gpd.GeoDataFrame.__dict__.copy() # exceptions_gdf = ["total_bounds"] # gdf_methods = {key: gdf_methods[key] for key in gdf_methods if key[0] != "_" and key not in exceptions_gdf} - def test_overriden_funcs_exist(self) -> None: + def test_overridden_funcs_exist(self) -> None: """Check that all methods listed above exist in Vector.""" # Check that all methods declared in the class above exist in Vector @@ -609,7 +632,7 @@ def test_overriden_funcs_exist(self) -> None: assert all(method in vector_methods.keys() for method in self.all_declared) def test_geopandas_coverage(self) -> None: - """Check that all existing methods of GeoPandas are overriden, with a couple exceptions.""" + """Check that all existing methods of GeoPandas are overridden, with a couple exceptions.""" # Merge the two all_methods = self.geobase_methods.copy() @@ -620,12 +643,12 @@ def test_geopandas_coverage(self) -> None: try: assert len(list_missing) == 0 except AssertionError: - print('Missing methods from GeoPandas:') + print("Missing methods from GeoPandas:") print(list_missing) @pytest.mark.parametrize("method", nongeo_methods + geo_methods) # type: ignore - def test_overriden_funcs_args(self, method: str) -> None: - """Check that all methods overriden have the same arguments as in GeoPandas.""" + def test_overridden_funcs_args(self, method: str) -> None: + """Check that all methods overridden have the same arguments as in GeoPandas.""" # Get GeoPandas class where the methods live if method in self.geobase_methods.keys(): @@ -647,8 +670,8 @@ def test_overriden_funcs_args(self, method: str) -> None: # Check that default argument values are the same assert argspec_upstream.defaults == argspec_geoutils.defaults - @pytest.mark.parametrize("vector", [synthvec1, synthvec2, realvec1, realvec2]) # type: ignore - @pytest.mark.parametrize("method", nongeo_properties) # type: ignore + @pytest.mark.parametrize("vector", [synthvec1, synthvec2, realvec1, realvec2]) # type: ignore + @pytest.mark.parametrize("method", nongeo_properties) # type: ignore def test_nongeo_properties(self, vector: gu.Vector, method: str) -> None: """Check non-geometric properties are consistent with GeoPandas.""" @@ -663,9 +686,9 @@ def test_nongeo_properties(self, vector: gu.Vector, method: str) -> None: # Assert equality assert_series_equal(output_geoutils, output_geopandas) - @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore - @pytest.mark.parametrize("vector2", [synthvec2, realvec2]) # type: ignore - @pytest.mark.parametrize("method", nongeo_methods) # type: ignore + @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore + @pytest.mark.parametrize("vector2", [synthvec2, realvec2]) # type: ignore + @pytest.mark.parametrize("method", nongeo_methods) # type: ignore def test_nongeo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) -> None: """ Check non-geometric methods are consistent with GeoPandas. @@ -705,19 +728,27 @@ def test_geo_properties(self, vector: gu.Vector, method: str) -> None: # Assert geoseries equality assert_geoseries_equal(output_geoutils.ds.geometry, output_geopandas) elif isinstance(output_geopandas, shapely.Geometry): - assert_geodataframe_equal(output_geoutils.ds, gpd.GeoDataFrame({"geometry": [output_geopandas]}, crs=vector.crs)) + assert_geodataframe_equal( + output_geoutils.ds, gpd.GeoDataFrame({"geometry": [output_geopandas]}, crs=vector.crs) + ) else: assert_geodataframe_equal(output_geoutils.ds, output_geopandas) - - specific_method_args = {"buffer": {"distance": 1}, "clip_by_rect": {"xmin": 10.5, "ymin": 10.5, "xmax": 11, "ymax": 11}, - "affine_transform": {"matrix": [1, 1, 1, 1, 1, 1]}, "translate": {"xoff": 1, "yoff": 1, "zoff": 0}, - "rotate": {"angle": 90}, "scale": {"xfact": 1.1, "yfact": 1.1, "zfact": 1.1, "origin": "center"}, - "skew": {"xs": 1.1, "ys": 1.1}, "interpolate": {"distance": 1}, "simplify": {"tolerance": 0.1}} - - @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore - @pytest.mark.parametrize("vector2", [synthvec2, realvec2]) # type: ignore - @pytest.mark.parametrize("method", geo_methods) # type: ignore + specific_method_args = { + "buffer": {"distance": 1}, + "clip_by_rect": {"xmin": 10.5, "ymin": 10.5, "xmax": 11, "ymax": 11}, + "affine_transform": {"matrix": [1, 1, 1, 1, 1, 1]}, + "translate": {"xoff": 1, "yoff": 1, "zoff": 0}, + "rotate": {"angle": 90}, + "scale": {"xfact": 1.1, "yfact": 1.1, "zfact": 1.1, "origin": "center"}, + "skew": {"xs": 1.1, "ys": 1.1}, + "interpolate": {"distance": 1}, + "simplify": {"tolerance": 0.1}, + } + + @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore + @pytest.mark.parametrize("vector2", [synthvec2, realvec2]) # type: ignore + @pytest.mark.parametrize("method", geo_methods) # type: ignore def test_geo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) -> None: """Check geometric properties are consistent with GeoPandas.""" @@ -726,7 +757,15 @@ def test_geo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) warnings.simplefilter("ignore", category=FutureWarning) # Methods that require two inputs - if method in ["difference", "symmetric_difference", "union", "intersection", "sjoin", "sjoin_nearest", "overlay"]: + if method in [ + "difference", + "symmetric_difference", + "union", + "intersection", + "sjoin", + "sjoin_nearest", + "overlay", + ]: output_geoutils = getattr(vector1, method)(vector2) output_geopandas = getattr(vector1.ds, method)(vector2.ds) # Methods that require zero input @@ -737,14 +776,14 @@ def test_geo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) output_geoutils = getattr(vector1, method)(**self.specific_method_args[method]) output_geopandas = getattr(vector1.ds, method)(**self.specific_method_args[method]) else: - raise ValueError("The method '{}' is not covered by this test.".format(method)) + raise ValueError(f"The method '{method}' is not covered by this test.") # Assert output types assert isinstance(output_geoutils, gu.Vector) assert isinstance(output_geopandas, (gpd.GeoSeries, gpd.GeoDataFrame)) # Separate cases depending on GeoPandas' output, and nature of the function - # Simplify is a special case that can make geometries unvalid, so adjust test + # Simplify is a special case that can make geometries invalid, so adjust test if method == "simplify": assert_geoseries_equal(output_geopandas.make_valid(), output_geoutils.ds.geometry.make_valid()) # For geoseries output, check equality of it @@ -752,4 +791,4 @@ def test_geo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) assert_geoseries_equal(output_geoutils.ds.geometry, output_geopandas) # For geodataframe output, check equality else: - assert_geodataframe_equal(output_geoutils.ds, output_geopandas) \ No newline at end of file + assert_geodataframe_equal(output_geoutils.ds, output_geopandas) From 605a3cee7bdaa0ffdf40fe40426963ab5cdcc4a9 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Mar 2023 23:08:49 -0700 Subject: [PATCH 127/184] Fix misc description --- geoutils/misc.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/geoutils/misc.py b/geoutils/misc.py index 50453755c..dabbea74d 100644 --- a/geoutils/misc.py +++ b/geoutils/misc.py @@ -84,9 +84,7 @@ def copy_doc( ) -> Callable: # type: ignore """ A decorator to copy docstring from a class to another class while replacing the docstring. - ---------- - **params - The classes used to reformat docstring template. + Use for parsing GeoPandas' documentation to geoutils.Vector. """ def decorator(decorated: Callable) -> Callable: # type: ignore From db1d77a3ccaa7abd3859ab57a61fbfce2ccea884 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 20 Mar 2023 23:09:14 -0700 Subject: [PATCH 128/184] Update pre-commit --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc0f3c23b..8362c44d6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,19 +44,19 @@ repos: files: ^(geoutils|tests) # Lint the code using mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.982 + rev: v1.1.1 hooks: - id: mypy args: [ --strict, - --implicit-optional, --ignore-missing-imports, # Don't warn about stubs since pre-commit runs in a limited env --allow-untyped-calls, # Dynamic function/method calls are okay. Untyped function definitions are not okay. --show-error-codes, --no-warn-unused-ignores, # Ignore 'type: ignore' comments that are not used. --disable-error-code=attr-defined, # "Module has no attribute 'XXX'" occurs because of the pre-commit env. - --disable-error-code=name-defined # "Name 'XXX' is not defined" occurs because of the pre-commit env. - + --disable-error-code=name-defined, # "Name 'XXX' is not defined" occurs because of the pre-commit env. + --disable-error-code=var-annotated, + --disable-error-code=no-any-return ] additional_dependencies: [tokenize-rt==3.2.0] files: ^(geoutils|tests) From 48d975816bf6d5b90c2085225c945d6a0b46db5c Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Tue, 21 Mar 2023 15:46:50 -0700 Subject: [PATCH 129/184] Remove not implemented geopandas functions --- doc/source/api.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index 3124faea7..731a04f70 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -422,8 +422,6 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r Vector.covers Vector.covered_by Vector.distance - Vector.relate - Vector.project ``` #### Other attributes and methods From 67956935503eeaf34486d7340f18e6aaacdf0fca Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 22 Mar 2023 15:05:27 -0700 Subject: [PATCH 130/184] Wrap I/O geopandas method and fix tests --- doc/source/api.md | 34 ++++++++ geoutils/vector.py | 200 +++++++++++++++++++++++++++++++++++++++---- tests/test_vector.py | 62 ++++++++++---- 3 files changed, 260 insertions(+), 36 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index 731a04f70..14ad5cdec 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -377,6 +377,12 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r Vector.sjoin Vector.sjoin_nearest Vector.overlay + Vector.clip + Vector.to_crs + Vector.set_crs + Vector.set_geometry + Vector.rename_geometry + Vector.cx ``` #### Non-geometric per-feature attributes and methods @@ -397,6 +403,7 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r Vector.interiors Vector.geom_type Vector.geom_bounds + Vector.is_valid Vector.is_empty Vector.is_ring Vector.is_simple @@ -424,6 +431,33 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r Vector.distance ``` + +#### I/O, conversions and others + +```{important} +The behaviour of methods below is not modified in {class}`~geoutils.Vector`, as they deal with outputs of different types. +To ensure those are up-to-date with GeoPandas, alternatively call those from {attr}`Vector.ds`. +``` + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + + Vector.from_file + Vector.from_features + Vector.from_postgis + Vector.from_dict + Vector.to_file + Vector.to_feather + Vector.to_parquet + Vector.to_wkt + Vector.to_wkb + Vector.to_json + Vector.to_postgis + Vector.to_csv +``` + + #### Other attributes and methods diff --git a/geoutils/vector.py b/geoutils/vector.py index be2dba661..d09548bd6 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -8,7 +8,8 @@ import warnings from collections import abc from numbers import Number -from typing import Any, Literal, TypeVar, overload +from typing import Any, Literal, TypeVar, overload, Generator, Iterable, Sequence, Hashable +from os import PathLike import fiona import geopandas as gpd @@ -16,6 +17,7 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd +from pandas._typing import WriteBuffer, ReadBuffer import rasterio as rio import rasterio.errors import shapely @@ -338,6 +340,11 @@ def is_ring(self) -> pd.Series: def is_simple(self) -> pd.Series: return self._override_gdf_output(self.ds.is_simple) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore + @property + def is_valid(self) -> pd.Series: + return self._override_gdf_output(self.ds.is_valid) + @copy_doc(gpd.GeoSeries, "Vector", replace_return_series_statement=True) # type: ignore @property def has_z(self) -> pd.Series: @@ -549,23 +556,6 @@ def skew(self, xs: float = 0.0, ys: float = 0.0, origin: str = "center", use_rad # GeoDataFrame - Methods that return a GeoSeries # ---------------------------------------------- - def __getitem__(self, key: gu.Raster | Vector | list[float] | tuple[float, ...] | Any) -> Vector: - """ - Index the geodataframe or crop the vector. - - If a raster, vector or tuple is passed, crops to its bounds. - Otherwise, indexes the geodataframe. - """ - - if isinstance(key, (gu.Raster, Vector)): - return self.crop(crop_geom=key, clip=False, inplace=False) - else: - return self._override_gdf_output(self.ds.__getitem__(key)) - - @copy_doc(gpd.GeoDataFrame, "Vector") - def __setitem__(self, key: Any, value: Any) -> None: - self.ds.__setitem__(key, value) - @copy_doc(gpd.GeoDataFrame, "Vector") def dissolve( self, @@ -591,6 +581,10 @@ def explode( self.ds.explode(column=column, ignore_index=ignore_index, index_parts=index_parts, **kwargs) ) + @copy_doc(gpd.GeoDataFrame, "Vector") + def clip(self, mask: Any, keep_geom_type: bool = False) -> Vector: + return self._override_gdf_output(self.ds.clip(mask=mask, keep_geom_type=keep_geom_type)) + @copy_doc(gpd.GeoDataFrame, "Vector") def sjoin(self, df: Vector | gpd.GeoDataFrame, *args: Any, **kwargs: Any) -> Vector: # Ensure input is a geodataframe @@ -646,6 +640,176 @@ def overlay( self.ds.overlay(right=gdf, how=how, keep_geom_type=keep_geom_type, make_valid=make_valid) ) + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_crs(self, crs: CRS | None = None, epsg: int | None = None, inplace: bool = False) -> Vector | None: + + if inplace: + self.ds = self.ds.to_crs(crs=crs, epsg=epsg) + else: + return self._override_gdf_output(self.ds.to_crs(crs=crs, epsg=epsg)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def set_crs(self, crs: CRS | None = None, epsg: int | None = None, inplace: bool = False, allow_override: bool = False) -> Vector | None: + + if inplace: + self.ds = self.ds.set_crs(crs=crs, epsg=epsg, allow_override=allow_override) + else: + return self._override_gdf_output(self.ds.set_crs(crs=crs, epsg=epsg, allow_override=allow_override)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def set_geometry(self, col: str, drop: bool = False, inplace: bool = False, crs: CRS = None) -> Vector | None: + + if inplace: + self.ds = self.ds.set_geometry(col=col, drop=drop, crs=crs) + else: + return self._override_gdf_output(self.ds.set_geometry(col=col, drop=drop, crs=crs)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def rename_geometry(self, col: str, inplace: bool = False) -> Vector | None: + + if inplace: + self.ds = self.ds.set_geometry(col=col) + else: + return self._override_gdf_output(self.ds.rename_geometry(col=col)) + + # ----------------------------------- + # GeoDataFrame: other functionalities + # ----------------------------------- + + def __getitem__(self, key: gu.Raster | Vector | list[float] | tuple[float, ...] | Any) -> Vector: + """ + Index the geodataframe or crop the vector. + + If a raster, vector or tuple is passed, crops to its bounds. + Otherwise, indexes the geodataframe. + """ + + if isinstance(key, (gu.Raster, Vector)): + return self.crop(crop_geom=key, clip=False, inplace=False) + else: + return self._override_gdf_output(self.ds.__getitem__(key)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def __setitem__(self, key: Any, value: Any) -> None: + self.ds.__setitem__(key, value) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def cx(self) -> Vector: + return self._override_gdf_output(self.ds.cx) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def estimate_utm_crs(self, datum_name: str = "WGS 84") -> CRS: + + return self.ds.estimate_utm_crs(datum_name=datum_name) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def iterfeatures(self, na: str | None = "null", show_bbox: bool = False, drop_id: bool = False) -> Generator[dict[str, str | dict | None | dict], Any, Any]: + + return self.ds.iterfeatures(na=na, show_bbox=show_bbox, drop_id=drop_id) + + @classmethod + @copy_doc(gpd.GeoDataFrame, "Vector") + def from_file(cls, filename: str, **kwargs: Any) -> Vector: + + return cls(gpd.GeoDataFrame.from_file(filename=filename, **kwargs)) + + @classmethod + @copy_doc(gpd.GeoDataFrame, "Vector") + def from_features(cls, features: Iterable[dict[str, Any]], crs: CRS, columns: list[str]) -> Vector: + + return cls(gpd.GeoDataFrame.from_features(features=features, crs=crs, columns=columns)) + + @classmethod + @copy_doc(gpd.GeoDataFrame, "Vector") + def from_postgis(cls, sql: str, con: Any, geom_col: str = "geom", crs: CRS | None = None, index_col: str | None = None, + coerce_float: bool = True, parse_dates: Any = None, params: Any = None, chunksize: Any = None) -> Vector: + + return cls(gpd.GeoDataFrame.from_postgis(sql=sql, con=con, geom_col=geom_col, crs=crs, index_col=index_col, coerce_float=coerce_float, + parse_dates=parse_dates, params=params, chunksize=chunksize)) + + @classmethod + @copy_doc(gpd.GeoDataFrame, "Vector") + def from_dict(cls, data: dict[str, Any], geometry: Any = None, crs: CRS | None = None, **kwargs: Any) -> Vector: + + return cls(gpd.GeoDataFrame.from_dict(data=data, geometry=geometry, crs=crs, **kwargs)) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_file(self, filename: str, + driver: Any = None, + schema: Any = None, + index: Any = None, + **kwargs: Any) -> None: + + return self.ds.to_file(filename=filename, driver=driver, schema=schema, index=index, **kwargs) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_feather(self, path: Any, index: Any = None, compression: Any = None, schema_version: Any = None, **kwargs: Any) -> None: + + return self.ds.to_feather(path=path, index=index, compression=compression, schema_version=schema_version, **kwargs) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_parquet(self, path: Any, index: Any = None, compression: Any = "snappy", schema_version: Any = None, **kwargs: Any) -> None: + + return self.ds.to_parquet(path=path, index=index, compression=compression, schema_version=schema_version, **kwargs) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_wkt(self, **kwargs: Any) -> pd.DataFrame: + + return self.ds.to_wkt(**kwargs) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_wkb(self, hex: bool = False, **kwargs: Any) -> pd.DataFrame: + + return self.ds.to_wkb(hex=hex, **kwargs) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_json(self, na: Any = "null", show_bbox: bool =False, drop_id: bool = False, **kwargs: Any) -> str | None: + + return self.ds.to_json(na=na, show_bbox=show_bbox, drop_id=drop_id, **kwargs) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_postgis(self, name: str, + con: Any, + schema: Any = None, + if_exists: Any = "fail", + index: Any = False, + index_label: Any = None, + chunksize: Any = None, + dtype: Any = None) -> None: + + return self.ds.to_postgis(name=name, con=con, schema=schema, if_exists=if_exists, index=index, + index_label=index_label, chunksize=chunksize, dtype=dtype) + + @copy_doc(gpd.GeoDataFrame, "Vector") + def to_csv(self, + path_or_buf: str | PathLike[str] | WriteBuffer[bytes] | WriteBuffer[str] | None = None, + sep: str = ",", + na_rep: str = "", + float_format: Any = None, + columns: Sequence[Hashable] | None = None, + header: bool | list[str] = True, + index: bool = True, + index_label: Hashable | Sequence[Hashable] | None = None, + mode: str = "w", + encoding: str | None = None, + compression: Literal["infer", "gzip", "bz2", "zip", "xz", "zstd", "tar"] | dict[str, Any] | None = "infer", + quoting: int | None = None, + quotechar: str = '\"', + lineterminator: str | None = None, + chunksize: int | None = None, + date_format: str | None = None, + doublequote: bool = True, + escapechar: str | None = None, + decimal: str = ".", + errors: str = "strict", + storage_options: dict[str, Any] | None = None + ) -> str | None: + + return self.ds.to_csv(path_or_buf=path_or_buf, sep=sep, na_rep=na_rep, float_format=float_format, columns=columns, header=header, + index=index, index_label=index_label, mode=mode, encoding=encoding, compression=compression, quoting=quoting, + quotechar=quotechar, lineterminator=lineterminator, chunksize=chunksize, date_format=date_format, + doublequote=doublequote, escapechar=escapechar, decimal=decimal, errors=errors, storage_options=storage_options) + # -------------------------------- # End of GeoPandas functionalities # -------------------------------- diff --git a/tests/test_vector.py b/tests/test_vector.py index 3a2c987a9..98f44a950 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -12,6 +12,7 @@ import geopandas.base import matplotlib.pyplot as plt import numpy as np +import pyproj import pytest import shapely from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal @@ -567,11 +568,13 @@ class TestGeoPandasMethods: # Properties and methods derived from Shapely or GeoPandas # List of properties and methods with non-geometric output that are implemented in GeoUtils - nongeo_properties = ["area", "length", "interiors", "geom_type", "is_empty", "is_ring", "is_simple"] + main_properties = ["crs", "geometry", "total_bounds"] + nongeo_properties = ["area", "length", "interiors", "geom_type", "is_empty", "is_ring", "is_simple", "is_valid", "has_z"] nongeo_methods = [ "contains", "geom_equals", "geom_almost_equals", + "geom_equals_exact", "crosses", "disjoint", "intersects", @@ -606,22 +609,31 @@ class TestGeoPandasMethods: "sjoin", "sjoin_nearest", "overlay", + "to_crs", + "set_crs", + "rename_geometry", + "set_geometry", + "clip" ] + # List of class methods + io_methods = ["from_file", "from_postgis", "from_dict", "from_features", "to_feather", + "to_parquet", "to_file", "to_postgis", "to_json", "to_wkb", "to_wkt", "to_csv"] # List of other properties and methods - other = ["has_sindex", "sindex"] - all_declared = nongeo_methods + nongeo_properties + geo_methods + geo_properties + other + other = ["has_sindex", "sindex", "estimate_utm_crs", "cx", "iterfeatures"] + all_declared = main_properties + nongeo_methods + nongeo_properties + geo_methods + geo_properties + other + io_methods + + # Exceptions for GeoPandasBase functions not implemented (or deprecrated) in GeoSeries/GeoDataFrame + exceptions_unimplemented = ["plot", "explore", "cascaded_union", "bounds", "relate", "project", "interpolate", "equals", "type", "convert_dtypes", + "merge", "apply", "astype"] + # Exceptions for IO/conversion that can be done directly from .ds + all_exceptions = exceptions_unimplemented # Get all GeoPandasBase public methods with some exceptions geobase_methods = gpd.base.GeoPandasBase.__dict__.copy() - # exceptions_geobase = ["plot", "explore", "equals"] - # geobase_methods = {key: geobase_methods[key] for key in geobase_methods if key[0] != "_" - # and key not in exceptions_geobase} # Get all GeoDataFrame public methods with some exceptions gdf_methods = gpd.GeoDataFrame.__dict__.copy() - # exceptions_gdf = ["total_bounds"] - # gdf_methods = {key: gdf_methods[key] for key in gdf_methods if key[0] != "_" and key not in exceptions_gdf} def test_overridden_funcs_exist(self) -> None: """Check that all methods listed above exist in Vector.""" @@ -629,7 +641,9 @@ def test_overridden_funcs_exist(self) -> None: # Check that all methods declared in the class above exist in Vector vector_methods = gu.Vector.__dict__ - assert all(method in vector_methods.keys() for method in self.all_declared) + list_missing = [method for method in self.all_declared if method not in vector_methods.keys()] + + assert len(list_missing) == 0, print("Test method listed that is not in GeoUtils: {}".format(list_missing)) def test_geopandas_coverage(self) -> None: """Check that all existing methods of GeoPandas are overridden, with a couple exceptions.""" @@ -638,13 +652,16 @@ def test_geopandas_coverage(self) -> None: all_methods = self.geobase_methods.copy() all_methods.update(self.gdf_methods) + # Remove exceptions we don't want to reuse from GeoPandas (mirrored in Vector) + name_all_methods = list(all_methods.keys()) + public_methods = [method for method in name_all_methods if method[0] != "_"] + + covered_methods = [method for method in public_methods if method not in self.all_exceptions] + # Check that all methods declared in the class above are covered in Vector - list_missing = [method for method in all_methods.keys() if method not in self.all_declared] - try: - assert len(list_missing) == 0 - except AssertionError: - print("Missing methods from GeoPandas:") - print(list_missing) + list_missing = [method for method in covered_methods if method not in self.all_declared] + + assert len(list_missing) == 0, print("Missing methods from GeoPandas: {}".format(list_missing)) @pytest.mark.parametrize("method", nongeo_methods + geo_methods) # type: ignore def test_overridden_funcs_args(self, method: str) -> None: @@ -692,7 +709,7 @@ def test_nongeo_properties(self, vector: gu.Vector, method: str) -> None: def test_nongeo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: str) -> None: """ Check non-geometric methods are consistent with GeoPandas. - All these methods require two inputs ("other", "df", or "right" argument). + All these methods require two inputs ("other", "df", or "right" argument), except one. """ # Remove warnings about operations in a non-projected system, and future changes @@ -700,8 +717,12 @@ def test_nongeo_methods(self, vector1: gu.Vector, vector2: gu.Vector, method: st warnings.simplefilter("ignore", category=FutureWarning) # Get method for each class - output_geoutils = getattr(vector1, method)(vector2) - output_geopandas = getattr(vector1.ds, method)(vector2.ds) + if method != "geom_equals_exact": + output_geoutils = getattr(vector1, method)(vector2) + output_geopandas = getattr(vector1.ds, method)(vector2.ds) + else: + output_geoutils = getattr(vector1, method)(vector2, tolerance=0.1) + output_geopandas = getattr(vector1.ds, method)(vector2.ds, tolerance=0.1) # Assert equality assert_series_equal(output_geoutils, output_geopandas) @@ -744,6 +765,11 @@ def test_geo_properties(self, vector: gu.Vector, method: str) -> None: "skew": {"xs": 1.1, "ys": 1.1}, "interpolate": {"distance": 1}, "simplify": {"tolerance": 0.1}, + "to_crs": {"crs": pyproj.CRS.from_epsg(32610)}, + "set_crs": {"crs": pyproj.CRS.from_epsg(32610), "allow_override": True}, + "rename_geometry": {"col": "lol"}, + "set_geometry": {"col": synthvec1.geometry}, + "clip": {"mask": poly} } @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore From 244bfe52a371b3d257873eaee1f22c109b558ca7 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 22 Mar 2023 15:08:04 -0700 Subject: [PATCH 131/184] Linting --- doc/source/api.md | 2 +- geoutils/vector.py | 149 ++++++++++++++++++++++++++++++++----------- tests/test_vector.py | 57 ++++++++++++++--- 3 files changed, 159 insertions(+), 49 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index 14ad5cdec..b46a132ec 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -435,7 +435,7 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r #### I/O, conversions and others ```{important} -The behaviour of methods below is not modified in {class}`~geoutils.Vector`, as they deal with outputs of different types. +The behaviour of methods below is not modified in {class}`~geoutils.Vector`, as they deal with outputs of different types. To ensure those are up-to-date with GeoPandas, alternatively call those from {attr}`Vector.ds`. ``` diff --git a/geoutils/vector.py b/geoutils/vector.py index d09548bd6..c18249d06 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -8,8 +8,17 @@ import warnings from collections import abc from numbers import Number -from typing import Any, Literal, TypeVar, overload, Generator, Iterable, Sequence, Hashable from os import PathLike +from typing import ( + Any, + Generator, + Hashable, + Iterable, + Literal, + Sequence, + TypeVar, + overload, +) import fiona import geopandas as gpd @@ -17,12 +26,12 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd -from pandas._typing import WriteBuffer, ReadBuffer import rasterio as rio import rasterio.errors import shapely from geopandas.testing import assert_geodataframe_equal from mpl_toolkits.axes_grid1 import make_axes_locatable +from pandas._typing import WriteBuffer from rasterio import features, warp from rasterio.crs import CRS from scipy.spatial import Voronoi @@ -645,14 +654,18 @@ def to_crs(self, crs: CRS | None = None, epsg: int | None = None, inplace: bool if inplace: self.ds = self.ds.to_crs(crs=crs, epsg=epsg) + return None else: return self._override_gdf_output(self.ds.to_crs(crs=crs, epsg=epsg)) @copy_doc(gpd.GeoDataFrame, "Vector") - def set_crs(self, crs: CRS | None = None, epsg: int | None = None, inplace: bool = False, allow_override: bool = False) -> Vector | None: + def set_crs( + self, crs: CRS | None = None, epsg: int | None = None, inplace: bool = False, allow_override: bool = False + ) -> Vector | None: if inplace: self.ds = self.ds.set_crs(crs=crs, epsg=epsg, allow_override=allow_override) + return None else: return self._override_gdf_output(self.ds.set_crs(crs=crs, epsg=epsg, allow_override=allow_override)) @@ -661,6 +674,7 @@ def set_geometry(self, col: str, drop: bool = False, inplace: bool = False, crs: if inplace: self.ds = self.ds.set_geometry(col=col, drop=drop, crs=crs) + return None else: return self._override_gdf_output(self.ds.set_geometry(col=col, drop=drop, crs=crs)) @@ -669,6 +683,7 @@ def rename_geometry(self, col: str, inplace: bool = False) -> Vector | None: if inplace: self.ds = self.ds.set_geometry(col=col) + return None else: return self._override_gdf_output(self.ds.rename_geometry(col=col)) @@ -703,7 +718,9 @@ def estimate_utm_crs(self, datum_name: str = "WGS 84") -> CRS: return self.ds.estimate_utm_crs(datum_name=datum_name) @copy_doc(gpd.GeoDataFrame, "Vector") - def iterfeatures(self, na: str | None = "null", show_bbox: bool = False, drop_id: bool = False) -> Generator[dict[str, str | dict | None | dict], Any, Any]: + def iterfeatures( + self, na: str | None = "null", show_bbox: bool = False, drop_id: bool = False + ) -> Generator[dict[str, str | dict[str, Any] | None | dict[str, Any]], Any, Any]: return self.ds.iterfeatures(na=na, show_bbox=show_bbox, drop_id=drop_id) @@ -721,11 +738,32 @@ def from_features(cls, features: Iterable[dict[str, Any]], crs: CRS, columns: li @classmethod @copy_doc(gpd.GeoDataFrame, "Vector") - def from_postgis(cls, sql: str, con: Any, geom_col: str = "geom", crs: CRS | None = None, index_col: str | None = None, - coerce_float: bool = True, parse_dates: Any = None, params: Any = None, chunksize: Any = None) -> Vector: + def from_postgis( + cls, + sql: str, + con: Any, + geom_col: str = "geom", + crs: CRS | None = None, + index_col: str | None = None, + coerce_float: bool = True, + parse_dates: Any = None, + params: Any = None, + chunksize: Any = None, + ) -> Vector: - return cls(gpd.GeoDataFrame.from_postgis(sql=sql, con=con, geom_col=geom_col, crs=crs, index_col=index_col, coerce_float=coerce_float, - parse_dates=parse_dates, params=params, chunksize=chunksize)) + return cls( + gpd.GeoDataFrame.from_postgis( + sql=sql, + con=con, + geom_col=geom_col, + crs=crs, + index_col=index_col, + coerce_float=coerce_float, + parse_dates=parse_dates, + params=params, + chunksize=chunksize, + ) + ) @classmethod @copy_doc(gpd.GeoDataFrame, "Vector") @@ -734,23 +772,27 @@ def from_dict(cls, data: dict[str, Any], geometry: Any = None, crs: CRS | None = return cls(gpd.GeoDataFrame.from_dict(data=data, geometry=geometry, crs=crs, **kwargs)) @copy_doc(gpd.GeoDataFrame, "Vector") - def to_file(self, filename: str, - driver: Any = None, - schema: Any = None, - index: Any = None, - **kwargs: Any) -> None: + def to_file(self, filename: str, driver: Any = None, schema: Any = None, index: Any = None, **kwargs: Any) -> None: return self.ds.to_file(filename=filename, driver=driver, schema=schema, index=index, **kwargs) @copy_doc(gpd.GeoDataFrame, "Vector") - def to_feather(self, path: Any, index: Any = None, compression: Any = None, schema_version: Any = None, **kwargs: Any) -> None: + def to_feather( + self, path: Any, index: Any = None, compression: Any = None, schema_version: Any = None, **kwargs: Any + ) -> None: - return self.ds.to_feather(path=path, index=index, compression=compression, schema_version=schema_version, **kwargs) + return self.ds.to_feather( + path=path, index=index, compression=compression, schema_version=schema_version, **kwargs + ) @copy_doc(gpd.GeoDataFrame, "Vector") - def to_parquet(self, path: Any, index: Any = None, compression: Any = "snappy", schema_version: Any = None, **kwargs: Any) -> None: + def to_parquet( + self, path: Any, index: Any = None, compression: Any = "snappy", schema_version: Any = None, **kwargs: Any + ) -> None: - return self.ds.to_parquet(path=path, index=index, compression=compression, schema_version=schema_version, **kwargs) + return self.ds.to_parquet( + path=path, index=index, compression=compression, schema_version=schema_version, **kwargs + ) @copy_doc(gpd.GeoDataFrame, "Vector") def to_wkt(self, **kwargs: Any) -> pd.DataFrame: @@ -763,25 +805,37 @@ def to_wkb(self, hex: bool = False, **kwargs: Any) -> pd.DataFrame: return self.ds.to_wkb(hex=hex, **kwargs) @copy_doc(gpd.GeoDataFrame, "Vector") - def to_json(self, na: Any = "null", show_bbox: bool =False, drop_id: bool = False, **kwargs: Any) -> str | None: + def to_json(self, na: Any = "null", show_bbox: bool = False, drop_id: bool = False, **kwargs: Any) -> str | None: return self.ds.to_json(na=na, show_bbox=show_bbox, drop_id=drop_id, **kwargs) @copy_doc(gpd.GeoDataFrame, "Vector") - def to_postgis(self, name: str, - con: Any, - schema: Any = None, - if_exists: Any = "fail", - index: Any = False, - index_label: Any = None, - chunksize: Any = None, - dtype: Any = None) -> None: - - return self.ds.to_postgis(name=name, con=con, schema=schema, if_exists=if_exists, index=index, - index_label=index_label, chunksize=chunksize, dtype=dtype) + def to_postgis( + self, + name: str, + con: Any, + schema: Any = None, + if_exists: Any = "fail", + index: Any = False, + index_label: Any = None, + chunksize: Any = None, + dtype: Any = None, + ) -> None: + + return self.ds.to_postgis( + name=name, + con=con, + schema=schema, + if_exists=if_exists, + index=index, + index_label=index_label, + chunksize=chunksize, + dtype=dtype, + ) @copy_doc(gpd.GeoDataFrame, "Vector") - def to_csv(self, + def to_csv( + self, path_or_buf: str | PathLike[str] | WriteBuffer[bytes] | WriteBuffer[str] | None = None, sep: str = ",", na_rep: str = "", @@ -794,7 +848,7 @@ def to_csv(self, encoding: str | None = None, compression: Literal["infer", "gzip", "bz2", "zip", "xz", "zstd", "tar"] | dict[str, Any] | None = "infer", quoting: int | None = None, - quotechar: str = '\"', + quotechar: str = '"', lineterminator: str | None = None, chunksize: int | None = None, date_format: str | None = None, @@ -802,13 +856,32 @@ def to_csv(self, escapechar: str | None = None, decimal: str = ".", errors: str = "strict", - storage_options: dict[str, Any] | None = None - ) -> str | None: - - return self.ds.to_csv(path_or_buf=path_or_buf, sep=sep, na_rep=na_rep, float_format=float_format, columns=columns, header=header, - index=index, index_label=index_label, mode=mode, encoding=encoding, compression=compression, quoting=quoting, - quotechar=quotechar, lineterminator=lineterminator, chunksize=chunksize, date_format=date_format, - doublequote=doublequote, escapechar=escapechar, decimal=decimal, errors=errors, storage_options=storage_options) + storage_options: dict[str, Any] | None = None, + ) -> str | None: + + return self.ds.to_csv( + path_or_buf=path_or_buf, + sep=sep, + na_rep=na_rep, + float_format=float_format, + columns=columns, + header=header, + index=index, + index_label=index_label, + mode=mode, + encoding=encoding, + compression=compression, + quoting=quoting, + quotechar=quotechar, + lineterminator=lineterminator, + chunksize=chunksize, + date_format=date_format, + doublequote=doublequote, + escapechar=escapechar, + decimal=decimal, + errors=errors, + storage_options=storage_options, + ) # -------------------------------- # End of GeoPandas functionalities diff --git a/tests/test_vector.py b/tests/test_vector.py index 98f44a950..716bebef0 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -569,7 +569,17 @@ class TestGeoPandasMethods: # Properties and methods derived from Shapely or GeoPandas # List of properties and methods with non-geometric output that are implemented in GeoUtils main_properties = ["crs", "geometry", "total_bounds"] - nongeo_properties = ["area", "length", "interiors", "geom_type", "is_empty", "is_ring", "is_simple", "is_valid", "has_z"] + nongeo_properties = [ + "area", + "length", + "interiors", + "geom_type", + "is_empty", + "is_ring", + "is_simple", + "is_valid", + "has_z", + ] nongeo_methods = [ "contains", "geom_equals", @@ -613,19 +623,46 @@ class TestGeoPandasMethods: "set_crs", "rename_geometry", "set_geometry", - "clip" + "clip", ] # List of class methods - io_methods = ["from_file", "from_postgis", "from_dict", "from_features", "to_feather", - "to_parquet", "to_file", "to_postgis", "to_json", "to_wkb", "to_wkt", "to_csv"] + io_methods = [ + "from_file", + "from_postgis", + "from_dict", + "from_features", + "to_feather", + "to_parquet", + "to_file", + "to_postgis", + "to_json", + "to_wkb", + "to_wkt", + "to_csv", + ] # List of other properties and methods other = ["has_sindex", "sindex", "estimate_utm_crs", "cx", "iterfeatures"] - all_declared = main_properties + nongeo_methods + nongeo_properties + geo_methods + geo_properties + other + io_methods + all_declared = ( + main_properties + nongeo_methods + nongeo_properties + geo_methods + geo_properties + other + io_methods + ) # Exceptions for GeoPandasBase functions not implemented (or deprecrated) in GeoSeries/GeoDataFrame - exceptions_unimplemented = ["plot", "explore", "cascaded_union", "bounds", "relate", "project", "interpolate", "equals", "type", "convert_dtypes", - "merge", "apply", "astype"] + exceptions_unimplemented = [ + "plot", + "explore", + "cascaded_union", + "bounds", + "relate", + "project", + "interpolate", + "equals", + "type", + "convert_dtypes", + "merge", + "apply", + "astype", + ] # Exceptions for IO/conversion that can be done directly from .ds all_exceptions = exceptions_unimplemented @@ -643,7 +680,7 @@ def test_overridden_funcs_exist(self) -> None: list_missing = [method for method in self.all_declared if method not in vector_methods.keys()] - assert len(list_missing) == 0, print("Test method listed that is not in GeoUtils: {}".format(list_missing)) + assert len(list_missing) == 0, print(f"Test method listed that is not in GeoUtils: {list_missing}") def test_geopandas_coverage(self) -> None: """Check that all existing methods of GeoPandas are overridden, with a couple exceptions.""" @@ -661,7 +698,7 @@ def test_geopandas_coverage(self) -> None: # Check that all methods declared in the class above are covered in Vector list_missing = [method for method in covered_methods if method not in self.all_declared] - assert len(list_missing) == 0, print("Missing methods from GeoPandas: {}".format(list_missing)) + assert len(list_missing) == 0, print(f"Missing methods from GeoPandas: {list_missing}") @pytest.mark.parametrize("method", nongeo_methods + geo_methods) # type: ignore def test_overridden_funcs_args(self, method: str) -> None: @@ -769,7 +806,7 @@ def test_geo_properties(self, vector: gu.Vector, method: str) -> None: "set_crs": {"crs": pyproj.CRS.from_epsg(32610), "allow_override": True}, "rename_geometry": {"col": "lol"}, "set_geometry": {"col": synthvec1.geometry}, - "clip": {"mask": poly} + "clip": {"mask": poly}, } @pytest.mark.parametrize("vector1", [synthvec1, realvec1]) # type: ignore From 9bae25f01fc653ea80c90ce8020d75961d33151f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 22 Mar 2023 15:21:54 -0700 Subject: [PATCH 132/184] Define raster transform and crs as properties, remove the old attrs --- doc/source/api.md | 2 -- geoutils/raster/raster.py | 68 ++++++++++++++++++++++++++++++++------- geoutils/raster/satimg.py | 6 +--- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index b46a132ec..9ff1b5c92 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -370,8 +370,6 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r Vector.rotate Vector.scale Vector.skew - Vector.interpolate - Vector.merge Vector.dissolve Vector.explode Vector.sjoin diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index 369bc4405..94b0b49a8 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -14,6 +14,7 @@ from numbers import Number from typing import IO, Any, Callable, TypeVar, overload +import affine import geopandas as gpd import matplotlib import matplotlib.pyplot as plt @@ -253,7 +254,6 @@ def __init__( downsample: AnyNumber = 1, masked: bool = True, nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, - attrs: list[str] | None = None, ) -> None: """ Instantiate a raster from a filename or rasterio dataset. @@ -269,11 +269,6 @@ def __init__( :param masked: Whether to load the array as a NumPy masked-array, with nodata values masked. Default is True. :param nodata: Nodata value to be used (overwrites the metadata). Default reads from metadata. - - :param attrs: Additional attributes from Rasterio's DataReader class to add to the Raster object. - Default list is set by geoutils.raster.raster._default_rio_attrs, i.e. - ['bounds', 'count', 'crs', 'driver', 'dtypes', 'height', 'indexes', - 'name', 'nodata', 'res', 'shape', 'transform', 'width'] - if no attrs are specified, these will be added. """ self._driver: str | None = None self._name: str | None = None @@ -281,6 +276,8 @@ def __init__( self.tags: dict[str, Any] = {} self._data: np.ma.masked_array | None = None + self._transform: affine.Affine | None = None + self._crs: CRS | None = None self._nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = nodata self._indexes = indexes self._indexes_loaded: int | tuple[int, ...] | None = None @@ -331,8 +328,8 @@ def __init__( load_data = True self.filename = None - self.transform = ds.transform - self.crs = ds.crs + self._transform = ds.transform + self._crs = ds.crs self._nodata = ds.nodata self._name = ds.name self._driver = ds.driver @@ -342,10 +339,6 @@ def __init__( self._disk_indexes = ds.indexes self._disk_dtypes = ds.dtypes - if attrs is not None: - for attr in attrs: - self.__setattr__(attr, ds.__getattr__(attr)) - # Check number of bands to be loaded if indexes is None: count = self.count @@ -1398,6 +1391,57 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: else: self._data = np.ma.masked_array(data=new_data, fill_value=self.nodata) + @property + def transform(self) -> affine.Affine: + """ + Geotransform of the raster. + + :returns: Affine matrix geotransform. + + """ + return self._transform + + @transform.setter + def transform(self, transform= tuple[float, ...] | Affine | None) -> None: + """ + Set the geotransform of the raster. + """ + + if transform is None: + self._transform = None + return + + if not isinstance(transform, Affine): + if isinstance(transform, tuple): + transform = Affine(*transform) + else: + raise TypeError("The transform argument needs to be Affine or tuple.") + + self._transform = transform + + @property + def crs(self) -> CRS: + """ + Coordinate reference system (CRS) of the raster. + + :returns: Pyproj coordinate reference system. + + """ + return self._crs + + @crs.setter + def crs(self, crs = CRS | int | str | None) -> None: + """ + Set the coordinate reference system (CRS) of the raster. + """ + + if crs is None: + self._crs = None + return + + crs = CRS.from_user_input(value=crs) + self._crs = crs + def set_mask(self, mask: np.ndarray | Mask) -> None: """ Set a mask on the raster array. diff --git a/geoutils/raster/satimg.py b/geoutils/raster/satimg.py index 5aa916334..2655eaa1e 100644 --- a/geoutils/raster/satimg.py +++ b/geoutils/raster/satimg.py @@ -252,7 +252,6 @@ class SatelliteImage(Raster): # type: ignore def __init__( self, filename_or_dataset: str | RasterType | rio.io.DatasetReader | rio.io.MemoryFile, - attrs: list[str] | None = None, load_data: bool = True, indexes: int | list[int] | None = None, read_from_fn: bool = True, @@ -270,9 +269,6 @@ def __init__( Load satellite data through the Raster class and parse additional attributes from filename or metadata. :param filename_or_dataset: The filename of the dataset. - :param attrs: Additional attributes from rasterio's DataReader class to add to the Raster object. - Default list is ['bounds', 'count', 'crs', 'dataset_mask', 'driver', 'dtypes', 'height', 'indexes', - 'name', 'nodata', 'res', 'shape', 'transform', 'width'] - if no attrs are specified, these will be added. :param load_data: Load the raster data into the object. Default is True. :param indexes: The band(s) to load into the object. Default is to load all bands. :param read_from_fn: Try to read metadata from the filename @@ -296,7 +292,7 @@ def __init__( return # Else rely on parent Raster class options (including raised errors) else: - super().__init__(filename_or_dataset, attrs=attrs, load_data=load_data, indexes=indexes) + super().__init__(filename_or_dataset, load_data=load_data, indexes=indexes) # priority to user input self._datetime = datetime From dfa2ae3764c6de3c9b18cb1bcbb4c2e1381ccda2 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 23 Mar 2023 13:34:47 -0700 Subject: [PATCH 133/184] Linting --- geoutils/raster/raster.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index 94b0b49a8..980065206 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -1397,50 +1397,48 @@ def transform(self) -> affine.Affine: Geotransform of the raster. :returns: Affine matrix geotransform. - """ return self._transform @transform.setter - def transform(self, transform= tuple[float, ...] | Affine | None) -> None: + def transform(self, new_transform: tuple[float, ...] | Affine | None) -> None: """ Set the geotransform of the raster. """ - if transform is None: + if new_transform is None: self._transform = None return - if not isinstance(transform, Affine): - if isinstance(transform, tuple): - transform = Affine(*transform) + if not isinstance(new_transform, Affine): + if isinstance(new_transform, tuple): + new_transform = Affine(*new_transform) else: raise TypeError("The transform argument needs to be Affine or tuple.") - self._transform = transform + self._transform = new_transform @property def crs(self) -> CRS: """ - Coordinate reference system (CRS) of the raster. + Coordinate reference system of the raster. :returns: Pyproj coordinate reference system. - """ return self._crs @crs.setter - def crs(self, crs = CRS | int | str | None) -> None: + def crs(self, new_crs: CRS | int | str | None) -> None: """ - Set the coordinate reference system (CRS) of the raster. + Set the coordinate reference system of the raster. """ - if crs is None: + if new_crs is None: self._crs = None return - crs = CRS.from_user_input(value=crs) - self._crs = crs + new_crs = CRS.from_user_input(value=new_crs) + self._crs = new_crs def set_mask(self, mask: np.ndarray | Mask) -> None: """ From f4d336964d9c18972c43c33872ce314f1a44a6e1 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 23 Mar 2023 17:03:22 -0700 Subject: [PATCH 134/184] Try to rename raster.py in raster to avoid errors on Mac and Windows --- geoutils/raster/__init__.py | 2 +- geoutils/raster/{raster.py => core.py} | 0 geoutils/raster/multiraster.py | 2 +- tests/test_raster.py | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename geoutils/raster/{raster.py => core.py} (100%) diff --git a/geoutils/raster/__init__.py b/geoutils/raster/__init__.py index aceb2c43b..31826ec6c 100644 --- a/geoutils/raster/__init__.py +++ b/geoutils/raster/__init__.py @@ -1,4 +1,4 @@ -from geoutils.raster.raster import Raster, RasterType, Mask # noqa isort:skip +from geoutils.raster.core import Raster, RasterType, Mask # noqa isort:skip from geoutils.raster.array import * # noqa from geoutils.raster.multiraster import * # noqa from geoutils.raster.sampling import * # noqa diff --git a/geoutils/raster/raster.py b/geoutils/raster/core.py similarity index 100% rename from geoutils/raster/raster.py rename to geoutils/raster/core.py diff --git a/geoutils/raster/multiraster.py b/geoutils/raster/multiraster.py index 385315a6c..21bff9fe5 100644 --- a/geoutils/raster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -12,7 +12,7 @@ import geoutils as gu from geoutils.misc import resampling_method_from_str from geoutils.raster import Raster, RasterType, get_array_and_mask -from geoutils.raster.raster import _default_nodata +from geoutils.raster.core import _default_nodata def load_multiple_rasters( diff --git a/tests/test_raster.py b/tests/test_raster.py index f3ca6d155..fb4af56f1 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -22,7 +22,7 @@ from geoutils import examples from geoutils.misc import resampling_method_from_str from geoutils.projtools import reproject_to_latlon -from geoutils.raster.raster import _default_nodata, _default_rio_attrs +from geoutils.raster.core import _default_nodata, _default_rio_attrs DO_PLOT = False From 7d1862bfd26a0f67c648838eec57ee3e6cf619ce Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 23 Mar 2023 17:08:00 -0700 Subject: [PATCH 135/184] Fix renaming with core --- geoutils/raster/multiraster.py | 2 +- geoutils/vector.py | 2 +- tests/test_raster.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/geoutils/raster/multiraster.py b/geoutils/raster/multiraster.py index 21bff9fe5..ea179613e 100644 --- a/geoutils/raster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -173,7 +173,7 @@ def stack_rasters( raster.load() raster.is_loaded = False - nodata = reference_raster.nodata or gu.raster.raster._default_nodata(reference_raster.data.dtype) + nodata = reference_raster.nodata or gu.raster.core._default_nodata(reference_raster.data.dtype) # Reproject to reference grid reprojected_raster = raster.reproject( dst_bounds=dst_bounds, diff --git a/geoutils/vector.py b/geoutils/vector.py index c18249d06..53426de22 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -1397,7 +1397,7 @@ def proximity( raster = gu.Raster.from_array(data=np.zeros((1000, 1000)), transform=transform, crs=self.crs) - proximity = gu.raster.raster.proximity_from_vector_or_raster( + proximity = gu.raster.core.proximity_from_vector_or_raster( raster=raster, vector=self, geometry_type=geometry_type, in_or_out=in_or_out, distance_unit=distance_unit ) diff --git a/tests/test_raster.py b/tests/test_raster.py index fb4af56f1..4afbcfdc6 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -3461,8 +3461,8 @@ class TestArrayInterface: # Most other math functions are already universal functions # Separate between two lists (single input and double input) for testing - handled_functions_2in = gu.raster.raster._HANDLED_FUNCTIONS_2NIN - handled_functions_1in = gu.raster.raster._HANDLED_FUNCTIONS_1NIN + handled_functions_2in = gu.raster.core._HANDLED_FUNCTIONS_2NIN + handled_functions_1in = gu.raster.core._HANDLED_FUNCTIONS_1NIN # Details below: # NaN functions: [f for f in np.lib.nanfunctions.__all__] From c7c5b52be5d2fd0f68edebfec92cc4fe34eb8ce3 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Mar 2023 12:04:59 -0700 Subject: [PATCH 136/184] Try to re-order init imports --- geoutils/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/geoutils/__init__.py b/geoutils/__init__.py index d99922258..c1bfdbe69 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -1,10 +1,10 @@ """ -GeoUtils is a python package of raster and vector tools. +GeoUtils is a Python package for the analysis of geospatial data. """ -from geoutils import examples, projtools, raster, vector # noqa -from geoutils.raster import Mask, Raster, SatelliteImage # noqa -from geoutils.vector import Vector # noqa +from geoutils.raster import Mask, Raster, SatelliteImage # noqa isort:skip +from geoutils.vector import Vector # noqa isort:skip +from geoutils import examples, projtools, raster, vector # noqa isort:skip try: from geoutils.version import version as __version__ # noqa From 6239334c023d0468cb7b8ee176ced71945da04b1 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Mar 2023 12:22:35 -0700 Subject: [PATCH 137/184] Change package setup to try fix on mac and windows --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 8910350a1..ff66c3cdd 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from os import path from typing import Optional -from setuptools import setup +from setuptools import setup, find_packages FULLVERSION = "0.0.10" VERSION = FULLVERSION @@ -40,7 +40,7 @@ def write_version_py(filename: Optional[str] = None) -> None: url="https://www.github.com/GlacioHack/geoutils/", author="The GlacioHack Team", license="BSD-3", - packages=["geoutils", "geoutils.raster"], + packages=find_packages(), python_requires=">=3.8", install_requires=[ "rasterio", From 24bb5c38a6c0fe95a4b5b2047c815577cf285ee1 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Mar 2023 13:23:28 -0700 Subject: [PATCH 138/184] Try re-installing via pip --- .github/workflows/python-app.yml | 4 +++- geoutils/__init__.py | 6 +++--- setup.py | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index b433a4b8a..cd4e57ca7 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -89,7 +89,9 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Setup pip dependencies - run: pip install pytest-cov coveralls coveragepy-lcov + run: | + pip install -e . --no-dependencies + pip install pytest-cov coveralls coveragepy-lcov - name: Test with pytest run: pytest -ra --cov=geoutils/ diff --git a/geoutils/__init__.py b/geoutils/__init__.py index c1bfdbe69..9aef8a322 100644 --- a/geoutils/__init__.py +++ b/geoutils/__init__.py @@ -2,9 +2,9 @@ GeoUtils is a Python package for the analysis of geospatial data. """ -from geoutils.raster import Mask, Raster, SatelliteImage # noqa isort:skip -from geoutils.vector import Vector # noqa isort:skip -from geoutils import examples, projtools, raster, vector # noqa isort:skip +from geoutils import examples, projtools, raster, vector # noqa +from geoutils.raster import Mask, Raster, SatelliteImage # noqa +from geoutils.vector import Vector # noqa try: from geoutils.version import version as __version__ # noqa diff --git a/setup.py b/setup.py index ff66c3cdd..8910350a1 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from os import path from typing import Optional -from setuptools import setup, find_packages +from setuptools import setup FULLVERSION = "0.0.10" VERSION = FULLVERSION @@ -40,7 +40,7 @@ def write_version_py(filename: Optional[str] = None) -> None: url="https://www.github.com/GlacioHack/geoutils/", author="The GlacioHack Team", license="BSD-3", - packages=find_packages(), + packages=["geoutils", "geoutils.raster"], python_requires=">=3.8", install_requires=[ "rasterio", From a0cc1fbc14225df105816ab85863e8c78cdee88f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 24 Mar 2023 14:35:44 -0700 Subject: [PATCH 139/184] Fix raster into core in sphinx conf and doc --- .github/workflows/python-app.yml | 4 +--- doc/source/conf.py | 4 ++-- doc/source/core_array_funcs.md | 2 +- doc/source/core_inheritance.md | 4 ++-- doc/source/quick_start.md | 4 ++-- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index cd4e57ca7..b433a4b8a 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -89,9 +89,7 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Setup pip dependencies - run: | - pip install -e . --no-dependencies - pip install pytest-cov coveralls coveragepy-lcov + run: pip install pytest-cov coveralls coveragepy-lcov - name: Test with pytest run: pytest -ra --cov=geoutils/ diff --git a/doc/source/conf.py b/doc/source/conf.py index c92b33180..e5c6927b1 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -93,8 +93,8 @@ # To avoid long path names in inheritance diagrams inheritance_alias = { - "geoutils.raster.raster.Raster": "geoutils.Raster", - "geoutils.raster.raster.Mask": "geoutils.Mask", + "geoutils.raster.core.Raster": "geoutils.Raster", + "geoutils.raster.core.Mask": "geoutils.Mask", "geoutils.raster.satimg.SatelliteImage": "geoutils.SatelliteImage", "geoutils.vector.Vector": "geoutils.Vector", "xdem.dem.DEM": "xdem.DEM", diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index df97d5499..46908f492 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -100,7 +100,7 @@ np.count_nonzero(raster, axis=2) Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), [masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). -A full list of supported array function is available in {attr}`geoutils.raster.raster.handled_array_funcs`. +A full list of supported array function is available in {attr}`geoutils.raster.core.handled_array_funcs`. ## Respecting masked values diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 2f9383507..741f5cf07 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -18,8 +18,8 @@ Below is a diagram showing current {class}`~geoutils.Raster` inheritance, which for analyzing digital elevation models. ```{eval-rst} -.. inheritance-diagram:: geoutils.raster.raster geoutils.raster.satimg xdem.dem.DEM - :top-classes: geoutils.raster.raster.Raster +.. inheritance-diagram:: geoutils.raster.core geoutils.raster.satimg xdem.dem.DEM + :top-classes: geoutils.raster.core.Raster ``` ```{note} diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index ff36f9b94..ac9984d38 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -267,8 +267,8 @@ There are many possible subclass to derive from a {class}`~geoutils.Raster`. Her [xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: ```{eval-rst} -.. inheritance-diagram:: geoutils.raster.raster geoutils.raster.satimg xdem.dem.DEM - :top-classes: geoutils.raster.raster.Raster +.. inheritance-diagram:: geoutils.raster.core geoutils.raster.satimg xdem.dem.DEM + :top-classes: geoutils.raster.core.Raster ``` ```{seealso} The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) From f9c6545a1a0f2f51631d1c9346c1e4f291aaa533 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 27 Mar 2023 12:16:53 -0800 Subject: [PATCH 140/184] Only run docs on Linux --- tests/test_docs.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/test_docs.py b/tests/test_docs.py index a22073702..72ad2706c 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1,6 +1,7 @@ """Functions to test the documentation.""" import os import shutil +import platform import sphinx.cmd.build @@ -12,16 +13,20 @@ class TestDocs: def test_build(self) -> None: """Try building the docs and see if it works.""" # Remove the build directory if it exists. - if os.path.isdir(os.path.join(self.docs_dir, "build/")): - shutil.rmtree(os.path.join(self.docs_dir, "build/")) - return_code = sphinx.cmd.build.main( - [ - "-j", - "1", - os.path.join(self.docs_dir, "source/"), - os.path.join(self.docs_dir, "build/html"), - ] - ) + # Test only on Linux + if platform.system() == "Linux": + # Remove the build directory if it exists. + if os.path.isdir(os.path.join(self.docs_dir, "build/")): + shutil.rmtree(os.path.join(self.docs_dir, "build/")) - assert return_code == 0 + return_code = sphinx.cmd.build.main( + [ + "-j", + "1", + os.path.join(self.docs_dir, "source/"), + os.path.join(self.docs_dir, "build/html"), + ] + ) + + assert return_code == 0 From 69c780be17a5ceb69680b1fd6f992410bc5a5cde Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 27 Mar 2023 12:17:13 -0800 Subject: [PATCH 141/184] Linting --- tests/test_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_docs.py b/tests/test_docs.py index 72ad2706c..caa7b86f9 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -1,7 +1,7 @@ """Functions to test the documentation.""" import os -import shutil import platform +import shutil import sphinx.cmd.build From 68226d7f9a9efa880773a5069587d4c13b7ca6a5 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 27 Mar 2023 13:39:30 -0800 Subject: [PATCH 142/184] Re-refactor into raster.py (not the source of error), and fix temporary files in test_save of raster and vector --- doc/source/api.md | 1 - doc/source/conf.py | 4 +-- doc/source/core_array_funcs.md | 2 +- doc/source/core_inheritance.md | 4 +-- doc/source/quick_start.md | 4 +-- doc/source/raster_class.md | 2 +- geoutils/raster/__init__.py | 2 +- geoutils/raster/multiraster.py | 4 +-- geoutils/raster/{core.py => raster.py} | 0 geoutils/vector.py | 2 +- tests/test_raster.py | 35 +++++++++++--------------- tests/test_vector.py | 10 ++++---- 12 files changed, 32 insertions(+), 38 deletions(-) rename geoutils/raster/{core.py => raster.py} (100%) diff --git a/doc/source/api.md b/doc/source/api.md index 9ff1b5c92..03de14f19 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -415,7 +415,6 @@ Otherwise, calling the method from {attr}`Vector.ds`, they r :toctree: gen_modules/ Vector.contains - Vector.equals Vector.geom_equals Vector.geom_almost_equals Vector.crosses diff --git a/doc/source/conf.py b/doc/source/conf.py index e5c6927b1..c92b33180 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -93,8 +93,8 @@ # To avoid long path names in inheritance diagrams inheritance_alias = { - "geoutils.raster.core.Raster": "geoutils.Raster", - "geoutils.raster.core.Mask": "geoutils.Mask", + "geoutils.raster.raster.Raster": "geoutils.Raster", + "geoutils.raster.raster.Mask": "geoutils.Mask", "geoutils.raster.satimg.SatelliteImage": "geoutils.SatelliteImage", "geoutils.vector.Vector": "geoutils.Vector", "xdem.dem.DEM": "xdem.DEM", diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index 46908f492..df97d5499 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -100,7 +100,7 @@ np.count_nonzero(raster, axis=2) Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), [masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). -A full list of supported array function is available in {attr}`geoutils.raster.core.handled_array_funcs`. +A full list of supported array function is available in {attr}`geoutils.raster.raster.handled_array_funcs`. ## Respecting masked values diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 741f5cf07..2f9383507 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -18,8 +18,8 @@ Below is a diagram showing current {class}`~geoutils.Raster` inheritance, which for analyzing digital elevation models. ```{eval-rst} -.. inheritance-diagram:: geoutils.raster.core geoutils.raster.satimg xdem.dem.DEM - :top-classes: geoutils.raster.core.Raster +.. inheritance-diagram:: geoutils.raster.raster geoutils.raster.satimg xdem.dem.DEM + :top-classes: geoutils.raster.raster.Raster ``` ```{note} diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index ac9984d38..ff36f9b94 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -267,8 +267,8 @@ There are many possible subclass to derive from a {class}`~geoutils.Raster`. Her [xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: ```{eval-rst} -.. inheritance-diagram:: geoutils.raster.core geoutils.raster.satimg xdem.dem.DEM - :top-classes: geoutils.raster.core.Raster +.. inheritance-diagram:: geoutils.raster.raster geoutils.raster.satimg xdem.dem.DEM + :top-classes: geoutils.raster.raster.Raster ``` ```{seealso} The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 2843681bc..4d22febea 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -249,7 +249,7 @@ For more details, see the {ref}`specific section and function descriptions in th ```{code-cell} ipython3 # Crop to smaller bounds -raster_crop = raster.crop(cropGeom=(0.3, 0.3, 1, 1), inplace=False) +raster_crop = raster.crop(crop_geom=(0.3, 0.3, 1, 1), inplace=False) raster_crop ``` diff --git a/geoutils/raster/__init__.py b/geoutils/raster/__init__.py index 31826ec6c..aceb2c43b 100644 --- a/geoutils/raster/__init__.py +++ b/geoutils/raster/__init__.py @@ -1,4 +1,4 @@ -from geoutils.raster.core import Raster, RasterType, Mask # noqa isort:skip +from geoutils.raster.raster import Raster, RasterType, Mask # noqa isort:skip from geoutils.raster.array import * # noqa from geoutils.raster.multiraster import * # noqa from geoutils.raster.sampling import * # noqa diff --git a/geoutils/raster/multiraster.py b/geoutils/raster/multiraster.py index ea179613e..385315a6c 100644 --- a/geoutils/raster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -12,7 +12,7 @@ import geoutils as gu from geoutils.misc import resampling_method_from_str from geoutils.raster import Raster, RasterType, get_array_and_mask -from geoutils.raster.core import _default_nodata +from geoutils.raster.raster import _default_nodata def load_multiple_rasters( @@ -173,7 +173,7 @@ def stack_rasters( raster.load() raster.is_loaded = False - nodata = reference_raster.nodata or gu.raster.core._default_nodata(reference_raster.data.dtype) + nodata = reference_raster.nodata or gu.raster.raster._default_nodata(reference_raster.data.dtype) # Reproject to reference grid reprojected_raster = raster.reproject( dst_bounds=dst_bounds, diff --git a/geoutils/raster/core.py b/geoutils/raster/raster.py similarity index 100% rename from geoutils/raster/core.py rename to geoutils/raster/raster.py diff --git a/geoutils/vector.py b/geoutils/vector.py index 53426de22..c18249d06 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -1397,7 +1397,7 @@ def proximity( raster = gu.Raster.from_array(data=np.zeros((1000, 1000)), transform=transform, crs=self.crs) - proximity = gu.raster.core.proximity_from_vector_or_raster( + proximity = gu.raster.raster.proximity_from_vector_or_raster( raster=raster, vector=self, geometry_type=geometry_type, in_or_out=in_or_out, distance_unit=distance_unit ) diff --git a/tests/test_raster.py b/tests/test_raster.py index 4afbcfdc6..4d4c4f788 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -22,7 +22,7 @@ from geoutils import examples from geoutils.misc import resampling_method_from_str from geoutils.projtools import reproject_to_latlon -from geoutils.raster.core import _default_nodata, _default_rio_attrs +from geoutils.raster.raster import _default_nodata, _default_rio_attrs DO_PLOT = False @@ -2158,29 +2158,26 @@ def test_save(self, example: str) -> None: temp_dir = tempfile.TemporaryDirectory() # Save file to temporary file, with defaults opts - temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) - img.save(temp_file.name) - saved = gu.Raster(temp_file.name) + temp_file = os.path.join(temp_dir.name, "test.tif") + img.save(temp_file) + saved = gu.Raster(temp_file) assert img.raster_equal(saved) # Try to save with a pathlib path (create a new temp file for Windows) - temp_file_1 = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) - path = pathlib.Path(temp_file_1.name) + path = pathlib.Path(temp_file) img.save(path) # Test additional options co_opts = {"TILED": "YES", "COMPRESS": "LZW"} metadata = {"Type": "test"} - temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) - img.save(temp_file.name, co_opts=co_opts, metadata=metadata) - saved = gu.Raster(temp_file.name) + img.save(temp_file, co_opts=co_opts, metadata=metadata) + saved = gu.Raster(temp_file) assert img.raster_equal(saved) assert saved.tags["Type"] == "test" # Test that nodata value is enforced when masking - since value 0 is not used, data should be unchanged - temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) - img.save(temp_file.name, nodata=0) - saved = gu.Raster(temp_file.name) + img.save(temp_file, nodata=0) + saved = gu.Raster(temp_file) assert np.ma.allequal(img.data, saved.data) assert saved.nodata == 0 @@ -2188,9 +2185,8 @@ def test_save(self, example: str) -> None: mask = img.data == np.min(img.data) img.set_mask(mask) if img.nodata is not None: - temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) - img.save(temp_file.name) - saved = gu.Raster(temp_file.name) + img.save(temp_file) + saved = gu.Raster(temp_file) assert np.array_equal(img.data.mask, saved.data.mask) # Test that a warning is raised if nodata is not set and a mask exists (defined above) @@ -2199,9 +2195,8 @@ def test_save(self, example: str) -> None: img.save(TemporaryFile()) # Test with blank argument - temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name) - img.save(temp_file.name, blank_value=0) - saved = gu.Raster(temp_file.name) + img.save(temp_file, blank_value=0) + saved = gu.Raster(temp_file) assert np.array_equal(saved.data.data, np.zeros(np.shape(saved.data))) @@ -3461,8 +3456,8 @@ class TestArrayInterface: # Most other math functions are already universal functions # Separate between two lists (single input and double input) for testing - handled_functions_2in = gu.raster.core._HANDLED_FUNCTIONS_2NIN - handled_functions_1in = gu.raster.core._HANDLED_FUNCTIONS_1NIN + handled_functions_2in = gu.raster.raster._HANDLED_FUNCTIONS_2NIN + handled_functions_1in = gu.raster.raster._HANDLED_FUNCTIONS_1NIN # Details below: # NaN functions: [f for f in np.lib.nanfunctions.__all__] diff --git a/tests/test_vector.py b/tests/test_vector.py index 716bebef0..d780bd723 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -82,17 +82,17 @@ def test_save(self) -> None: # Create a temporary file in a temporary directory temp_dir = tempfile.TemporaryDirectory() - temp_file = NamedTemporaryFile(mode="w", delete=False, dir=temp_dir.name, suffix=".gpkg") - os.remove(temp_file.name) + temp_file = os.path.join(temp_dir.name, "test.gpkg") # Save and check the file exists - vector.save(temp_file.name, overwrite=True) - assert os.path.exists(temp_file.name) + vector.save(temp_file) + assert os.path.exists(temp_file) # Open and check the object is the same - vector_save = gu.Vector(temp_file.name) + vector_save = gu.Vector(temp_file) vector_save.vector_equal(vector) + def test_bounds(self) -> None: bounds = self.glacier_outlines.bounds From b02b3bbb73d2bdbd0a989146bf516013eac1e6c1 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 27 Mar 2023 13:40:32 -0800 Subject: [PATCH 143/184] Linting --- tests/test_raster.py | 2 +- tests/test_vector.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_raster.py b/tests/test_raster.py index 4d4c4f788..c4d4229b0 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -8,7 +8,7 @@ import re import tempfile import warnings -from tempfile import NamedTemporaryFile, TemporaryFile +from tempfile import TemporaryFile import matplotlib.pyplot as plt import numpy as np diff --git a/tests/test_vector.py b/tests/test_vector.py index d780bd723..0349f9ca7 100644 --- a/tests/test_vector.py +++ b/tests/test_vector.py @@ -6,7 +6,6 @@ import re import tempfile import warnings -from tempfile import NamedTemporaryFile import geopandas as gpd import geopandas.base @@ -92,7 +91,6 @@ def test_save(self) -> None: vector_save = gu.Vector(temp_file) vector_save.vector_equal(vector) - def test_bounds(self) -> None: bounds = self.glacier_outlines.bounds From 9d487d0a85fe2acffbeb4b75d2e3765d23090f66 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 27 Mar 2023 15:02:07 -0800 Subject: [PATCH 144/184] Make sphinx gallery thumbnail not visible, remove open_save example files with post-build sphinx cleanup --- .gitignore | 5 +++++ doc/source/conf.py | 12 +++++++++++- .../array_numerics/numpy_interfacing.py | 3 ++- .../array_numerics/python_arithmetic.py | 3 ++- examples/analysis/geospatial/buffer_voronoi.py | 3 ++- .../analysis/geospatial/proximity_metric.py | 3 ++- .../analysis/point_extraction/interpolation.py | 3 ++- examples/analysis/point_extraction/reduction.py | 3 ++- examples/handling/georeferencing/crop_raster.py | 3 ++- examples/handling/georeferencing/crop_vector.py | 3 ++- .../handling/georeferencing/reproj_raster.py | 2 +- .../handling/georeferencing/reproj_vector.py | 4 ++-- examples/handling/interface/create_mask.py | 3 ++- examples/handling/interface/polygonize.py | 3 ++- examples/handling/interface/rasterize.py | 3 ++- examples/handling/interface/topoints.py | 3 ++- examples/io/open_save/myraster.tif | Bin 23791 -> 0 bytes examples/io/open_save/read_vector.py | 2 +- 18 files changed, 44 insertions(+), 17 deletions(-) delete mode 100644 examples/io/open_save/myraster.tif diff --git a/.gitignore b/.gitignore index 410255d81..4047e2651 100644 --- a/.gitignore +++ b/.gitignore @@ -151,3 +151,8 @@ examples/data/ # Directory where myst_nb executes jupyter code doc/jupyter_execute/ + +# Files that should have been deleted by Sphinx at end of build (but can exist if build fails) +examples/io/open_save/myraster.tif +examples/io/open_save/myvector.gpkg + diff --git a/doc/source/conf.py b/doc/source/conf.py index c92b33180..0e87b4f12 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,6 +1,7 @@ # # Configuration file for the Sphinx documentation builder. # +import glob import os import sys @@ -80,7 +81,8 @@ "filename_pattern": r".*\.py", # Run all python files in the gallery (by default, only files starting with "plot_" are run) # directory where function/class granular galleries are stored "backreferences_dir": "gen_modules/backreferences", - "doc_module": ("geoutils"), # which function/class levels are used to create galleries + "doc_module": ("geoutils"), # Which function/class levels are used to create galleries + 'remove_config_comments': True, # To remove comments such as sphinx-gallery-thumbnail-number (only works in code, not in text) } extlinks = { @@ -123,12 +125,20 @@ # "special-members": "__init__", # } +def clean_gallery_files(app, exception): + fn_myraster = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../examples/io/open_save/myraster.tif")) + fn_myvector = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../examples/io/open_save/myvector.gpkg")) + if os.path.exists(fn_myraster): + os.remove(fn_myraster) + if os.path.exists(fn_myvector): + os.remove(fn_myvector) # To ignore warnings due to having myst-nb reading the .ipynb created by sphinx-gallery # Should eventually be fixed, see: https://github.com/executablebooks/MyST-NB/issues/363 def setup(app): # Ignore .ipynb files app.registry.source_suffix.pop(".ipynb", None) + app.connect("build-finished", clean_gallery_files) # -- Options for HTML output ------------------------------------------------- diff --git a/examples/analysis/array_numerics/numpy_interfacing.py b/examples/analysis/array_numerics/numpy_interfacing.py index 1fce6aa72..889ca6ef0 100644 --- a/examples/analysis/array_numerics/numpy_interfacing.py +++ b/examples/analysis/array_numerics/numpy_interfacing.py @@ -4,9 +4,10 @@ This example demonstrates NumPy interfacing with rasters on :class:`Rasters`. See :ref:`core-array-funcs` for more details. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open a raster. + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) diff --git a/examples/analysis/array_numerics/python_arithmetic.py b/examples/analysis/array_numerics/python_arithmetic.py index e632165a8..4cab61f31 100644 --- a/examples/analysis/array_numerics/python_arithmetic.py +++ b/examples/analysis/array_numerics/python_arithmetic.py @@ -4,9 +4,10 @@ This example demonstrates arithmetic operations using raster arithmetic on :class:`Rasters`. See :ref:`core-py-ops` for more details. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open a raster + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) diff --git a/examples/analysis/geospatial/buffer_voronoi.py b/examples/analysis/geospatial/buffer_voronoi.py index 068ae418c..009e555fe 100644 --- a/examples/analysis/geospatial/buffer_voronoi.py +++ b/examples/analysis/geospatial/buffer_voronoi.py @@ -4,9 +4,10 @@ This example demonstrates the metric buffering of a vector using :func:`~geoutils.Vector.buffer_metric` and :func:`~geoutils.Vector.buffer_without_overlap`. """ -# sphinx_gallery_thumbnail_number = 3 # %% # We open an example vector + +# sphinx_gallery_thumbnail_number = 3 import geoutils as gu vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) diff --git a/examples/analysis/geospatial/proximity_metric.py b/examples/analysis/geospatial/proximity_metric.py index 37d754463..c2c727f2c 100644 --- a/examples/analysis/geospatial/proximity_metric.py +++ b/examples/analysis/geospatial/proximity_metric.py @@ -4,9 +4,10 @@ This example demonstrates the calculation of proximity distances to a raster or vector using :func:`~geoutils.Raster.proximity`. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open an example raster, and a vector for which we select a single feature + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) diff --git a/examples/analysis/point_extraction/interpolation.py b/examples/analysis/point_extraction/interpolation.py index 9f48c36f2..83823f5ef 100644 --- a/examples/analysis/point_extraction/interpolation.py +++ b/examples/analysis/point_extraction/interpolation.py @@ -4,9 +4,10 @@ This example demonstrates raster interpolation to point values using :func:`~geoutils.Raster.interp_points`. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open an example raster, a digital elevation model in South America + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) diff --git a/examples/analysis/point_extraction/reduction.py b/examples/analysis/point_extraction/reduction.py index ca62fbb88..fa4b3d543 100644 --- a/examples/analysis/point_extraction/reduction.py +++ b/examples/analysis/point_extraction/reduction.py @@ -4,9 +4,10 @@ This example demonstrates raster reduction to point values using :func:`~geoutils.Raster.value_at_coords`. """ -# sphinx_gallery_thumbnail_number = 3 # %% # We open an example raster, a digital elevation model in South America + +# sphinx_gallery_thumbnail_number = 3 import geoutils as gu rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) diff --git a/examples/handling/georeferencing/crop_raster.py b/examples/handling/georeferencing/crop_raster.py index 2aeefbee3..61a32e40c 100644 --- a/examples/handling/georeferencing/crop_raster.py +++ b/examples/handling/georeferencing/crop_raster.py @@ -4,9 +4,10 @@ This example demonstrates the cropping of a raster using :func:`geoutils.Raster.crop`. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open a raster and vector, and subset the latter. + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) diff --git a/examples/handling/georeferencing/crop_vector.py b/examples/handling/georeferencing/crop_vector.py index 9340f9525..043cc1e5d 100644 --- a/examples/handling/georeferencing/crop_vector.py +++ b/examples/handling/georeferencing/crop_vector.py @@ -4,9 +4,10 @@ This example demonstrates the cropping of a vector using :func:`geoutils.Vector.crop`. """ -# sphinx_gallery_thumbnail_number = 3 # %% # We open a raster and vector. + +# sphinx_gallery_thumbnail_number = 3 import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) diff --git a/examples/handling/georeferencing/reproj_raster.py b/examples/handling/georeferencing/reproj_raster.py index f996021c1..2bec585b8 100644 --- a/examples/handling/georeferencing/reproj_raster.py +++ b/examples/handling/georeferencing/reproj_raster.py @@ -6,6 +6,7 @@ """ # %% # We open two example rasters. + import geoutils as gu rast1 = gu.Raster(gu.examples.get_path("everest_landsat_b4")) @@ -18,7 +19,6 @@ # %% # Let's plot the first raster, with the warped extent of the second one. -import matplotlib.pyplot as plt rast1.show(cmap="Blues") vect_bounds_rast2 = gu.Vector.from_bounds_projected(rast2) diff --git a/examples/handling/georeferencing/reproj_vector.py b/examples/handling/georeferencing/reproj_vector.py index 566ba7a8c..82fdcfd33 100644 --- a/examples/handling/georeferencing/reproj_vector.py +++ b/examples/handling/georeferencing/reproj_vector.py @@ -4,10 +4,10 @@ This example demonstrates the reprojection of a vector using :func:`geoutils.Vector.reproject`. """ -# sphinx_gallery_thumbnail_number = 3 - # %% # We open a raster and vector. + +# sphinx_gallery_thumbnail_number = 3 import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) diff --git a/examples/handling/interface/create_mask.py b/examples/handling/interface/create_mask.py index a21a56d79..bce36f9ec 100644 --- a/examples/handling/interface/create_mask.py +++ b/examples/handling/interface/create_mask.py @@ -4,9 +4,10 @@ This example demonstrates the creation of a mask from a vector using :func:`geoutils.Vector.create_mask`. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open a raster and vector. + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) diff --git a/examples/handling/interface/polygonize.py b/examples/handling/interface/polygonize.py index 0d241a2ae..aa00a999b 100644 --- a/examples/handling/interface/polygonize.py +++ b/examples/handling/interface/polygonize.py @@ -4,9 +4,10 @@ This example demonstrates the polygonizing of a raster using :func:`geoutils.Raster.polygonize` and :func:`geoutils.Mask.polygonize`. """ -# sphinx_gallery_thumbnail_number = 3 # %% # We open a raster. + +# sphinx_gallery_thumbnail_number = 3 import geoutils as gu rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) diff --git a/examples/handling/interface/rasterize.py b/examples/handling/interface/rasterize.py index e7489d623..2ace9f6f3 100644 --- a/examples/handling/interface/rasterize.py +++ b/examples/handling/interface/rasterize.py @@ -4,9 +4,10 @@ This example demonstrates the rasterizing of a vector using :func:`geoutils.Vector.rasterize`. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open a raster and vector. + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) diff --git a/examples/handling/interface/topoints.py b/examples/handling/interface/topoints.py index 4b58e2f00..1a95e8e61 100644 --- a/examples/handling/interface/topoints.py +++ b/examples/handling/interface/topoints.py @@ -4,9 +4,10 @@ This example demonstrates the conversion of a raster to point vector using :func:`geoutils.Raster.to_points`. """ -# sphinx_gallery_thumbnail_number = 2 # %% # We open a raster. + +# sphinx_gallery_thumbnail_number = 2 import geoutils as gu rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) diff --git a/examples/io/open_save/myraster.tif b/examples/io/open_save/myraster.tif deleted file mode 100644 index c33b81ad3c1552adcf3fa2cac398a75a508f8c43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23791 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 Date: Mon, 27 Mar 2023 15:02:27 -0800 Subject: [PATCH 145/184] Linting --- .gitignore | 1 - doc/source/conf.py | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4047e2651..3e9576d1e 100644 --- a/.gitignore +++ b/.gitignore @@ -155,4 +155,3 @@ doc/jupyter_execute/ # Files that should have been deleted by Sphinx at end of build (but can exist if build fails) examples/io/open_save/myraster.tif examples/io/open_save/myvector.gpkg - diff --git a/doc/source/conf.py b/doc/source/conf.py index 0e87b4f12..f843bcd1c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -82,7 +82,7 @@ # directory where function/class granular galleries are stored "backreferences_dir": "gen_modules/backreferences", "doc_module": ("geoutils"), # Which function/class levels are used to create galleries - 'remove_config_comments': True, # To remove comments such as sphinx-gallery-thumbnail-number (only works in code, not in text) + "remove_config_comments": True, # To remove comments such as sphinx-gallery-thumbnail-number (only works in code, not in text) } extlinks = { @@ -125,6 +125,7 @@ # "special-members": "__init__", # } + def clean_gallery_files(app, exception): fn_myraster = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../examples/io/open_save/myraster.tif")) fn_myvector = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../examples/io/open_save/myvector.gpkg")) @@ -133,6 +134,7 @@ def clean_gallery_files(app, exception): if os.path.exists(fn_myvector): os.remove(fn_myvector) + # To ignore warnings due to having myst-nb reading the .ipynb created by sphinx-gallery # Should eventually be fixed, see: https://github.com/executablebooks/MyST-NB/issues/363 def setup(app): From f1027f3133d5e508876479b81ee31deb98c98d2f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 29 Mar 2023 15:59:11 -0800 Subject: [PATCH 146/184] Update logo --- doc/source/_static/logo.svg | 383 +++++++++++++++++++++++++++++++ doc/source/_static/logo_only.svg | 371 ++++++++++++++++++++++++++++++ 2 files changed, 754 insertions(+) create mode 100644 doc/source/_static/logo.svg create mode 100644 doc/source/_static/logo_only.svg diff --git a/doc/source/_static/logo.svg b/doc/source/_static/logo.svg new file mode 100644 index 000000000..5793f9f4f --- /dev/null +++ b/doc/source/_static/logo.svg @@ -0,0 +1,383 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eoUtils + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/source/_static/logo_only.svg b/doc/source/_static/logo_only.svg new file mode 100644 index 000000000..6b1711c3f --- /dev/null +++ b/doc/source/_static/logo_only.svg @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a8295211736c6301f5219341d81c9c9d321080e0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 29 Mar 2023 15:59:43 -0800 Subject: [PATCH 147/184] Move forward on Vector documentation --- doc/source/_static/logo_only_v3.svg | 313 --------------------------- doc/source/_static/logo_v3.svg | 325 ---------------------------- doc/source/about_geoutils.md | 6 +- doc/source/api.md | 17 +- doc/source/background.md | 55 +++-- doc/source/conf.py | 4 +- doc/source/core_composition.md | 19 +- doc/source/index.md | 2 +- doc/source/raster_class.md | 18 +- doc/source/vector_class.md | 204 ++++++++++++++++- geoutils/vector.py | 30 ++- 11 files changed, 289 insertions(+), 704 deletions(-) delete mode 100644 doc/source/_static/logo_only_v3.svg delete mode 100644 doc/source/_static/logo_v3.svg diff --git a/doc/source/_static/logo_only_v3.svg b/doc/source/_static/logo_only_v3.svg deleted file mode 100644 index a6dd0a730..000000000 --- a/doc/source/_static/logo_only_v3.svg +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/source/_static/logo_v3.svg b/doc/source/_static/logo_v3.svg deleted file mode 100644 index cf9bf0b60..000000000 --- a/doc/source/_static/logo_v3.svg +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eoUtils - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 1c84337ce..c01b3a376 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -11,9 +11,9 @@ making such analysis accessible, efficient and reliable. 1With name standing for *Geospatial Utilities*. ``` -In a few words, GeoUtils can be described as a **convenience wrapper package for end-users** focusing on geospatial analysis. It allows to write shorter -code through consistent higher-level operations, implicit object behaviour and numerical interfacing. In addition, GeoUtils adds **analysis-oriented -functions** that require many steps to perform with other packages, and which are robustly tested. +In a few words, GeoUtils can be described as a **convenience package for end-users focusing on geospatial analysis**. It allows to write shorter +code through consistent higher-level operations, implicit object behaviour and numerical interfacing. In addition, GeoUtils adds analysis-oriented +functions that require many steps to perform with other packages, and which are robustly tested. GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for remote sensing and Earth's surface applications** that rely on moderate- to high-resolution georeferenced data. All applications that, for analysis, require robust reprojections, re-gridding, diff --git a/doc/source/api.md b/doc/source/api.md index 03de14f19..5ee99242d 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -276,6 +276,13 @@ And reverse operations. Vector.name ``` +```{caution} +The {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Vector` corresponds to the {attr}`~geopandas.GeoDataFrame.total_bounds` attribute of a +{class}`~geopandas.GeoDataFrame`, for consistency between rasters and vectors (and can also be accessed through {attr}`~geoutils.Vector.total_bounds`). + +The equivalent of {attr}`geopandas.GeoDataFrame.bounds` (i.e., a per-feature bounds) for {class}`Vectors` is {attr}`~geoutils.Vector.geom_bounds`. +``` + ### Geospatial handling methods ```{eval-rst} @@ -316,23 +323,22 @@ And reverse operations. Vector.get_bounds_projected ``` -### Indexing and assignment +### Indexing ```{eval-rst} .. autosummary:: :toctree: gen_modules/ Vector.__getitem__ - Vector.__setitem__ ``` +(vector-from-geopandas)= + ### From Shapely and GeoPandas #### Geometric attributes and methods -This first category of attributes and methods return a geometric output, converted to a {class}`~geoutils.Vector` by default. - -Otherwise, calling the method from {attr}`Vector.ds`, they return a {class}`geopandas.GeoSeries` or {class}`geopandas.GeoDataFrame` as in GeoPandas. +This first category of attributes and methods return a geometric output converted to a {class}`~geoutils.Vector` by default. **Attributes:** @@ -464,6 +470,7 @@ To ensure those are up-to-date with GeoPandas, alternatively call those from {at Vector.has_sindex Vector.sindex + Vector.total_bounds ``` ```{seealso} diff --git a/doc/source/background.md b/doc/source/background.md index 2276e8d97..ca50864a3 100644 --- a/doc/source/background.md +++ b/doc/source/background.md @@ -2,7 +2,38 @@ # Background -More information on the people behind GeoUtils, and the package's mission. +More information on how the package was created, who are the people behind it, and its mission. + +## Inspiration + +GeoUtils was created during the [GlacioHack](https://github.com/GlacioHack) hackaton event, that took place online on November 8, 2020 and was initiated by +Amaury Dehecq2. + +```{margin} +2More on our GlacioHack founder at [adehecq.github.io](https://adehecq.github.io/)! +``` + +GeoUtils is inspired by previous efforts that were built directly on top of GDAL and OGR, namely: + +- the older, homonymous package [GeoUtils](https://github.com/adehecq/geoutils_old), +- the class [pybob.GeoImg](https://github.com/iamdonovan/pybob/blob/master/pybob/GeoImg.py), +- the package [pygeotools](https://github.com/dshean/pygeotools), and +- the package [salem](https://github.com/fmaussion/salem). + +## The people behind GeoUtils + +The initial core development of GeoUtils was mainly performed by members of the Glaciology group of the _Laboratory of Hydraulics, Hydrology and +Glaciology (VAW)_ at ETH Zürich3 and of the _University of Fribourg_, both in Switzerland. The package also received contributions by members of +the _University of Oslo_, Norway, the _University of Washington_, US and _Université Grenobles Alpes_, France. + +```{margin} +3Check-out [glaciology.ch](https://glaciology.ch) on our founding group of VAW glaciology! +``` + +We are not software developers but geoscientists, and we try our best to offer tools that can be useful to a larger group, +documented, reliable and maintained. All development and maintenance is made on a voluntary basis and we welcome +any new contributors. See some information on how to contribute in the dedicated page of our +[GitHub repository](https://github.com/GlacioHack/geoutils/blob/main/CONTRIBUTING.md). ## Mission @@ -35,25 +66,3 @@ And, additionally: - **State-of-the-art**: all methods should be at the cutting edge of remote sensing science, to provide users with the most reliable and up-to-date tools. - -## The people behind GeoUtils - -```{margin} -2More on our GlacioHack founder at [adehecq.github.io](https://adehecq.github.io/)! -``` - -GeoUtils was created during the [GlacioHack](https://github.com/GlacioHack) hackaton event, that was initiated by -Amaury Dehecq2 and took place online on November 8, 2020. - -```{margin} -3Check-out [glaciology.ch](https://glaciology.ch) on our founding group of VAW glaciology! -``` - -The initial core development of GeoUtils was performed by members of the Glaciology group of the Laboratory of Hydraulics, Hydrology and -Glaciology (VAW) at ETH Zürich3, with contributions by members of the University of Oslo, the University of Washington, and University -Grenobles Alpes. - -We are not software developers but geoscientists, and we try our best to offer tools that can be useful to a larger group, -documented, reliable and maintained. All development and maintenance is made on a voluntary basis and we welcome -any new contributors. See some information on how to contribute in the dedicated page of our -[GitHub repository](https://github.com/GlacioHack/geoutils/blob/main/CONTRIBUTING.md). diff --git a/doc/source/conf.py b/doc/source/conf.py index f843bcd1c..9ccd1a266 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -149,8 +149,8 @@ def setup(app): # a list of builtin themes. # html_theme = "sphinx_book_theme" -html_favicon = "_static/logo_only_v3.svg" -html_logo = "_static/logo_v3.svg" +html_favicon = "_static/logo_only.svg" +html_logo = "_static/logo.svg" html_title = "GeoUtils" html_theme_options = { diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index 6e5f69cae..bb811b013 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -64,13 +64,14 @@ To check if a {class}`~geoutils.Raster` was loaded from the **on-disk** data, us To check if the {class}`~geoutils.Raster` was modified since loading from the **on-disk** data, use the {attr}`~geoutils.Raster.is_modified` attribute. ``` +See {ref}`(raster-class)` for more details. + + ## The {class}`~geoutils.Vector` class composition A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`. -Because lazy loading is a lesser priority with vector data, a {class}`~geoutils.Vector` directly loads its {attr}`~geoutils.Vector.ds`. Besides, many -higher-level geospatial methods are already available in {class}`~geopandas.GeoDataFrame`. We thus only wrap those directly into {class}`~geoutils.Vector`, -in order to easily call them from the vector object, and build additional methods on top. +Because lazy loading is a lesser priority with vector data, a {class}`~geoutils.Vector` directly loads its {attr}`~geoutils.Vector.ds`. ```{code-cell} ipython3 :tags: [hide-output] @@ -83,3 +84,15 @@ vector # Show summarized information print(vector.info()) ``` + +All geospatial methods of {class}`~geopandas.GeoDataFrame` are directly available into {class}`~geoutils.Vector`, and cast the output logically depending on +its type: to a {class}`~geoutils.Vector` for a geometric output (e.g., {class}`~geoutils.Vector.boundary`), or to {class}`pandas.Series` that can be immediatelly appended to the {class} +`~geoutils.Vector` for a per-feature non-geometric output (e.g., {class}`~geoutils.Vector.area`). + +```{code-cell} ipython3 +:tags: [hide-output] +# Compute the vector's boundary +vector.boundary +``` + +See {ref}`(vector-class)` for more details. diff --git a/doc/source/index.md b/doc/source/index.md index 2a2fc9f23..8e9d2fb1b 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -10,7 +10,7 @@ title: GeoUtils :::{grid-item} :columns: 4 -```{image} ./_static/logo_only_v3.svg +```{image} ./_static/logo_only.svg :width: 300px :class: dark-light ``` diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 4d22febea..c40fee57f 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -63,7 +63,7 @@ raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) raster ``` -Detailed information on the {class}`~geoutils.Raster` is printed using {class}`~geoutils.Raster.info()`, along with basic statistics using `stats=True`: +Detailed information on the {class}`~geoutils.Raster` is printed using {func}`~geoutils.Raster.info`, along with basic statistics using `stats=True`: ```{code-cell} ipython3 # Print details of raster @@ -238,17 +238,18 @@ Resampling methods are listed in **[the dedicated section of Rasterio's API](htt Cropping a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.crop` function, which enforces new {attr}`~geoutils.Raster.bounds`. ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument. +As with all geospatial handling methods, the {func}`~geoutils.Raster.crop` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` +as argument. See {ref}`core-match-ref` for more details. ``` -The {func}`~geoutils.Raster.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). +The {func}`~geoutils.Raster.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). By default, +{func}`~geoutils.Raster.crop` is done in-place. For more details, see the {ref}`specific section and function descriptions in the API`. - ```{code-cell} ipython3 -# Crop to smaller bounds +# Crop raster to smaller bounds raster_crop = raster.crop(crop_geom=(0.3, 0.3, 1, 1), inplace=False) raster_crop ``` @@ -258,8 +259,6 @@ raster_crop Polygonizing a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.polygonize` function, which converts target pixels into a multi-polygon {class}`~geoutils.Vector`. -By default, values equal to `1` are used. - ```{note} For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly targets `True` values and thus does not require target pixels. See {ref}`mask-class-poly-overloaded`. @@ -267,15 +266,14 @@ For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly ta ```{code-cell} ipython3 # Polygonize all values lower than 100 - vector_lt_100 = (raster < 100).polygonize() vector_lt_100 ``` ## Proximity -Computing proximity from a {class}`~geoutils.Raster` is done through by the {func}`~geoutils.Raster.proximity` function, which computes the distance to any -target pixels in the {class}`~geoutils.Raster`. +Computing proximity from a {class}`~geoutils.Raster` is done through by the {func}`~geoutils.Raster.proximity` function, which computes the closest distance +to any target pixels in the {class}`~geoutils.Raster`. ```{note} For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.proximity` implicitly targets `True` values and thus does not require target pixels. See diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index cd1bc4b8d..048a22349 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -1,3 +1,8 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (vector-class)= # The georeferenced vector ({class}`~geoutils.Vector`) @@ -10,34 +15,215 @@ A {class}`~geoutils.Vector` contains **a single main attribute**: a {class}`~geo All other attributes are derivatives of the {class}`~geopandas.GeoDataFrame`. -```{important} -In short, {class}`~geoutils.Vector` is a "convenience" composition class built on top of GeoPandas, to facilitate interfacing with {class}`~geoutils.Raster`, -and to allow the addition of more complex vector functionalities. +In short, {class}`~geoutils.Vector` is a "convenience" composition class built on top of GeoPandas, to consistently cast geometric outputs to a +{class}`geoutils.Vector`, facilitate the interface with {class}`~geoutils.Raster`, and allow the addition of more complex vector functionalities. + +**All geometric functionalities of {class}`~geopandas.GeoDataFrame`'s methods are available directly from a {class}`~geoutils.Vector`**, as if working +directly on the {class}`~geopandas.GeoDataFrame`. Dataframe functionalities from Pandas can be called from its {attr}`~geoutils.Vector.ds`. + +```{caution} +The {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Vector` corresponds to the {attr}`~geopandas.GeoDataFrame.total_bounds` attribute of a +{class}`~geopandas.GeoDataFrame` converted to a {class}`rasterio.coords.BoundingBox`, for consistency between rasters and vectors. -**All of {class}`~geopandas.GeoDataFrame`'s methods are available directly from a {class}`~geoutils.Vector`**, as if working directly on the {class}`~geopandas.GeoDataFrame`. +The equivalent of {attr}`geopandas.GeoDataFrame.bounds` (i.e., a per-feature bounds) for {class}`Vectors` is {attr}`~geoutils.Vector.geom_bounds`. ``` ## Open and save +A {class}`~geoutils.Vector` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path`, a {class}`geopandas.GeoDataFrame`, +a {class}`geopandas.GeoSeries` or a {class}`shapely.Geometry`. + + +```{code-cell} ipython3 +:tags: [hide-output] + +import geoutils as gu + +# Initiate a vector from disk +vect = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) +vect +``` + +Detailed information on the {class}`~geoutils.Vector` is printed using {func}`~geoutils.Vector.info`: + +```{code-cell} ipython3 +# Print details of vector +print(vect.info()) +``` + +A {class}`~geoutils.Vector` is saved to file by calling {func}`~geoutils.Raster.save` with a {class}`str` or a {class}`pathlib.Path`. + +```{code-cell} ipython3 +# Save vector to disk +vect.save("myvector.gpkg") +``` +```{code-cell} ipython3 +:tags: [remove-cell] +import os +os.remove("myvector.gpkg") +``` + +```{note} +GeoPandas functions with the same behaviour such as {func}`geopandas.GeoDataFrame.to_file` can also be used directly on a {class}`~geoutils.Vector`, +for example calling {func}`geoutils.Vector.to_file`. +``` + -## Arithmetic +## From Shapely and GeoPandas +Nearly all geometric attributes and functions of GeoPandas (and sometimes, under the hood, Shapely) can be called from a {class}`~geoutils.Vector`. + +In {class}`~geoutils.Vector`, those have three types of behaviour: + +1. Methods that return a geometric output (e.g., {attr}`~geoutils.Vector.boundary` or {func}`~geoutils.Vector.symmetric_difference`), which are cast into a + {class}`~geoutils.Vector`, +2. Methods that return a non-geometric series of same length as the number of features (e.g., {attr}`~geoutils.Vector.area` or {func}`~geoutils.Vector.overlaps`), + which can optionally be appended to the {class}`~geoutils.Vector` (instead of returning of the default {class}`pandas.Series`), +3. Methods that return any other type of output (e.g., {func}`~geoutils.Vector.has_sindex` or {func}`~geoutils.Vector.to_feather`), for which the output is + preserved. + +```{important} +See the full list of supported methods in the {ref}`dedicated section of the API`. +``` + +These behaviours aim to simplify the analysis of vectors, removing the need to operate on many different objects due to varying function outputs +({class}`geopandas.GeoDataFrame`, {class}`geopandas.GeoSeries`, {class}`shapely.Geometry`, {class}`pandas.Series`). + +```{code-cell} ipython3 +:tags: [hide-output] + +# Example of method with geometric output +vect.boundary +``` + +```{code-cell} ipython3 +:tags: [hide-output] + +# Example of method with non-geometry output +vect.area +``` + +```{code-cell} ipython3 +:tags: [hide-output] + +# Example of method with other output type +vect.to_json() +``` ## Reproject +Reprojecting a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.reproject` function, which enforces a new {attr}`~geoutils.Vector.crs`. + +```{important} +As with all geospatial handling methods, the {func}`~geoutils.Vector.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument to match its {class}`~geoutils.Raster.crs`. + +See {ref}`core-match-ref` for more details. +``` + +The {func}`~geoutils.Vector.reproject` function can also be passed a `dst_crs` argument directly. + +```{code-cell} ipython3 +# Original CRS +print(vect.crs) +``` + +```{code-cell} ipython3 +# Open a raster for which we want to match the CRS +rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +# Reproject the vector to the raster's CRS +vect_reproj = vect.reproject(rast) +# New CRS +print(vect_reproj.crs) +``` + +```{note} +Calling {func}`geoutils.Vector.to_crs` is also possible, but mirrors GeoPandas' API (a match-reference argument cannot be passed). +``` ## Crop +Cropping a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.crop` function, which enforces new {attr}`~geoutils.Vector.bounds`. + + +```{important} +As with all geospatial handling methods, the {func}`~geoutils.Vector.crop` function can be passed only a {class}`~geoutils.Raster` or +{class}`~geoutils.Vector` as argument. + +See {ref}`core-match-ref` for more details. +``` + +The {func}`~geoutils.Vector.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). + +By default, {func}`~geoutils.Vector.crop` is done in-place and keeps all intersecting geometries. It can also be passed the `clip` argument to clip +intersecting geometries to the extent. + +```{code-cell} ipython3 +# Crop vector to smaller bounds +vect_crop = vect.crop(crop_geom=(-73.5, -46.6, -73.4, -46.5), inplace=False, clip=True) +print(vect_crop.info()) +``` ## Rasterize -The {func}`~geoutils.Vector.rasterize` operation to convert from {class}`~geoutils.Vector` to {class}`~geoutils.Raster` inherently requires a -{attr}`~geoutils.Raster.res` or {attr}`~geoutils.Raster.shape` attribute to define the grid. While those can be passed on their own. +Rasterizing a {class}`~geoutils.Vector` to a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Vector.rasterize` function, which converts vector +geometries into gridded values. -## Proximity +By default, the value of index of the {class}`~geoutils.Vector`'s {attr}`~geoutils.Vector.ds` is burned on a raster grid for each respective geometry. +```{note} +If an `out_value` of `0` (default) and `in_value` value of `1` are passed (i.e., boolean output), {func}`~geoutils.Vector.rasterize` will automatically cast +the output to a {class}`~geoutils.Mask`. +``` + +To define the grid on which to rasterize, a reference {class}`~geoutils.Raster` to match can be passed. Alternatively, a {attr}`~geoutils.Raster.res` or {attr} +`~geoutils.Raster.shape` can be passed to define the grid. + +```{code-cell} ipython3 +# Rasterize all geometries by index +rasterized_vect = vect.rasterize(rast) +rasterized_vect +``` ## Create a `Mask` +Creating a {class}`~geoutils.Mask` from a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.create_mask` function, which converts vector +geometries into boolean gridded values for all features. + +Similarly as for {func}`~geoutils.Vector.rasterize`, the function expects parameters to define the grid on which to rasterize the output. A reference +{class}`~geoutils.Raster` to match can be passed or, alternatively, individual parameters. + +```{code-cell} ipython3 +# Create a mask of all geometries on the raster grid +mask_vect = vect.create_mask(rast) +mask_vect +``` + +## Proximity + +Computing proximity from a {class}`~geoutils.Vector` is done through by the {func}`~geoutils.Vector.proximity` function, which computes the closest distance +to any geometry in the {class}`~geoutils.Vector`. + +Similarly as for {func}`~geoutils.Vector.rasterize`, the function expects parameters to define the grid on which to rasterize the output. A reference +{class}`~geoutils.Raster` to match can be passed or, alternatively, individual parameters. + +```{code-cell} ipython3 +# Compute proximity from vector on the raster grid +proximity_to_vect = vect.proximity(rast) +proximity_to_vect +``` + +## Metric buffering + +Computing a buffer accurately in a local metric projection is done through the {func}`~geoutils.Vector.buffer_metric` function, which computes the buffer in +a local UTM zone. + +```{code-cell} ipython3 +:tags: [hide-output] + +# Compute buffer of 100 m on the vector +buffered_vect = vect.buffer_metric(100) +buffered_vect +``` -## Buffering +Additionally, to prevent buffers from overlapping, the {func}`~geoutils.Vector.buffer_without_overlap` function uses Voronoi polygons to reconcile the buffer +output of each feature. diff --git a/geoutils/vector.py b/geoutils/vector.py index c18249d06..d419e2ce8 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -61,11 +61,11 @@ class Vector: See the API for more details. """ - def __init__(self, filename_or_dataset: str | pathlib.Path | gpd.GeoDataFrame): + def __init__(self, filename_or_dataset: str | pathlib.Path | gpd.GeoDataFrame | gpd.GeoSeries | shapely.Geometry): """ - Instantiate a vector from a filename or GeoPandas geodataframe. + Instantiate a vector from either a filename, a GeoPandas dataframe or series, or a Shapely geometry. - :param filename_or_dataset: Path to file or GeoPandas geodataframe. + :param filename_or_dataset: Path to file, or GeoPandas dataframe or series, or Shapely geometry. """ if isinstance(filename_or_dataset, (str, pathlib.Path)): @@ -75,9 +75,14 @@ def __init__(self, filename_or_dataset: str | pathlib.Path | gpd.GeoDataFrame): ds = gpd.read_file(filename_or_dataset) self._ds = ds self._name: str | gpd.GeoDataFrame | None = filename_or_dataset - elif isinstance(filename_or_dataset, gpd.GeoDataFrame): - self._ds = filename_or_dataset + elif isinstance(filename_or_dataset, (gpd.GeoDataFrame, gpd.GeoSeries, shapely.Geometry)): self._name = None + if isinstance(filename_or_dataset, gpd.GeoDataFrame): + self._ds = filename_or_dataset + elif isinstance(filename_or_dataset, gpd.GeoSeries): + self._ds = gpd.GeoDataFrame(geometry=filename_or_dataset) + else: + self._ds = gpd.GeoDataFrame({"geometry": [filename_or_dataset]}, crs=None) else: raise TypeError("Filename argument should be a string, Path or geopandas.GeoDataFrame.") @@ -132,15 +137,14 @@ def info(self) -> str: """ Summarize information about the vector. - :returns: Unformation about vector attributes. + :returns: Information about vector attributes. """ as_str = [ # 'Driver: {} \n'.format(self.driver), f"Filename: {self.name} \n", f"Coordinate System: EPSG:{self.ds.crs.to_epsg()}\n", - f"Number of features: {len(self.ds)} \n", f"Extent: {self.ds.total_bounds.tolist()} \n", - f"Attributes: {self.ds.columns.tolist()} \n", - self.ds.__repr__(), + f"Number of features: {len(self.ds)} \n", + f"Attributes: {self.ds.columns.tolist()}", ] return "".join(as_str) @@ -415,7 +419,13 @@ def total_bounds(self) -> rio.coords.BoundingBox: # Exception ! Vector.bounds corresponds to the total_bounds @property def bounds(self) -> rio.coords.BoundingBox: - """Total bounding box of the vector.""" + """ + Total bounding box of the vector. + + Caution: this is equivalent to ``GeoDataFrame.total_bounds``, + but not ``GeoDataFrame.bounds`` (per-feature bounds) which is instead defined as + ``Vector.geom_bounds``. + """ return rio.coords.BoundingBox(*self.ds.total_bounds) # -------------------------------------------- From e110657bddbc04c1f85ea4ad6ed123704bb67985 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 29 Mar 2023 16:00:17 -0800 Subject: [PATCH 148/184] Linting --- doc/source/api.md | 2 +- doc/source/background.md | 9 ++++--- doc/source/core_composition.md | 4 ++-- doc/source/raster_class.md | 6 ++--- doc/source/vector_class.md | 44 +++++++++++++++++----------------- 5 files changed, 32 insertions(+), 33 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index 5ee99242d..acface42f 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -277,7 +277,7 @@ And reverse operations. ``` ```{caution} -The {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Vector` corresponds to the {attr}`~geopandas.GeoDataFrame.total_bounds` attribute of a +The {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Vector` corresponds to the {attr}`~geopandas.GeoDataFrame.total_bounds` attribute of a {class}`~geopandas.GeoDataFrame`, for consistency between rasters and vectors (and can also be accessed through {attr}`~geoutils.Vector.total_bounds`). The equivalent of {attr}`geopandas.GeoDataFrame.bounds` (i.e., a per-feature bounds) for {class}`Vectors` is {attr}`~geoutils.Vector.geom_bounds`. diff --git a/doc/source/background.md b/doc/source/background.md index ca50864a3..f357fc2c7 100644 --- a/doc/source/background.md +++ b/doc/source/background.md @@ -13,17 +13,17 @@ Amaury Dehecq2. 2More on our GlacioHack founder at [adehecq.github.io](https://adehecq.github.io/)! ``` -GeoUtils is inspired by previous efforts that were built directly on top of GDAL and OGR, namely: +GeoUtils is inspired by previous efforts that were built directly on top of GDAL and OGR, namely: -- the older, homonymous package [GeoUtils](https://github.com/adehecq/geoutils_old), -- the class [pybob.GeoImg](https://github.com/iamdonovan/pybob/blob/master/pybob/GeoImg.py), +- the older, homonymous package [GeoUtils](https://github.com/adehecq/geoutils_old), +- the class [pybob.GeoImg](https://github.com/iamdonovan/pybob/blob/master/pybob/GeoImg.py), - the package [pygeotools](https://github.com/dshean/pygeotools), and - the package [salem](https://github.com/fmaussion/salem). ## The people behind GeoUtils The initial core development of GeoUtils was mainly performed by members of the Glaciology group of the _Laboratory of Hydraulics, Hydrology and -Glaciology (VAW)_ at ETH Zürich3 and of the _University of Fribourg_, both in Switzerland. The package also received contributions by members of +Glaciology (VAW)_ at ETH Zürich3 and of the _University of Fribourg_, both in Switzerland. The package also received contributions by members of the _University of Oslo_, Norway, the _University of Washington_, US and _Université Grenobles Alpes_, France. ```{margin} @@ -65,4 +65,3 @@ And, additionally: - **Scalability**: all methods should support both lazy processing and distributed parallelized processing, to work with high-resolution data on local machines as well as on HPCs; - **State-of-the-art**: all methods should be at the cutting edge of remote sensing science, to provide users with the most reliable and up-to-date tools. - diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index bb811b013..a2a33373f 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -85,8 +85,8 @@ vector print(vector.info()) ``` -All geospatial methods of {class}`~geopandas.GeoDataFrame` are directly available into {class}`~geoutils.Vector`, and cast the output logically depending on -its type: to a {class}`~geoutils.Vector` for a geometric output (e.g., {class}`~geoutils.Vector.boundary`), or to {class}`pandas.Series` that can be immediatelly appended to the {class} +All geospatial methods of {class}`~geopandas.GeoDataFrame` are directly available into {class}`~geoutils.Vector`, and cast the output logically depending on +its type: to a {class}`~geoutils.Vector` for a geometric output (e.g., {class}`~geoutils.Vector.boundary`), or to {class}`pandas.Series` that can be immediately appended to the {class} `~geoutils.Vector` for a per-feature non-geometric output (e.g., {class}`~geoutils.Vector.area`). ```{code-cell} ipython3 diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index c40fee57f..48a33129a 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -238,13 +238,13 @@ Resampling methods are listed in **[the dedicated section of Rasterio's API](htt Cropping a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster.crop` function, which enforces new {attr}`~geoutils.Raster.bounds`. ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Raster.crop` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` +As with all geospatial handling methods, the {func}`~geoutils.Raster.crop` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument. See {ref}`core-match-ref` for more details. ``` -The {func}`~geoutils.Raster.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). By default, +The {func}`~geoutils.Raster.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). By default, {func}`~geoutils.Raster.crop` is done in-place. For more details, see the {ref}`specific section and function descriptions in the API`. @@ -272,7 +272,7 @@ vector_lt_100 ## Proximity -Computing proximity from a {class}`~geoutils.Raster` is done through by the {func}`~geoutils.Raster.proximity` function, which computes the closest distance +Computing proximity from a {class}`~geoutils.Raster` is done through by the {func}`~geoutils.Raster.proximity` function, which computes the closest distance to any target pixels in the {class}`~geoutils.Raster`. ```{note} diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index 048a22349..e13018c21 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -15,14 +15,14 @@ A {class}`~geoutils.Vector` contains **a single main attribute**: a {class}`~geo All other attributes are derivatives of the {class}`~geopandas.GeoDataFrame`. -In short, {class}`~geoutils.Vector` is a "convenience" composition class built on top of GeoPandas, to consistently cast geometric outputs to a +In short, {class}`~geoutils.Vector` is a "convenience" composition class built on top of GeoPandas, to consistently cast geometric outputs to a {class}`geoutils.Vector`, facilitate the interface with {class}`~geoutils.Raster`, and allow the addition of more complex vector functionalities. -**All geometric functionalities of {class}`~geopandas.GeoDataFrame`'s methods are available directly from a {class}`~geoutils.Vector`**, as if working +**All geometric functionalities of {class}`~geopandas.GeoDataFrame`'s methods are available directly from a {class}`~geoutils.Vector`**, as if working directly on the {class}`~geopandas.GeoDataFrame`. Dataframe functionalities from Pandas can be called from its {attr}`~geoutils.Vector.ds`. ```{caution} -The {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Vector` corresponds to the {attr}`~geopandas.GeoDataFrame.total_bounds` attribute of a +The {attr}`~geoutils.Vector.bounds` attribute of a {class}`~geoutils.Vector` corresponds to the {attr}`~geopandas.GeoDataFrame.total_bounds` attribute of a {class}`~geopandas.GeoDataFrame` converted to a {class}`rasterio.coords.BoundingBox`, for consistency between rasters and vectors. The equivalent of {attr}`geopandas.GeoDataFrame.bounds` (i.e., a per-feature bounds) for {class}`Vectors` is {attr}`~geoutils.Vector.geom_bounds`. @@ -30,7 +30,7 @@ The equivalent of {attr}`geopandas.GeoDataFrame.bounds` (i.e., a per-feature bou ## Open and save -A {class}`~geoutils.Vector` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path`, a {class}`geopandas.GeoDataFrame`, +A {class}`~geoutils.Vector` is opened by instantiating with either a {class}`str`, a {class}`pathlib.Path`, a {class}`geopandas.GeoDataFrame`, a {class}`geopandas.GeoSeries` or a {class}`shapely.Geometry`. @@ -64,7 +64,7 @@ os.remove("myvector.gpkg") ``` ```{note} -GeoPandas functions with the same behaviour such as {func}`geopandas.GeoDataFrame.to_file` can also be used directly on a {class}`~geoutils.Vector`, +GeoPandas functions with the same behaviour such as {func}`geopandas.GeoDataFrame.to_file` can also be used directly on a {class}`~geoutils.Vector`, for example calling {func}`geoutils.Vector.to_file`. ``` @@ -75,19 +75,19 @@ Nearly all geometric attributes and functions of GeoPandas (and sometimes, under In {class}`~geoutils.Vector`, those have three types of behaviour: -1. Methods that return a geometric output (e.g., {attr}`~geoutils.Vector.boundary` or {func}`~geoutils.Vector.symmetric_difference`), which are cast into a +1. Methods that return a geometric output (e.g., {attr}`~geoutils.Vector.boundary` or {func}`~geoutils.Vector.symmetric_difference`), which are cast into a {class}`~geoutils.Vector`, -2. Methods that return a non-geometric series of same length as the number of features (e.g., {attr}`~geoutils.Vector.area` or {func}`~geoutils.Vector.overlaps`), +2. Methods that return a non-geometric series of same length as the number of features (e.g., {attr}`~geoutils.Vector.area` or {func}`~geoutils.Vector.overlaps`), which can optionally be appended to the {class}`~geoutils.Vector` (instead of returning of the default {class}`pandas.Series`), -3. Methods that return any other type of output (e.g., {func}`~geoutils.Vector.has_sindex` or {func}`~geoutils.Vector.to_feather`), for which the output is +3. Methods that return any other type of output (e.g., {func}`~geoutils.Vector.has_sindex` or {func}`~geoutils.Vector.to_feather`), for which the output is preserved. ```{important} See the full list of supported methods in the {ref}`dedicated section of the API`. ``` -These behaviours aim to simplify the analysis of vectors, removing the need to operate on many different objects due to varying function outputs -({class}`geopandas.GeoDataFrame`, {class}`geopandas.GeoSeries`, {class}`shapely.Geometry`, {class}`pandas.Series`). +These behaviours aim to simplify the analysis of vectors, removing the need to operate on many different objects due to varying function outputs +({class}`geopandas.GeoDataFrame`, {class}`geopandas.GeoSeries`, {class}`shapely.Geometry`, {class}`pandas.Series`). ```{code-cell} ipython3 :tags: [hide-output] @@ -146,15 +146,15 @@ Cropping a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Vector.crop` function can be passed only a {class}`~geoutils.Raster` or +As with all geospatial handling methods, the {func}`~geoutils.Vector.crop` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument. See {ref}`core-match-ref` for more details. ``` -The {func}`~geoutils.Vector.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). +The {func}`~geoutils.Vector.crop` function can also be passed a {class}`list` or {class}`tuple` of bounds (`xmin`, `ymin`, `xmax`, `ymax`). -By default, {func}`~geoutils.Vector.crop` is done in-place and keeps all intersecting geometries. It can also be passed the `clip` argument to clip +By default, {func}`~geoutils.Vector.crop` is done in-place and keeps all intersecting geometries. It can also be passed the `clip` argument to clip intersecting geometries to the extent. ```{code-cell} ipython3 @@ -165,18 +165,18 @@ print(vect_crop.info()) ## Rasterize -Rasterizing a {class}`~geoutils.Vector` to a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Vector.rasterize` function, which converts vector -geometries into gridded values. +Rasterizing a {class}`~geoutils.Vector` to a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Vector.rasterize` function, which converts vector +geometries into gridded values. By default, the value of index of the {class}`~geoutils.Vector`'s {attr}`~geoutils.Vector.ds` is burned on a raster grid for each respective geometry. ```{note} -If an `out_value` of `0` (default) and `in_value` value of `1` are passed (i.e., boolean output), {func}`~geoutils.Vector.rasterize` will automatically cast +If an `out_value` of `0` (default) and `in_value` value of `1` are passed (i.e., boolean output), {func}`~geoutils.Vector.rasterize` will automatically cast the output to a {class}`~geoutils.Mask`. ``` To define the grid on which to rasterize, a reference {class}`~geoutils.Raster` to match can be passed. Alternatively, a {attr}`~geoutils.Raster.res` or {attr} -`~geoutils.Raster.shape` can be passed to define the grid. +`~geoutils.Raster.shape` can be passed to define the grid. ```{code-cell} ipython3 # Rasterize all geometries by index @@ -186,10 +186,10 @@ rasterized_vect ## Create a `Mask` -Creating a {class}`~geoutils.Mask` from a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.create_mask` function, which converts vector +Creating a {class}`~geoutils.Mask` from a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.create_mask` function, which converts vector geometries into boolean gridded values for all features. -Similarly as for {func}`~geoutils.Vector.rasterize`, the function expects parameters to define the grid on which to rasterize the output. A reference +Similarly as for {func}`~geoutils.Vector.rasterize`, the function expects parameters to define the grid on which to rasterize the output. A reference {class}`~geoutils.Raster` to match can be passed or, alternatively, individual parameters. ```{code-cell} ipython3 @@ -200,10 +200,10 @@ mask_vect ## Proximity -Computing proximity from a {class}`~geoutils.Vector` is done through by the {func}`~geoutils.Vector.proximity` function, which computes the closest distance +Computing proximity from a {class}`~geoutils.Vector` is done through by the {func}`~geoutils.Vector.proximity` function, which computes the closest distance to any geometry in the {class}`~geoutils.Vector`. -Similarly as for {func}`~geoutils.Vector.rasterize`, the function expects parameters to define the grid on which to rasterize the output. A reference +Similarly as for {func}`~geoutils.Vector.rasterize`, the function expects parameters to define the grid on which to rasterize the output. A reference {class}`~geoutils.Raster` to match can be passed or, alternatively, individual parameters. ```{code-cell} ipython3 @@ -214,7 +214,7 @@ proximity_to_vect ## Metric buffering -Computing a buffer accurately in a local metric projection is done through the {func}`~geoutils.Vector.buffer_metric` function, which computes the buffer in +Computing a buffer accurately in a local metric projection is done through the {func}`~geoutils.Vector.buffer_metric` function, which computes the buffer in a local UTM zone. ```{code-cell} ipython3 From 16101a87d4d1cbbf2ac9cca3ca3ce6de423b2489 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 30 Mar 2023 10:02:24 -0800 Subject: [PATCH 149/184] Incremental commit on doc --- doc/source/about_geoutils.md | 34 +++++++++---------- doc/source/core_inheritance.md | 3 +- doc/source/index.md | 17 ++++++---- doc/source/quick_start.md | 60 +++++++++++++--------------------- doc/source/vector_class.md | 6 ++-- 5 files changed, 52 insertions(+), 68 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index c01b3a376..161c37b1a 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -12,19 +12,11 @@ making such analysis accessible, efficient and reliable. ``` In a few words, GeoUtils can be described as a **convenience package for end-users focusing on geospatial analysis**. It allows to write shorter -code through consistent higher-level operations, implicit object behaviour and numerical interfacing. In addition, GeoUtils adds analysis-oriented +code through consistent higher-level operations, implicit object behaviour and interfacing. In addition, GeoUtils adds several analysis-oriented functions that require many steps to perform with other packages, and which are robustly tested. -GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for remote sensing and Earth's surface -applications** that rely on moderate- to high-resolution georeferenced data. All applications that, for analysis, require robust reprojections, re-gridding, -point interpolation, and other types of fine-grid analysis with millions of pixels. - - -```{important} -GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for -**reproducibility**! -We are working on making features fully consistent for the first long-term release ``v0.1`` (likely sometime in 2023). -``` +GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for surface applications that rely on +moderate- to high-resolution data** (requiring reprojection, re-gridding, point interpolation, and other types of fine-grid analysis). ## Why use GeoUtils? @@ -33,7 +25,7 @@ and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenc [SciPy](https://docs.scipy.org/doc/scipy/) and [Xarray](https://docs.xarray.dev/en/stable/) for scientific computing to provide: - A **common and consistent framework** for efficient raster and vector handling, - A structure following the **principal of least knowledge**2 to foster accessibility, -- A **pythonic arithmetic** and **NumPy interfacing** for robust numerical computing and intuitive analysis. +- A **pythonic arithmetic** and **NumPy interfacing** for robust numerical computing. ```{margin} 2Or the [Law of Demeter](https://en.wikipedia.org/wiki/Law_of_Demeter) for software development. @@ -41,9 +33,9 @@ and [PyProj](https://pyproj4.github.io/pyproj/stable/index.html) for georeferenc In particular, GeoUtils: - Rarely requires more than **single-line operations** thanks to its object-based structure, -- Strives to rely on **lazy-operations** under-the-hood to avoid unnecessary data loading, +- Strives to rely on **lazy operations** under-the-hood to avoid unnecessary data loading, - Allows for **match-reference operations** to facilitate geospatial handling, -- Re-implements **several of [GDAL](https://gdal.org/)'s missing features** (Proximity, DEM, Calc, etc), +- Re-implements **several of [GDAL](https://gdal.org/)'s missing features** (Proximity, DEM through xDEM, etc), - Naturally handles **different `dtypes` and `nodata`** values through its NumPy masked-array interface. @@ -58,11 +50,15 @@ complexity of [GDAL and OGR](https://gdal.org/)'s Python bindings** for raster a [Rasterio](https://rasterio.readthedocs.io/en/latest/) and [GeoPandas](https://geopandas.org/en/stable/docs.html). However, these new packages still maintain a relatively low-level API to serve all types of geospatial informatics users, **slowing down end-users focusing -on data analysis**. As a result, basic interfacing between vector data and raster data is not always straightforward and simple higher-level operation (such as -reprojection to match a reference) are not always computed consistently in the community. - -Additionally, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses mostly on reading, projecting and writing, and thus **requires array extraction -and re-encapsulation** either before, during or after any numerical computation to interface with other georeferencing packages. +on data analysis**. As a result, basic interfacing between vectors and rasters is not always straightforward and simple higher-level operations (such as +reprojection to match a vector or raster reference, or point interpolation) are not always computed consistently in the community. + +On one hand, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses largely on reading, projecting and writing, and thus **requires +array extraction, re-encapsulation, and the volatile passing of metadata** either before, during or after any numerical calculations. On the other hand, +[GeoPandas](https://geopandas.org/en/stable/docs.html) focuses on integrating [Shapely](https://shapely.readthedocs.io/en/stable/) geometries in the +[Pandas](https://pandas.pydata.org/) framework, which is practical for tabular analysis but **yields a multitude of outputs (dataframes, series, geoseries, +geometries), often requiring object re-construction and specific reprojection routines** to analyze with other data, or derive metric attributes (area, +length). Finally, **many common geospatial analysis tools are generally unavailable** in existing packages (e.g., boolean-masking from vectors, [proximity](https://gdal.org/programs/gdal_proximity.html) estimation, metric buffering) as they rely on a combination of lower-level operations. diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 2f9383507..7a60a65b7 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -27,8 +27,7 @@ The {class}`~xdem.DEM` class re-implements all methods of [gdalDEM](https://gdal (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same results. Among others, it also adds a {attr}`~xdem.DEM.vref` property to consistently manage vertical referencing (ellipsoid, geoids). -If you are DEM-enthuisiastic, **[check-out our sister-package xDEM](https://xdem.readthedocs.io/en/latest/index.html)** for the analysis of digital -elevation models. +If you are DEM-enthusiastic, **[check-out our sister package xDEM](https://xdem.readthedocs.io/en/latest/index.html) for digital elevation models.** ``` ## The internal {class}`~geoutils.SatelliteImage` subclass diff --git a/doc/source/index.md b/doc/source/index.md index 8e9d2fb1b..962ec1993 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -38,6 +38,13 @@ its testing suite and type checking ([Pytest](https://docs.pytest.org/en/7.2.x/) # Where to start? +```{important} +:class: margin +GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for +**reproducibility**! +We are working on making features fully consistent for the first long-term release ``v0.1`` (likely sometime in 2023). +``` + ::::{grid} 1 2 2 3 :gutter: 1 1 1 2 @@ -73,14 +80,10 @@ Dive into the full documentation. :::: ----------------- - -```{important} -:class: margin -GeoUtils is in early stages of development and its features might evolve rapidly. Note the version you are working on for -**reproducibility**! -We are working on making features fully consistent for the first long-term release ``v0.1`` (likely sometime in 2023). +```{seealso} +If you are DEM-enthusiastic, **[check-out our sister package xDEM](https://xdem.readthedocs.io/en/latest/index.html) for digital elevation models.** ``` +---------------- # Table of contents diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index ff36f9b94..0256ff005 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -30,9 +30,9 @@ rast = gu.Raster(filename_rast) vect = gu.Vector(filename_vect) ``` -A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`~numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, a -{class}`~pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, an {class}`~affine.Affine` as {attr}`~geoutils.Raster.transform`, and a {class}`float` or {class} -`int` as {attr}`geoutils.Raster.nodata`. +A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, a +{class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, and a {class}`float` or +{class}`int` as {attr}`~geoutils.Raster.nodata`. ```{code-cell} ipython3 @@ -135,7 +135,7 @@ To facilitate the analysis process, GeoUtils includes quick plotting tools that Those are wrapped from {func}`rasterio.plot.show` and {func}`geopandas.GeoDataFrame.plot`, and relay any argument passed. ```{seealso} -GeoUtils' plotting tools only aim to get rid off the most common hassles when quickly plotting raster and vector data during analysis. +GeoUtils' plotting tools only aim to smooth out the most common hassles when quickly plotting raster and vectors. For advanced plotting tools to create "publication-quality" figures, see [Cartopy](https://scitools.org.uk/cartopy/docs/latest/) or [GeoPlot](https://residentmario.github.io/geoplot/index.html). @@ -155,7 +155,7 @@ vect.show(rast_proximity_to_vec, fc="none") All {class}`~geoutils.Raster` objects support Python arithmetic ({func}`+`, {func}`-`, {func}`/`, {func}`//`, {func}`*`, {func}`**`, {func}`%`) with any other {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or -number. For other {class}`~geoutils.Raster`, the georeferencing must match, while only the shape for other {class}`~numpy.ndarray`. +number. With another {class}`~geoutils.Raster`, the georeferencing must match, while only the shape with a {class}`~numpy.ndarray`. ```{code-cell} ipython3 # Add 1 to the raster array @@ -208,13 +208,29 @@ Finally, for saving a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` to ```{code-cell} ipython3 # Save our AOI vector -# vect_aoi.save() +vect_aoi.save("myaoi.gpkg") ``` ```{code-cell} ipython3 :tags: [remove-cell] import os -# os.remove() +os.remove("myaoi.gpkg") +``` + +## Parsing metadata with {class}`~geoutils.SatelliteImage` + +In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses +metadata recognized from the filename or auxiliary files. + +```{code-cell} ipython3 +# Name of the image we used +import os +print(os.path.basename(filename_rast)) +``` + +```{code-cell} ipython3 +# Open while parsing metadata +rast = gu.SatelliteImage(filename_rast, silent=False) ``` ```{admonition} Wrap-up @@ -227,8 +243,6 @@ In a few lines, we: **Our result:** a vector of high infrared absorption indexes at least 200 meters away from glaciers near Everest, which likely corresponds to **perennial snowfields**. -For a **bonus** example on parsing satellite metadata and DEMs, continue below. - Otherwise, for more **hands-on** examples, explore GeoUtils' gallery of examples! ``` @@ -247,31 +261,3 @@ See also the full concatenated list of examples below. :add-heading: Examples using rasters and vectors ``` -## **Bonus:** Parsing metadata with {class}`~geoutils.SatelliteImage` - -In our case, `rast` would be better opened using the {class}`~geoutils.Raster` subclass {class}`~geoutils.SatelliteImage` instead, which tentatively parses -metadata recognized from the filename or auxiliary files. - -```{code-cell} ipython3 -# Name of the image we used -import os -print(os.path.basename(filename_rast)) -``` - -```{code-cell} ipython3 -# Open while parsing metadata -rast = gu.SatelliteImage(filename_rast, silent=False) -``` - -There are many possible subclass to derive from a {class}`~geoutils.Raster`. Here's an **overview of current {class}`~geoutils.Raster` class inheritance**, which extends into -[xDEM](https://xdem.readthedocs.io/en/latest/index.html) through the {class}`~xdem.DEM` class for analyzing digital elevation models: - -```{eval-rst} -.. inheritance-diagram:: geoutils.raster.raster geoutils.raster.satimg xdem.dem.DEM - :top-classes: geoutils.raster.raster.Raster -``` -```{seealso} -The {class}`~xdem.DEM` class of [xDEM](https://xdem.readthedocs.io/en/latest/index.html) re-implements all methods of [gdalDEM](https://gdal.org/programs/gdaldem.html) -(and more) to derive topographic attributes (hillshade, slope, aspect, etc), coded directly in Python for scalability and tested to yield the exact same -results. -``` diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index e13018c21..07941e058 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -175,8 +175,8 @@ If an `out_value` of `0` (default) and `in_value` value of `1` are passed (i.e., the output to a {class}`~geoutils.Mask`. ``` -To define the grid on which to rasterize, a reference {class}`~geoutils.Raster` to match can be passed. Alternatively, a {attr}`~geoutils.Raster.res` or {attr} -`~geoutils.Raster.shape` can be passed to define the grid. +To define the grid on which to rasterize, a reference {class}`~geoutils.Raster` to match can be passed. Alternatively, a {attr}`~geoutils.Raster.res` or +{attr}`~geoutils.Raster.shape` can be passed to define the grid. ```{code-cell} ipython3 # Rasterize all geometries by index @@ -184,7 +184,7 @@ rasterized_vect = vect.rasterize(rast) rasterized_vect ``` -## Create a `Mask` +## Create a {class}`~geoutils.Mask` Creating a {class}`~geoutils.Mask` from a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.create_mask` function, which converts vector geometries into boolean gridded values for all features. From 4cde0585d29d8701b2c1291649c52317233ea086 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 30 Mar 2023 10:02:44 -0800 Subject: [PATCH 150/184] Linting --- doc/source/about_geoutils.md | 14 +++++++------- doc/source/quick_start.md | 3 +-- doc/source/vector_class.md | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index 161c37b1a..d07993469 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -15,7 +15,7 @@ In a few words, GeoUtils can be described as a **convenience package for end-use code through consistent higher-level operations, implicit object behaviour and interfacing. In addition, GeoUtils adds several analysis-oriented functions that require many steps to perform with other packages, and which are robustly tested. -GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for surface applications that rely on +GeoUtils is designed for all Earth and planetary observation science. However, it is generally **most useful for surface applications that rely on moderate- to high-resolution data** (requiring reprojection, re-gridding, point interpolation, and other types of fine-grid analysis). ## Why use GeoUtils? @@ -51,13 +51,13 @@ complexity of [GDAL and OGR](https://gdal.org/)'s Python bindings** for raster a However, these new packages still maintain a relatively low-level API to serve all types of geospatial informatics users, **slowing down end-users focusing on data analysis**. As a result, basic interfacing between vectors and rasters is not always straightforward and simple higher-level operations (such as -reprojection to match a vector or raster reference, or point interpolation) are not always computed consistently in the community. +reprojection to match a vector or raster reference, or point interpolation) are not always computed consistently in the community. -On one hand, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses largely on reading, projecting and writing, and thus **requires -array extraction, re-encapsulation, and the volatile passing of metadata** either before, during or after any numerical calculations. On the other hand, -[GeoPandas](https://geopandas.org/en/stable/docs.html) focuses on integrating [Shapely](https://shapely.readthedocs.io/en/stable/) geometries in the -[Pandas](https://pandas.pydata.org/) framework, which is practical for tabular analysis but **yields a multitude of outputs (dataframes, series, geoseries, -geometries), often requiring object re-construction and specific reprojection routines** to analyze with other data, or derive metric attributes (area, +On one hand, [Rasterio](https://rasterio.readthedocs.io/en/latest/) focuses largely on reading, projecting and writing, and thus **requires +array extraction, re-encapsulation, and the volatile passing of metadata** either before, during or after any numerical calculations. On the other hand, +[GeoPandas](https://geopandas.org/en/stable/docs.html) focuses on integrating [Shapely](https://shapely.readthedocs.io/en/stable/) geometries in the +[Pandas](https://pandas.pydata.org/) framework, which is practical for tabular analysis but **yields a multitude of outputs (dataframes, series, geoseries, +geometries), often requiring object re-construction and specific reprojection routines** to analyze with other data, or derive metric attributes (area, length). Finally, **many common geospatial analysis tools are generally unavailable** in existing packages (e.g., boolean-masking from vectors, diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 0256ff005..8acedf327 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -31,7 +31,7 @@ vect = gu.Vector(filename_vect) ``` A {class}`~geoutils.Raster` is a composition class with four main attributes: a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, a -{class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, and a {class}`float` or +{class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, and a {class}`float` or {class}`int` as {attr}`~geoutils.Raster.nodata`. @@ -260,4 +260,3 @@ See also the full concatenated list of examples below. .. minigallery:: geoutils.Raster :add-heading: Examples using rasters and vectors ``` - diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index 07941e058..c16e3c156 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -175,7 +175,7 @@ If an `out_value` of `0` (default) and `in_value` value of `1` are passed (i.e., the output to a {class}`~geoutils.Mask`. ``` -To define the grid on which to rasterize, a reference {class}`~geoutils.Raster` to match can be passed. Alternatively, a {attr}`~geoutils.Raster.res` or +To define the grid on which to rasterize, a reference {class}`~geoutils.Raster` to match can be passed. Alternatively, a {attr}`~geoutils.Raster.res` or {attr}`~geoutils.Raster.shape` can be passed to define the grid. ```{code-cell} ipython3 From ba6cad184df9dbd082eb7751a8940b5f25aa87a0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 13:01:31 -0800 Subject: [PATCH 151/184] Add tests for get_footprint_projected --- doc/source/api.md | 4 +- doc/source/core_array_funcs.md | 36 ++++++++++++++---- doc/source/proj_tools.md | 53 +++++++++++++++++++++++++- geoutils/projtools.py | 68 ++++++++++++++++++++++++++++++++++ geoutils/raster/raster.py | 14 +++++++ geoutils/vector.py | 20 ++++++++-- tests/test_projtools.py | 44 ++++++++++++++++++++++ 7 files changed, 226 insertions(+), 13 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index acface42f..b32e8cf68 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -314,13 +314,15 @@ The equivalent of {attr}`geopandas.GeoDataFrame.bounds` (i.e., a per-feature bou Vector.buffer_without_overlap ``` -### Coordinate and extent methods +### Projection tools ```{eval-rst} .. autosummary:: :toctree: gen_modules/ + Vector.from_bounds_projected Vector.get_bounds_projected + Vector.get_footprint_projected ``` ### Indexing diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index df97d5499..4cf92952b 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -87,21 +87,43 @@ which are all other non-universal functions that can be applied to an array. Tho np.max(raster) ``` -```{code-cell} ipython3 -# Masked-array function -np.ma.median(raster) -``` - ```{code-cell} ipython3 # Expliciting an axis for reduction np.count_nonzero(raster, axis=2) ``` - Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), [masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). A full list of supported array function is available in {attr}`geoutils.raster.raster.handled_array_funcs`. ## Respecting masked values -TODO: finalize once masked-array recursion bug is fixed +There are two ways to compute statistics on {class}`Rasters` while respecting masked values: + +1. Use any NumPy core function (`np.func`) directly on the {class}`~geoutils.Raster` (this includes NaN functions `np.nanfunc`), +2. Use any NumPy masked-array function (`np.ma.func`) on {attr}`Raster.data`. + +```{code-cell} ipython3 +# Numpy core function applied to the raster +np.median(raster) +``` + +```{code-cell} ipython3 +# Numpy NaN function applied to the raster +np.nanmedian(raster) +``` + +```{code-cell} ipython3 +# Masked-array function on the data +np.ma.median(raster.data) +``` + +If a NumPy core function raises an error (e.g., `np.percentile`), {class}`~geoutils.Raster.nodata` values might not be respected. In this case, use the NaN +function on the {class}`~geoutils.Raster`. + + +```{note} +Unfortunately, masked-array functions `np.ma.func` cannot be recognized yet if applied directly to a {class}`~geoutils.Raster`, but **this should come +soon** as related interfacing is in the works in NumPy! +``` + diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md index cbcf3dea9..2cbb71798 100644 --- a/doc/source/proj_tools.md +++ b/doc/source/proj_tools.md @@ -1,8 +1,59 @@ +--- +file_format: mystnb +kernelspec: + name: geoutils +--- (proj-tools)= # Projection tools -Convenient `pyproj` wrappers. +This section describes projection tools that are common to {class}`Rasters` and {class}`Vectors`, and facilitate +geospatial analysis. + +## Get projected bounds + +Projected bounds can be directly derived from both {class}`Rasters` and {class}`Vectors` through the +{func}`~geoutils.Raster.get_bounds_projected` function. + +```{code-cell} ipython3 +import geoutils as gu + +# Initiate a raster from disk +rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +print(rast.info()) + +# Get raster bounds in geographic CRS by passing its EPSG code +rast.get_bounds_projected(4326) +``` + +```{important} +When projecting to a new CRS, the footprint shape of the data is generally deformed. To account for this, use {func}`~geoutils.Raster. +get_footprint_projected` described below. +``` + +## Get projected footprint + +A projected footprint can be derived from both {class}`Rasters` and {class}`Vectors` through the +{func}`~geoutils.Raster.get_footprint_projected` function. + +For this, the original rectangular footprint polygon lines are densified to respect the deformation during reprojection. + +```{code-cell} ipython3 +# Get raster footprint in geographic CRS +rast_footprint = rast.get_footprint_projected(3412) + +rast_footprint.show() +``` + +This is for instance useful to check for intersection with other data. + +```{code-cell} ipython3 +# Open a vector +vect = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) + +# Do these raster and vector intersect? +any(vect.intersects(rast_footprint)) +``` ## Find a local metric projection diff --git a/geoutils/projtools.py b/geoutils/projtools.py index d45dbbd46..9d15b4304 100644 --- a/geoutils/projtools.py +++ b/geoutils/projtools.py @@ -6,6 +6,7 @@ from collections import abc from math import ceil, floor +import geopandas import geopandas as gpd import numpy as np import pyproj @@ -293,3 +294,70 @@ def _get_bounds_projected( new_bounds = rio.coords.BoundingBox(*new_bounds) return new_bounds + + +def _densify_geometry(line_geometry: shapely.LineString, densify_pts: int = 5000) -> shapely.LineString: + """ + Densify a linestring geometry. + + Inspired by: https://gis.stackexchange.com/questions/372912/how-to-densify-linestring-vertices-in-shapely-geopandas. + + :param line_geometry: Linestring. + :param densify_pts: Number of points to densify each line. + + :return: Densified linestring. + """ + + # Get the segments (list of linestrings) + segments = list(map(shapely.LineString, zip(line_geometry.coords[:-1], line_geometry.coords[1:]))) + + # To store new coordinate tuples + xy = [] + + # For each segment, densify the points + for i, seg in enumerate(segments): + + # Get the segment length + length_m = seg.length + + # Loop over a distance on the segment length + densified_seg = np.linspace(0, length_m, 1 + densify_pts) + # (removing the last point, as it will be the first point of the next segment, + # except for the last segment) + if i < len(segments) - 1: + densified_seg = densified_seg[:-1] + + for distance_along_old_line in densified_seg: + # Interpolate a point every step along the old line + point = seg.interpolate(distance_along_old_line) + # Extract the coordinates and store them in xy list + xp, yp = point.x, point.y + xy.append((xp, yp)) + + # Recreate a new line with densified points + densified_line_geometry = shapely.LineString(xy) + + return densified_line_geometry + + +def _get_footprint_projected( + bounds: rio.coords.BoundingBox, in_crs: CRS, out_crs: CRS, densify_pts: int = 5000 +) -> gpd.GeoDataFrame: + + # Get bounds + left, bottom, right, top = bounds + + # Create linestring + linestring = shapely.LineString([[left, bottom], [left, top], [right, top], [right, bottom], [left, bottom]]) + + # Densify linestring + densified_line_geometry = _densify_geometry(linestring, densify_pts=densify_pts) + + # Get polygon from new linestring + densified_poly = shapely.Polygon(densified_line_geometry) + + # Reproject the polygon + df = gpd.GeoDataFrame({"geometry": [densified_poly]}, crs=in_crs) + reproj_df = df.to_crs(crs=out_crs) + + return reproj_df \ No newline at end of file diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index 980065206..8bef15bd1 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -2241,6 +2241,20 @@ def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coo return new_bounds + def get_footprint_projected(self, out_crs: CRS, densify_pts: int = 5000) -> Vector: + """ + Get raster footprint projected in a specified CRS. + + The polygon points of the vector are densified during reprojection to warp + the rectangular square footprint of the original projection into the new one. + + :param out_crs: Output CRS. + :param densify_pts: Maximum points to be added between image corners to account for non linear edges. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + """ + + return Vector.from_bounds_projected(self, out_crs=out_crs, densify_pts=densify_pts) + def intersection(self, rst: str | Raster, match_ref: bool = True) -> tuple[float, float, float, float]: """ Returns the bounding box of intersection between this image and another. diff --git a/geoutils/vector.py b/geoutils/vector.py index d419e2ce8..5a3804ee9 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -39,7 +39,7 @@ import geoutils as gu from geoutils.misc import copy_doc -from geoutils.projtools import _get_bounds_projected, bounds2poly +from geoutils.projtools import _get_bounds_projected, _get_footprint_projected, bounds2poly # This is a generic Vector-type (if subclasses are made, this will change appropriately) VectorType = TypeVar("VectorType", bound="Vector") @@ -1337,9 +1337,7 @@ def from_bounds_projected( if out_crs is None: out_crs = raster_or_vector.crs - poly = gu.projtools.bounds2poly(raster_or_vector, out_crs=out_crs) - - df = gpd.GeoDataFrame(geometry=[poly], crs=out_crs) + df = _get_footprint_projected(raster_or_vector.bounds, in_crs=raster_or_vector.crs, out_crs=out_crs, densify_pts=densify_pts) return cls(df) # type: ignore @@ -1468,6 +1466,20 @@ def get_bounds_projected(self, out_crs: CRS, densify_pts: int = 5000) -> rio.coo return new_bounds + def get_footprint_projected(self, out_crs: CRS, densify_pts: int = 5000) -> Vector: + """ + Get vector footprint projected in a specified CRS. + + The polygon points of the vector are densified during reprojection to warp + the rectangular square footprint of the original projection into the new one. + + :param out_crs: Output CRS. + :param densify_pts: Maximum points to be added between image corners to account for non linear edges. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + """ + + return Vector.from_bounds_projected(self, out_crs=out_crs, densify_pts=densify_pts) + def buffer_without_overlap(self, buffer_size: int | float, metric: bool = True, plot: bool = False) -> Vector: """ Buffer the vector geometries without overlapping each other. diff --git a/tests/test_projtools.py b/tests/test_projtools.py index 3e4c9904a..55a985c54 100644 --- a/tests/test_projtools.py +++ b/tests/test_projtools.py @@ -1,9 +1,13 @@ """ Test projtools """ +import os.path + import numpy as np import pyproj.exceptions import pytest +import shapely +import geopandas as gpd import geoutils as gu import geoutils.projtools as pt @@ -14,7 +18,9 @@ class TestProjTools: landsat_b4_path = examples.get_path("everest_landsat_b4") landsat_b4_crop_path = examples.get_path("everest_landsat_b4_cropped") landsat_rgb_path = examples.get_path("everest_landsat_rgb") + everest_outlines_path = examples.get_path("everest_rgi_outlines") aster_dem_path = examples.get_path("exploradores_aster_dem") + aster_outlines_path = examples.get_path("exploradores_rgi_outlines") def test_latlon_to_utm(self) -> None: # First: Check errors are raised when format is invalid @@ -141,3 +147,41 @@ def test_merge_bounds(self) -> None: assert out_bounds[1] == min(img1.bounds.bottom, outlines.ds.total_bounds[1]) assert out_bounds[2] == max(img1.bounds.right, outlines.ds.total_bounds[2]) assert out_bounds[3] == max(img1.bounds.top, outlines.ds.total_bounds[3]) + + + # Try all vectors and rasters + @pytest.mark.parametrize("fn_raster_or_vector", + [landsat_b4_path, landsat_rgb_path, landsat_b4_crop_path, everest_outlines_path, aster_dem_path, aster_outlines_path]) # type: ignore + # Try with geographic, a UTM zone and a Robinson + @pytest.mark.parametrize("out_crs", [pyproj.CRS.from_epsg(4326), pyproj.CRS.from_epsg(32610)]) # type: ignore + @pytest.mark.parametrize("densify_pts", [2, 10, 5000]) + def test_get_footprint_projected(self, fn_raster_or_vector: str, out_crs: pyproj.CRS, densify_pts: int) -> None: + """Test the get footprint projected function.""" + + # Open raster or vector + if os.path.splitext(os.path.basename(fn_raster_or_vector))[-1] == ".tif": + rast_or_vect = gu.Raster(fn_raster_or_vector) + else: + rast_or_vect = gu.Vector(fn_raster_or_vector) + + # Get footprint + footprint = rast_or_vect.get_footprint_projected(out_crs=out_crs, densify_pts=densify_pts) + + # Assert it is a vector containing a polygon geometry + assert isinstance(footprint, gu.Vector) + assert isinstance(footprint.geometry[0], shapely.Polygon) + + # Check that the original corner points were conserved + left, bottom, right, top = rast_or_vect.bounds + corners = [shapely.Point([x, y]) for (x,y) in [(left, bottom), (left, top), (right, top), (right, bottom)]] + df = gpd.GeoDataFrame({"geometry": corners}, crs=rast_or_vect.crs) + df_reproj = df.to_crs(crs=out_crs) + + assert all(point.coords[0] in footprint.geometry[0].exterior.coords[:] for point in df_reproj.geometry) + + # Check that densification yields a logical amount of points + # (4 initial corner points times the densification factor + the last point) + assert len(footprint.geometry[0].exterior.coords[:]) == densify_pts * 4 + 1 + + + From 07f34a3c68742ee2333f31d18fd50f1016038efe Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 14:53:25 -0800 Subject: [PATCH 152/184] Add get_metric_crs and tests --- doc/source/api.md | 11 +++++++- doc/source/proj_tools.md | 8 ++---- geoutils/projtools.py | 35 ++++++++++++++++++++++++ geoutils/raster/raster.py | 23 ++++++++++++++-- geoutils/vector.py | 56 +++++++++++++++++++-------------------- tests/test_projtools.py | 27 +++++++++++++++++++ 6 files changed, 122 insertions(+), 38 deletions(-) diff --git a/doc/source/api.md b/doc/source/api.md index b32e8cf68..ebbb84549 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -132,9 +132,18 @@ documentation. Raster.ij2xy Raster.coords Raster.shift + Raster.outside_image +``` + +### Projection methods + +```{eval-rst} +.. autosummary:: + :toctree: gen_modules/ + Raster.get_bounds_projected + Raster.get_footprint_projected Raster.intersection - Raster.outside_image ``` ### Testing methods diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md index 2cbb71798..0319ff97f 100644 --- a/doc/source/proj_tools.md +++ b/doc/source/proj_tools.md @@ -40,7 +40,7 @@ For this, the original rectangular footprint polygon lines are densified to resp ```{code-cell} ipython3 # Get raster footprint in geographic CRS -rast_footprint = rast.get_footprint_projected(3412) +rast_footprint = rast.get_footprint_projected(4326) rast_footprint.show() ``` @@ -55,10 +55,6 @@ vect = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) any(vect.intersects(rast_footprint)) ``` -## Find a local metric projection +## Estimate a local metric projection -## Merge or align multiple bounds -## Create a tiling - -## Shape-preserving vector reprojection diff --git a/geoutils/projtools.py b/geoutils/projtools.py index 9d15b4304..333a0d540 100644 --- a/geoutils/projtools.py +++ b/geoutils/projtools.py @@ -5,6 +5,8 @@ from collections import abc from math import ceil, floor +import warnings +from typing import Literal import geopandas import geopandas as gpd @@ -63,6 +65,39 @@ def utm_to_epsg(utm: str) -> int: return int(epsg) +def _get_utm_ups_crs(df: gpd.GeoDataFrame, method: Literal["centroid"] | Literal["geopandas"] = "centroid") -> CRS: + + # Check input + if method.lower() not in ["centroid", "geopandas"]: + raise ValueError("Method to get local CRS should be one of 'centroid' and 'geopandas'.") + + # Use geopandas if that is the desired method + if method == "geopandas": + crs = df.estimate_utm_crs() + + # Else, compute the centroid of dissolved geometries and get UTM or UPS + else: + # Get a rough centroid in geographic coordinates (ignore the warning that it is not the most precise): + with warnings.catch_warnings(): + warnings.simplefilter(action="ignore", category=UserWarning) + shp_wgs84 = df.to_crs(epsg=4326).dissolve() + lat, lon = shp_wgs84.centroid.y.values[0], shp_wgs84.centroid.x.values[0] + del shp_wgs84 + + # If absolute latitude is below 80, get the EPSG code of the local UTM + if -80 <= lat <= 80: + utm = latlon_to_utm(lat, lon) + epsg = utm_to_epsg(utm) + crs = pyproj.CRS.from_epsg(epsg) + # If latitude is below 80, get UPS South + elif lat < -80: + crs = pyproj.CRS.from_epsg(32761) + # Else, get UPS North + else: + crs = pyproj.CRS.from_epsg(32661) + + return crs + def bounds2poly( boundsGeom: list[float] | rio.io.DatasetReader, diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index 8bef15bd1..7ca08d61d 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -33,7 +33,7 @@ import geoutils.vector as gv from geoutils._typing import AnyNumber, ArrayLike, DTypeLike -from geoutils.projtools import _get_bounds_projected +from geoutils.projtools import _get_bounds_projected, _get_footprint_projected, _get_utm_ups_crs from geoutils.raster.sampling import subsample_array from geoutils.vector import Vector @@ -2253,7 +2253,26 @@ def get_footprint_projected(self, out_crs: CRS, densify_pts: int = 5000) -> Vect Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. """ - return Vector.from_bounds_projected(self, out_crs=out_crs, densify_pts=densify_pts) + return Vector(_get_footprint_projected(bounds=self.bounds, in_crs=self.crs, out_crs=out_crs, densify_pts=densify_pts)) + + def get_metric_crs(self, local_crs_type: Literal["universal"] | Literal["custom"] = "universal", + method: Literal["centroid"] | Literal["geopandas"] = "centroid") -> CRS: + """ + Get local metric coordinate reference system for the raster (UTM, UPS, or custom Mercator or Polar). + + :param local_crs_type: Whether to get a "universal" local CRS (UTM or UPS) or a "custom" local CRS + (Mercator or Polar centered on centroid). + :param method: Method to choose the zone of the CRS, either based on the centroid of the footprint + or the extent as implemented in :func:`geopandas.GeoDataFrame.estimate_utm_crs`. + Forced to centroid if `local_crs="custom"`. + """ + + # For universal CRS (UTM or UPS) + if local_crs_type == "universal": + return _get_utm_ups_crs(self.get_footprint_projected(out_crs=self.crs).ds, method=method) + # For a custom CRS + else: + raise NotImplementedError("This is not implemented yet.") def intersection(self, rst: str | Raster, match_ref: bool = True) -> tuple[float, float, float, float]: """ diff --git a/geoutils/vector.py b/geoutils/vector.py index 5a3804ee9..b4ebf5a4f 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -39,7 +39,7 @@ import geoutils as gu from geoutils.misc import copy_doc -from geoutils.projtools import _get_bounds_projected, _get_footprint_projected, bounds2poly +from geoutils.projtools import _get_bounds_projected, _get_footprint_projected, _get_utm_ups_crs, bounds2poly # This is a generic Vector-type (if subclasses are made, this will change appropriately) VectorType = TypeVar("VectorType", bound="Vector") @@ -1413,30 +1413,19 @@ def proximity( def buffer_metric(self, buffer_size: float) -> Vector: """ - Buffer the vector features in a local metric system (UTM only). + Buffer the vector features in a local metric system (UTM or UPS). - The outlines are projected to a local UTM, then reverted to the original projection after buffering. + The outlines are projected to the local UTM or UPS, then reverted to the original projection after buffering. :param buffer_size: Buffering distance in meters. :return: Buffered shapefile. """ - from geoutils.projtools import latlon_to_utm, utm_to_epsg - - # Get a rough centroid in geographic coordinates (ignore the warning that it is not the most precise): - with warnings.catch_warnings(): - warnings.simplefilter(action="ignore", category=UserWarning) - shp_wgs84 = self.ds.to_crs(epsg=4326) - lat, lon = shp_wgs84.centroid.y.values[0], shp_wgs84.centroid.x.values[0] - del shp_wgs84 - - # Get the EPSG code of the local UTM - utm = latlon_to_utm(lat, lon) - epsg = utm_to_epsg(utm) + crs_utm_ups = _get_utm_ups_crs(df=self.ds) # Reproject the shapefile in the local UTM - ds_utm = self.ds.to_crs(epsg=epsg) + ds_utm = self.ds.to_crs(crs=crs_utm_ups) # Buffer the shapefile ds_buffered = ds_utm.buffer(distance=buffer_size) @@ -1478,7 +1467,26 @@ def get_footprint_projected(self, out_crs: CRS, densify_pts: int = 5000) -> Vect Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. """ - return Vector.from_bounds_projected(self, out_crs=out_crs, densify_pts=densify_pts) + return Vector(_get_footprint_projected(bounds=self.bounds, in_crs=self.crs, out_crs=out_crs, densify_pts=densify_pts)) + + def get_metric_crs(self, local_crs_type: Literal["universal"] | Literal["custom"] = "universal", + method: Literal["centroid"] | Literal["geopandas"] = "centroid") -> CRS: + """ + Get local metric coordinate reference system for the vector (UTM, UPS, or custom Mercator or Polar). + + :param local_crs_type: Whether to get a "universal" local CRS (UTM or UPS) or a "custom" local CRS + (Mercator or Polar centered on centroid). + :param method: Method to choose the zone of the CRS, either based on the centroid of the footprint + or the extent as implemented in :func:`geopandas.GeoDataFrame.estimate_utm_crs`. + Forced to centroid if `local_crs="custom"`. + """ + + # For universal CRS (UTM or UPS) + if local_crs_type == "universal": + return _get_utm_ups_crs(self.ds, method=method) + # For a custom CRS + else: + raise NotImplementedError("This is not implemented yet.") def buffer_without_overlap(self, buffer_size: int | float, metric: bool = True, plot: bool = False) -> Vector: """ @@ -1513,18 +1521,8 @@ def buffer_without_overlap(self, buffer_size: int | float, metric: bool = True, # Project in local UTM if metric is True if metric: - # Get a rough centroid in geographic coordinates (ignore the warning that it is not the most precise): - with warnings.catch_warnings(): - warnings.simplefilter(action="ignore", category=UserWarning) - shp_wgs84 = self.ds.to_crs(epsg=4326) - lat, lon = shp_wgs84.centroid.y.values[0], shp_wgs84.centroid.x.values[0] - del shp_wgs84 - - # Get the EPSG code of the local UTM - utm = latlon_to_utm(lat, lon) - epsg = utm_to_epsg(utm) - - gdf = self.ds.to_crs(epsg=epsg) + crs_utm_ups = _get_utm_ups_crs(df=self.ds) + gdf = self.ds.to_crs(crs=crs_utm_ups) else: gdf = self.ds diff --git a/tests/test_projtools.py b/tests/test_projtools.py index 55a985c54..0ed2d16c8 100644 --- a/tests/test_projtools.py +++ b/tests/test_projtools.py @@ -90,6 +90,33 @@ def test_utm_to_epsg(self) -> None: assert pt.utm_to_epsg("1N") == pt.utm_to_epsg("01N") == pt.utm_to_epsg("01n") assert pt.utm_to_epsg("08s") == pt.utm_to_epsg("8S") == pt.utm_to_epsg("08S") + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore + def test_get_metric_crs_utm(self, example: str) -> None: + """Check that the function works consistently with GeoPandas for UTM.""" + + # Open raster + rast = gu.Raster(example) + + # Get utm zone from GeoPandas and GeoUtils + crs_gu = rast.get_metric_crs() + crs_geopandas = rast.get_metric_crs(method="geopandas") + + # Verify they are the same + assert crs_gu == crs_geopandas + + def test_get_metric_crs_ups(self): + """Check that the function works for UPS with points at high latitude.""" + + # Create a vector of a single point in geographic coordinates + point_north = shapely.Point([0, 84]) + point_south = shapely.Point([0, -84]) + vect_north = gu.Vector(gpd.GeoDataFrame({"geometry": [point_north]}, crs=pyproj.CRS.from_epsg(4326))) + vect_south = gu.Vector(gpd.GeoDataFrame({"geometry": [point_south]}, crs=pyproj.CRS.from_epsg(4326))) + + # Check that UPS North and South are indeed returned + assert vect_north.get_metric_crs() == pyproj.CRS.from_epsg(32661) + assert vect_south.get_metric_crs() == pyproj.CRS.from_epsg(32761) + @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_latlon_reproject(self, example: str) -> None: """ From ab3aa3591b4f20cbcc85a48e06001ed955c0a586 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 14:56:52 -0800 Subject: [PATCH 153/184] Add description of functions in projtools --- geoutils/projtools.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/geoutils/projtools.py b/geoutils/projtools.py index 333a0d540..cf741c02f 100644 --- a/geoutils/projtools.py +++ b/geoutils/projtools.py @@ -66,7 +66,14 @@ def utm_to_epsg(utm: str) -> int: return int(epsg) def _get_utm_ups_crs(df: gpd.GeoDataFrame, method: Literal["centroid"] | Literal["geopandas"] = "centroid") -> CRS: + """ + Get universal metric coordinate reference system for the vector passed (UTM or UPS). + :param df: Input geodataframe. + :param method: Method to choose the zone of the CRS, either based on the centroid of the footprint + or the extent as implemented in :func:`geopandas.GeoDataFrame.estimate_utm_crs`. + Forced to centroid if `local_crs="custom"`. + """ # Check input if method.lower() not in ["centroid", "geopandas"]: raise ValueError("Method to get local CRS should be one of 'centroid' and 'geopandas'.") @@ -323,6 +330,15 @@ def compare_proj(proj1: CRS, proj2: CRS) -> bool: def _get_bounds_projected( bounds: rio.coords.BoundingBox, in_crs: CRS, out_crs: CRS, densify_pts: int = 5000 ) -> rio.coords.BoundingBox: + """ + Get bounds projected in a specified CRS. + + :param in_crs: Input CRS. + :param out_crs: Output CRS. + :param densify_pts: Maximum points to be added between image corners to account for nonlinear edges. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + """ + # Calculate new bounds left, bottom, right, top = bounds new_bounds = rio.warp.transform_bounds(in_crs, out_crs, left, bottom, right, top, densify_pts) @@ -378,6 +394,17 @@ def _densify_geometry(line_geometry: shapely.LineString, densify_pts: int = 5000 def _get_footprint_projected( bounds: rio.coords.BoundingBox, in_crs: CRS, out_crs: CRS, densify_pts: int = 5000 ) -> gpd.GeoDataFrame: + """ + Get bounding box footprint projected in a specified CRS. + + The polygon points of the vector are densified during reprojection to warp + the rectangular square footprint of the original projection into the new one. + + :param in_crs: Input CRS. + :param out_crs: Output CRS. + :param densify_pts: Maximum points to be added between image corners to account for non linear edges. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + """ # Get bounds left, bottom, right, top = bounds From efcbfd24e2440bd1e494bc02afaa16eded219729 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 14:58:05 -0800 Subject: [PATCH 154/184] Linting --- doc/source/core_array_funcs.md | 5 ++--- doc/source/proj_tools.md | 6 ++---- geoutils/projtools.py | 21 +++++++++++---------- geoutils/raster/raster.py | 17 +++++++++++++---- geoutils/vector.py | 24 +++++++++++++++++------- tests/test_projtools.py | 27 ++++++++++++++++----------- 6 files changed, 61 insertions(+), 39 deletions(-) diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index 4cf92952b..c31fecef1 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -118,12 +118,11 @@ np.nanmedian(raster) np.ma.median(raster.data) ``` -If a NumPy core function raises an error (e.g., `np.percentile`), {class}`~geoutils.Raster.nodata` values might not be respected. In this case, use the NaN +If a NumPy core function raises an error (e.g., `np.percentile`), {class}`~geoutils.Raster.nodata` values might not be respected. In this case, use the NaN function on the {class}`~geoutils.Raster`. ```{note} -Unfortunately, masked-array functions `np.ma.func` cannot be recognized yet if applied directly to a {class}`~geoutils.Raster`, but **this should come +Unfortunately, masked-array functions `np.ma.func` cannot be recognized yet if applied directly to a {class}`~geoutils.Raster`, but **this should come soon** as related interfacing is in the works in NumPy! ``` - diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md index 0319ff97f..aa7e4314e 100644 --- a/doc/source/proj_tools.md +++ b/doc/source/proj_tools.md @@ -7,7 +7,7 @@ kernelspec: # Projection tools -This section describes projection tools that are common to {class}`Rasters` and {class}`Vectors`, and facilitate +This section describes projection tools that are common to {class}`Rasters` and {class}`Vectors`, and facilitate geospatial analysis. ## Get projected bounds @@ -36,7 +36,7 @@ get_footprint_projected` described below. A projected footprint can be derived from both {class}`Rasters` and {class}`Vectors` through the {func}`~geoutils.Raster.get_footprint_projected` function. -For this, the original rectangular footprint polygon lines are densified to respect the deformation during reprojection. +For this, the original rectangular footprint polygon lines are densified to respect the deformation during reprojection. ```{code-cell} ipython3 # Get raster footprint in geographic CRS @@ -56,5 +56,3 @@ any(vect.intersects(rast_footprint)) ``` ## Estimate a local metric projection - - diff --git a/geoutils/projtools.py b/geoutils/projtools.py index cf741c02f..77febf7a6 100644 --- a/geoutils/projtools.py +++ b/geoutils/projtools.py @@ -3,9 +3,9 @@ """ from __future__ import annotations +import warnings from collections import abc from math import ceil, floor -import warnings from typing import Literal import geopandas @@ -65,6 +65,7 @@ def utm_to_epsg(utm: str) -> int: return int(epsg) + def _get_utm_ups_crs(df: gpd.GeoDataFrame, method: Literal["centroid"] | Literal["geopandas"] = "centroid") -> CRS: """ Get universal metric coordinate reference system for the vector passed (UTM or UPS). @@ -395,16 +396,16 @@ def _get_footprint_projected( bounds: rio.coords.BoundingBox, in_crs: CRS, out_crs: CRS, densify_pts: int = 5000 ) -> gpd.GeoDataFrame: """ - Get bounding box footprint projected in a specified CRS. + Get bounding box footprint projected in a specified CRS. - The polygon points of the vector are densified during reprojection to warp - the rectangular square footprint of the original projection into the new one. + The polygon points of the vector are densified during reprojection to warp + the rectangular square footprint of the original projection into the new one. - :param in_crs: Input CRS. - :param out_crs: Output CRS. - :param densify_pts: Maximum points to be added between image corners to account for non linear edges. - Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. - """ + :param in_crs: Input CRS. + :param out_crs: Output CRS. + :param densify_pts: Maximum points to be added between image corners to account for non linear edges. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + """ # Get bounds left, bottom, right, top = bounds @@ -422,4 +423,4 @@ def _get_footprint_projected( df = gpd.GeoDataFrame({"geometry": [densified_poly]}, crs=in_crs) reproj_df = df.to_crs(crs=out_crs) - return reproj_df \ No newline at end of file + return reproj_df diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index 7ca08d61d..b6ab36ea3 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -33,7 +33,11 @@ import geoutils.vector as gv from geoutils._typing import AnyNumber, ArrayLike, DTypeLike -from geoutils.projtools import _get_bounds_projected, _get_footprint_projected, _get_utm_ups_crs +from geoutils.projtools import ( + _get_bounds_projected, + _get_footprint_projected, + _get_utm_ups_crs, +) from geoutils.raster.sampling import subsample_array from geoutils.vector import Vector @@ -2253,10 +2257,15 @@ def get_footprint_projected(self, out_crs: CRS, densify_pts: int = 5000) -> Vect Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. """ - return Vector(_get_footprint_projected(bounds=self.bounds, in_crs=self.crs, out_crs=out_crs, densify_pts=densify_pts)) + return Vector( + _get_footprint_projected(bounds=self.bounds, in_crs=self.crs, out_crs=out_crs, densify_pts=densify_pts) + ) - def get_metric_crs(self, local_crs_type: Literal["universal"] | Literal["custom"] = "universal", - method: Literal["centroid"] | Literal["geopandas"] = "centroid") -> CRS: + def get_metric_crs( + self, + local_crs_type: Literal["universal"] | Literal["custom"] = "universal", + method: Literal["centroid"] | Literal["geopandas"] = "centroid", + ) -> CRS: """ Get local metric coordinate reference system for the raster (UTM, UPS, or custom Mercator or Polar). diff --git a/geoutils/vector.py b/geoutils/vector.py index b4ebf5a4f..ffa61c874 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -39,7 +39,12 @@ import geoutils as gu from geoutils.misc import copy_doc -from geoutils.projtools import _get_bounds_projected, _get_footprint_projected, _get_utm_ups_crs, bounds2poly +from geoutils.projtools import ( + _get_bounds_projected, + _get_footprint_projected, + _get_utm_ups_crs, + bounds2poly, +) # This is a generic Vector-type (if subclasses are made, this will change appropriately) VectorType = TypeVar("VectorType", bound="Vector") @@ -1337,7 +1342,9 @@ def from_bounds_projected( if out_crs is None: out_crs = raster_or_vector.crs - df = _get_footprint_projected(raster_or_vector.bounds, in_crs=raster_or_vector.crs, out_crs=out_crs, densify_pts=densify_pts) + df = _get_footprint_projected( + raster_or_vector.bounds, in_crs=raster_or_vector.crs, out_crs=out_crs, densify_pts=densify_pts + ) return cls(df) # type: ignore @@ -1467,10 +1474,15 @@ def get_footprint_projected(self, out_crs: CRS, densify_pts: int = 5000) -> Vect Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. """ - return Vector(_get_footprint_projected(bounds=self.bounds, in_crs=self.crs, out_crs=out_crs, densify_pts=densify_pts)) + return Vector( + _get_footprint_projected(bounds=self.bounds, in_crs=self.crs, out_crs=out_crs, densify_pts=densify_pts) + ) - def get_metric_crs(self, local_crs_type: Literal["universal"] | Literal["custom"] = "universal", - method: Literal["centroid"] | Literal["geopandas"] = "centroid") -> CRS: + def get_metric_crs( + self, + local_crs_type: Literal["universal"] | Literal["custom"] = "universal", + method: Literal["centroid"] | Literal["geopandas"] = "centroid", + ) -> CRS: """ Get local metric coordinate reference system for the vector (UTM, UPS, or custom Mercator or Polar). @@ -1517,8 +1529,6 @@ def buffer_without_overlap(self, buffer_size: int | float, metric: bool = True, >>> plt.show() # doctest: +SKIP """ - from geoutils.projtools import latlon_to_utm, utm_to_epsg - # Project in local UTM if metric is True if metric: crs_utm_ups = _get_utm_ups_crs(df=self.ds) diff --git a/tests/test_projtools.py b/tests/test_projtools.py index 0ed2d16c8..7edc25fc0 100644 --- a/tests/test_projtools.py +++ b/tests/test_projtools.py @@ -3,11 +3,11 @@ """ import os.path +import geopandas as gpd import numpy as np import pyproj.exceptions import pytest import shapely -import geopandas as gpd import geoutils as gu import geoutils.projtools as pt @@ -104,7 +104,7 @@ def test_get_metric_crs_utm(self, example: str) -> None: # Verify they are the same assert crs_gu == crs_geopandas - def test_get_metric_crs_ups(self): + def test_get_metric_crs_ups(self) -> None: """Check that the function works for UPS with points at high latitude.""" # Create a vector of a single point in geographic coordinates @@ -175,13 +175,21 @@ def test_merge_bounds(self) -> None: assert out_bounds[2] == max(img1.bounds.right, outlines.ds.total_bounds[2]) assert out_bounds[3] == max(img1.bounds.top, outlines.ds.total_bounds[3]) - # Try all vectors and rasters - @pytest.mark.parametrize("fn_raster_or_vector", - [landsat_b4_path, landsat_rgb_path, landsat_b4_crop_path, everest_outlines_path, aster_dem_path, aster_outlines_path]) # type: ignore + @pytest.mark.parametrize( + "fn_raster_or_vector", + [ + landsat_b4_path, + landsat_rgb_path, + landsat_b4_crop_path, + everest_outlines_path, + aster_dem_path, + aster_outlines_path, + ], + ) # type: ignore # Try with geographic, a UTM zone and a Robinson - @pytest.mark.parametrize("out_crs", [pyproj.CRS.from_epsg(4326), pyproj.CRS.from_epsg(32610)]) # type: ignore - @pytest.mark.parametrize("densify_pts", [2, 10, 5000]) + @pytest.mark.parametrize("out_crs", [pyproj.CRS.from_epsg(4326), pyproj.CRS.from_epsg(32610)]) # type: ignore + @pytest.mark.parametrize("densify_pts", [2, 10, 5000]) # type: ignore def test_get_footprint_projected(self, fn_raster_or_vector: str, out_crs: pyproj.CRS, densify_pts: int) -> None: """Test the get footprint projected function.""" @@ -200,7 +208,7 @@ def test_get_footprint_projected(self, fn_raster_or_vector: str, out_crs: pyproj # Check that the original corner points were conserved left, bottom, right, top = rast_or_vect.bounds - corners = [shapely.Point([x, y]) for (x,y) in [(left, bottom), (left, top), (right, top), (right, bottom)]] + corners = [shapely.Point([x, y]) for (x, y) in [(left, bottom), (left, top), (right, top), (right, bottom)]] df = gpd.GeoDataFrame({"geometry": corners}, crs=rast_or_vect.crs) df_reproj = df.to_crs(crs=out_crs) @@ -209,6 +217,3 @@ def test_get_footprint_projected(self, fn_raster_or_vector: str, out_crs: pyproj # Check that densification yields a logical amount of points # (4 initial corner points times the densification factor + the last point) assert len(footprint.geometry[0].exterior.coords[:]) == densify_pts * 4 + 1 - - - From a9405a5afa5019af9355d194d3eec0fc49701290 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 17:38:11 -0800 Subject: [PATCH 155/184] Streamline README and CONTRIBUTING --- CONTRIBUTING.md | 92 +++++++++------------------------------- README.md | 61 ++++++++++---------------- doc/source/api.md | 2 + doc/source/proj_tools.md | 32 +++++++------- 4 files changed, 62 insertions(+), 125 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8f6be1b4..e4425ff37 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,103 +1,53 @@ # How to contribute ## Overview: making a contribution + For more details, see the rest of this document. -1. Fork and clone the repository. -2. Set up the development environment. +1. Fork _GlacioHack/geoutils_ and clone your fork repository locally. +2. Set up the development environment (section below). 3. Create a branch for the new feature or bug fix. -4. Make your changes, ensuring they are conventions-compliant. +4. Make your changes, and add or modify related tests in _tests/_. 5. Commit, making sure to run `pre-commit` separately if not installed as git hook. -6. Push. -7. Open a Pull Request to discuss and eventually merge. -8. You can now delete your feature branch. - - -## Rights -The license (see LICENSE) applies to all contributions. - - -## Issue Conventions -When submitting bugs, please fill out the bug report template in full. - -Please search existing issues, open and closed, before creating a new one. - - -## Git conventions -Work on features should be made on a fork of `geoutils` and submitted as a pull request (PR) to master or a relevant branch. - - -## Code conventions - -Contributors of `geoutils` should attempt to conform to pep8 coding standards. -An exception to the standard is having a 120 max character line length (instead of 80). - -Suggested linters are: -1. prospector -2. mypy (git version) -3. pydocstyle - -Suggested formatters are: -1. autopep8 -2. isort - -## Test conventions -At least one test per feature (in the associated `tests/test_*.py` file) should be included in the PR, but more than one is suggested. -We use `pytest`. - +6. Push to your fork. +7. Open a pull request from GitHub to discuss and eventually merge. ## Development environment -We target Python 3 or higher for `geoutils`. -Some features may require later versions of Python (3.6+) to function correctly. +GeoUtils currently supports only Python versions of 3.8 and higher, see `environment.yml` for detailed dependencies. ### Setup -Clone the git repo and create a conda environment: +Clone the git repo and create a `mamba` environment (see how to install `mamba` in the [mamba documentation](https://mamba.readthedocs.io/en/latest/)): ```bash git clone https://github.com/GlacioHack/geoutils.git cd geoutils -conda env create -f environment.yml # add '-n custom_name' if you want. -conda activate geoutils # or any other name specified above -pip install -e . # Install geoutils in developer mode +mamba env create -f dev-environment.yml # Add '-n custom_name' if you want. +mamba activate geoutils-dev # Or any other name specified above ``` -Now install the additional development dependencies: -```bash -pip install -r dev-requirements.txt -``` - -Finally, we also recommend installing the formatting and linting tools listed above to help with adhering to code conventions: -```bash -pip install prospector git+https://github.com/python/mypy.git pydocstyle autopep8 isort -``` - -Note that your text editor of choice will also need to be configured with these tools (and max character line length changed to 120). +### Tests +At least one test per feature (in the associated `tests/test_*.py` file) should be included in the PR, using `pytest` (see existing tests for examples). -### Running the tests To run the entire test suite, run `pytest` in the current directory: ```bash pytest ``` -It is also recommended to try the tests from the parent directory, to validate that import statements work as they should: -```bash -cd ../ # Change to the parent directory -pytest geoutils -``` - ### Formatting and linting -To merge a PR in geoutils, the code has to adhere to the standards set in place. -We use a number of tools to validate contributions, triggered using `pre-commit` (see `.pre-commit-config.yaml` for the exact tools). -`pre-commit` is made to be installed as a "pre-commit hook" for git, so the checks have to pass before committing. Before committing for the first time, you need to install the hook: -```bash -pre-commit install -``` +Install and run `pre-commit` (see [pre-commit documentation](https://pre-commit.com/)), which will use `.pre-commit-config.yaml` to verify spelling errors, +import sorting, type checking, formatting and linting. -Pre-commit can also be run as a separate tool: +You can then run pre-commit manually: ```bash pre-commit run --all-files ``` + +Optionally, `pre-commit` can be installed as a git hook to ensure checks have to pass before committing. + +## Rights + +The license (see LICENSE) applies to all contributions. diff --git a/README.md b/README.md index 1dc549829..18c8c1fb4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ -# geoutils -Set of tools to handle raster and vector data sets in Python. +# GeoUtils + +Handling and analysis of georeferenced rasters and vectors in Python. ![](https://readthedocs.org/projects/geoutils/badge/?version=latest) [![build](https://github.com/GlacioHack/geoutils/actions/workflows/python-app.yml/badge.svg)](https://github.com/GlacioHack/GeoUtils/actions/workflows/python-app.yml) @@ -9,51 +10,33 @@ Set of tools to handle raster and vector data sets in Python. [![PyPI version](https://badge.fury.io/py/geoutils.svg)](https://badge.fury.io/py/geoutils) [![Coverage Status](https://coveralls.io/repos/github/GlacioHack/geoutils/badge.svg?branch=main)](https://coveralls.io/github/GlacioHack/geoutils?branch=main) -This package offers Python classes and functions as well as command line tools to work with both geospatial raster and vector datasets. It is built upon rasterio and GeoPandas. In a single command it can import any geo-referenced dataset that is understood by these libraries, complete with all geo-referencing information, various helper functions and interface between vector/raster data. +[![Pre-Commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) +[![Formatted with black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) +[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) +[![isort Status]](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) +GeoUtils is community effort to develop a core Python package for geospatial analysis and to foster inter-operability between Python GIS packages. +It aims at facilitating end-user geospatial tasks by revolving around consistent `Raster` and `Vector` objects that interface easily, have implicit +loading behaviour, robust numerical interfacing and convenient single-line methods for all the most common higher-level methods used in the geospatial +community. -## Installation +GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced +calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. -#### With conda (recommended) -```bash -conda install --channel conda-forge --strict-channel-priority geoutils -``` -The `--strict-channel-priority` flag seems essential for Windows installs to function correctly, and is recommended for UNIX-based systems as well. +## Documentation -#### With pip +For a quick start, gallery examples, a full feature description or a search through the API, see GeoUtils' documentation at: https://geoutils.readthedocs.io. -From PyPI: -```bash -pip install geoutils -``` +## Installation -Or from the repository tarball: make sure GDAL and PROJ are properly installed, then: ```bash -pip install https://github.com/GlacioHack/geoutils/tarball/main +mamba install -c conda-forge geoutils ``` -## Documentation -See the full documentation at https://geoutils.readthedocs.io. - - -## Structure - -GeoUtils is composed of three libraries: -- `raster.py` to handle raster data set. In particular, a Raster class to load a raster file along with metadata. -- `vector.py` to handle vector data set. In particular, a Vector class to load a raster file along with metadata. -- `projtools.py` with various tools around projections. - - -## How to contribute - -You can find ways to improve the libraries in the [issues](https://github.com/GlacioHack/geoutils/issues) section. All contributions are welcome. - -1. Fork the repository to your personal GitHub account, clone to your computer. -2. (Optional but preferred:) Make a feature branch. -3. Push to your feature branch. -4. When ready, submit a Pull Request from your feature branch to `GlacioHack/geoutils:master`. -5. The PR will be reviewed by at least one other person. Usually your PR will be merged via 'squash and merge'. +## Start contributing -Direct pushing to the GlacioHack repository is not permitted. +1. Fork the repository, make a feature branches and push changes. +2. When ready, submit a pull request from the feature branch of your fork to `GlacioHack/geoutils:master`. +3. The PR will be reviewed by at least one maintainer, discussed, then merged. -A more detailed contribution instruction [can be found here](CONTRIBUTING.md). +More info on [our contributing page](CONTRIBUTING.md). diff --git a/doc/source/api.md b/doc/source/api.md index ebbb84549..4219fcda6 100644 --- a/doc/source/api.md +++ b/doc/source/api.md @@ -141,6 +141,7 @@ documentation. .. autosummary:: :toctree: gen_modules/ + Raster.get_metric_crs Raster.get_bounds_projected Raster.get_footprint_projected Raster.intersection @@ -329,6 +330,7 @@ The equivalent of {attr}`geopandas.GeoDataFrame.bounds` (i.e., a per-feature bou .. autosummary:: :toctree: gen_modules/ + Vector.get_metric_crs Vector.from_bounds_projected Vector.get_bounds_projected Vector.get_footprint_projected diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md index aa7e4314e..6ddadd69d 100644 --- a/doc/source/proj_tools.md +++ b/doc/source/proj_tools.md @@ -10,10 +10,13 @@ kernelspec: This section describes projection tools that are common to {class}`Rasters` and {class}`Vectors`, and facilitate geospatial analysis. -## Get projected bounds +## Get a metric coordinate system -Projected bounds can be directly derived from both {class}`Rasters` and {class}`Vectors` through the -{func}`~geoutils.Raster.get_bounds_projected` function. +A local metric coordinate system can be estimated for both {class}`Rasters` and {class}`Vectors` through the +{func}`~geoutils.Raster.get_metric_crs` function. + +The metric system returned can be either "universal" (zone of the Universal Transverse Mercator or Universal Polar Stereographic system), or "custom" +(Mercator or Polar projection centered on the {class}`Raster` or {class}`Vector`). ```{code-cell} ipython3 import geoutils as gu @@ -22,13 +25,22 @@ import geoutils as gu rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) print(rast.info()) +# Estimate a universal metric CRS for the raster +rast.get_metric_crs() +``` + +## Get projected bounds + +Projected bounds can be directly derived from both {class}`Rasters` and {class}`Vectors` through the +{func}`~geoutils.Raster.get_bounds_projected` function. + +```{code-cell} ipython3 # Get raster bounds in geographic CRS by passing its EPSG code rast.get_bounds_projected(4326) ``` ```{important} -When projecting to a new CRS, the footprint shape of the data is generally deformed. To account for this, use {func}`~geoutils.Raster. -get_footprint_projected` described below. +When projecting to a new CRS, the footprint shape of the data is generally deformed. To account for this, use {func}`~geoutils.Raster.get_footprint_projected` described below. ``` ## Get projected footprint @@ -46,13 +58,3 @@ rast_footprint.show() ``` This is for instance useful to check for intersection with other data. - -```{code-cell} ipython3 -# Open a vector -vect = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) - -# Do these raster and vector intersect? -any(vect.intersects(rast_footprint)) -``` - -## Estimate a local metric projection From ce102b6780c288920ffb8a7dbbfb77114032155f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 17:38:30 -0800 Subject: [PATCH 156/184] Linting --- CONTRIBUTING.md | 2 +- README.md | 12 ++++++------ doc/source/proj_tools.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4425ff37..f29242587 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,7 +38,7 @@ pytest ### Formatting and linting -Install and run `pre-commit` (see [pre-commit documentation](https://pre-commit.com/)), which will use `.pre-commit-config.yaml` to verify spelling errors, +Install and run `pre-commit` (see [pre-commit documentation](https://pre-commit.com/)), which will use `.pre-commit-config.yaml` to verify spelling errors, import sorting, type checking, formatting and linting. You can then run pre-commit manually: diff --git a/README.md b/README.md index 18c8c1fb4..c14f6554f 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,13 @@ Handling and analysis of georeferenced rasters and vectors in Python. [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) [![isort Status]](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) -GeoUtils is community effort to develop a core Python package for geospatial analysis and to foster inter-operability between Python GIS packages. -It aims at facilitating end-user geospatial tasks by revolving around consistent `Raster` and `Vector` objects that interface easily, have implicit -loading behaviour, robust numerical interfacing and convenient single-line methods for all the most common higher-level methods used in the geospatial -community. +GeoUtils is community effort to develop a core Python package for geospatial analysis and to foster inter-operability between Python GIS packages. +It aims at facilitating end-user geospatial tasks by revolving around consistent `Raster` and `Vector` objects that interface easily, have implicit +loading behaviour, robust numerical interfacing and convenient single-line methods for all the most common higher-level methods used in the geospatial +community. -GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced -calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. +GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced +calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. ## Documentation diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md index 6ddadd69d..0b30cc2e5 100644 --- a/doc/source/proj_tools.md +++ b/doc/source/proj_tools.md @@ -15,7 +15,7 @@ geospatial analysis. A local metric coordinate system can be estimated for both {class}`Rasters` and {class}`Vectors` through the {func}`~geoutils.Raster.get_metric_crs` function. -The metric system returned can be either "universal" (zone of the Universal Transverse Mercator or Universal Polar Stereographic system), or "custom" +The metric system returned can be either "universal" (zone of the Universal Transverse Mercator or Universal Polar Stereographic system), or "custom" (Mercator or Polar projection centered on the {class}`Raster` or {class}`Vector`). ```{code-cell} ipython3 From fa53a1fd4e5cdc71f1733e36e5bee4bbc936e5a9 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 17:59:28 -0800 Subject: [PATCH 157/184] Streamline README further --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c14f6554f..e01fb722e 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,21 @@ Handling and analysis of georeferenced rasters and vectors in Python. [![Pre-Commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) [![Formatted with black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) -[![isort Status]](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) +[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) -GeoUtils is community effort to develop a core Python package for geospatial analysis and to foster inter-operability between Python GIS packages. -It aims at facilitating end-user geospatial tasks by revolving around consistent `Raster` and `Vector` objects that interface easily, have implicit -loading behaviour, robust numerical interfacing and convenient single-line methods for all the most common higher-level methods used in the geospatial -community. +GeoUtils is a community effort to develop a core Python package for geospatial analysis and foster inter-operability between Python GIS packages. +It aims at facilitating end-user geospatial methods by revolving around accessible and consistent `Raster` and `Vector` objects that easily interface between +themselves. GeoUtils is founded on implicit loading behaviour, robust numerical interfacing and convenient object methods to easily perform the most +common analyses needed by geospatial users. + +If you are looking for an accessible Python package to write the equivalent of your [GDAL](https://gdal.org/) command lines or of your [QGIS](https://www.qgis.org/en/site/) analysis +pipeline in Python without a steep learning curve on GIS syntax, GeoUtils is perfect for you! + +For more advanced users, GeoUtils also aims at being efficient, scalable and robust by supporting lazy loading and parallel computing. GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced -calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. +calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. It allows easy access to +the functionalities of these packages through composition, and good inter-operability with rapid conversions. ## Documentation From 5f58c35ec75a39d480ce3425ea5fe0bf420f02f0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:04:12 -0800 Subject: [PATCH 158/184] Streamline README further --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e01fb722e..0bfccbb95 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,14 @@ Handling and analysis of georeferenced rasters and vectors in Python. [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) -GeoUtils is a community effort to develop a core Python package for geospatial analysis and foster inter-operability between Python GIS packages. -It aims at facilitating end-user geospatial methods by revolving around accessible and consistent `Raster` and `Vector` objects that easily interface between -themselves. GeoUtils is founded on implicit loading behaviour, robust numerical interfacing and convenient object methods to easily perform the most -common analyses needed by geospatial users. +GeoUtils is a community effort to develop a core Python package for geospatial analysis and foster inter-operability between other Python GIS packages. +It aims at **facilitating end-user geospatial analysis by revolving around consistent `Raster` and `Vector` objects** that easily interface between +themselves. GeoUtils is founded on **implicit loading behaviour**, **robust numerical interfacing** and **convenient object-based methods** to easily perform +the most common higher-level tasks needed by geospatial users. -If you are looking for an accessible Python package to write the equivalent of your [GDAL](https://gdal.org/) command lines or of your [QGIS](https://www.qgis.org/en/site/) analysis -pipeline in Python without a steep learning curve on GIS syntax, GeoUtils is perfect for you! - -For more advanced users, GeoUtils also aims at being efficient, scalable and robust by supporting lazy loading and parallel computing. +If you are looking for an accessible Python package to write the Python equivalent of your [GDAL](https://gdal.org/) command lines, or of your [QGIS] +(https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve on GIS syntax**, GeoUtils is perfect for you! For more advanced users, +GeoUtils also aims at being efficient and scalable by supporting lazy loading and parallel computing. GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. It allows easy access to @@ -31,7 +30,8 @@ the functionalities of these packages through composition, and good inter-operab ## Documentation -For a quick start, gallery examples, a full feature description or a search through the API, see GeoUtils' documentation at: https://geoutils.readthedocs.io. +For installation, quick start, gallery examples, a full feature description or a search through the API, see GeoUtils' documentation at: https://geoutils. +readthedocs.io. ## Installation From 61f27e7a521c9ed4f9c41b125cfd0cc42125e20d Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:05:30 -0800 Subject: [PATCH 159/184] Streamline agaaain --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0bfccbb95..986461f85 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ It aims at **facilitating end-user geospatial analysis by revolving around consi themselves. GeoUtils is founded on **implicit loading behaviour**, **robust numerical interfacing** and **convenient object-based methods** to easily perform the most common higher-level tasks needed by geospatial users. -If you are looking for an accessible Python package to write the Python equivalent of your [GDAL](https://gdal.org/) command lines, or of your [QGIS] -(https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve on GIS syntax**, GeoUtils is perfect for you! For more advanced users, -GeoUtils also aims at being efficient and scalable by supporting lazy loading and parallel computing. +If you are looking for an accessible Python package to write the Python equivalent of your [GDAL](https://gdal.org/) command lines, or of your +[QGIS](https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve** on GIS syntax, GeoUtils is perfect for you! For more advanced +users, GeoUtils also aims at being efficient and scalable by supporting lazy loading and parallel computing. GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. It allows easy access to From f08df58223cb1c09f6206236e52664cd3ea9cacd Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:09:19 -0800 Subject: [PATCH 160/184] Finish README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 986461f85..ab5bddc23 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,12 @@ themselves. GeoUtils is founded on **implicit loading behaviour**, **robust nume the most common higher-level tasks needed by geospatial users. If you are looking for an accessible Python package to write the Python equivalent of your [GDAL](https://gdal.org/) command lines, or of your -[QGIS](https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve** on GIS syntax, GeoUtils is perfect for you! For more advanced -users, GeoUtils also aims at being efficient and scalable by supporting lazy loading and parallel computing. +[QGIS](https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve** on Python GIS syntax, GeoUtils is perfect for you! For more advanced +users, GeoUtils also aims at being efficient and scalable by supporting lazy loading and parallel computing (ongoing). GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced calculations, and on [NumPy](https://github.com/numpy/numpy) and [Xarray](https://github.com/pydata/xarray) for numerical analysis. It allows easy access to -the functionalities of these packages through composition, and good inter-operability with rapid conversions. +the functionalities of these packages through interfacing or composition, and quick inter-operability through object conversion. ## Documentation From ade54cfb590b243f94b1a1be2a8e3074da783e0c Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:12:43 -0800 Subject: [PATCH 161/184] Again --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ab5bddc23..9d250c2d0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ -# GeoUtils - -Handling and analysis of georeferenced rasters and vectors in Python. +# GeoUtils: handling and analysis of georeferenced rasters and vectors in Python. ![](https://readthedocs.org/projects/geoutils/badge/?version=latest) [![build](https://github.com/GlacioHack/geoutils/actions/workflows/python-app.yml/badge.svg)](https://github.com/GlacioHack/GeoUtils/actions/workflows/python-app.yml) @@ -15,13 +13,14 @@ Handling and analysis of georeferenced rasters and vectors in Python. [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/) -GeoUtils is a community effort to develop a core Python package for geospatial analysis and foster inter-operability between other Python GIS packages. +**GeoUtils** is an open source project to develop a core Python package for geospatial analysis and foster inter-operability between other Python GIS packages. + It aims at **facilitating end-user geospatial analysis by revolving around consistent `Raster` and `Vector` objects** that easily interface between themselves. GeoUtils is founded on **implicit loading behaviour**, **robust numerical interfacing** and **convenient object-based methods** to easily perform the most common higher-level tasks needed by geospatial users. If you are looking for an accessible Python package to write the Python equivalent of your [GDAL](https://gdal.org/) command lines, or of your -[QGIS](https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve** on Python GIS syntax, GeoUtils is perfect for you! For more advanced +[QGIS](https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve** on Python GIS syntax, GeoUtils is perfect for you! For more advanced users, GeoUtils also aims at being efficient and scalable by supporting lazy loading and parallel computing (ongoing). GeoUtils relies on [Rasterio](https://github.com/rasterio/rasterio), [GeoPandas](https://github.com/geopandas/geopandas) and [Pyproj](https://github.com/pyproj4/pyproj) for georeferenced From 6211fc9489f02e94935cbb29a61dbf2045c2c997 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:14:46 -0800 Subject: [PATCH 162/184] Try this --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9d250c2d0..89a6dc16b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GeoUtils: handling and analysis of georeferenced rasters and vectors in Python. +# GeoUtils: consistent geospatial analysis in Python. ![](https://readthedocs.org/projects/geoutils/badge/?version=latest) [![build](https://github.com/GlacioHack/geoutils/actions/workflows/python-app.yml/badge.svg)](https://github.com/GlacioHack/GeoUtils/actions/workflows/python-app.yml) From dfd3c8d0d601b51839381c2579b94212661b1e79 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:20:29 -0800 Subject: [PATCH 163/184] Make a bit better again --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 89a6dc16b..e63ffac7d 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ the functionalities of these packages through interfacing or composition, and qu ## Documentation -For installation, quick start, gallery examples, a full feature description or a search through the API, see GeoUtils' documentation at: https://geoutils. -readthedocs.io. +For installation, quick start, gallery examples, a full feature description or a search through the API, see GeoUtils' documentation at: +https://geoutils.readthedocs.io. ## Installation @@ -38,6 +38,8 @@ readthedocs.io. mamba install -c conda-forge geoutils ``` +See [mamba's documentation](https://mamba.readthedocs.io/en/latest/) to install `mamba`, which will solve your environment much faster than `conda`. + ## Start contributing 1. Fork the repository, make a feature branches and push changes. From e6058b406b89d53ebbdcc5d2f4fa4d8ba9ac00f0 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:21:46 -0800 Subject: [PATCH 164/184] And a bit more again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e63ffac7d..c5c8232ae 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ **GeoUtils** is an open source project to develop a core Python package for geospatial analysis and foster inter-operability between other Python GIS packages. -It aims at **facilitating end-user geospatial analysis by revolving around consistent `Raster` and `Vector` objects** that easily interface between +It aims at **facilitating end-user geospatial analysis by revolving around consistent `Raster` and `Vector` objects** that effortlessly interface between themselves. GeoUtils is founded on **implicit loading behaviour**, **robust numerical interfacing** and **convenient object-based methods** to easily perform the most common higher-level tasks needed by geospatial users. From 1416b5ae75d159646a0f39330d95ceebc97e53de Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 31 Mar 2023 18:40:47 -0800 Subject: [PATCH 165/184] Linting --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c5c8232ae..ada8e5fff 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ **GeoUtils** is an open source project to develop a core Python package for geospatial analysis and foster inter-operability between other Python GIS packages. It aims at **facilitating end-user geospatial analysis by revolving around consistent `Raster` and `Vector` objects** that effortlessly interface between -themselves. GeoUtils is founded on **implicit loading behaviour**, **robust numerical interfacing** and **convenient object-based methods** to easily perform +themselves. GeoUtils is founded on **implicit loading behaviour**, **robust numerical interfacing** and **convenient object-based methods** to easily perform the most common higher-level tasks needed by geospatial users. -If you are looking for an accessible Python package to write the Python equivalent of your [GDAL](https://gdal.org/) command lines, or of your +If you are looking for an accessible Python package to write the Python equivalent of your [GDAL](https://gdal.org/) command lines, or of your [QGIS](https://www.qgis.org/en/site/) analysis pipeline **without a steep learning curve** on Python GIS syntax, GeoUtils is perfect for you! For more advanced users, GeoUtils also aims at being efficient and scalable by supporting lazy loading and parallel computing (ongoing). @@ -29,7 +29,7 @@ the functionalities of these packages through interfacing or composition, and qu ## Documentation -For installation, quick start, gallery examples, a full feature description or a search through the API, see GeoUtils' documentation at: +For installation, quick start, gallery examples, a full feature description or a search through the API, see GeoUtils' documentation at: https://geoutils.readthedocs.io. ## Installation From 67bed6a1abcbbd15d983e2c5130f4cb914f80285 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 6 Apr 2023 11:02:53 -0800 Subject: [PATCH 166/184] Cast .data of single-band rasters to 2D, fix methods and tests --- geoutils/raster/multiraster.py | 5 +- geoutils/raster/raster.py | 283 +++++++++++++++++++-------------- tests/test_raster.py | 147 ++++++++--------- 3 files changed, 244 insertions(+), 191 deletions(-) diff --git a/geoutils/raster/multiraster.py b/geoutils/raster/multiraster.py index 385315a6c..b867df372 100644 --- a/geoutils/raster/multiraster.py +++ b/geoutils/raster/multiraster.py @@ -193,7 +193,10 @@ def stack_rasters( else: # img_data, _ = get_array_and_mask(reprojected_raster.data.squeeze()) # Use only first band - data.append(reprojected_raster.data[0, :]) + if reprojected_raster.count == 1: + data.append(reprojected_raster.data[:]) + else: + data.append(reprojected_raster.data[0, :]) # Remove unloaded rasters if not raster.is_loaded: diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index b6ab36ea3..a723e36bb 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -222,8 +222,6 @@ def _load_rio( data = dataset.read(masked=masked, window=window, **kwargs) else: data = dataset.read(indexes=indexes, masked=masked, window=window, **kwargs) - if len(data.shape) == 2: - data = data[np.newaxis, :, :] return data @@ -363,6 +361,10 @@ def __init__( res = tuple(np.asarray(self.res) * downsample) self.transform = rio.transform.from_origin(self.bounds.left, self.bounds.top, res[0], res[1]) + # Rebuild out_shape for squeezing 2D arrays + if count == 1: + out_shape = out_shape[1:] + # This will record the downsampled out_shape is data is only loaded later on by .load() self._out_shape = out_shape @@ -400,7 +402,10 @@ def count_on_disk(self) -> None | int: def count(self) -> int: """Count of bands loaded in memory if they are, otherwise the one on disk.""" if self.is_loaded: - return int(self.data.shape[0]) + if len(self.data.shape) == 2: + return 1 + else: + return int(self.data.shape[0]) # This can only happen if data is not loaded, with a DatasetReader on disk is open, never returns None return self.count_on_disk # type: ignore @@ -409,25 +414,38 @@ def height(self) -> int: """Height of the raster in pixels.""" if not self.is_loaded: return self._disk_shape[1] # type: ignore - return int(self.data.shape[1]) + else: + # If the raster is single-band + if len(self.data.shape) == 2: + return int(self.data.shape[0]) + # Or multi-band + else: + return int(self.data.shape[1]) @property def width(self) -> int: """Width of the raster in pixels.""" if not self.is_loaded: return self._disk_shape[2] # type: ignore - return int(self.data.shape[2]) + else: + # If the raster is single-band + if len(self.data.shape) == 2: + return int(self.data.shape[1]) + # Or multi-band + else: + return int(self.data.shape[2]) @property def shape(self) -> tuple[int, int]: """Shape (i.e., height, width) of the raster in pixels.""" # If a downsampling argument was defined but data not loaded yet if self._out_shape is not None and not self.is_loaded: - return self._out_shape[1], self._out_shape[2] - # If data is not loaded, pass the disk shape - if not self.is_loaded: - return self._disk_shape[1], self._disk_shape[2] # type: ignore - return int(self.data.shape[1]), int(self.data.shape[2]) + if len(self._out_shape) == 2: + return self._out_shape + else: + return self._out_shape[1:] + # If data loaded or not, pass the disk/data shape through height and width + return self.height, self.width @property def res(self) -> tuple[float | int, float | int]: @@ -513,7 +531,12 @@ def load(self, indexes: int | list[int] | None = None, **kwargs: Any) -> None: valid_indexes = tuple(indexes) # Update out_shape if self._out_shape is not None: - self._out_shape = (len(valid_indexes), self._out_shape[1], self._out_shape[2]) + # The shape attribute will automatically pass out_shape width and height, + # just need to add the first axis + if len(valid_indexes) > 1: + self._out_shape = (len(valid_indexes), *self.shape) + else: + self._out_shape = self.shape # Save which indexes are loaded self._indexes_loaded = valid_indexes @@ -605,7 +628,10 @@ def to_rio_dataset(self) -> rio.io.DatasetReader: nodata=self.nodata, driver="GTiff", ) as ds: - ds.write(self.data) + if self.count == 1: + ds.write(self.data[np.newaxis, :, :]) + else: + ds.write(self.data) # Then open as a DatasetReader return mfh.open() @@ -690,7 +716,10 @@ def __getitem__(self, index: Raster | Vector | np.ndarray | list[float] | tuple[ if isinstance(index, Mask): if not self.georeferenced_grid_equal(index): raise ValueError("Indexing a raster with a mask requires the two being on the same georeferenced grid.") - return self.data[:, index.data.squeeze()] + if self.count == 1: + return self.data[index.data.squeeze()] + else: + return self.data[:, index.data.squeeze()] # If input is array with the same shape elif isinstance(index, np.ndarray): if np.shape(index) != self.shape: @@ -698,7 +727,10 @@ def __getitem__(self, index: Raster | Vector | np.ndarray | list[float] | tuple[ if str(index.dtype) != "bool": index = index.astype(bool) warnings.warn(message="Input array was cast to boolean for indexing.", category=UserWarning) - return self.data[:, index] + if self.count == 1: + return self.data[index] + else: + return self.data[:, index] # Otherwise, subset with crop else: @@ -719,7 +751,7 @@ def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> if not self.georeferenced_grid_equal(index): raise ValueError("Indexing a raster with a mask requires the two being on the same georeferenced grid.") - ind = index.data.squeeze() + ind = index.data # If input is array with the same shape elif isinstance(index, np.ndarray): if np.shape(index) != self.shape: @@ -741,8 +773,10 @@ def __setitem__(self, index: Mask | np.ndarray, assign: np.ndarray | Number) -> if not self.is_loaded: self.load() # Assign the values to the index - self._data[:, ind] = assign # type: ignore - + if self.count == 1: + self._data[ind] = assign # type: ignore + else: + self._data[:, ind] = assign # type: ignore return None def raster_equal(self, other: object) -> bool: @@ -823,18 +857,19 @@ def _overloading_check( elif isinstance(other, np.ndarray): # Check that both array have the same shape - if len(other.shape) == 2: - other_data = other[np.newaxis, :, :] - else: - other_data = other + other_data = other + + # Squeeze first axis of other data if possible + if len(other_data.shape) == 3 and other_data.shape[0] == 1: + other_data = other_data.squeeze(axis=0) if self.data.shape == other_data.shape: pass else: - raise ValueError("Both rasters must have the same shape.") + raise ValueError("The raster and array must have the same shape.") nodata2 = None - dtype2 = other_data.dtype + dtype2 = other.dtype # Case 3 - other is a single number else: @@ -1180,11 +1215,11 @@ def nodata(self, new_nodata: int | float | tuple[int, ...] | tuple[float, ...] | :param new_nodata: New nodata to assign to this instance of Raster. """ - self.set_nodata(nodata=new_nodata) + self.set_nodata(new_nodata=new_nodata) def set_nodata( self, - nodata: int | float | tuple[int, ...] | tuple[float, ...] | None, + new_nodata: int | float | None, update_array: bool = True, update_mask: bool = True, ) -> None: @@ -1207,93 +1242,76 @@ def set_nodata( If you wish to set nodata value without updating the mask, run this function with the update_mask argument as False. - If None is passed as nodata, only the metadata is updated and the mask of oldnodata unset. + If None is passed as nodata, only the metadata is updated and the mask of old nodata unset. - :param nodata: Nodata values. + :param new_nodata: New nodata value. :param update_array: Update the old nodata values into new nodata values in the data array. :param update_mask: Update the old mask by unmasking old nodata and masking new nodata (if array is updated, old nodata are changed to new nodata and thus stay masked). """ - if nodata is not None and not isinstance(nodata, (tuple, int, float, np.integer, np.floating)): - raise ValueError("Type of nodata not understood, must be tuple or float or int") - - elif (isinstance(nodata, (int, float, np.integer, np.floating))) and self.count > 1: - nodata = (nodata,) * self.count + if new_nodata is not None and not isinstance(new_nodata, (int, float, np.integer, np.floating)): + raise ValueError("Type of nodata not understood, must be float or int.") - elif isinstance(nodata, tuple) and self.count == 1: - nodata = nodata[0] - - elif nodata is None: - nodata = None - - # Check that nodata has same length as number of bands in self - if isinstance(nodata, tuple): - if len(nodata) != self.count: - raise ValueError(f"Length of nodata ({len(nodata)}) incompatible with number of bands ({self.count})") - # Check that nodata value is compatible with dtype - for k in range(len(nodata)): - if not rio.dtypes.can_cast_dtype(nodata[k], self.dtypes[k]): - raise ValueError(f"nodata value {nodata[k]} incompatible with self.dtype {self.dtypes[k]}") - elif isinstance(nodata, (int, float, np.integer, np.floating)): - if not rio.dtypes.can_cast_dtype(nodata, self.dtypes[0]): - raise ValueError(f"nodata value {nodata} incompatible with self.dtype {self.dtypes[0]}") + if new_nodata is not None: + if not rio.dtypes.can_cast_dtype(new_nodata, self.dtypes[0]): + raise ValueError(f"nodata value {new_nodata} incompatible with self.dtype {self.dtypes[0]}") # If we update mask or array, get the masked array if update_array or update_mask: + # Extract the data variable, so the self.data property doesn't have to be called a bunch of times imgdata = self.data - # Loop through the bands - for i, new_nodata in enumerate(nodata if isinstance(nodata, Iterable) else [nodata]): - # Get the index of old nodatas - index_old_nodatas = imgdata.data[i, :, :] == self.nodata - - # Get the index of new nodatas, if it is defined - index_new_nodatas = imgdata.data[i, :, :] == new_nodata - - if np.count_nonzero(index_new_nodatas) > 0: - if update_array and update_mask: - warnings.warn( - message="New nodata value found in the data array. Those will be masked, and the old " - "nodata cells will now take the same value. Use set_nodata() with update_array=False " - "and/or update_mask=False to change this behaviour.", - category=UserWarning, - ) - elif update_array: - warnings.warn( - "New nodata value found in the data array. The old nodata cells will now take the same " - "value. Use set_nodata() with update_array=False to change this behaviour.", - category=UserWarning, - ) - elif update_mask: - warnings.warn( - "New nodata value found in the data array. Those will be masked. Use set_nodata() " - "with update_mask=False to change this behaviour.", - category=UserWarning, - ) - - if update_array: - # Only update array with new nodata if it is defined - if nodata is not None: - # Replace the nodata value in the Raster - imgdata.data[i, index_old_nodatas] = new_nodata - - if update_mask: - # If a mask already exists, unmask the old nodata values before masking the new ones - # Can be skipped if array is updated (nodata is transferred from old to new, this part of the mask - # stays the same) - if np.ma.is_masked(imgdata) and (not update_array or nodata is None): - # No way to unmask a value from the masked array, so we modify the mask directly - imgdata.mask[i, index_old_nodatas] = False - - # Masking like this works from the masked array directly, whether a mask exists or not - imgdata[i, index_new_nodatas] = np.ma.masked + # Get the index of old nodatas + index_old_nodatas = imgdata.data == self.nodata + + # Get the index of new nodatas, if it is defined + index_new_nodatas = imgdata.data == new_nodata + + if np.count_nonzero(index_new_nodatas) > 0: + if update_array and update_mask: + warnings.warn( + message="New nodata value found in the data array. Those will be masked, and the old " + "nodata cells will now take the same value. Use set_nodata() with update_array=False " + "and/or update_mask=False to change this behaviour.", + category=UserWarning, + ) + elif update_array: + warnings.warn( + "New nodata value found in the data array. The old nodata cells will now take the same " + "value. Use set_nodata() with update_array=False to change this behaviour.", + category=UserWarning, + ) + elif update_mask: + warnings.warn( + "New nodata value found in the data array. Those will be masked. Use set_nodata() " + "with update_mask=False to change this behaviour.", + category=UserWarning, + ) + + if update_array: + # Only update array with new nodata if it is defined + if new_nodata is not None: + # Replace the nodata value in the Raster + imgdata.data[index_old_nodatas] = new_nodata + + if update_mask: + # If a mask already exists, unmask the old nodata values before masking the new ones + # Can be skipped if array is updated (nodata is transferred from old to new, this part of the mask + # stays the same) + if np.ma.is_masked(imgdata) and (not update_array or new_nodata is None): + # No way to unmask a value from the masked array, so we modify the mask directly + imgdata.mask[index_old_nodatas] = False + + # Masking like this works from the masked array directly, whether a mask exists or not + imgdata[index_new_nodatas] = np.ma.masked + # Update the data self._data = imgdata # Update the nodata value - self._nodata = nodata + self._nodata = new_nodata @property def data(self) -> np.ma.masked_array: @@ -1315,7 +1333,7 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: The data setter behaviour is the following: 1. Writes the data in a masked array, whether the input is a classic array or a masked_array, - 2. Reshapes the data in a 3D array if it is 2D that can be broadcasted, raises an error otherwise, + 2. Reshapes the data to a 2D array if it is single band, 3. Raises an error if the dtype is different from that of the Raster, and points towards .copy() or .astype(), 4. Sets a new nodata value to the Raster if none is set and if the provided array contains non-finite values that are unmasked (including if there is no mask at all, e.g. NaNs in a classic array), @@ -1325,23 +1343,27 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: :param new_data: New data to assign to this instance of Raster. """ - # Check that new_data is a Numpy array + # Check that new_data is a NumPy array if not isinstance(new_data, np.ndarray): raise ValueError("New data must be a numpy array.") - if len(new_data.shape) == 2: - new_data = new_data[np.newaxis, :, :] + if len(new_data.shape) not in [2, 3]: + raise ValueError("Data array must have 2 or 3 dimensions.") + + # Squeeze 3D data if the band axis is of length 1 + if len(new_data.shape) == 3 and new_data.shape[0] == 1: + new_data = new_data.squeeze(axis=0) # Check that new_data has correct shape - if self._data is not None: + if self.is_loaded: dtype = str(self._data.dtype) - orig_shape = self._data.shape[1:] + orig_shape = self._data.shape elif self.filename is not None: - dtype = self.dtypes[0] - orig_shape = self.shape + dtype = self._disk_dtypes[0] + orig_shape = self._out_shape else: dtype = str(new_data.dtype) - orig_shape = new_data.shape[1:] + orig_shape = new_data.shape # Check that new_data has the right type if str(new_data.dtype) != dtype: @@ -1350,9 +1372,9 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: "different dtype, or astype() to change type.".format(dtype) ) - if new_data.shape[1:] != orig_shape: + if new_data.shape != orig_shape: raise ValueError( - f"New data must be of the same shape as existing data: {orig_shape}. Given: {new_data.shape[1:]}." + f"New data must be of the same shape as existing data: {orig_shape}. Given: {new_data.shape}." ) # If the new data is not masked and has non-finite values, we define a default nodata value @@ -1696,7 +1718,11 @@ def __array_function__( first_arg = args[0].data.compressed() elif func.__name__ in ["gradient"]: - first_arg = args[0].data[0, :, :] + if self.count == 1: + first_arg = args[0].data + else: + warnings.warn("Applying np.gradient to first raster band only.") + first_arg = args[0].data[0, :, :] # Otherwise, we run the numpy function normally (most take masks into account) else: @@ -1805,7 +1831,10 @@ def crop( if self.is_loaded: (rowmin, rowmax), (colmin, colmax) = final_window.toranges() - crop_img = self.data[:, rowmin:rowmax, colmin:colmax] + if self.count == 1: + crop_img = self.data[rowmin:rowmax, colmin:colmax] + else: + crop_img = self.data[:, rowmin:rowmax, colmin:colmax] else: with rio.open(self.filename) as raster: crop_img = raster.read( @@ -2154,7 +2183,7 @@ def save( elif blank_value is not None: if isinstance(blank_value, int) | isinstance(blank_value, float): save_data = np.zeros(self.data.shape) - save_data[:, :, :] = blank_value + save_data[:] = blank_value else: raise ValueError("blank_values must be one of int, float (or None).") else: @@ -2168,6 +2197,10 @@ def save( warnings.warn(f"No nodata set, will use default value of {nodata}") save_data = save_data.filled(nodata) + # Cast to 3D before saving if single band + if self.count == 1: + save_data = save_data[np.newaxis, :, :] + with rio.open( filename, "w", @@ -2378,6 +2411,12 @@ def show( else: raise ValueError("Index must be int or None") + # Get data + if self.count == 1: + data = self.data + else: + data = self.data[index - 1, :, :] + # If multiple bands (RGB), cbar does not make sense if isinstance(index, abc.Sequence): if len(index) > 1: @@ -2394,10 +2433,10 @@ def show( # Set colorbar min/max values (needed for ScalarMappable) if vmin is None: - vmin = np.nanmin(self.data[index - 1, :, :]) + vmin = np.nanmin(data) if vmax is None: - vmax = np.nanmax(self.data[index - 1, :, :]) + vmax = np.nanmax(data) # Make sure they are numbers, to avoid mpl error try: @@ -2423,7 +2462,7 @@ def show( # Use data array directly, as rshow on self.ds will re-load data rshow( - self.data[index - 1, :, :], + data, transform=self.transform, ax=ax0, cmap=cmap, @@ -2579,7 +2618,10 @@ def format_value(value: Any) -> Any: rio_window = rio.windows.Window(col, row, width, height) if self.is_loaded: - data = self.data[slice(None) if index is None else index - 1, row : row + height, col : col + width] + if self.count == 1: + data = self.data[row : row + height, col : col + width] + else: + data = self.data[slice(None) if index is None else index - 1, row : row + height, col : col + width] if not masked: data = data.filled() value = format_value(data) @@ -2835,7 +2877,11 @@ def interp_points( ind_invalid = np.vectorize(lambda k1, k2: self.outside_image(k1, k2, index=True))(j, i) - rpts = map_coordinates(self.data[index - 1, :, :].astype(np.float32), [i, j], **kwargs) + if self.count == 1: + rpts = map_coordinates(self.data[:, :].astype(np.float32), [i, j], **kwargs) + else: + rpts = map_coordinates(self.data[index - 1, :, :].astype(np.float32), [i, j], **kwargs) + rpts = np.array(rpts, dtype=np.float32) rpts[np.array(ind_invalid)] = np.nan @@ -2877,7 +2923,7 @@ def split_bands(self: RasterType, copy: bool = False, subset: list[int] | int | # Set the data to a slice of the original array bands.append( self.from_array( - self.data[band_n - 1, :, :].reshape((1,) + self.data.shape[1:]), + self.data[band_n - 1, :, :], transform=self.transform, crs=self.crs, nodata=self.nodata, @@ -2947,7 +2993,10 @@ def to_points( # If the Raster is loaded, pick from the data, otherwise use the disk-sample method from rasterio. if self.is_loaded: - pixel_data = self.data[:, rows, cols] + if self.count == 1: + pixel_data = self.data[rows, cols] + else: + pixel_data = self.data[:, rows, cols] else: with rio.open(self.filename) as raster: pixel_data = np.array(list(raster.sample(zip(x_coords, y_coords)))).T @@ -3135,7 +3184,7 @@ def __init__( category=UserWarning, message="Multi-band raster provided to create a Mask, only the first band will be used.", ) - self._data = np.reshape(self.data[0, :], (1, self.shape[0], self.shape[1])) + self._data = self.data[0, :, :] # Convert masked array to boolean self._data = self.data.astype(bool) diff --git a/tests/test_raster.py b/tests/test_raster.py index c4d4229b0..df2589e0b 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -108,13 +108,13 @@ def test_init(self, example: str) -> None: # For re-instantiation via Raster (r2 above), we check the behaviour: # By default, raster were unloaded, and were loaded during raster_equal() independently # So the instances should not be pointing to the same data and not mirror modifs - r0.data[0, 0, 0] += 5 - assert r2.data[0, 0, 0] != r0.data[0, 0, 0] + r0.data[0, 0] += 5 + assert r2.data[0, 0] != r0.data[0, 0] # However, if we reinstantiate now that the data is loaded, it should point to the same r5 = gu.Raster(r0) - r0.data[0, 0, 0] += 5 - assert r5.data[0, 0, 0] == r0.data[0, 0, 0] + r0.data[0, 0] += 5 + assert r5.data[0, 0] == r0.data[0, 0] # With r.count = 2 r0._data = np.repeat(r0.data, 2).reshape((2,) + r0.shape) @@ -259,7 +259,7 @@ def test_load(self) -> None: assert r.count_on_disk == 1 assert r.indexes == (1,) assert r.indexes_on_disk == (1,) - assert r.data.shape == (r.count, r.height, r.width) + assert r.data.shape == (r.height, r.width) # Test 3 - single band, loading data r = gu.Raster(self.landsat_b4_path, load_data=True) @@ -268,7 +268,7 @@ def test_load(self) -> None: assert r.count_on_disk == 1 assert r.indexes == (1,) assert r.indexes_on_disk == (1,) - assert r.data.shape == (r.count, r.height, r.width) + assert r.data.shape == (r.height, r.width) # Test 4 - multiple bands, load all bands r = gu.Raster(self.landsat_rgb_path, load_data=True) @@ -292,7 +292,7 @@ def test_load(self) -> None: assert r.count_on_disk == 3 assert r.indexes == (1,) assert r.indexes_on_disk == (1, 2, 3) - assert r.data.shape == (r.count, r.height, r.width) + assert r.data.shape == (r.height, r.width) # Test 6 - multiple bands, load a list of bands r = gu.Raster(self.landsat_rgb_path, load_data=True, indexes=[2, 3]) @@ -309,7 +309,7 @@ def test_load(self) -> None: assert r.count_on_disk == 3 assert r.indexes == (1,) assert r.indexes_on_disk == (1, 2, 3) - assert r.data.shape == (r.count, r.height, r.width) + assert r.data.shape == (r.height, r.width) # Test 8 - load a list of band a posteriori calling load() r = gu.Raster(self.landsat_rgb_path) @@ -348,8 +348,8 @@ def test_to_rio_dataset(self, example: str): assert rst.__getattribute__(attr) == rio_ds.__getattribute__(attr) # Check that the masked arrays are equal - assert np.array_equal(rst.data.data, rio_ds.read().data) - assert np.array_equal(rst.data.mask, rio_ds.read(masked=True).mask) + assert np.array_equal(rst.data.data, rio_ds.read().squeeze()) + assert np.array_equal(rst.data.mask, rio_ds.read(masked=True).mask.squeeze()) @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path, landsat_rgb_path]) # type: ignore def test_to_xarray(self, example: str): @@ -363,7 +363,8 @@ def test_to_xarray(self, example: str): assert isinstance(ds, xr.DataArray) # Check that all attributes are equal - assert ds.band.size == rst.count + if rst.count > 1: + assert ds.band.size == rst.count assert ds.x.size == rst.width assert ds.y.size == rst.height @@ -381,7 +382,10 @@ def test_to_xarray(self, example: str): assert ds.spatial_ref.crs_wkt == rst.crs.to_wkt() # Check that the arrays are equal in NaN type - assert np.array_equal(rst.data.data, ds.data) + if rst.count > 1: + assert np.array_equal(rst.data.data, ds.data) + else: + assert np.array_equal(rst.data.data, ds.data.squeeze()) @pytest.mark.parametrize("nodata_init", [None, "type_default"]) # type: ignore @pytest.mark.parametrize( @@ -418,15 +422,15 @@ def test_data_setter(self, dtype: str, nodata_init: str | None) -> None: # Fix the random seed np.random.seed(42) - arr = np.random.randint(low=val_min, high=val_max, size=(1, width, height), dtype=randint_dtype).astype(dtype) - mask = np.random.randint(0, 2, size=(1, width, height), dtype=bool) + arr = np.random.randint(low=val_min, high=val_max, size=(width, height), dtype=randint_dtype).astype(dtype) + mask = np.random.randint(0, 2, size=(width, height), dtype=bool) # Check that we are actually masking stuff assert np.count_nonzero(mask) > 0 # Add a random floating point value if the data type is float if "float" in dtype: - arr += np.random.normal(size=(1, width, height)) + arr += np.random.normal(size=(width, height)) # Use either the default nodata or None if nodata_init == "type_default": @@ -483,7 +487,7 @@ def test_data_setter(self, dtype: str, nodata_init: str | None) -> None: indices = np.indices(np.shape(arr)) ind_nm = indices[:, ~mask] rand_ind = np.random.randint(low=0, high=ind_nm.shape[1], size=1)[0] - arr_with_unmasked_nodata[ind_nm[0, rand_ind], ind_nm[1, rand_ind], ind_nm[2, rand_ind]] = np.nan + arr_with_unmasked_nodata[ind_nm[0, rand_ind], ind_nm[1, rand_ind]] = np.nan if nodata is None: with pytest.warns( @@ -604,7 +608,7 @@ def test_downsampling(self) -> None: """ # Test single band r = gu.Raster(self.landsat_b4_path, downsample=4) - assert r.data.shape == (1, 164, 200) + assert r.data.shape == (164, 200) assert r.height == 164 assert r.width == 200 @@ -814,18 +818,18 @@ def test_masking(self, example: str) -> None: # Test boolean mask r = gu.Raster(example) # We need to know the existing nodata in case they exist, as set_mask only masks new values - orig_mask = r.data.mask.copy().squeeze() + orig_mask = r.data.mask.copy() mask = r.data.data == np.nanmin(r.data) r.set_mask(mask) assert (np.count_nonzero(mask) > 0) & np.array_equal(orig_mask | mask > 0, r.data.mask) # Test mask object r2 = gu.Raster(example) - mask2 = r2 == np.nanmin(r2) + mask2 = r2.data == np.nanmin(r2) r2.set_mask(mask2) # Indexing at 0 for the mask in case the data has multiple bands assert (np.count_nonzero(mask2) > 0) & np.array_equal( - orig_mask | mask2.data.filled(False).squeeze(), r2.data.mask[0, :, :] + orig_mask | mask2.filled(False), r2.data.mask ) # The two last masking (array or Mask) should yield the same result when the data is only 2D if r.count == 1: @@ -885,7 +889,7 @@ def test_getitem_setitem(self, example: str) -> None: # It should work with a number, or a 1D array of the same length as the indexed one rst[mask] = 1.0 - rst2[arr] = np.ones(rst2.shape)[arr] + rst2[mask] = np.ones(rst2.shape)[arr] # The rasters should be the same assert rst2.raster_equal(rst) @@ -942,29 +946,29 @@ def test_crop(self, data: list[str]) -> None: crop_geom2 = [crop_geom[0] + rand_int * r.res[0], crop_geom[1], crop_geom[2], crop_geom[3]] r_cropped = r.crop(crop_geom2, inplace=False) assert list(r_cropped.bounds) == crop_geom2 - assert np.array_equal(r.data[:, :, rand_int:].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, :, rand_int:].mask, r_cropped.data.mask) + assert np.array_equal(r.data[:, rand_int:].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[ :, rand_int:].mask, r_cropped.data.mask) # Right crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2] - rand_int * r.res[0], crop_geom[3]] r_cropped = r.crop(crop_geom2, inplace=False) assert list(r_cropped.bounds) == crop_geom2 - assert np.array_equal(r.data[:, :, :-rand_int].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, :, :-rand_int].mask, r_cropped.data.mask) + assert np.array_equal(r.data[:, :-rand_int].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[:, :-rand_int].mask, r_cropped.data.mask) # Bottom crop_geom2 = [crop_geom[0], crop_geom[1] + rand_int * abs(r.res[1]), crop_geom[2], crop_geom[3]] r_cropped = r.crop(crop_geom2, inplace=False) assert list(r_cropped.bounds) == crop_geom2 - assert np.array_equal(r.data[:, :-rand_int, :].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, :-rand_int, :].mask, r_cropped.data.mask) + assert np.array_equal(r.data[:-rand_int, :].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[:-rand_int, :].mask, r_cropped.data.mask) # Top crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3] - rand_int * abs(r.res[1])] r_cropped = r.crop(crop_geom2, inplace=False) assert list(r_cropped.bounds) == crop_geom2 - assert np.array_equal(r.data[:, rand_int:, :].data, r_cropped.data, equal_nan=True) - assert np.array_equal(r.data[:, rand_int:, :].mask, r_cropped.data.mask) + assert np.array_equal(r.data[rand_int:, :].data, r_cropped.data, equal_nan=True) + assert np.array_equal(r.data[rand_int:, :].mask, r_cropped.data.mask) # Same but tuple crop_geom3: tuple[float, float, float, float] = ( @@ -975,8 +979,8 @@ def test_crop(self, data: list[str]) -> None: ) r_cropped = r.crop(crop_geom3, inplace=False) assert list(r_cropped.bounds) == list(crop_geom3) - assert np.array_equal(r.data[:, rand_int:, :].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, rand_int:, :].mask, r_cropped.data.mask) + assert np.array_equal(r.data[rand_int:, :].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[rand_int:, :].mask, r_cropped.data.mask) # -- Test with crop_geom being a Raster -- # r_cropped2 = r.crop(r_cropped, inplace=False) @@ -1010,29 +1014,29 @@ def test_crop(self, data: list[str]) -> None: crop_geom2 = [crop_geom[0] + rand_float * r.res[0], crop_geom[1], crop_geom[2], crop_geom[3]] r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[1] - (r_cropped.bounds.right - r_cropped.bounds.left) / r.res[0] == int(rand_float) - assert np.array_equal(r.data[:, :, int(rand_float) :].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, :, int(rand_float) :].mask, r_cropped.data.mask) + assert np.array_equal(r.data[:, int(rand_float) :].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[:, int(rand_float) :].mask, r_cropped.data.mask) # right crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2] - rand_float * r.res[0], crop_geom[3]] r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[1] - (r_cropped.bounds.right - r_cropped.bounds.left) / r.res[0] == int(rand_float) - assert np.array_equal(r.data[:, :, : -int(rand_float)].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, :, : -int(rand_float)].mask, r_cropped.data.mask) + assert np.array_equal(r.data[:, : -int(rand_float)].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[:, : -int(rand_float)].mask, r_cropped.data.mask) # bottom crop_geom2 = [crop_geom[0], crop_geom[1] + rand_float * abs(r.res[1]), crop_geom[2], crop_geom[3]] r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[0] - (r_cropped.bounds.top - r_cropped.bounds.bottom) / r.res[1] == int(rand_float) - assert np.array_equal(r.data[:, : -int(rand_float), :].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, : -int(rand_float), :].mask, r_cropped.data.mask) + assert np.array_equal(r.data[: -int(rand_float), :].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[: -int(rand_float), :].mask, r_cropped.data.mask) # top crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3] - rand_float * abs(r.res[1])] r_cropped = r.crop(crop_geom2, inplace=False) assert r.shape[0] - (r_cropped.bounds.top - r_cropped.bounds.bottom) / r.res[1] == int(rand_float) - assert np.array_equal(r.data[:, int(rand_float) :, :].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[:, int(rand_float) :, :].mask, r_cropped.data.mask) + assert np.array_equal(r.data[int(rand_float) :, :].data, r_cropped.data.data, equal_nan=True) + assert np.array_equal(r.data[int(rand_float) :, :].mask, r_cropped.data.mask) # -- Test with mode='match_extent' -- # # Test all sides at once, with rand_float less than half the smallest extent @@ -1166,7 +1170,7 @@ def test_reproject(self, example: str) -> None: ) # data is cropped to the same extent - new_data = r.data[:, rand_int::, rand_int::] + new_data = r.data[rand_int::, rand_int::] r2b = gu.Raster.from_array(data=new_data, transform=new_transform, crs=r.crs, nodata=r.nodata) # Create a raster with different resolution @@ -1592,7 +1596,6 @@ def test_xy2ij_and_interp(self) -> None: # 2x2 slices z_ind = np.mean( img[ - 0, slice(int(np.floor(i[k])), int(np.ceil(i[k])) + 1), slice(int(np.floor(j[k])), int(np.ceil(j[k])) + 1), ] @@ -1629,7 +1632,7 @@ def test_xy2ij_and_interp(self) -> None: img = r.data for k in range(len(xrand)): # We directly sample the values - z_ind = img[0, int(i[k]), int(j[k])] + z_ind = img[int(i[k]), int(j[k])] list_z_ind.append(z_ind) rpts = r.interp_points(pts, order=1) @@ -1641,7 +1644,7 @@ def test_xy2ij_and_interp(self) -> None: y = 3101000.0 i, j = r.xy2ij(x, y) val = r.interp_points([(x, y)], order=1)[0] - assert img[0, int(i), int(j)] == val + assert img[int(i), int(j)] == val # Finally, check that interp convert to latlon lat, lon = gu.projtools.reproject_to_latlon((x, y), in_crs=r.crs) @@ -1675,7 +1678,7 @@ def test_value_at_coords(self) -> None: # Check that the value at this coordinate is the same as when indexing z_val = r.value_at_coords(xtest0, ytest0) - z = r.data.data[0, itest0, jtest0] + z = r.data.data[itest0, jtest0] assert z == z_val # Check that the value is the same the other 4 corners of the pixel @@ -1763,17 +1766,17 @@ def test_value_at_coords(self) -> None: # Lower right pixel x, y = [r.bounds.right - r.res[0] / 2, r.bounds.bottom + r.res[1] / 2] lat, lon = pt.reproject_to_latlon([x, y], r.crs) - assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -1, -1] + assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[-1, -1] # One pixel above x, y = [r.bounds.right - r.res[0] / 2, r.bounds.bottom + 3 * r.res[1] / 2] lat, lon = pt.reproject_to_latlon([x, y], r.crs) - assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -2, -1] + assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[-2, -1] # One pixel left x, y = [r.bounds.right - 3 * r.res[0] / 2, r.bounds.bottom + r.res[1] / 2] lat, lon = pt.reproject_to_latlon([x, y], r.crs) - assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[0, -1, -2] + assert r.value_at_coords(x, y) == r.value_at_coords(lon, lat, latlon=True) == r.data[-1, -2] @pytest.mark.parametrize("example", [landsat_b4_path, aster_dem_path]) # type: ignore def test_set_nodata(self, example: str) -> None: @@ -1794,7 +1797,7 @@ def test_set_nodata(self, example: str) -> None: # Check that the new_nodata does not exist in the raster yet, and set it assert np.count_nonzero(r_copy.data.data == new_nodata) == 0 - r.set_nodata(nodata=new_nodata) + r.set_nodata(new_nodata=new_nodata) # The nodata value should have been set in the metadata assert r.nodata == new_nodata @@ -1814,7 +1817,7 @@ def test_set_nodata(self, example: str) -> None: # Then, we repeat for a nodata that already existed, we artificially modify the value on an unmasked pixel r = r_copy.copy() mask_pixel_artificially_set = np.zeros(np.shape(r.data), dtype=bool) - mask_pixel_artificially_set[0, 0, 0] = True + mask_pixel_artificially_set[0, 0] = True r.data.data[mask_pixel_artificially_set] = new_nodata # We set the value as masked before unmasking to create the mask if it does not exist yet r.data[mask_pixel_artificially_set] = np.ma.masked @@ -1831,7 +1834,7 @@ def test_set_nodata(self, example: str) -> None: "and/or update_mask=False to change this behaviour." ), ): - r.set_nodata(nodata=new_nodata) + r.set_nodata(new_nodata=new_nodata) # The nodata value should have been set in the metadata assert r.nodata == new_nodata @@ -1873,7 +1876,7 @@ def test_set_nodata(self, example: str) -> None: "with update_mask=False to change this behaviour." ), ): - r.set_nodata(nodata=new_nodata, update_array=False) + r.set_nodata(new_nodata=new_nodata, update_array=False) # The nodata value should have been set in the metadata assert r.nodata == new_nodata @@ -1901,7 +1904,7 @@ def test_set_nodata(self, example: str) -> None: "value. Use set_nodata() with update_array=False to change this behaviour." ), ): - r.set_nodata(nodata=new_nodata, update_mask=False) + r.set_nodata(new_nodata=new_nodata, update_mask=False) # The nodata value should have been set in the metadata assert r.nodata == new_nodata @@ -1923,7 +1926,7 @@ def test_set_nodata(self, example: str) -> None: # -- Fourth, test set_nodata() with both update_array=False and update_mask=False -- r = r_copy.copy() - r.set_nodata(nodata=new_nodata, update_array=False, update_mask=False) + r.set_nodata(new_nodata=new_nodata, update_array=False, update_mask=False) r.data.data[mask_pixel_artificially_set] = new_nodata # We set the value as masked before unmasking to create the mask if it does not exist yet r.data[mask_pixel_artificially_set] = np.ma.masked @@ -1940,8 +1943,8 @@ def test_set_nodata(self, example: str) -> None: # -- Fifth, let's check that errors are raised when they should -- # A ValueError if input nodata is neither a list, tuple, integer, floating - with pytest.raises(ValueError, match="Type of nodata not understood, must be tuple or float or int"): - r.set_nodata(nodata="this_should_not_work") # type: ignore + with pytest.raises(ValueError, match="Type of nodata not understood, must be float or int."): + r.set_nodata(new_nodata="this_should_not_work") # type: ignore # A ValueError if nodata value is incompatible with dtype expected_message = r"nodata value .* incompatible with self.dtype .*" @@ -2260,9 +2263,9 @@ def test_from_array(self) -> None: # Test that data mask is taken into account img.data.mask = np.zeros((img.shape), dtype="bool") - img.data.mask[0, 0, 0] = True + img.data.mask[0, 0] = True out_img = gu.Raster.from_array(img.data, img.transform, img.crs, nodata=0) - assert out_img.data.mask[0, 0, 0] + assert out_img.data.mask[0, 0] # Check that error is raised if the transform is not affine with pytest.raises(TypeError, match="The transform argument needs to be Affine or tuple."): @@ -2314,9 +2317,7 @@ def test_split_bands(self) -> None: # Check that the shapes are correct. assert red.count == 1 - assert red.data.shape[0] == 1 assert img.count == 3 - assert img.data.shape[0] == 3 # Extract only one band (then it will not return a list) red2 = img.split_bands(copy=False, subset=1)[0] @@ -2757,36 +2758,36 @@ def test_crop(self, mask: gu.Mask) -> None: crop_geom2 = [crop_geom[0] + rand_int * mask.res[0], crop_geom[1], crop_geom[2], crop_geom[3]] mask_cropped = mask.crop(crop_geom2, inplace=False) assert list(mask_cropped.bounds) == crop_geom2 - assert np.array_equal(mask.data[:, :, rand_int:].data, mask_cropped.data.data, equal_nan=True) - assert np.array_equal(mask.data[:, :, rand_int:].mask, mask_cropped.data.mask) + assert np.array_equal(mask.data[:, rand_int:].data, mask_cropped.data.data, equal_nan=True) + assert np.array_equal(mask.data[:, rand_int:].mask, mask_cropped.data.mask) # Right crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2] - rand_int * mask.res[0], crop_geom[3]] mask_cropped = mask.crop(crop_geom2, inplace=False) assert list(mask_cropped.bounds) == crop_geom2 - assert np.array_equal(mask.data[:, :, :-rand_int].data, mask_cropped.data.data, equal_nan=True) - assert np.array_equal(mask.data[:, :, :-rand_int].mask, mask_cropped.data.mask) + assert np.array_equal(mask.data[:, :-rand_int].data, mask_cropped.data.data, equal_nan=True) + assert np.array_equal(mask.data[:, :-rand_int].mask, mask_cropped.data.mask) # Bottom crop_geom2 = [crop_geom[0], crop_geom[1] + rand_int * abs(mask.res[1]), crop_geom[2], crop_geom[3]] mask_cropped = mask.crop(crop_geom2, inplace=False) assert list(mask_cropped.bounds) == crop_geom2 - assert np.array_equal(mask.data[:, :-rand_int, :].data, mask_cropped.data.data, equal_nan=True) - assert np.array_equal(mask.data[:, :-rand_int, :].mask, mask_cropped.data.mask) + assert np.array_equal(mask.data[:-rand_int, :].data, mask_cropped.data.data, equal_nan=True) + assert np.array_equal(mask.data[:-rand_int, :].mask, mask_cropped.data.mask) # Top crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3] - rand_int * abs(mask.res[1])] mask_cropped = mask.crop(crop_geom2, inplace=False) assert list(mask_cropped.bounds) == crop_geom2 - assert np.array_equal(mask.data[:, rand_int:, :].data, mask_cropped.data, equal_nan=True) - assert np.array_equal(mask.data[:, rand_int:, :].mask, mask_cropped.data.mask) + assert np.array_equal(mask.data[rand_int:, :].data, mask_cropped.data, equal_nan=True) + assert np.array_equal(mask.data[rand_int:, :].mask, mask_cropped.data.mask) # Test inplace mask_orig = mask.copy() mask.crop(crop_geom2) assert list(mask.bounds) == crop_geom2 - assert np.array_equal(mask_orig.data[:, rand_int:, :].data, mask.data, equal_nan=True) - assert np.array_equal(mask_orig.data[:, rand_int:, :].mask, mask.data.mask) + assert np.array_equal(mask_orig.data[rand_int:, :].data, mask.data, equal_nan=True) + assert np.array_equal(mask_orig.data[rand_int:, :].mask, mask.data.mask) # Run with match_extent, check that inplace or not yields the same result @@ -2856,7 +2857,7 @@ class TestArithmetic: crs=None, nodata=_default_nodata("float32"), ) - r2_zero.data[0, 0, 0] = 0 + r2_zero.data[0, 0] = 0 # Create rasters with different shape, crs or transforms for testing errors r1_wrong_shape = gu.Raster.from_array( @@ -3004,7 +3005,7 @@ def test_ops_2args_expl(self, op: str) -> None: r2 = self.r2 r2_zero = self.r2_zero satimg = self.satimg - array = np.random.randint(1, 255, (1, self.height, self.width)).astype("float64") + array = np.random.randint(1, 255, (self.height, self.width)).astype("float64") floatval = 3.14 intval = 1 @@ -3105,7 +3106,7 @@ def test_reflectivity(self, ops: list[str]) -> None: warnings.filterwarnings("ignore", message="invalid value encountered") # Test various inputs: Raster with different dtypes, np.ndarray, single number - array = np.random.randint(1, 255, (1, self.height, self.width)).astype("float64") + array = np.random.randint(1, 255, (self.height, self.width)).astype("float64") floatval = 3.14 intval = 1 @@ -3672,7 +3673,7 @@ def test_array_functions_1nin(self, arrfunc_str: str, dtype: str, nodata_init: N elif "gradient" in arrfunc_str: # For the gradient, we need to take a single band output_rst = arrfunc(rst) - output_ma = np.gradient(rst.data[0, :, :]) + output_ma = np.gradient(rst.data) else: output_rst = arrfunc(rst) output_ma = arrfunc(rst.data) From d5e66ce0b8c27dec64f5049c43a2d27ef603f594 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 6 Apr 2023 14:42:39 -0800 Subject: [PATCH 167/184] Linting and fix some inconsistencies --- geoutils/raster/raster.py | 72 +++++++++++++++++++++------------------ tests/test_raster.py | 6 ++-- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index a723e36bb..a0897bb04 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -8,7 +8,6 @@ import pathlib import warnings from collections import abc -from collections.abc import Iterable from contextlib import ExitStack from math import floor from numbers import Number @@ -174,10 +173,11 @@ def _default_nodata(dtype: str | np.dtype | type) -> int: def _load_rio( dataset: rio.io.DatasetReader, - indexes: int | tuple[int, ...] | None = None, + indexes: int | list[int] | None = None, masked: bool = False, transform: Affine | None = None, shape: tuple[int, int] | None = None, + out_count: int | None = None, **kwargs: Any, ) -> np.ma.masked_array: r""" @@ -190,6 +190,7 @@ def _load_rio( :param masked: Whether the mask should be read (if any exists) to use the nodata to mask values. :param transform: Create a window from the given transform (to read only parts of the raster) :param shape: Expected shape of the read ndarray. Must be given together with the `transform` argument. + :param out_count: Specify the count for a subsampled version (to be used with kwargs out_shape). :raises ValueError: If only one of ``transform`` and ``shape`` are given. @@ -198,13 +199,16 @@ def _load_rio( \*\*kwargs: any additional arguments to rasterio.io.DatasetReader.read. Useful ones are: .. hlist:: - * out_shape : to load a subsampled version + * out_shape : to load a subsampled version, always use with out_count * window : to load a cropped version * resampling : to set the resampling algorithm """ # If out_shape is passed, no need to account for transform and shape if kwargs["out_shape"] is not None: window = None + # If multi-band raster, the out_shape needs to contain the count + if out_count is not None and out_count > 1: + kwargs["out_shape"] = (out_count, *kwargs["out_shape"]) else: if transform is not None and shape is not None: if transform == dataset.transform: @@ -255,7 +259,7 @@ def __init__( load_data: bool = False, downsample: AnyNumber = 1, masked: bool = True, - nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = None, + nodata: int | float | None = None, ) -> None: """ Instantiate a raster from a filename or rasterio dataset. @@ -280,11 +284,12 @@ def __init__( self._data: np.ma.masked_array | None = None self._transform: affine.Affine | None = None self._crs: CRS | None = None - self._nodata: int | float | tuple[int, ...] | tuple[float, ...] | None = nodata + self._nodata: int | float | None = nodata self._indexes = indexes self._indexes_loaded: int | tuple[int, ...] | None = None self._masked = masked - self._out_shape: tuple[int, int, int] | None = None + self._out_count: int | None = None + self._out_shape: tuple[int, int] | None = None self._disk_hash: int | None = None self._is_modified = True self._disk_shape: tuple[int, int, int] | None = None @@ -346,32 +351,35 @@ def __init__( count = self.count elif isinstance(indexes, int): count = 1 - elif isinstance(indexes, abc.Iterable): + else: count = len(indexes) # Downsampled image size if not isinstance(downsample, (int, float)): raise TypeError("downsample must be of type int or float.") if downsample == 1: - out_shape = (count, self.height, self.width) + out_shape = (self.height, self.width) else: down_width = int(np.ceil(self.width / downsample)) down_height = int(np.ceil(self.height / downsample)) - out_shape = (count, down_height, down_width) + out_shape = (down_height, down_width) res = tuple(np.asarray(self.res) * downsample) self.transform = rio.transform.from_origin(self.bounds.left, self.bounds.top, res[0], res[1]) - # Rebuild out_shape for squeezing 2D arrays - if count == 1: - out_shape = out_shape[1:] - # This will record the downsampled out_shape is data is only loaded later on by .load() self._out_shape = out_shape + self._out_count = count if load_data: # Mypy doesn't like the out_shape for some reason. I can't figure out why! (erikmannerfelt, 14/01/2022) # Don't need to pass shape and transform, because out_shape overrides it - self.data = _load_rio(ds, indexes=indexes, masked=masked, out_shape=out_shape) # type: ignore + self.data = _load_rio( + ds, + indexes=indexes, + masked=masked, + out_shape=out_shape, + out_count=count, + ) # type: ignore # Probably don't want to use set_nodata that can update array, setting self._nodata is sufficient # Set nodata only if data is loaded @@ -440,10 +448,7 @@ def shape(self) -> tuple[int, int]: """Shape (i.e., height, width) of the raster in pixels.""" # If a downsampling argument was defined but data not loaded yet if self._out_shape is not None and not self.is_loaded: - if len(self._out_shape) == 2: - return self._out_shape - else: - return self._out_shape[1:] + return self._out_shape # If data loaded or not, pass the disk/data shape through height and width return self.height, self.width @@ -529,14 +534,9 @@ def load(self, indexes: int | list[int] | None = None, **kwargs: Any) -> None: valid_indexes = (indexes,) else: valid_indexes = tuple(indexes) - # Update out_shape + # Update out_count if out_shape exists (when a downsampling has been passed) if self._out_shape is not None: - # The shape attribute will automatically pass out_shape width and height, - # just need to add the first axis - if len(valid_indexes) > 1: - self._out_shape = (len(valid_indexes), *self.shape) - else: - self._out_shape = self.shape + self._out_count = len(valid_indexes) # Save which indexes are loaded self._indexes_loaded = valid_indexes @@ -545,11 +545,12 @@ def load(self, indexes: int | list[int] | None = None, **kwargs: Any) -> None: with rio.open(self.filename) as dataset: self.data = _load_rio( dataset, - indexes=valid_indexes, + indexes=list(valid_indexes), masked=self._masked, transform=self.transform, shape=self.shape, out_shape=self._out_shape, + out_count=self._out_count, **kwargs, ) @@ -1192,7 +1193,7 @@ def is_modified(self) -> bool: return self._is_modified @property - def nodata(self) -> int | float | tuple[int, ...] | tuple[float, ...] | None: + def nodata(self) -> int | float | None: """ Nodata value of the raster. @@ -1201,7 +1202,7 @@ def nodata(self) -> int | float | tuple[int, ...] | tuple[float, ...] | None: return self._nodata @nodata.setter - def nodata(self, new_nodata: int | float | tuple[int, ...] | tuple[float, ...] | None) -> None: + def nodata(self, new_nodata: int | float | None) -> None: """ Set .nodata and update .data by calling set_nodata() with default parameters. @@ -1224,7 +1225,7 @@ def set_nodata( update_mask: bool = True, ) -> None: """ - Set a new nodata value for each band. This updates the old nodata into a new nodata value in the metadata, + Set a new nodata value for all bands. This updates the old nodata into a new nodata value in the metadata, replaces the nodata values in the data of the masked array, and updates the mask of the masked array. Careful! If the new nodata value already exists in the array, the related grid cells will be masked by default. @@ -1306,7 +1307,6 @@ def set_nodata( # Masking like this works from the masked array directly, whether a mask exists or not imgdata[index_new_nodatas] = np.ma.masked - # Update the data self._data = imgdata @@ -1355,12 +1355,18 @@ def data(self, new_data: np.ndarray | np.ma.masked_array) -> None: new_data = new_data.squeeze(axis=0) # Check that new_data has correct shape - if self.is_loaded: + + # If data is loaded + if self._data is not None: dtype = str(self._data.dtype) orig_shape = self._data.shape - elif self.filename is not None: + # If filename exists + elif self._disk_dtypes is not None: dtype = self._disk_dtypes[0] - orig_shape = self._out_shape + if self._out_count == 1: + orig_shape = self._out_shape + else: + orig_shape = (self._out_count, *self._out_shape) # type: ignore else: dtype = str(new_data.dtype) orig_shape = new_data.shape diff --git a/tests/test_raster.py b/tests/test_raster.py index df2589e0b..b1163dceb 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -828,9 +828,7 @@ def test_masking(self, example: str) -> None: mask2 = r2.data == np.nanmin(r2) r2.set_mask(mask2) # Indexing at 0 for the mask in case the data has multiple bands - assert (np.count_nonzero(mask2) > 0) & np.array_equal( - orig_mask | mask2.filled(False), r2.data.mask - ) + assert (np.count_nonzero(mask2) > 0) & np.array_equal(orig_mask | mask2.filled(False), r2.data.mask) # The two last masking (array or Mask) should yield the same result when the data is only 2D if r.count == 1: assert np.array_equal(r.data.mask, r2.data.mask) @@ -947,7 +945,7 @@ def test_crop(self, data: list[str]) -> None: r_cropped = r.crop(crop_geom2, inplace=False) assert list(r_cropped.bounds) == crop_geom2 assert np.array_equal(r.data[:, rand_int:].data, r_cropped.data.data, equal_nan=True) - assert np.array_equal(r.data[ :, rand_int:].mask, r_cropped.data.mask) + assert np.array_equal(r.data[:, rand_int:].mask, r_cropped.data.mask) # Right crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2] - rand_int * r.res[0], crop_geom[3]] From 20094d7ce5c5cc7a5bda15da27eb6eefca1ca30b Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 6 Apr 2023 15:26:47 -0800 Subject: [PATCH 168/184] Fix docs --- doc/source/core_array_funcs.md | 2 +- doc/source/core_composition.md | 4 ++-- geoutils/raster/raster.py | 7 +++++-- geoutils/vector.py | 2 +- tests/test_raster.py | 30 ++++++++++++++++++++++++++---- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index c31fecef1..090813b52 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -89,7 +89,7 @@ np.max(raster) ```{code-cell} ipython3 # Expliciting an axis for reduction -np.count_nonzero(raster, axis=2) +np.count_nonzero(raster, axis=1) ``` Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index a2a33373f..037880ea3 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -64,7 +64,7 @@ To check if a {class}`~geoutils.Raster` was loaded from the **on-disk** data, us To check if the {class}`~geoutils.Raster` was modified since loading from the **on-disk** data, use the {attr}`~geoutils.Raster.is_modified` attribute. ``` -See {ref}`(raster-class)` for more details. +See {ref}`raster-class` for more details. ## The {class}`~geoutils.Vector` class composition @@ -95,4 +95,4 @@ its type: to a {class}`~geoutils.Vector` for a geometric output (e.g., {class}`~ vector.boundary ``` -See {ref}`(vector-class)` for more details. +See {ref}`vector-class` for more details. diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index a0897bb04..a1eb5ce5f 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -1848,6 +1848,9 @@ def crop( masked=self._masked, window=final_window, ) + # Squeeze first axis for single-band + if len(crop_img.shape) == 3 and crop_img.shape[0] == 1: + crop_img = crop_img.squeeze(axis=0) else: bbox = rio.coords.BoundingBox(left=xmin, bottom=ymin, right=xmax, top=ymax) @@ -2884,7 +2887,7 @@ def interp_points( ind_invalid = np.vectorize(lambda k1, k2: self.outside_image(k1, k2, index=True))(j, i) if self.count == 1: - rpts = map_coordinates(self.data[:, :].astype(np.float32), [i, j], **kwargs) + rpts = map_coordinates(self.data.astype(np.float32), [i, j], **kwargs) else: rpts = map_coordinates(self.data[index - 1, :, :].astype(np.float32), [i, j], **kwargs) @@ -3017,7 +3020,7 @@ def to_points( points = Vector( gpd.GeoDataFrame( points[:, 2:], - columns=[f"b{i}" for i in range(1, pixel_data.shape[0] + 1)], + columns=[f"b{i}" for i in range(1, self.count + 1)], geometry=gpd.points_from_xy(points[:, 0], points[:, 1]), crs=self.crs, ) diff --git a/geoutils/vector.py b/geoutils/vector.py index ffa61c874..31082385a 100644 --- a/geoutils/vector.py +++ b/geoutils/vector.py @@ -1336,7 +1336,7 @@ def from_bounds_projected( :param raster_or_vector: A raster or vector :param out_crs: In which CRS to compute the bounds :param densify_pts: Maximum points to be added between image corners to account for nonlinear edges. - Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. + Reduce if time computation is really critical (ms) or increase if extent is not accurate enough. """ if out_crs is None: diff --git a/tests/test_raster.py b/tests/test_raster.py index b1163dceb..6a0925e1e 100644 --- a/tests/test_raster.py +++ b/tests/test_raster.py @@ -928,6 +928,12 @@ def test_crop(self, data: list[str]) -> None: # -- Test with crop_geom being a list/tuple -- ## crop_geom: list[float] = list(r.bounds) + # Test inplace unloaded cropping conserves the shape + r.crop(crop_geom=[crop_geom[0] + r.res[0], crop_geom[1], crop_geom[2], crop_geom[3]]) + assert len(r.data.shape) == 2 + + r = gu.Raster(raster_path) + # Test with same bounds -> should be the same # crop_geom2 = [crop_geom[0], crop_geom[1], crop_geom[2], crop_geom[3]] r_cropped = r.crop(crop_geom2, inplace=False) @@ -2527,22 +2533,38 @@ def test_proximity_parameters(self) -> None: def test_to_points(self) -> None: """Test the outputs of the to_points method and that it doesn't load if not needed.""" + # Create a small raster to test point sampling on - img1 = gu.Raster.from_array( + img0 = gu.Raster.from_array( np.arange(25, dtype="int32").reshape(5, 5), transform=rio.transform.from_origin(0, 5, 1, 1), crs=4326 ) # Sample the whole raster (fraction==1) - points = img1.to_points(1, as_array=True) + points = img0.to_points(1, as_array=True) # Validate that 25 points were sampled (equating to img1.height * img1.width) with x, y, and band0 values. assert isinstance(points, np.ndarray) assert points.shape == (25, 3) assert np.array_equal(np.asarray(points[:, 0]), np.tile(np.linspace(0.5, 4.5, 5), 5)) - assert img1.to_points(0.2, as_array=True).shape == (5, 3) + assert img0.to_points(0.2, as_array=True).shape == (5, 3) + + # Try with a single-band raster + img1 = gu.Raster(self.aster_dem_path) + + points = img1.to_points(10, as_array=True) + + assert points.shape == (10, 3) + assert not img1.is_loaded + + points_frame = img1.to_points(10) + + assert isinstance(points_frame, gu.Vector) + assert np.array_equal(points_frame.ds.columns, ["b1", "geometry"]) + assert points_frame.crs == img1.crs - img2 = gu.Raster(self.landsat_rgb_path, load_data=False) + # Try with a multi-band raster + img2 = gu.Raster(self.landsat_rgb_path) points = img2.to_points(10, as_array=True) From c57778fa608135db3da7fdb8d203f339106ab46f Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 6 Apr 2023 16:59:53 -0800 Subject: [PATCH 169/184] Add xarray and rioxarray to main environment --- dev-environment.yml | 3 ++- environment.yml | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/dev-environment.yml b/dev-environment.yml index cbed28090..f16432ff0 100644 --- a/dev-environment.yml +++ b/dev-environment.yml @@ -9,6 +9,8 @@ dependencies: - rasterio>=1.3 - scipy - tqdm + - xarray + - rioxarray # Development-specific - autovizwidget @@ -27,7 +29,6 @@ dependencies: - sphinx-gallery - scikit-image - pyyaml - - rioxarray - sphinx-design - sphinx-book-theme diff --git a/environment.yml b/environment.yml index 6a2c76860..668128139 100644 --- a/environment.yml +++ b/environment.yml @@ -9,3 +9,5 @@ dependencies: - rasterio>=1.3 - scipy - tqdm + - xarray + - rioxarray From 5250ac3416f162304944a8e1b858cad579a8ad86 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 10 Apr 2023 16:37:38 -0800 Subject: [PATCH 170/184] Account for amaurys comments --- doc/source/conf.py | 18 +++- doc/source/core_array_funcs.md | 34 +++---- doc/source/core_composition.md | 28 +++--- doc/source/core_inheritance.md | 10 +- doc/source/core_lazy_load.md | 31 ++++--- doc/source/core_py_ops.md | 26 +++--- doc/source/mask_class.md | 21 +++-- doc/source/quick_start.md | 12 +-- doc/source/raster_class.md | 92 ++++++++++--------- doc/source/satimg_class.md | 12 ++- doc/source/vector_class.md | 16 ++-- .../array_numerics/numpy_interfacing.py | 3 +- .../array_numerics/python_arithmetic.py | 3 +- .../analysis/geospatial/buffer_voronoi.py | 18 ++-- .../analysis/geospatial/proximity_metric.py | 15 ++- .../point_extraction/interpolation.py | 7 +- .../analysis/point_extraction/reduction.py | 9 +- .../handling/georeferencing/crop_raster.py | 16 ++-- .../handling/georeferencing/crop_vector.py | 6 +- .../handling/georeferencing/reproj_raster.py | 6 +- .../handling/georeferencing/reproj_vector.py | 8 +- examples/handling/interface/create_mask.py | 6 +- examples/handling/interface/polygonize.py | 3 +- examples/handling/interface/rasterize.py | 6 +- examples/handling/interface/topoints.py | 4 +- examples/io/import_export/from_array.py | 2 + examples/io/import_export/import_raster.py | 5 +- examples/io/import_export/import_vector.py | 3 +- examples/io/open_save/read_raster.py | 8 +- examples/io/open_save/read_satimg.py | 12 +-- examples/io/open_save/read_vector.py | 9 +- geoutils/raster/__init__.py | 2 +- geoutils/raster/raster.py | 17 +++- tests/test_docs.py | 2 +- 34 files changed, 272 insertions(+), 198 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 9ccd1a266..ee0ad156d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -10,6 +10,7 @@ sys.path.append(os.path.abspath("../../geoutils/")) sys.path.append(os.path.abspath("..")) +from sphinx_gallery.sorting import ExampleTitleSortKey, ExplicitOrder project = "GeoUtils" copyright = "2021, GeoUtils Developers" @@ -62,17 +63,28 @@ "pandas": ("https://pandas.pydata.org/docs/", None), } +example_path = os.path.join("../", "../", "examples") + sphinx_gallery_conf = { "examples_dirs": [ - os.path.join(os.path.dirname(__file__), "../", "../", "examples/io"), - os.path.join(os.path.dirname(__file__), "../", "../", "examples/handling"), - os.path.join(os.path.dirname(__file__), "../", "../", "examples/analysis"), + os.path.join(example_path, "io"), + os.path.join(example_path, "handling"), + os.path.join(example_path, "analysis"), ], # path to your example scripts "gallery_dirs": [ "io_examples", "handling_examples", "analysis_examples", ], # path to where to save gallery generated output + 'subsection_order': ExplicitOrder([os.path.join(example_path, "io", "open_save"), + os.path.join(example_path, "io", "import_export"), + os.path.join(example_path, "handling", "georeferencing"), + os.path.join(example_path, "handling", "interface"), + os.path.join(example_path, "analysis", "array_numerics"), + os.path.join(example_path, "analysis", "geospatial"), + os.path.join(example_path, "analysis", "point_extraction"), + ]), + 'within_subsection_order': ExampleTitleSortKey, "inspect_global_variables": True, # Make links to the class/function definitions. "reference_url": { # The module you locally document uses None diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index 090813b52..0087be74b 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -42,29 +42,29 @@ arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") ma = np.ma.masked_array(data=arr, mask=mask) -# Create an example Raster -raster = gu.Raster.from_array( - data = ma, - transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), - crs = pyproj.CRS.from_epsg(4326), - nodata = 255 +# Create an example raster +rast = gu.Raster.from_array( + data = ma, + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 ) -raster +rast ``` ```{code-cell} ipython3 # Universal function with a single input and output -np.sin(raster) +np.sin(rast) ``` ```{code-cell} ipython3 # Universal function with a two inputs and single output -np.add(arr, raster) +np.add(arr, rast) ``` ```{code-cell} ipython3 # Universal function with a single input and two outputs -np.modf(raster) +np.modf(rast) ``` Similar to with Python operators, NumPy's [logical comparison functions](https://numpy.org/doc/stable/reference/ufuncs.html#comparison-functions) cast @@ -72,7 +72,7 @@ Similar to with Python operators, NumPy's [logical comparison functions](https:/ ```{code-cell} ipython3 # Universal function with a single input and two outputs -np.greater(raster, raster + np.random.normal(size=np.shape(arr))) +np.greater(rast, rast + np.random.normal(size=np.shape(arr))) ``` ## Array functions @@ -84,17 +84,17 @@ which are all other non-universal functions that can be applied to an array. Tho ```{code-cell} ipython3 # Traditional mathematical function -np.max(raster) +np.max(rast) ``` ```{code-cell} ipython3 # Expliciting an axis for reduction -np.count_nonzero(raster, axis=1) +np.count_nonzero(rast, axis=1) ``` Not all array functions are supported, however. GeoUtils supports nearly all [mathematical functions](https://numpy.org/doc/stable/reference/routines.math.html), [masked-array functions](https://numpy.org/doc/stable/reference/routines.ma.html) and [logical functions](https://numpy.org/doc/stable/reference/routines.logic.html). -A full list of supported array function is available in {attr}`geoutils.raster.raster.handled_array_funcs`. +A full list of supported array function is available in {attr}`geoutils.raster.handled_array_funcs`. ## Respecting masked values @@ -105,17 +105,17 @@ There are two ways to compute statistics on {class}`Rasters` wh ```{code-cell} ipython3 # Numpy core function applied to the raster -np.median(raster) +np.median(rast) ``` ```{code-cell} ipython3 # Numpy NaN function applied to the raster -np.nanmedian(raster) +np.nanmedian(rast) ``` ```{code-cell} ipython3 # Masked-array function on the data -np.ma.median(raster.data) +np.ma.median(rast.data) ``` If a NumPy core function raises an error (e.g., `np.percentile`), {class}`~geoutils.Raster.nodata` values might not be respected. In this case, use the NaN diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index 037880ea3..b2963cb78 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -8,7 +8,7 @@ kernelspec: # Composition from Rasterio and GeoPandas GeoUtils' main classes {class}`~geoutils.Raster` and {class}`~geoutils.Vector` are linked to [Rasterio](https://rasterio.readthedocs.io/en/latest/) and -[GeoPandas](https://geopandas.org/en/stable/docs.html), respectively, through class composition. +[GeoPandas](https://geopandas.org/en/stable/docs.html), respectively, through [class composition](https://realpython.com/inheritance-composition-python/#whats-composition). They directly rely on their robust geospatial handling functionalities, as well of that of [PyProj](https://pyproj4.github.io/pyproj/stable/index.html), and add a layer on top for interfacing between rasters and vectors with higher-level operations, performing easier numerical analysis, and adding more advanced geospatial functionalities. @@ -18,7 +18,7 @@ add a layer on top for interfacing between rasters and vectors with higher-level The {class}`~geoutils.Raster` is a composition class with **four main attributes**: 1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, -2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform` +2. an [{class}`affine.Affine`](https://rasterio.readthedocs.io/en/stable/topics/migrating-to-v1.html#affine-affine-vs-gdal-style-geotransforms) as {attr}`~geoutils.Raster.transform`, 3. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, and 4. a {class}`float` or {class}`int` as {attr}`~geoutils.Raster.nodata`. @@ -27,9 +27,10 @@ The {class}`~geoutils.Raster` is a composition class with **four main attributes import geoutils as gu -# Initiate a Raster from disk -raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) -raster +# Instantiate a raster from a filename on disk +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) +rast ``` From these **four main attributes**, many other derivatives attributes exist, such as {attr}`~geoutils.Raster.bounds` or {attr}`~geoutils.Raster.res` to @@ -43,7 +44,7 @@ By default, {attr}`~geoutils.Raster.data` is not loaded during instantiation. Se ```{code-cell} ipython3 :tags: [hide-output] # Show summarized information -print(raster.info()) +print(rast.info()) ``` ```{important} @@ -71,18 +72,21 @@ See {ref}`raster-class` for more details. A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`. -Because lazy loading is a lesser priority with vector data, a {class}`~geoutils.Vector` directly loads its {attr}`~geoutils.Vector.ds`. +A {class}`~geoutils.Vector`'s dataframe {attr}`~geoutils.Vector.ds` is directly loaded in-memory +(might evolve towards lazy behaviour soon through [Dask-GeoPandas](https://dask-geopandas.readthedocs.io/en/stable/)). ```{code-cell} ipython3 :tags: [hide-output] -# Initiate a Vector from disk -vector = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) -vector +# Instantiate a vector from a filename on disk +filename_vect = gu.examples.get_path("exploradores_rgi_outlines") +vect = gu.Vector(filename_vect) +vect ``` + ```{code-cell} ipython3 :tags: [hide-output] # Show summarized information -print(vector.info()) +print(vect.info()) ``` All geospatial methods of {class}`~geopandas.GeoDataFrame` are directly available into {class}`~geoutils.Vector`, and cast the output logically depending on @@ -92,7 +96,7 @@ its type: to a {class}`~geoutils.Vector` for a geometric output (e.g., {class}`~ ```{code-cell} ipython3 :tags: [hide-output] # Compute the vector's boundary -vector.boundary +vect.boundary ``` See {ref}`vector-class` for more details. diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 7a60a65b7..5c10327d6 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -41,13 +41,15 @@ and other information. ```{code-cell} ipython3 import geoutils as gu -# Initiate a geo-image from an ASTER image -geoimg = gu.SatelliteImage(gu.examples.get_path("exploradores_aster_dem"), silent=False) +# Instantiate a geo-image from an ASTER image +filename_geoimg = gu.examples.get_path("exploradores_aster_dem") +geoimg = gu.SatelliteImage(filename_geoimg, silent=False) ``` ```{code-cell} ipython3 -# Initiate a geo-image from a Landsat 7 image -geoimg2 = gu.SatelliteImage(gu.examples.get_path("everest_landsat_b4"), silent=False) +# Instantiate a geo-image from a Landsat 7 image +filename_geoimg2 = gu.examples.get_path("everest_landsat_b4") +geoimg2 = gu.SatelliteImage(filename_geoimg2, silent=False) ``` Along these additional attributes, the {class}`~geoutils.SatelliteImage` possesses the same main attributes as a {class}`~geoutils.Raster`. diff --git a/doc/source/core_lazy_load.md b/doc/source/core_lazy_load.md index 72811c57e..f831eb466 100644 --- a/doc/source/core_lazy_load.md +++ b/doc/source/core_lazy_load.md @@ -21,23 +21,24 @@ metadata ({attr}`~geoutils.Raster.transform`, {attr}`~geoutils.Raster.crs`, {att import geoutils as gu -# Initiate a Raster from disk -raster = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +# Instantiate a raster from a filename on disk +filename_rast = gu.examples.get_path("everest_landsat_b4") +rast = gu.Raster(filename_rast) -# This Raster is not loaded -raster +# This raster is not loaded +rast ``` To load the data explicitly during instantiation opening, `load_data=True` can be passed to {class}`~geoutils.Raster`. Or the {func}`~geoutils.Raster.load` method can be called after. The two are equivalent. ```{code-cell} ipython3 -# Initiate another Raster just for the purpose of loading -raster_toload = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -raster_toload.load() +# Initiate another raster just for the purpose of loading +rast_to_load = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +rast_to_load.load() -# This Raster is loaded -raster_toload +# This raster is loaded +rast_to_load ``` ## Lazy passing of georeferencing metadata @@ -50,11 +51,11 @@ always conserve the lazy loading of that match-reference object. ```{code-cell} ipython3 # Use a smaller Raster as reference to crop the initial one -smaller_raster = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) -raster.crop(smaller_raster) +smaller_rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) +rast.crop(smaller_rast) -# The reference Raster is not loaded -smaller_raster +# The reference raster is not loaded +smaller_rast ``` ## Optimized geospatial subsetting @@ -64,10 +65,10 @@ These features are a work in progress, we aim to make GeoUtils more lazy-friendl package! ``` -Some georeferencing operations be done without loading the entire array Right now, relying directly on Rasterio, GeoUtils supports optimized subsetting +Some georeferencing operations can be done without loading the entire array. Right now, relying directly on Rasterio, GeoUtils supports optimized subsetting through the {func}`~geoutils.Raster.crop` method. ```{code-cell} ipython3 # The previously cropped Raster was loaded without accessing the entire array -raster +rast ``` diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index 5cb199bda..be5d6f538 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -32,30 +32,30 @@ arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") ma = np.ma.masked_array(data=arr, mask=mask) -# Create an example Raster -raster = gu.Raster.from_array( - data = ma, - transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), - crs = pyproj.CRS.from_epsg(4326), - nodata = 255 +# Create an example raster +rast = gu.Raster.from_array( + data = ma, + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 ) -raster +rast ``` ```{code-cell} ipython3 # Arithmetic with a number -raster + 1 +rast + 1 ``` ```{code-cell} ipython3 # Arithmetic with an array -raster / arr +rast / arr ``` ```{code-cell} ipython3 # Arithmetic with a raster -raster - (raster**0.5) +rast - (rast**0.5) ``` If an unmasked {class}`~numpy.ndarray` is passed, it will internally be cast into a {class}`~numpy.ma.MaskedArray` to respect the propagation of @@ -73,7 +73,7 @@ as {class}`~geoutils.Raster.data`. ```{code-cell} ipython3 # Logical comparison with a number -mask = raster > 100 +mask = rast > 100 mask ``` @@ -89,7 +89,7 @@ combine a {class}`~geoutils.Mask` with another {class}`~geoutils.Mask`, and alwa ```{code-cell} ipython3 # Logical bitwise operation between masks -mask = (raster > 100) & ((raster % 2) == 0) +mask = (rast > 100) & ((rast % 2) == 0) mask ``` @@ -109,5 +109,5 @@ behaviour, simply index without the mask using {attr}`Mask.data.data`. ```{code-cell} ipython3 # Indexing the raster with the previous mask -raster[mask] +rast[mask] ``` diff --git a/doc/source/mask_class.md b/doc/source/mask_class.md index dc97c6f07..c53b3e07b 100644 --- a/doc/source/mask_class.md +++ b/doc/source/mask_class.md @@ -14,7 +14,7 @@ Below, a summary of the {class}`~geoutils.Mask` object and its methods. A {class}`~geoutils.Mask` is a subclass of {class}`~geoutils.Raster` that contains **three main attributes**: 1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data` of {class}`~numpy.boolean` {class}`~numpy.dtype`, -2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, and +2. an [{class}`affine.Affine`](https://rasterio.readthedocs.io/en/stable/topics/migrating-to-v1.html#affine-affine-vs-gdal-style-geotransforms) as {attr}`~geoutils.Raster.transform`, and 3. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`. A {class}`~geoutils.Mask` also inherits the same derivative attributes as a {class}`~geoutils.Raster`. @@ -42,8 +42,9 @@ On opening, all data will be forced to a {class}`bool` {class}`numpy.dtype`. ```{code-cell} ipython3 import geoutils as gu -# Initiate a raster from disk -mask = gu.Mask(gu.examples.get_path("exploradores_aster_dem"), load_data=True) +# Instantiate a mask from a filename on disk +filename_mask = gu.examples.get_path("exploradores_aster_dem") +mask = gu.Mask(filename_mask, load_data=True) mask ``` @@ -53,11 +54,12 @@ mask {class}`~geoutils.Raster`, a {class}`~numpy.ndarray` or a number. ```{code-cell} ipython3 -# Initiate a raster from disk -raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem"), load_data=True) +# Instantiate a raster from disk +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast, load_data=True) # Which pixels are below 1500 m? -raster < 1500 +rast < 1500 ``` See {ref}`core-py-ops` for more details. @@ -111,10 +113,11 @@ Georeferencing attributes to create the {class}`~geoutils.Mask` can also be pass ```{code-cell} ipython3 # Open a vector of glacier outlines -vector = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) +filename_vect = gu.examples.get_path("exploradores_rgi_outlines") +vect = gu.Vector(filename_vect) # Create mask using the raster as reference to match for bounds, resolution and projection -mask_outlines = vector.create_mask(raster) +mask_outlines = vect.create_mask(rast) mask_outlines ``` @@ -142,7 +145,7 @@ When indexing, a flattened {class}`~numpy.ma.MaskedArray` is returned with the i ```{code-cell} ipython3 # Index raster values on mask -raster[mask_outlines] +rast[mask_outlines] ``` See {ref}`py-ops-indexing` for more details. diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 8acedf327..f56f5b26b 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -25,7 +25,7 @@ import geoutils as gu filename_rast = gu.examples.get_path("everest_landsat_b4") filename_vect = gu.examples.get_path("everest_rgi_outlines") -# Open files +# Open files by instantiating Raster and Vector rast = gu.Raster(filename_rast) vect = gu.Vector(filename_vect) ``` @@ -51,7 +51,6 @@ A {class}`~geoutils.Vector` is a composition class with a single main attribute: most methods are wrapped directly into {class}`~geoutils.Vector`. ```{code-cell} ipython3 -:tags: [hide-output] # The opened vector vect ``` @@ -70,11 +69,12 @@ For convenience and consistency, nearly all of these methods can be passed solel ```{code-cell} ipython3 -:tags: [hide-output] +# Print initial bounds of the raster +print(rast.bounds) # Crop raster to vector's extent rast.crop(vect) -# Print info of cropped raster -print(rast.info()) +# Print bounds of cropped raster +print(rast.bounds) ``` ```{margin} @@ -105,7 +105,7 @@ and the other way around for {func}`~geoutils.Vector.rasterize`. - {func}`~geoutils.Vector.rasterize` ``` -All methods can be also be passed any number of georeferencing arguments such as {attr}`~geoutils.Raster.shape` or {attr}`~geoutils.Raster.res`, and will +All methods can also be passed any number of georeferencing arguments such as {attr}`~geoutils.Raster.shape` or {attr}`~geoutils.Raster.res`, and will naturally deduce others from the input {class}`~geoutils.Raster` or {class}`~geoutils.Vector`, much as in [GDAL](https://gdal.org/)'s command line. diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index 48a33129a..a936742ec 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -16,7 +16,7 @@ Below, a summary of the {class}`~geoutils.Raster` object and its methods. A {class}`~geoutils.Raster` contains **four main attributes**: 1. a {class}`numpy.ma.MaskedArray` as {attr}`~geoutils.Raster.data`, of either {class}`~numpy.integer` or {class}`~numpy.floating` {class}`~numpy.dtype`, -2. an {class}`affine.Affine` as {attr}`~geoutils.Raster.transform`, +2. an [{class}`affine.Affine`](https://rasterio.readthedocs.io/en/stable/topics/migrating-to-v1.html#affine-affine-vs-gdal-style-geotransforms) as {attr}`~geoutils.Raster.transform`, 3. a {class}`pyproj.crs.CRS` as {attr}`~geoutils.Raster.crs`, and 4. a {class}`float` or {class}`int` as {attr}`~geoutils.Raster.nodata`. @@ -58,16 +58,17 @@ A {class}`~geoutils.Raster` is opened by instantiating with either a {class}`str ```{code-cell} ipython3 import geoutils as gu -# Initiate a raster from disk -raster = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) -raster +# Instantiate a raster from a filename on disk +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) +rast ``` Detailed information on the {class}`~geoutils.Raster` is printed using {func}`~geoutils.Raster.info`, along with basic statistics using `stats=True`: ```{code-cell} ipython3 # Print details of raster -print(raster.info(stats=True)) +print(rast.info(stats=True)) ``` ```{note} @@ -78,7 +79,7 @@ A {class}`~geoutils.Raster` is saved to file by calling {func}`~geoutils.Raster. ```{code-cell} ipython3 # Save raster to disk -raster.save("myraster.tif") +rast.save("myraster.tif") ``` ```{code-cell} ipython3 :tags: [remove-cell] @@ -102,14 +103,14 @@ arr = np.random.randint(0, 255, size=(3, 3), dtype="uint8") mask = np.random.randint(0, 2, size=(3, 3), dtype="bool") ma = np.ma.masked_array(data=arr, mask=mask) -# Create a Raster from array -raster = gu.Raster.from_array( - data = ma, - transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), - crs = pyproj.CRS.from_epsg(4326), - nodata = 255 +# Create a raster from array +rast = gu.Raster.from_array( + data = ma, + transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3), + crs = pyproj.CRS.from_epsg(4326), + nodata = 255 ) -raster +rast ``` ```{important} @@ -124,7 +125,7 @@ The array of a {class}`~geoutils.Raster` is available in {class}`~geoutils.Raste ```{code-cell} ipython3 # Get raster's masked-array -raster.data +rast.data ``` For those less familiar with {class}`MaskedArrays` and the associated functions in NumPy, an unmasked {class}`~numpy.ndarray` filled with @@ -132,7 +133,7 @@ For those less familiar with {class}`MaskedArrays` and the ```{code-cell} ipython3 # Get raster's nan-array -raster.get_nanarray() +rast.get_nanarray() ``` ```{important} @@ -153,7 +154,7 @@ A {class}`~geoutils.Raster` can be applied any pythonic arithmetic operation ({f ```{code-cell} ipython3 # Add 1 and divide raster by 2 -(raster + 1)/2 +(rast + 1)/2 ``` A {class}`~geoutils.Raster` can also be applied any pythonic logical comparison operation ({func}`==`, {func}` != `, {func}`>=`, {func}`>`, {func}`<=`, @@ -161,7 +162,7 @@ A {class}`~geoutils.Raster` can also be applied any pythonic logical comparison ```{code-cell} ipython3 # What raster pixels are less than 100? -raster < 100 +rast < 100 ``` See {ref}`core-py-ops` for more details. @@ -172,8 +173,8 @@ A {class}`~geoutils.Raster` can be applied any NumPy universal functions and mos {class}`~geoutils.Raster`, {class}`~numpy.ndarray` or number. ```{code-cell} ipython3 -# Compute the element-wise sqrt -np.sqrt(raster) +# Compute the element-wise square-root +np.sqrt(rast) ``` Logical comparison functions will cast to a {class}`~geoutils.Mask`. @@ -181,7 +182,7 @@ Logical comparison functions will cast to a {class}`~geoutils.Mask`. ```{code-cell} ipython3 # Is the raster close to another within tolerance? -np.isclose(raster, raster+0.05, atol=0.1) +np.isclose(rast, rast+0.05, atol=0.1) ``` See {ref}`core-array-funcs` for more details. @@ -193,8 +194,8 @@ and/or {class}`~geoutils.Raster.crs`. ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils. -Vector` as argument. +As with all geospatial handling methods, the {func}`~geoutils.Raster.reproject` function can be passed a {class}`~geoutils.Raster` or {class}`~geoutils. +Vector` as a reference to match. In that case, no other argument is necessary. A {class}`~geoutils.Raster` reference will enforce to match its {attr}`~geoutils.Raster.transform` and {class}`~geoutils.Raster.crs`. A {class}`~geoutils.Vector` reference will enforce to match its {attr}`~geoutils.Vector.bounds` and {class}`~geoutils.Vector.crs`. @@ -207,23 +208,23 @@ attributes. For more details, see the {ref}`specific section and function descri ```{code-cell} ipython3 # Original bounds and resolution -print(raster.res) -print(raster.bounds) +print(rast.res) +print(rast.bounds) ``` ```{code-cell} ipython3 # Reproject to smaller bounds and higher resolution -raster_reproj = raster.reproject( +rast_reproj = rast.reproject( dst_res=0.1, dst_bounds={"left": 0, "bottom": 0, "right": 0.75, "top": 0.75}, resampling="cubic") -raster_reproj +rast_reproj ``` ```{code-cell} ipython3 # New bounds and resolution -print(raster.res) -print(raster.bounds) +print(rast.res) +print(rast.bounds) ``` ```{note} @@ -239,7 +240,7 @@ Cropping a {class}`~geoutils.Raster` is done through the {func}`~geoutils.Raster ```{important} As with all geospatial handling methods, the {func}`~geoutils.Raster.crop` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` -as argument. +as a reference to match. In that case, no other argument is necessary. See {ref}`core-match-ref` for more details. ``` @@ -250,8 +251,8 @@ For more details, see the {ref}`specific section and function descriptions in th ```{code-cell} ipython3 # Crop raster to smaller bounds -raster_crop = raster.crop(crop_geom=(0.3, 0.3, 1, 1), inplace=False) -raster_crop +rast_crop = rast.crop(crop_geom=(0.3, 0.3, 1, 1), inplace=False) +print(rast_crop.bounds) ``` ## Polygonize @@ -266,8 +267,8 @@ For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.polygonize` implicitly ta ```{code-cell} ipython3 # Polygonize all values lower than 100 -vector_lt_100 = (raster < 100).polygonize() -vector_lt_100 +vect_lt_100 = (rast < 100).polygonize() +vect_lt_100 ``` ## Proximity @@ -282,34 +283,34 @@ For a {class}`~geoutils.Mask`, {func}`~geoutils.Raster.proximity` implicitly tar ```{code-cell} ipython3 # Compute proximity from mask for all values lower than 100 -proximity_lt_100 = (raster < 100).proximity() -proximity_lt_100 +prox_lt_100 = (rast < 100).proximity() +prox_lt_100 ``` Optionally, instead of target pixel values, a {class}`~geoutils.Vector` can be passed to compute the proximity from the geometry. ```{code-cell} ipython3 # Compute proximity from mask for all values lower than 100 -proximity_lt_100_from_vector = raster.proximity(vector=vector_lt_100) -proximity_lt_100_from_vector +prox_lt_100_from_vect = rast.proximity(vector=vect_lt_100) +prox_lt_100_from_vect ``` ## Interpolate or extract to point Interpolating or extracting {class}`~geoutils.Raster` values at specific points can be done through: -- the {func}`~geoutils.Raster.value_at_coords` function, who extracts the single closest pixel or a surrounding window for each coordinate, on which +- the {func}`~geoutils.Raster.value_at_coords` function, that extracts the single closest pixel or a surrounding window for each coordinate, on which can be applied reducing any function ({func}`numpy.ma.mean` by default), or -- the {func}`~geoutils.Raster.interp_points` function, who interpolates the {class}`~geoutils.Raster`'s regular grid to each coordinate using a +- the {func}`~geoutils.Raster.interp_points` function, that interpolates the {class}`~geoutils.Raster`'s regular grid to each coordinate using a resampling algorithm. ```{code-cell} ipython3 # Extract median value in a 3 x 3 pixel window -raster_reproj.value_at_coords(x=0.5, y=0.5, reducer_function=np.ma.median) +rast_reproj.value_at_coords(x=0.5, y=0.5, window=3, reducer_function=np.ma.median) ``` ```{code-cell} ipython3 # Interpolate coordinate value with quintic algorithm -raster_reproj.interp_points([(0.5, 0.5)], mode="quintic") +rast_reproj.interp_points([(0.5, 0.5)], mode="quintic") ``` ```{note} @@ -328,10 +329,15 @@ Those include exporting to: ```{code-cell} ipython3 # Export to rasterio dataset-reader through a memoryfile -raster_reproj.to_rio_dataset() +rast_reproj.to_rio_dataset() ``` ```{code-cell} ipython3 # Export to geopandas dataframe -raster_reproj.to_points() +rast_reproj.to_points() +``` + +```{code-cell} ipython3 +# Export to xarray data array +rast_reproj.to_xarray() ``` diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md index aa6f41a30..03d093c02 100644 --- a/doc/source/satimg_class.md +++ b/doc/source/satimg_class.md @@ -13,7 +13,7 @@ Below, a summary of the {class}`~geoutils.SatelliteImage` object and its methods A {class}`~geoutils.SatelliteImage` is a subclass of {class}`~geoutils.Raster` that contains all its main attributes, and retains additional ones: - a {class}`numpy.datetime64` as {class}`~geoutils.SatelliteImage.datetime`, and -- several {class}`strings` for`~geoutils.SatelliteImage.satellite`, {class}`~geoutils.SatelliteImage.sensor`, {class}`~geoutils.SatelliteImage. version`, {class}`~geoutils.SatelliteImage.tile_name` and {class}`~geoutils.SatelliteImage.product`. +- several {class}`strings` for {class}`~geoutils.SatelliteImage.satellite`, {class}`~geoutils.SatelliteImage.sensor`, {class}`~geoutils.SatelliteImage.version`, {class}`~geoutils.SatelliteImage.tile_name` and {class}`~geoutils.SatelliteImage.product`. A {class}`~geoutils.SatelliteImage` also inherits the same derivative attributes as a {class}`~geoutils.Raster`. @@ -39,13 +39,15 @@ For tiled products such as SRTM, the tile naming is also retrieved, which can be ```{code-cell} ipython3 import geoutils as gu -# Initiate a geo-image from an ASTER image -geoimg = gu.SatelliteImage(gu.examples.get_path("exploradores_aster_dem"), silent=False) +# Instantiate a geo-image from an ASTER image +filename_geoimg = gu.examples.get_path("exploradores_aster_dem") +geoimg = gu.SatelliteImage(filename_geoimg, silent=False) ``` ```{code-cell} ipython3 -# Initiate a geo-image from a Landsat 7 image -geoimg2 = gu.SatelliteImage(gu.examples.get_path("everest_landsat_b4"), silent=False) +# Instantiate a geo-image from a Landsat 7 image +filename_geoimg2 = gu.examples.get_path("everest_landsat_b4") +geoimg2 = gu.SatelliteImage(filename_geoimg2, silent=False) ``` ```{important} diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index c16e3c156..1073286b1 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -39,8 +39,9 @@ a {class}`geopandas.GeoSeries` or a {class}`shapely.Geometry`. import geoutils as gu -# Initiate a vector from disk -vect = gu.Vector(gu.examples.get_path("exploradores_rgi_outlines")) +# Instantiate a vector from disk +filename_vect = gu.examples.get_path("exploradores_rgi_outlines") +vect = gu.Vector(filename_vect) vect ``` @@ -115,7 +116,9 @@ vect.to_json() Reprojecting a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.reproject` function, which enforces a new {attr}`~geoutils.Vector.crs`. ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Vector.reproject` function can be passed only a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as argument to match its {class}`~geoutils.Raster.crs`. +As with all geospatial handling methods, the {func}`~geoutils.Vector.reproject` function can be passed a +{class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a reference to match its {class}`~geoutils.Raster.crs`. +In that case, no other argument is necessary. See {ref}`core-match-ref` for more details. ``` @@ -129,7 +132,8 @@ print(vect.crs) ```{code-cell} ipython3 # Open a raster for which we want to match the CRS -rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) # Reproject the vector to the raster's CRS vect_reproj = vect.reproject(rast) # New CRS @@ -146,8 +150,8 @@ Cropping a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Vector.crop` function can be passed only a {class}`~geoutils.Raster` or -{class}`~geoutils.Vector` as argument. +As with all geospatial handling methods, the {func}`~geoutils.Vector.crop` function can be passed a {class}`~geoutils.Raster` or +{class}`~geoutils.Vector` as a reference to match. In that case, no other argument is necessary. See {ref}`core-match-ref` for more details. ``` diff --git a/examples/analysis/array_numerics/numpy_interfacing.py b/examples/analysis/array_numerics/numpy_interfacing.py index 889ca6ef0..6b07d2429 100644 --- a/examples/analysis/array_numerics/numpy_interfacing.py +++ b/examples/analysis/array_numerics/numpy_interfacing.py @@ -10,7 +10,8 @@ # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) # %% We plot it. rast.show(cmap="terrain") diff --git a/examples/analysis/array_numerics/python_arithmetic.py b/examples/analysis/array_numerics/python_arithmetic.py index 4cab61f31..42312222d 100644 --- a/examples/analysis/array_numerics/python_arithmetic.py +++ b/examples/analysis/array_numerics/python_arithmetic.py @@ -10,7 +10,8 @@ # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +filename_rast = gu.examples.get_path("everest_landsat_b4") +rast = gu.Raster(filename_rast) rast # %% We plot the original raster. diff --git a/examples/analysis/geospatial/buffer_voronoi.py b/examples/analysis/geospatial/buffer_voronoi.py index 009e555fe..c5dd866ea 100644 --- a/examples/analysis/geospatial/buffer_voronoi.py +++ b/examples/analysis/geospatial/buffer_voronoi.py @@ -10,7 +10,8 @@ # sphinx_gallery_thumbnail_number = 3 import geoutils as gu -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +filename_vect = gu.examples.get_path("everest_rgi_outlines") +vect = gu.Vector(filename_vect) # %% # We buffer in metric units directly using :func:`~geoutils.Vector.buffer_metric`. Under the hood, this functionality reprojects to a local projection, @@ -19,20 +20,17 @@ # %% # Let's plot the raster and vector -ax = vect.ds.plot() -vect_buff.ds.plot(ax=ax, ec="k", fc="none") +ax = vect.show() +vect_buff.show(ec="k", fc="none") # %% # Many buffers are overlapping. To compute a buffer without overlap, one can use :func:`~geoutils.Vector.buffer_without_overlap`. # vect_buff_nolap = vect.buffer_without_overlap(buffer_size=500) -ax = vect_buff_nolap.ds.plot() -vect.ds.plot(ax=ax, ec="k", fc="none") +vect.show(ax="new") +vect_buff_nolap.show(ec="k", fc="none") # %% # We plot with color to see that the attributes are retained for every feature. -import matplotlib.pyplot as plt - -ax = vect_buff_nolap.ds.plot(column="RGIId") -vect.ds.plot(ax=ax, ec="k", column="RGIId", alpha=0.5) -plt.show() +vect_buff_nolap.show(ax="new", column="Area") +vect.show(ec="k", column="Area", alpha=0.5) diff --git a/examples/analysis/geospatial/proximity_metric.py b/examples/analysis/geospatial/proximity_metric.py index c2c727f2c..ee662d4b2 100644 --- a/examples/analysis/geospatial/proximity_metric.py +++ b/examples/analysis/geospatial/proximity_metric.py @@ -10,17 +10,16 @@ # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) -vect = gu.Vector(vect.ds[vect.ds["RGIId"] == "RGI60-15.10055"]) +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) +vect = vect[vect["RGIId"] == "RGI60-15.10055"] rast.crop(vect) # Plot the raster and vector -import matplotlib.pyplot as plt - -ax = plt.gca() -rast.show(ax=ax, cmap="Blues") -vect.reproject(rast).ds.plot(ax=ax, fc="none", ec="k", lw=2) +rast.show(cmap="Blues") +vect.reproject(rast).show(fc="none", ec="k", lw=2) # %% # We use the raster as a reference to match for rasterizing the proximity distances with :func:`~geoutils.Vector.proximity`. See :ref:`core-match-ref` for more details. diff --git a/examples/analysis/point_extraction/interpolation.py b/examples/analysis/point_extraction/interpolation.py index 83823f5ef..9d7805721 100644 --- a/examples/analysis/point_extraction/interpolation.py +++ b/examples/analysis/point_extraction/interpolation.py @@ -5,19 +5,20 @@ This example demonstrates raster interpolation to point values using :func:`~geoutils.Raster.interp_points`. """ # %% -# We open an example raster, a digital elevation model in South America +# We open an example raster, a digital elevation model in South America. # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 2000, rast.bounds.bottom + 2000]) # Plot the raster rast.show(cmap="terrain") # %% -# We generate a random subsample of 100 points to interpolate, and extract the coordinates +# We generate a random subsample of 100 points to interpolate, and extract the coordinates. import geopandas as gpd import numpy as np diff --git a/examples/analysis/point_extraction/reduction.py b/examples/analysis/point_extraction/reduction.py index fa4b3d543..eb01f51f1 100644 --- a/examples/analysis/point_extraction/reduction.py +++ b/examples/analysis/point_extraction/reduction.py @@ -5,19 +5,20 @@ This example demonstrates raster reduction to point values using :func:`~geoutils.Raster.value_at_coords`. """ # %% -# We open an example raster, a digital elevation model in South America +# We open an example raster, a digital elevation model in South America. # sphinx_gallery_thumbnail_number = 3 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 2000, rast.bounds.bottom + 2000]) # Plot the raster rast.show(cmap="terrain") # %% -# We generate a random subsample of 100 coordinates to extract +# We generate a random subsample of 100 coordinates to extract. import geopandas as gpd import numpy as np @@ -45,7 +46,7 @@ # %% # The mean difference in extracted values is quite significant at 0.3 meters! -# We can visualize how the sampling took place in window +# We can visualize how the sampling took place in window. # Replace by Vector fonction once done coords = rast.coords(grid=True) diff --git a/examples/handling/georeferencing/crop_raster.py b/examples/handling/georeferencing/crop_raster.py index 61a32e40c..84aa9983e 100644 --- a/examples/handling/georeferencing/crop_raster.py +++ b/examples/handling/georeferencing/crop_raster.py @@ -10,24 +10,24 @@ # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) -vect = gu.Vector(vect.ds[vect.ds["RGIId"] == "RGI60-15.10055"]) +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) +vect = vect[vect["RGIId"] == "RGI60-15.10055"] # %% -# The first raster has larger extent and higher resolution than the second one. +# The first raster has larger extent and higher resolution than the vector. print(rast.info()) print(vect.bounds) # %% # Let's plot the raster and vector. -import matplotlib.pyplot as plt - rast.show(cmap="Purples") vect.show(ref_crs=rast, fc="none", ec="k", lw=2) # %% -# **First option:** using the second raster as a reference to match, we reproject the first one. We simply have to pass the second :class:`~geoutils.Raster` +# **First option:** using the vector as a reference to match, we reproject the raster. We simply have to pass the :class:`~geoutils.Vector` # as single argument to :func:`~geoutils.Raster.crop`. See :ref:`core-match-ref` for more details. rast.crop(vect) @@ -44,7 +44,7 @@ # %% # **Second option:** we can pass other ``crop_geom`` argument to :func:`~geoutils.Raster.crop`, including another :class:`~geoutils.Raster` or a -# simple :class:`tuple` of bounds. +# simple :class:`tuple` of bounds. For instance, we can re-crop the raster to be smaller than the vector. rast.crop((rast.bounds.left + 1000, rast.bounds.bottom, rast.bounds.right, rast.bounds.top - 500)) diff --git a/examples/handling/georeferencing/crop_vector.py b/examples/handling/georeferencing/crop_vector.py index 043cc1e5d..6c67b3771 100644 --- a/examples/handling/georeferencing/crop_vector.py +++ b/examples/handling/georeferencing/crop_vector.py @@ -10,8 +10,10 @@ # sphinx_gallery_thumbnail_number = 3 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +filename_rast = gu.examples.get_path("everest_landsat_b4_cropped") +filename_vect = gu.examples.get_path("everest_rgi_outlines") +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) # %% # Let's plot the raster and vector. The raster has smaller extent than the vector. diff --git a/examples/handling/georeferencing/reproj_raster.py b/examples/handling/georeferencing/reproj_raster.py index 2bec585b8..2e81a291e 100644 --- a/examples/handling/georeferencing/reproj_raster.py +++ b/examples/handling/georeferencing/reproj_raster.py @@ -9,8 +9,10 @@ import geoutils as gu -rast1 = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -rast2 = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) +filename_rast1 = gu.examples.get_path("everest_landsat_b4") +filename_rast2 = gu.examples.get_path("everest_landsat_b4_cropped") +rast1 = gu.Raster(filename_rast1) +rast2 = gu.Raster(filename_rast2) # %% # The first raster has larger extent and higher resolution than the second one. diff --git a/examples/handling/georeferencing/reproj_vector.py b/examples/handling/georeferencing/reproj_vector.py index 82fdcfd33..3465f4a83 100644 --- a/examples/handling/georeferencing/reproj_vector.py +++ b/examples/handling/georeferencing/reproj_vector.py @@ -10,8 +10,10 @@ # sphinx_gallery_thumbnail_number = 3 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +filename_rast = gu.examples.get_path("everest_landsat_b4_cropped") +filename_vect = gu.examples.get_path("everest_rgi_outlines") +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) # %% # The two objects are in different projections. @@ -20,8 +22,6 @@ # %% # Let's plot the two in their original projection. -import matplotlib.pyplot as plt - rast.show(cmap="Greys_r") vect.show(ax="new", fc="none", ec="tab:purple", lw=3) diff --git a/examples/handling/interface/create_mask.py b/examples/handling/interface/create_mask.py index bce36f9ec..af7844fe0 100644 --- a/examples/handling/interface/create_mask.py +++ b/examples/handling/interface/create_mask.py @@ -10,8 +10,10 @@ # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) # %% # Let's plot the raster and vector. diff --git a/examples/handling/interface/polygonize.py b/examples/handling/interface/polygonize.py index aa00a999b..c966eee9e 100644 --- a/examples/handling/interface/polygonize.py +++ b/examples/handling/interface/polygonize.py @@ -10,7 +10,8 @@ # sphinx_gallery_thumbnail_number = 3 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 5000, rast.bounds.bottom + 5000]) # %% # Let's plot the raster. diff --git a/examples/handling/interface/rasterize.py b/examples/handling/interface/rasterize.py index 2ace9f6f3..3ce5f93f5 100644 --- a/examples/handling/interface/rasterize.py +++ b/examples/handling/interface/rasterize.py @@ -10,8 +10,10 @@ # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +filename_rast = gu.examples.get_path("everest_landsat_b4") +filename_vect = gu.examples.get_path("everest_rgi_outlines") +rast = gu.Raster(filename_rast) +vect = gu.Vector(filename_vect) # %% # Let's plot the raster and vector. diff --git a/examples/handling/interface/topoints.py b/examples/handling/interface/topoints.py index 1a95e8e61..09ef85f65 100644 --- a/examples/handling/interface/topoints.py +++ b/examples/handling/interface/topoints.py @@ -10,8 +10,10 @@ # sphinx_gallery_thumbnail_number = 2 import geoutils as gu -rast = gu.Raster(gu.examples.get_path("exploradores_aster_dem")) +filename_rast = gu.examples.get_path("exploradores_aster_dem") +rast = gu.Raster(filename_rast) rast.crop([rast.bounds.left, rast.bounds.bottom, rast.bounds.left + 500, rast.bounds.bottom + 500]) + # %% # Let's plot the raster. rast.show(cmap="terrain") diff --git a/examples/io/import_export/from_array.py b/examples/io/import_export/from_array.py index 5e58c415f..6a4aa617a 100644 --- a/examples/io/import_export/from_array.py +++ b/examples/io/import_export/from_array.py @@ -16,6 +16,8 @@ # A random 3 x 3 masked array np.random.seed(42) arr = np.random.normal(size=(5, 5)) +# Introduce a NaN value +arr[2, 2] = np.nan # A transform with 3 x 3 pixels in a [0-1, 0-1] bound square transform = rio.transform.from_bounds(0, 0, 1, 1, 3, 3) # A CRS, here geographic (latitude/longitude) diff --git a/examples/io/import_export/import_raster.py b/examples/io/import_export/import_raster.py index 034194507..bfe87da05 100644 --- a/examples/io/import_export/import_raster.py +++ b/examples/io/import_export/import_raster.py @@ -12,7 +12,8 @@ # A raster can be imported from a :class:`rasterio.io.DatasetReader` or :class:`rasterio.io.MemoryFile` simply by instantiating :class:`~geoutils.Raster`. import geoutils as gu -ds = rio.DatasetReader(gu.examples.get_path("exploradores_aster_dem")) +filename_rast = gu.examples.get_path("exploradores_aster_dem") +ds = rio.DatasetReader(filename_rast) rast = gu.Raster(ds) rast @@ -24,7 +25,7 @@ # %% # We can also pass a :class:`rasterio.io.MemoryFile` during instantiation. -mem = rio.MemoryFile(open(gu.examples.get_path("exploradores_aster_dem"), "rb")) +mem = rio.MemoryFile(open(filename_rast, "rb")) rast = gu.Raster(mem) rast diff --git a/examples/io/import_export/import_vector.py b/examples/io/import_export/import_vector.py index af5a6e185..4a76c0a95 100644 --- a/examples/io/import_export/import_vector.py +++ b/examples/io/import_export/import_vector.py @@ -12,7 +12,8 @@ import geoutils as gu -ds = gpd.read_file(gu.examples.get_path("exploradores_rgi_outlines")) +filename_vect = gu.examples.get_path("exploradores_rgi_outlines") +ds = gpd.read_file(filename_vect) vect = gu.Vector(ds) vect diff --git a/examples/io/open_save/read_raster.py b/examples/io/open_save/read_raster.py index f0fb45b69..945f3e61a 100644 --- a/examples/io/open_save/read_raster.py +++ b/examples/io/open_save/read_raster.py @@ -9,9 +9,15 @@ # We open an example raster. The data is, by default, unloaded. import geoutils as gu -rast = gu.Raster(gu.examples.get_path("everest_landsat_b4")) +filename_rast = gu.examples.get_path("everest_landsat_b4") +rast = gu.Raster(filename_rast) rast +# %% +# A raster is composed of four main attributes: a :class:`~geoutils.Raster.data` array, an affine :class:`~geoutils.Raster.transform`, +# a coordinate reference system :class:`~geoutils.Raster.crs` and a :class:`~geoutils.Raster.nodata` value. +# All other attributes are derivatives of those or the file on disk, and can be found in the :ref:`dedicated section of the API`. See also :ref:`raster-class`. + # %% # # .. note:: diff --git a/examples/io/open_save/read_satimg.py b/examples/io/open_save/read_satimg.py index fab40cbac..daae9c355 100644 --- a/examples/io/open_save/read_satimg.py +++ b/examples/io/open_save/read_satimg.py @@ -1,6 +1,6 @@ """ -Parsing metadata -================ +Parsing image metadata +====================== This example demonstrates the instantiation of an image through :class:`~geoutils.SatelliteImage`. """ @@ -9,14 +9,14 @@ # %% # We print the filename of our raster that, as often with satellite data, holds metadata information. -filename = gu.examples.get_path("everest_landsat_b4") +filename_geoimg = gu.examples.get_path("everest_landsat_b4") import os -print(os.path.basename(filename)) +print(os.path.basename(filename_geoimg)) # %% -# We open it as a geo-image, unsilencing the attribute retrieval to see the parsed data. -img = gu.SatelliteImage(gu.examples.get_path("everest_landsat_b4"), silent=False) +# We open it as a geo-image, un-silencing the attribute retrieval to see the parsed data. +img = gu.SatelliteImage(filename_geoimg, silent=False) # %% # We have now retrieved the metadata. For the rest, the :class:`~geoutils.SatelliteImage` is a subclass of :class:`~geoutils.Raster`, and behaves similarly. diff --git a/examples/io/open_save/read_vector.py b/examples/io/open_save/read_vector.py index a86aa72fe..b8e82fe04 100644 --- a/examples/io/open_save/read_vector.py +++ b/examples/io/open_save/read_vector.py @@ -9,16 +9,21 @@ # %% # We open an example vector. -vect = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) +filename_vect = gu.examples.get_path("everest_rgi_outlines") +vect = gu.Vector(filename_vect) vect +# %% +# A vector is composed of a single main attribute: a :class:`~geoutils.Vector.ds` geodataframe. +# All other attributes are :ref:`inherited from Shapely and GeoPandas`. See also :ref:`vector-class`. + # %% # # .. note:: # A vector can also be instantiated with a :class:`geopandas.GeoDataFrame`, see :ref:`sphx_glr_io_examples_import_export_import_vector.py`. # # We can print more info on the vector. -print(vect) +print(vect.info()) # %% # Let's plot by vector area diff --git a/geoutils/raster/__init__.py b/geoutils/raster/__init__.py index aceb2c43b..5402aa465 100644 --- a/geoutils/raster/__init__.py +++ b/geoutils/raster/__init__.py @@ -1,4 +1,4 @@ -from geoutils.raster.raster import Raster, RasterType, Mask # noqa isort:skip +from geoutils.raster.raster import Raster, RasterType, Mask, handled_array_funcs # noqa isort:skip from geoutils.raster.array import * # noqa from geoutils.raster.multiraster import * # noqa from geoutils.raster.sampling import * # noqa diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index a1eb5ce5f..e6badebdd 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -640,9 +640,15 @@ def to_rio_dataset(self) -> rio.io.DatasetReader: def __repr__(self) -> str: """Convert raster to string representation.""" + # Shape to load + if self.count == 1: + shape_to_load = self._out_shape + else: + shape_to_load = (self._out_count, *self._out_shape) + # If data not loaded, return and string and avoid calling .data if not self.is_loaded: - str_data = "not_loaded; shape on disk " + str(self._disk_shape) + str_data = "not_loaded; shape on disk " + str(self._disk_shape) + "; will load " + str(shape_to_load) else: str_data = "\n ".join(self.data.__str__().split("\n")) @@ -671,9 +677,16 @@ def __repr__(self) -> str: def _repr_html_(self) -> str: """Convert raster to HTML representation for documentation.""" + # Shape to load + if self.count == 1: + shape_to_load = self._out_shape + else: + shape_to_load = (self._out_count, *self._out_shape) + # If data not loaded, return and string and avoid calling .data if not self.is_loaded: - str_data = "not_loaded; shape on disk " + str(self._disk_shape) + "" + str_data = "not_loaded; shape on disk " + str(self._disk_shape) + "; will load " \ + + str(shape_to_load) + "" else: str_data = "\n ".join(self.data.__str__().split("\n")) diff --git a/tests/test_docs.py b/tests/test_docs.py index caa7b86f9..cc78efcc2 100644 --- a/tests/test_docs.py +++ b/tests/test_docs.py @@ -25,7 +25,7 @@ def test_build(self) -> None: "-j", "1", os.path.join(self.docs_dir, "source/"), - os.path.join(self.docs_dir, "build/html"), + os.path.join(self.docs_dir, "build/"), ] ) From 4fe01c5fe371051f2c03e7e793ae409cdaeb8456 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Mon, 10 Apr 2023 16:48:21 -0800 Subject: [PATCH 171/184] Linting --- doc/source/conf.py | 21 ++++++++++++--------- doc/source/core_composition.md | 2 +- doc/source/vector_class.md | 2 +- geoutils/raster/raster.py | 11 ++++++----- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index ee0ad156d..f13187c3a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -76,15 +76,18 @@ "handling_examples", "analysis_examples", ], # path to where to save gallery generated output - 'subsection_order': ExplicitOrder([os.path.join(example_path, "io", "open_save"), - os.path.join(example_path, "io", "import_export"), - os.path.join(example_path, "handling", "georeferencing"), - os.path.join(example_path, "handling", "interface"), - os.path.join(example_path, "analysis", "array_numerics"), - os.path.join(example_path, "analysis", "geospatial"), - os.path.join(example_path, "analysis", "point_extraction"), - ]), - 'within_subsection_order': ExampleTitleSortKey, + "subsection_order": ExplicitOrder( + [ + os.path.join(example_path, "io", "open_save"), + os.path.join(example_path, "io", "import_export"), + os.path.join(example_path, "handling", "georeferencing"), + os.path.join(example_path, "handling", "interface"), + os.path.join(example_path, "analysis", "array_numerics"), + os.path.join(example_path, "analysis", "geospatial"), + os.path.join(example_path, "analysis", "point_extraction"), + ] + ), + "within_subsection_order": ExampleTitleSortKey, "inspect_global_variables": True, # Make links to the class/function definitions. "reference_url": { # The module you locally document uses None diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index b2963cb78..9f30bd5c4 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -72,7 +72,7 @@ See {ref}`raster-class` for more details. A {class}`~geoutils.Vector` is a composition class with a single main attribute: a {class}`~geopandas.GeoDataFrame` as {attr}`~geoutils.Vector.ds`. -A {class}`~geoutils.Vector`'s dataframe {attr}`~geoutils.Vector.ds` is directly loaded in-memory +A {class}`~geoutils.Vector`'s dataframe {attr}`~geoutils.Vector.ds` is directly loaded in-memory (might evolve towards lazy behaviour soon through [Dask-GeoPandas](https://dask-geopandas.readthedocs.io/en/stable/)). ```{code-cell} ipython3 diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index 1073286b1..de62d2a78 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -116,7 +116,7 @@ vect.to_json() Reprojecting a {class}`~geoutils.Vector` is done through the {func}`~geoutils.Vector.reproject` function, which enforces a new {attr}`~geoutils.Vector.crs`. ```{important} -As with all geospatial handling methods, the {func}`~geoutils.Vector.reproject` function can be passed a +As with all geospatial handling methods, the {func}`~geoutils.Vector.reproject` function can be passed a {class}`~geoutils.Raster` or {class}`~geoutils.Vector` as a reference to match its {class}`~geoutils.Raster.crs`. In that case, no other argument is necessary. diff --git a/geoutils/raster/raster.py b/geoutils/raster/raster.py index e6badebdd..1eaeac1ef 100644 --- a/geoutils/raster/raster.py +++ b/geoutils/raster/raster.py @@ -644,7 +644,7 @@ def __repr__(self) -> str: if self.count == 1: shape_to_load = self._out_shape else: - shape_to_load = (self._out_count, *self._out_shape) + shape_to_load = (self._out_count, *self._out_shape) # type: ignore # If data not loaded, return and string and avoid calling .data if not self.is_loaded: @@ -678,15 +678,16 @@ def _repr_html_(self) -> str: """Convert raster to HTML representation for documentation.""" # Shape to load - if self.count == 1: + if self._out_count == 1: shape_to_load = self._out_shape else: - shape_to_load = (self._out_count, *self._out_shape) + shape_to_load = (self._out_count, *self._out_shape) # type: ignore # If data not loaded, return and string and avoid calling .data if not self.is_loaded: - str_data = "not_loaded; shape on disk " + str(self._disk_shape) + "; will load " \ - + str(shape_to_load) + "" + str_data = ( + "not_loaded; shape on disk " + str(self._disk_shape) + "; will load " + str(shape_to_load) + "" + ) else: str_data = "\n ".join(self.data.__str__().split("\n")) From ecc2ba7d189f3f19234a9957dde649cd85ca2014 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 12 Apr 2023 17:14:20 -0800 Subject: [PATCH 172/184] Finish accounting for comments --- doc/source/about_geoutils.md | 158 +++++++++++++++++- doc/source/raster_class.md | 4 +- .../point_extraction/interpolation.py | 6 +- .../analysis/point_extraction/reduction.py | 6 +- 4 files changed, 165 insertions(+), 9 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index d07993469..a4a543931 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -35,7 +35,7 @@ In particular, GeoUtils: - Rarely requires more than **single-line operations** thanks to its object-based structure, - Strives to rely on **lazy operations** under-the-hood to avoid unnecessary data loading, - Allows for **match-reference operations** to facilitate geospatial handling, -- Re-implements **several of [GDAL](https://gdal.org/)'s missing features** (Proximity, DEM through xDEM, etc), +- Re-implements **several of [GDAL](https://gdal.org/)'s features** missing in other packages (e.g., proximity, gdalDEM), - Naturally handles **different `dtypes` and `nodata`** values through its NumPy masked-array interface. @@ -66,3 +66,159 @@ Finally, **many common geospatial analysis tools are generally unavailable** in ```{admonition} Conclusion Having higher-level geospatial tools implemented in a **consistent** manner and tested for **robustness** is essential for the wider geospatial community. ``` + +## Side-by-side examples with Rasterio and GeoPandas + +This first side-by-side example demonstrates the difference with Rasterio for opening a raster, reprojecting on +another "reference" raster, performing array operations respectful of nodata values, and saving to file. + + +```{note} +**GeoUtils does not just wrap the Rasterio or GeoPandas operations showed below**. Instead, it defines **raster- and +vector-centered objects to ensure consistent geospatial object behaviour that facilitates those operations** (e.g., by implicitly passing metadata, loading, or interfacing). +``` + +`````{list-table} +--- +header-rows: 1 +--- +* - GeoUtils + - Rasterio +* - ```python + import geoutils as gu + + # Opening of two rasters + rast1 = gu.Raster("myraster1.tif") + rast2 = gu.Raster("myraster2.tif") + + # Reproject 1 to match 2 + # (raster 2 not loaded, only metadata) + rast1_reproj = rast1.reproject( + dst_ref = rast2 + ) + + # Array interfacing and implicit loading + # (raster 2 loads implicitly) + rast_result = (1 + rast2) / rast1_reproj + + # Saving + rast_result.save("myresult.tif") + ``` + + - ```python + import rasterio as rio + import numpy as np + + # Opening of two rasters + rast1 = rio.io.DatasetReader("myraster1.tif") + rast2 = rio.io.DatasetReader("myraster2.tif") + + # Equivalent of a match-reference reprojection + # (returns an array, not a raster-type object) + arr1_reproj, _ = rio.warp.reproject( + source = rast1.read(), + destination = np.ones(rast2.shape), + src_transform = rast1.transform, + src_crs = rast1.crs, + src_nodata = rast1.nodata, + dst_transform = rast2.transform, + dst_crs = rast2.crs, + dst_nodata = rast2.nodata, + ) + + # Equivalent of array interfacing + # (ensuring nodata and dtypes are rightly + # propagated through masked arrays) + ma1_reproj = np.ma.MaskedArray( + data = arr1_reproj, + mask = (arr1_reproj == rast2.nodata) + ) + ma2 = rast2.read(masked = True) + ma_result = (1 + ma2) / (ma1_reproj) + + # Equivalent of saving + # (requires to define a logical + # nodata for the data type) + out_nodata = custom_func( + dtype = ma_result.dtype, + nodata1 = rast1.nodata, + nodata2 = rast2.nodata + ) + with rio.open( + "myresult.tif", + mode = "w", + height = rast2.height, + width = rast2.width, + count = rast1.count, + dtype = ma_result.dtype, + crs = rast2.crs, + transform = rast2.transform, + nodata = rast2.nodata, + ) as dst: + dst.write(ma_result.filled(out_nodata)) + ``` +````` + +This second side-by-side example demonstrates the difference with GeoPandas (and Rasterio) for opening a vector, +applying a metric geometric operation (buffering), rasterizing into a boolean mask, and indexing a raster with that mask. + +`````{list-table} +--- +header-rows: 1 +--- +* - GeoUtils + - GeoPandas (and Rasterio) +* - ```python + import geoutils as gu + + # Opening a vector and a raster + vect = gu.Vector("myvector.shp") + rast = gu.Raster("myraster.tif") + + # Metric buffering + vect_buff = vect.buffer(distance = 100) + + # Create a mask on the raster grid + # (raster not loaded, only metadata) + mask = vect_buff.create_mask( + dst_ref = rast + ) + + # Index raster values on mask + # (raster loads implicitly) + values = rast[mask] + ``` + + - ```python + import geopandas as gpd + import rasterio as rio + + # Opening a vector and a raster + df = gpd.read_file("myvector.tif") + rast2 = rio.io.DatasetReader("myraster.tif") + + # Equivalent of a metric buffering + # (while keeping a frame object) + gs_m_crs = df.to_crs(df.estimate_utm_crs()) + gs_m_crs_buff = gs_m_crs.buffer(distance = 100) + gs_buff = gs_m_crs_buff.to_crs(df.crs) + df_buff = gpd.GeoDataFrame( + geometry = gs_buff + ) + + # Equivalent of creating a rasterized mask + # (ensuring CRS are similar) + df_buff = df_buff.to_crs(rast2.crs) + mask = features.rasterize( + shapes = gdf.geometry, + fill = 0, + out_shape = rast2.shape, + transform = rast2.transform, + default_value = 1, + dtype = "uint8" + ) + mask = mask.astype("bool") + + # Equivalent of indexing with mask + values = rast2.read(1, masked = True)[mask] +````` \ No newline at end of file diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index a936742ec..ff4505415 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -223,8 +223,8 @@ rast_reproj ```{code-cell} ipython3 # New bounds and resolution -print(rast.res) -print(rast.bounds) +print(rast_reproj.res) +print(rast_reproj.bounds) ``` ```{note} diff --git a/examples/analysis/point_extraction/interpolation.py b/examples/analysis/point_extraction/interpolation.py index 9d7805721..a7260de65 100644 --- a/examples/analysis/point_extraction/interpolation.py +++ b/examples/analysis/point_extraction/interpolation.py @@ -1,8 +1,8 @@ """ -Regular-grid interpolation -========================== +Interpolation from grid +======================= -This example demonstrates raster interpolation to point values using :func:`~geoutils.Raster.interp_points`. +This example demonstrates the 2D interpolation of raster values to points using :func:`~geoutils.Raster.interp_points`. """ # %% # We open an example raster, a digital elevation model in South America. diff --git a/examples/analysis/point_extraction/reduction.py b/examples/analysis/point_extraction/reduction.py index eb01f51f1..32496554c 100644 --- a/examples/analysis/point_extraction/reduction.py +++ b/examples/analysis/point_extraction/reduction.py @@ -1,8 +1,8 @@ """ -Window point reduction -====================== +Reduction from window +===================== -This example demonstrates raster reduction to point values using :func:`~geoutils.Raster.value_at_coords`. +This example demonstrates the reduction of windowed raster values around a point using :func:`~geoutils.Raster.value_at_coords`. """ # %% # We open an example raster, a digital elevation model in South America. From c5e066272b507eeb26ef5bd280a51341e753d372 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Wed, 12 Apr 2023 17:14:35 -0800 Subject: [PATCH 173/184] Linting --- doc/source/about_geoutils.md | 74 ++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/doc/source/about_geoutils.md b/doc/source/about_geoutils.md index a4a543931..496df8992 100644 --- a/doc/source/about_geoutils.md +++ b/doc/source/about_geoutils.md @@ -69,12 +69,12 @@ Having higher-level geospatial tools implemented in a **consistent** manner and ## Side-by-side examples with Rasterio and GeoPandas -This first side-by-side example demonstrates the difference with Rasterio for opening a raster, reprojecting on +This first side-by-side example demonstrates the difference with Rasterio for opening a raster, reprojecting on another "reference" raster, performing array operations respectful of nodata values, and saving to file. ```{note} -**GeoUtils does not just wrap the Rasterio or GeoPandas operations showed below**. Instead, it defines **raster- and +**GeoUtils does not just wrap the Rasterio or GeoPandas operations showed below**. Instead, it defines **raster- and vector-centered objects to ensure consistent geospatial object behaviour that facilitates those operations** (e.g., by implicitly passing metadata, loading, or interfacing). ``` @@ -86,56 +86,56 @@ header-rows: 1 - Rasterio * - ```python import geoutils as gu - + # Opening of two rasters rast1 = gu.Raster("myraster1.tif") rast2 = gu.Raster("myraster2.tif") - + # Reproject 1 to match 2 # (raster 2 not loaded, only metadata) rast1_reproj = rast1.reproject( dst_ref = rast2 ) - + # Array interfacing and implicit loading # (raster 2 loads implicitly) rast_result = (1 + rast2) / rast1_reproj - + # Saving rast_result.save("myresult.tif") ``` - + - ```python import rasterio as rio import numpy as np - + # Opening of two rasters rast1 = rio.io.DatasetReader("myraster1.tif") rast2 = rio.io.DatasetReader("myraster2.tif") - + # Equivalent of a match-reference reprojection # (returns an array, not a raster-type object) arr1_reproj, _ = rio.warp.reproject( - source = rast1.read(), - destination = np.ones(rast2.shape), - src_transform = rast1.transform, - src_crs = rast1.crs, - src_nodata = rast1.nodata, - dst_transform = rast2.transform, - dst_crs = rast2.crs, + source = rast1.read(), + destination = np.ones(rast2.shape), + src_transform = rast1.transform, + src_crs = rast1.crs, + src_nodata = rast1.nodata, + dst_transform = rast2.transform, + dst_crs = rast2.crs, dst_nodata = rast2.nodata, ) - + # Equivalent of array interfacing - # (ensuring nodata and dtypes are rightly + # (ensuring nodata and dtypes are rightly # propagated through masked arrays) ma1_reproj = np.ma.MaskedArray( - data = arr1_reproj, + data = arr1_reproj, mask = (arr1_reproj == rast2.nodata) ) ma2 = rast2.read(masked = True) ma_result = (1 + ma2) / (ma1_reproj) - + # Equivalent of saving # (requires to define a logical # nodata for the data type) @@ -159,7 +159,7 @@ header-rows: 1 ``` ````` -This second side-by-side example demonstrates the difference with GeoPandas (and Rasterio) for opening a vector, +This second side-by-side example demonstrates the difference with GeoPandas (and Rasterio) for opening a vector, applying a metric geometric operation (buffering), rasterizing into a boolean mask, and indexing a raster with that mask. `````{list-table} @@ -170,34 +170,34 @@ header-rows: 1 - GeoPandas (and Rasterio) * - ```python import geoutils as gu - + # Opening a vector and a raster vect = gu.Vector("myvector.shp") rast = gu.Raster("myraster.tif") - + # Metric buffering vect_buff = vect.buffer(distance = 100) - + # Create a mask on the raster grid # (raster not loaded, only metadata) mask = vect_buff.create_mask( dst_ref = rast ) - + # Index raster values on mask # (raster loads implicitly) values = rast[mask] ``` - + - ```python import geopandas as gpd import rasterio as rio - + # Opening a vector and a raster df = gpd.read_file("myvector.tif") rast2 = rio.io.DatasetReader("myraster.tif") - - # Equivalent of a metric buffering + + # Equivalent of a metric buffering # (while keeping a frame object) gs_m_crs = df.to_crs(df.estimate_utm_crs()) gs_m_crs_buff = gs_m_crs.buffer(distance = 100) @@ -205,20 +205,20 @@ header-rows: 1 df_buff = gpd.GeoDataFrame( geometry = gs_buff ) - + # Equivalent of creating a rasterized mask # (ensuring CRS are similar) df_buff = df_buff.to_crs(rast2.crs) mask = features.rasterize( - shapes = gdf.geometry, - fill = 0, - out_shape = rast2.shape, - transform = rast2.transform, - default_value = 1, + shapes = gdf.geometry, + fill = 0, + out_shape = rast2.shape, + transform = rast2.transform, + default_value = 1, dtype = "uint8" ) mask = mask.astype("bool") - + # Equivalent of indexing with mask values = rast2.read(1, masked = True)[mask] -````` \ No newline at end of file +````` From 660615cd495a5fc050a04f57be69fa79d1239360 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 13 Apr 2023 12:21:30 -0800 Subject: [PATCH 174/184] Change default mode to dark to remove logo issue --- doc/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index f13187c3a..bd4b00851 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -203,7 +203,7 @@ def setup(app): # For dark mode html_context = { # ... - "default_mode": "auto" + "default_mode": "dark" } # Add the search bar to be always displayed (not only on top) From 44979ac16b3b8f40f27270435ec25c5f2fa1746b Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Thu, 13 Apr 2023 15:41:28 -0800 Subject: [PATCH 175/184] Add binder --- README.md | 4 ++-- binder/environment.yml | 1 + binder/extra-environment.yml | 4 ++++ binder/postBuild | 6 ++++++ doc/source/conf.py | 3 +++ 5 files changed, 16 insertions(+), 2 deletions(-) create mode 120000 binder/environment.yml create mode 100644 binder/extra-environment.yml create mode 100644 binder/postBuild diff --git a/README.md b/README.md index ada8e5fff..e787a2cfd 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ See [mamba's documentation](https://mamba.readthedocs.io/en/latest/) to install ## Start contributing -1. Fork the repository, make a feature branches and push changes. -2. When ready, submit a pull request from the feature branch of your fork to `GlacioHack/geoutils:master`. +1. Fork the repository, make a feature branch and push changes. +2. When ready, submit a pull request from the feature branch of your fork to `GlacioHack/geoutils:main`. 3. The PR will be reviewed by at least one maintainer, discussed, then merged. More info on [our contributing page](CONTRIBUTING.md). diff --git a/binder/environment.yml b/binder/environment.yml new file mode 120000 index 000000000..7a9c79057 --- /dev/null +++ b/binder/environment.yml @@ -0,0 +1 @@ +../environment.yml \ No newline at end of file diff --git a/binder/extra-environment.yml b/binder/extra-environment.yml new file mode 100644 index 000000000..23cdfb27a --- /dev/null +++ b/binder/extra-environment.yml @@ -0,0 +1,4 @@ +channels: + - conda-forge +dependencies: + - jupytext diff --git a/binder/postBuild b/binder/postBuild new file mode 100644 index 000000000..eb7bb6dd2 --- /dev/null +++ b/binder/postBuild @@ -0,0 +1,6 @@ +#!/bin/bash +set -e # To avoid silent errors + +# ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "environment.yml" +pip install -e . +${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "binder/extra-environment.yml" diff --git a/doc/source/conf.py b/doc/source/conf.py index bd4b00851..aa2f750a9 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -178,6 +178,9 @@ def setup(app): "use_source_button": True, "use_issues_button": True, "use_download_button": True, + "launch_buttons": { + "binderhub_url": "https://{your-binderhub-url}" + }, # "logo_only": True, # "icon_links": [ # { From f74484774ae849ef13d334c3b17622c6ebcbb12a Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 10:55:19 -0800 Subject: [PATCH 176/184] Add myst-nb to binder environment to open .md files as jupyter notebooks --- binder/extra-environment.yml | 1 + doc/source/conf.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/binder/extra-environment.yml b/binder/extra-environment.yml index 23cdfb27a..d619a16fd 100644 --- a/binder/extra-environment.yml +++ b/binder/extra-environment.yml @@ -2,3 +2,4 @@ channels: - conda-forge dependencies: - jupytext + - myst-parser diff --git a/doc/source/conf.py b/doc/source/conf.py index aa2f750a9..54209d752 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -171,15 +171,15 @@ def setup(app): html_theme_options = { "path_to_docs": "doc/source", "use_sidenotes": True, - "repository_url": "https://github.com/GlacioHack/geoutils", - "repository_branch": "main", + "repository_url": "https://github.com/rhugonnet/geoutils", + "repository_branch": "docs", "use_repository_button": True, "use_edit_page_button": True, "use_source_button": True, "use_issues_button": True, "use_download_button": True, "launch_buttons": { - "binderhub_url": "https://{your-binderhub-url}" + "binderhub_url": "https://mybinder.org/" }, # "logo_only": True, # "icon_links": [ From 5746d2a624f4b0d31d161eda96b901a48078eba7 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 11:17:06 -0800 Subject: [PATCH 177/184] Replace by myst-nb for binder env --- binder/extra-environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binder/extra-environment.yml b/binder/extra-environment.yml index d619a16fd..ac7000172 100644 --- a/binder/extra-environment.yml +++ b/binder/extra-environment.yml @@ -2,4 +2,4 @@ channels: - conda-forge dependencies: - jupytext - - myst-parser + - myst-nb From d1339085c089e61d68e44c3f949c4ec6797bac60 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 11:30:44 -0800 Subject: [PATCH 178/184] Add default parameters for JupyterLab opening of markdown into Binder build --- binder/postBuild | 2 ++ 1 file changed, 2 insertions(+) diff --git a/binder/postBuild b/binder/postBuild index eb7bb6dd2..7d5f75391 100644 --- a/binder/postBuild +++ b/binder/postBuild @@ -4,3 +4,5 @@ set -e # To avoid silent errors # ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "environment.yml" pip install -e . ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "binder/extra-environment.yml" + +wget https://raw.githubusercontent.com/mwouts/jupytext/main/binder/labconfig/default_setting_overrides.json -P ~/.jupyter/labconfig/ # To automatically update Markdown files as notebooks with Jupytext, see https://github.com/mwouts/jupytext \ No newline at end of file From c4430fbb710badef72642b0d5e2d55145a77bf32 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 11:56:32 -0800 Subject: [PATCH 179/184] Add display name --- doc/source/quick_start.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index f56f5b26b..f850fdee1 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -1,6 +1,7 @@ --- file_format: mystnb kernelspec: + display_name: geoutils-env name: geoutils --- (quick-start)= From 094245625a6ae1e758e2e414238d10e4b3581343 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 12:01:51 -0800 Subject: [PATCH 180/184] Try with more jupytext settings --- doc/source/quick_start.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index f850fdee1..415b95b9a 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -1,7 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: display_name: geoutils-env + language: python name: geoutils --- (quick-start)= From 50485191e75cad742e1c501f030ad8f99b07f95c Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 14:04:55 -0800 Subject: [PATCH 181/184] Update documentation pages with binder setup --- README.md | 1 + binder/postBuild | 1 - doc/source/conf.py | 3 ++- doc/source/core_array_funcs.md | 7 +++++++ doc/source/core_composition.md | 7 +++++++ doc/source/core_index.md | 2 ++ doc/source/core_inheritance.md | 7 +++++++ doc/source/core_lazy_load.md | 7 +++++++ doc/source/core_py_ops.md | 8 ++++++++ doc/source/index.md | 9 +++++++++ doc/source/mask_class.md | 7 +++++++ doc/source/proj_tools.md | 7 +++++++ doc/source/quick_start.md | 6 ++++++ doc/source/raster_class.md | 7 +++++++ doc/source/satimg_class.md | 7 +++++++ doc/source/vector_class.md | 7 +++++++ 16 files changed, 91 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e787a2cfd..7031420c9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![PyPI version](https://badge.fury.io/py/geoutils.svg)](https://badge.fury.io/py/geoutils) [![Coverage Status](https://coveralls.io/repos/github/GlacioHack/geoutils/badge.svg?branch=main)](https://coveralls.io/github/GlacioHack/geoutils?branch=main) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GlacioHack/geoutils/main) [![Pre-Commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) [![Formatted with black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/) diff --git a/binder/postBuild b/binder/postBuild index 7d5f75391..9204d190d 100644 --- a/binder/postBuild +++ b/binder/postBuild @@ -4,5 +4,4 @@ set -e # To avoid silent errors # ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "environment.yml" pip install -e . ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "binder/extra-environment.yml" - wget https://raw.githubusercontent.com/mwouts/jupytext/main/binder/labconfig/default_setting_overrides.json -P ~/.jupyter/labconfig/ # To automatically update Markdown files as notebooks with Jupytext, see https://github.com/mwouts/jupytext \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index 54209d752..2ed083924 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -179,7 +179,8 @@ def setup(app): "use_issues_button": True, "use_download_button": True, "launch_buttons": { - "binderhub_url": "https://mybinder.org/" + "binderhub_url": "https://mybinder.org/", + "notebook_interface": "jupyterlab", }, # "logo_only": True, # "icon_links": [ diff --git a/doc/source/core_array_funcs.md b/doc/source/core_array_funcs.md index 0087be74b..e42c6c7ea 100644 --- a/doc/source/core_array_funcs.md +++ b/doc/source/core_array_funcs.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (core-array-funcs)= diff --git a/doc/source/core_composition.md b/doc/source/core_composition.md index 9f30bd5c4..ba42fffd1 100644 --- a/doc/source/core_composition.md +++ b/doc/source/core_composition.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (core-composition)= diff --git a/doc/source/core_index.md b/doc/source/core_index.md index bdabbd054..ca4595f01 100644 --- a/doc/source/core_index.md +++ b/doc/source/core_index.md @@ -1,6 +1,8 @@ (core-index)= # Fundamentals +Prefer to learn by running examples? Explore our example galleries on {ref}`examples-io`, {ref}`examples-handling` and {ref}`examples-analysis`. + ```{toctree} :maxdepth: 2 diff --git a/doc/source/core_inheritance.md b/doc/source/core_inheritance.md index 5c10327d6..9d3bbeb7f 100644 --- a/doc/source/core_inheritance.md +++ b/doc/source/core_inheritance.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (core-inheritance)= diff --git a/doc/source/core_lazy_load.md b/doc/source/core_lazy_load.md index f831eb466..9901bf987 100644 --- a/doc/source/core_lazy_load.md +++ b/doc/source/core_lazy_load.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (core-lazy-load)= diff --git a/doc/source/core_py_ops.md b/doc/source/core_py_ops.md index be5d6f538..af1bd5d10 100644 --- a/doc/source/core_py_ops.md +++ b/doc/source/core_py_ops.md @@ -1,6 +1,14 @@ --- file_format: mystnb +file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- diff --git a/doc/source/index.md b/doc/source/index.md index 962ec1993..8380cc2ae 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -24,6 +24,13 @@ title: GeoUtils GeoUtils is a Python package for **accessible**, **efficient** and **reliable** geospatial analysis. :::: +```{tip} +:class: margin +**Run any page of this documentation interactively** by clicking the top launch button! + +Or **start your own test notebook**: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GlacioHack/geoutils/main). +``` + **Accessible** owing to its convenient object-based structure, intuitive match-reference operations and familiar geospatial dependencies ([Rasterio](https://rasterio.readthedocs.io/en/latest/), [Rioxarray](https://corteva.github.io/rioxarray/stable/), [GeoPandas](https://geopandas.org/en/stable/docs.html), [PyProj](https://pyproj4.github.io/pyproj/stable/index.html)). @@ -80,6 +87,8 @@ Dive into the full documentation. :::: +Looking to **learn a specific feature by running an example**? Jump straight into our **example galleries on {ref}`examples-io`, {ref}`examples-handling` and {ref}`examples-analysis`**. + ```{seealso} If you are DEM-enthusiastic, **[check-out our sister package xDEM](https://xdem.readthedocs.io/en/latest/index.html) for digital elevation models.** ``` diff --git a/doc/source/mask_class.md b/doc/source/mask_class.md index c53b3e07b..218ca2952 100644 --- a/doc/source/mask_class.md +++ b/doc/source/mask_class.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (mask-class)= diff --git a/doc/source/proj_tools.md b/doc/source/proj_tools.md index 0b30cc2e5..61012ef8a 100644 --- a/doc/source/proj_tools.md +++ b/doc/source/proj_tools.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (proj-tools)= diff --git a/doc/source/quick_start.md b/doc/source/quick_start.md index 415b95b9a..346756ccb 100644 --- a/doc/source/quick_start.md +++ b/doc/source/quick_start.md @@ -20,6 +20,12 @@ For more details, refer to the {ref}`core-index`, {ref}`rasters-index` or {ref}` To find an example about a specific functionality, jump to {ref}`quick-gallery`. +```{tip} +All pages of this documentation containing code cells can be **run interactively online without the need of setting up your own environment**. Simply click the top launch button! + +Alternatively, start your own notebook to test GeoUtils at [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GlacioHack/geoutils/main). +``` + ## The core {class}`~geoutils.Raster` and {class}`~geoutils.Vector` classes In GeoUtils, geospatial handling is object-based and revolves around {class}`~geoutils.Raster` and {class}`~geoutils.Vector`. diff --git a/doc/source/raster_class.md b/doc/source/raster_class.md index ff4505415..c7d743dd5 100644 --- a/doc/source/raster_class.md +++ b/doc/source/raster_class.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (raster-class)= diff --git a/doc/source/satimg_class.md b/doc/source/satimg_class.md index 03d093c02..67e7df819 100644 --- a/doc/source/satimg_class.md +++ b/doc/source/satimg_class.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (satimg-class)= diff --git a/doc/source/vector_class.md b/doc/source/vector_class.md index de62d2a78..80cc7bdf4 100644 --- a/doc/source/vector_class.md +++ b/doc/source/vector_class.md @@ -1,6 +1,13 @@ --- file_format: mystnb +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst kernelspec: + display_name: geoutils-env + language: python name: geoutils --- (vector-class)= From 33376fcbb04a15dfd577cd8a831460df98ce5b55 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 14:05:10 -0800 Subject: [PATCH 182/184] Linting --- binder/postBuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/binder/postBuild b/binder/postBuild index 9204d190d..4329f7b3b 100644 --- a/binder/postBuild +++ b/binder/postBuild @@ -4,4 +4,4 @@ set -e # To avoid silent errors # ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "environment.yml" pip install -e . ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "binder/extra-environment.yml" -wget https://raw.githubusercontent.com/mwouts/jupytext/main/binder/labconfig/default_setting_overrides.json -P ~/.jupyter/labconfig/ # To automatically update Markdown files as notebooks with Jupytext, see https://github.com/mwouts/jupytext \ No newline at end of file +wget https://raw.githubusercontent.com/mwouts/jupytext/main/binder/labconfig/default_setting_overrides.json -P ~/.jupyter/labconfig/ # To automatically update Markdown files as notebooks with Jupytext, see https://github.com/mwouts/jupytext From aa8aadc55a3196c8b0e6065eedbb567a832202e9 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 14:18:52 -0800 Subject: [PATCH 183/184] Modify repo url to main before merging --- doc/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 2ed083924..e8e65c3e7 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -171,8 +171,8 @@ def setup(app): html_theme_options = { "path_to_docs": "doc/source", "use_sidenotes": True, - "repository_url": "https://github.com/rhugonnet/geoutils", - "repository_branch": "docs", + "repository_url": "https://github.com/GlacioHack/geoutils", + "repository_branch": "main", "use_repository_button": True, "use_edit_page_button": True, "use_source_button": True, From 91ed7e8dfd00f6d8de381308e456f94fd2a60941 Mon Sep 17 00:00:00 2001 From: Romain Hugonnet Date: Fri, 14 Apr 2023 14:22:43 -0800 Subject: [PATCH 184/184] Clarify settings of conf and postbuild --- binder/postBuild | 2 +- doc/source/conf.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/binder/postBuild b/binder/postBuild index 4329f7b3b..01eb2fbf3 100644 --- a/binder/postBuild +++ b/binder/postBuild @@ -4,4 +4,4 @@ set -e # To avoid silent errors # ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "environment.yml" pip install -e . ${MAMBA_EXE} env update -p ${NB_PYTHON_PREFIX} --file "binder/extra-environment.yml" -wget https://raw.githubusercontent.com/mwouts/jupytext/main/binder/labconfig/default_setting_overrides.json -P ~/.jupyter/labconfig/ # To automatically update Markdown files as notebooks with Jupytext, see https://github.com/mwouts/jupytext +wget https://raw.githubusercontent.com/mwouts/jupytext/main/binder/labconfig/default_setting_overrides.json -P ~/.jupyter/labconfig/ # To automatically open Markdown files as notebooks with Jupytext, see https://github.com/mwouts/jupytext diff --git a/doc/source/conf.py b/doc/source/conf.py index e8e65c3e7..5d1703ed0 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -180,7 +180,7 @@ def setup(app): "use_download_button": True, "launch_buttons": { "binderhub_url": "https://mybinder.org/", - "notebook_interface": "jupyterlab", + "notebook_interface": "jupyterlab", # For launching Binder in Jupyterlab to open MD files as notebook (downloads them otherwise) }, # "logo_only": True, # "icon_links": [

    0cVW@Agb5@4j6N}zxwCc7+X&? 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 @rH^(5Spw{h2V$pQBG?g$Al$(`p)X( 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 diff --git a/doc/source/io_examples/images/sphx_glr_read_satimg_001.png b/doc/source/io_examples/images/sphx_glr_read_satimg_001.png deleted file mode 100644 index f805df8c33f1640dd836439824e362ebd8666f9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151024 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%0D+tSA>d2NQSEl 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 diff --git a/doc/source/io_examples/images/sphx_glr_read_vector_001.png b/doc/source/io_examples/images/sphx_glr_read_vector_001.png deleted file mode 100644 index aba17aa651005c6824b4fd809235efad0491496a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100827 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

  • RBTK6Sy#L_-s*bCz%z(Th8Ad?MAglwc$y08v@Yg{5=Ys6Z zY-I+9Qrn`ZG*CPCV(-gK3NN<9dkEiTv&&Ppl$ zEM1IRxrHjSvY4=vOLJ%iw}k5#POq}jvngd$bsM|Nr%M;MJvu{E6wmUY&H}h;%4TJq z{}!p2NGqG3@9sAralf7S+sYO z+#L@2{#>|wqKKx)V6T>V5E(-7%rfEQd zN!V1V75zrD{az{;E49lQg7)ZV2KSp@J+GA4>&id^R5cRLKsKBJ@OR5ScyoRLRL=p9U z87oFf9tbKtRPcOFQplJG$?9t#E3^Ml%-J*n7T|h0ypL?Fr?sv`oV>M?P&^lK|F3+O z_c-OY$b)az@7#G6Z^fCdSKn8kW6_YaG3uC+R_ttDcE5$WyTT`fi=km*GM&eqq`tWt zisk27?g%wgf0bd$ro$?Gn(EewrImQGG?V!Fnv%I*PhlzT@ShfwvB`X)6xuy2zmZ

    0cVW@Agb5@4j6N}zxwCc7+X&? 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 @rH^(5Spw{h2V$pQBG?g$Al$(`p)X( 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 literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/images/sphx_glr_read_satimg_001.png b/doc/source/handling_examples/images/sphx_glr_read_satimg_001.png new file mode 100644 index 0000000000000000000000000000000000000000..f805df8c33f1640dd836439824e362ebd8666f9a GIT binary patch literal 151024 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%0D+tSA>d2NQSEl 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 literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/images/sphx_glr_read_vector_001.png b/doc/source/handling_examples/images/sphx_glr_read_vector_001.png new file mode 100644 index 0000000000000000000000000000000000000000..aba17aa651005c6824b4fd809235efad0491496a GIT binary patch literal 100827 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

    M0ZutWrv23r6lJ^STUW$S+;A*K-lb+}FA(WVVVL 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�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

    KelfWhcf6=N+0+!2S))j7EZ0O*iL#U5#z$Etn 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�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 literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/images/thumb/sphx_glr_read_satimg_thumb.png b/doc/source/handling_examples/images/thumb/sphx_glr_read_satimg_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..1ec7c1c3c0f48ec409231fe0a8958e088800fe3c GIT binary patch literal 70802 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 literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/images/thumb/sphx_glr_read_vector_thumb.png b/doc/source/handling_examples/images/thumb/sphx_glr_read_vector_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..af3c71a4bcdc24711b745817a3996bf2f1bc5a49 GIT binary patch literal 53375 zcmdpdV|Qg;({=20Y$qpn$F_}*?T&5Rw%Jj~9ox2T+fJTy-S5A6Yn=0Ck3DMaRaI+N zty#4q6y+rl;c($VKtK?sBt?}$KtSVxe{2{i;1frkd=C(i8F495AyxP6iySw1RnxVw zYnIOG;^$ms>66YNI514~!9bX&rt0?dw)0Xe-SM`wwWa#@NZq<9NV@QcEiZWL;%5_2 zzT@|O17cKwDDsSxzB+@!7}xbg@O{=Zb+<%3AVPkooRXRfDA|cHIUb2;?lrS|K2H3p#m$+2g(dJI;fH&P)^Vz{r}TN&A; z*lh~Ur*nh*cau8t|9cHtt12~xR{f{Poyi}$EZ6!pC!H5L*l_6*qqm3CY=gaAU%_+v2F~Mm#w=xv;osSf?Q=Ep6G^ zSy2I(HZG>6g)3fEH|dluPl|)s<_{DXXfA9uX1IeRBSvt6u0G z=O6(1Kkg5b6!z|4%W7)O?+!*~Gk%76+#lV3yHIIeryS-gYt1Gz#avw2kOV%MK=S;)xYTuBFoK|wj(|%w><>i@yQF~-5=0JO@>9s> zMDdpd^p&LPNsEa=o}Qk9h)PH}eSba|DVMu{y&n6yNYVYbYmx?~s``M-P1ES|=5pKe zFt>6qudhc1>C}sxJ|;tlQ&Lv$8;ZdLy>aJVSX!D}S&>vxK}!VG*{rjlou6y|vp^}N zYNA*c!o&;e^_MSIaV4U}p+@g(cEL;d^XLBs;C_!3T0HPu_6DP1!}@t|dcZ%PyT8TI z#jZN;UR_Rb2%N=TFjD0Mn!o5zYT~3Ky%(VcYnsT zIvqPs^F99y9YU8U@Lq3L@;pR%()VG*aQg@eU-zj`E6&fqVL{=}(BP+Un4K2*=H=qz zsv=!$1de5d~2&#R3kihKOaa{E_D>S0lxcCR4+hE@;-IRq^fXr=p+BJ8#U z!;9s-itf#z{lcj5F2}b~z0APCK8OFkB)-3Xy|Y~h?vuu_!25u#q^=|Mk@p2dbGR%o zw7c9R#kl|L1%ot_73fr#X}+*E+tS1RxQNzU3Zv@QH)L^B^E9`B5$6j7EC_K&m7lAB5gu~|mdyg#pxX+7d(J8ZGSEZ%~2s|Ole!jVU zc5|=V7Q;D{85vHt0zr`PoCJE)+6Ja!O$DZ60mn~Y% z&q?}TFh8tn<_K6EQn(+JzFr#&AiNS}dm!cf7fR* zNyets@Nuz4aHRb4G10?XV?yrasPPeKi+23@N@_5665X!&GFi#Nfo)BB=_JA+A zgS)`e5iBl9Skd+ildk!Gg;qNkG={j%Z}=%<8+uk%>o+(p&;@k&v^YxqeDsk5e{ZuX z{OGqgLdxofXZx5%53e%}wE~}5?LUa`CY$aNPcYN>S-=2U+lGaa@hrhl%{y~Lx!^Kc zDn0L;rh~3{HaN8ockihJU^q7@LL9J?scJ@0aO28(d$&KIJJ!z(4D7A9Jz{Cb`JJmF zw0cMuAP1_!TRz@$)%N}<2RB$BM&>t({oKJT@Wx24;o(09CcqQcNdIgz>^2w5T00`3 zOV&E|O<4=3CkGpxak6({qYJm=+!ie-iX;l^*W-l?_VKv!p1dsR0#g|AJLr9=0FMES z-+aFR(*#VU*=7)M@Uw20kp&WQWc(CIXO*|r`U>M|`nfZWFK!otk)ljb7ZC?Y#3cCL z1TccK4B4MlbRMXYn4`_*3rrX6D||?nz8Oq8z>NJ43?Oclh#lQ_sPS4un>!+bp_vB% z6RVn%qu%a)&DOhM`r+5fW(zd5*^-jo^(I6)Yu2+o4fWC!ADo9a^rJ7Fj+i@}3^L+Y zBswo#!0k)!^>Qo#rJ}5d%Z;IM*{fc?$Ud_OX5_bMOF8WK6G6H}4ELF_v0Vqs`oU=O z_ZNF6|HsWsO-TXh@dl%Vjw_B4vA^4)G+mK+4tCK1%M}ze!nxV#_tJ^sg1*7?vSQoW zOWw75sNXUJ?zU&tSocRtAv{C=$;LfZ`zxk&O$X{U&EcoY39mHS?r+ij7KoJ5Uf2&?}#8qQrJ6<|Z^y&}vg*7y%t9kUVLyRL{6t7;+6++B%~Z3v*_#Di&ROybI8!~Frfy6u?bBMBBN z%4NPc*X~Qz6kXV+7`p+As?jTR!eteaJ71r`z#}^? z=r$(cd8(b>V0CAr4}wv=P3%tSaZC1lKSc&VG{PXIWx^YvE}5B&S<@xIm#Uv;ZK z4OpT51TcKz&}=OLI=`gmy{`8^&aAA_D3QJ9V;s8&l@zt# z+e^B_Gl6a69M+5$AZ)GKUcdZovMo8tRA`#*K@CK#{mp+J0^J%khwq;aX3*ys+ zFy^N#x#AK?YJBk*lHvljqMsSatP)=?FjtHv7pYmZe@YYX2KFTjB^Pzwie%&|u8^8P zH`3Ob3!U7Cqlp!Kn%MR7rp@5fAI`G%6!IL z1}WVkRILzY?4Ar6^wac?BLbKtiBZy`#=Ne?vf>5r1PBLbsLiO-GtnRZLJ;d9|mKvV%?9N?Rk4 z2s@%eBY%?aB7;x0#)BR!keocCdD+)2Iu{W{Hw-q@OALmUA;YCzT@{nQGr;~AIenf_ECWYcvL9arupHVwOmpykWc$t78O}gf z^qIE%Soo7*SKHk@Vk)(})$0cevj5j|&pOsfOvorDXY$eyBhF}XyB?Efw3PI|rR#3P z=dlt9AAY*JIuoo1gY+U~k=r!;q0V{&nJ_40g2Ox7GJ}C@gL~^ft1S_)A-UlWS06pX zw+UIxu51}4BL{@y^Dv0hT&X%H}ouz@-DJc=>A{jw>KoDc*CjColv$apM7 zGBJ3~OkxI~y15Hl%C`e#d3}Wl+4q88`$Fb@^?4d6? z`FtX>vtrxeb_F4gM|`8a(Xzt0OE>YXY}pO{FX2P|@4)>++SLl>+vW=Z#lUE5a)QO z*}{EVVq5}IbKy${&V^C2su16K!!Ms1)iqbQJNN>Q45`bdq_NI+Y`=XQ)vK*h|NB4r z92q;&4Hmw>ECG{wB#3Q-nY_eU5a9{ay7Oc)5@ zX%@--aZ&xZwe`o~l3yP(1hT?k6eHR~$;~LnI=enmX@=J1KS%=H^{_<%5>H-#!Qxh& z_{{*m@idk1yYXZlU#@~o+&$Y?94YiV`E>h_WxRFBvuAu?uVts+9sd|RqHibH_>~8Q z%nBOGvCF<2nbPq+P|h6rqgPL|lp6Auy>3a6FEgqSJ%-Vk$t1Lc9MPQ2IMyfr;FxP< z1|2Wi=|{V5`AwGT{8LTA>lbYPtDGQ_%H6w3FP?j{yPbl&X0CJZ^M`pqW%GhzyVe}}|=e<0CO53{@fLslbK zMDmoH<>14U2du=3ZzdY3GF%~lA@PRaH%!;*yOW&Obc?stfi`Z&ng(ZI(7UA87dk@9 z(4^KDSb}#InhCS%0sOG3y`%Ni9CQn8mv@`bCuWWvS6qw`0e+D!(R2_(V#cN(S?kMm z`trXyG1cR6ES9?jM>ZjzSXYlL?Lk*m4s%n9P!OUZxL>}?-eiSp#mSP1ve+X0-d#U< z+zfGfu}7~d=gW-Mrs<9l&!htuM^dyS-JSKx_n5hPjx!Kf7A##oK87E7L*}EBF#F~# z8$Z6DyP=608RIB(-3uSP78X?R?(ZEq@nOPrWPj0M9OwD*CrB2%xS=9IX*QT4GIrd6 z!NS7ocpRpnV`9#AdOf>NdsLuRREfcb`MH`VK}hm5hLZ;NM(5k=Cqp#irVK_h@I(R2 zw9t=g0cwL-c!nNcd*Utvo^aOYd>L8Mtkg($w#G(;6;>0Nn3_)caqa89Zg13OUoV2( z{H)Ykv?7V|_jaP;i`v3iHjsQ*?C>@wtdv&pJO$?_ov|}3sG_1vpE}(`LU_L@UWEfJ zar2)AY$vze+EiQra_P5y#HJmRR{s4a^YMD3uz8RLoAwPjo5v&OMs%HotO9-0rG9S3 z7Zyv3^AcDWFWl@Y!g}I8YyG8kE1C#RV)Fv>`@x1_d=~sDfy_b2oB8qkm61Mgs&}EA zXi2H)WUX6Z+Ewm&!h)yynntV=^~FbA*s9ewgE3qmb@b=i?7#bW)Y5sQ<+9biyPp(m zYjuNE*{<0S_~GM+z|-^>2ap8d0s)6Y*eNf~HM&2V#72NJTKnw@Pw>;JZ~r!5xx5zt zWU5Jnu4NOtc@s!=O=PmcH?L2QrO>wS$MVSE(4O%I>>1J6zJt?r5?-&dwP4W;rzQM= zfryF>Z_$w~gpn+(Qd1QtJ6qv&{#|Ziw6BQsPq7vD*SLIesxql5Pl7P0Dy$~VXeaWe zuHtc=plOs%V9nwCs8#z%Coz1-+D^Xti(We^26}wthWaeAT9RW`v#>8 zfo>??c6X3Pe`l)14VCHb_pD61t0?kZqs4x73ucP(NK_wh3%DV^%D?P;`(;7~VsYrQ ze>>$o)L=&SYqK~ps@Ll>vu|uK*^}tk8}d%x6q>DEq7$%m%{jw8M>Z>u;^C8NAw}c6 z1zl;FSa*X*(nfTi3`EKD)5&Drx&F)jYpwbQmh$;_`#PjD-(j8uj}jkq_Ec-d0OdT7 z%$}`I&h9U$?LTt=yt%OCoLOgCst-QORSMh)-K^OK>(!s&L2^^LTE)Yx4%KQ zfu4qgv&gu|3GDUfY=#snl`#ET_%ox|KpYtYOzb@&5ES5t%#1Dg08vZ6h_YP*BZUr9cP>7 z2oSBo@>%wF#yL&4(G(I92K&~yN6P%1?66;gH(9TTov0aV_yZ?=cf6~m99pMNN3*Zw zOa7>)`1=6AFHY(A!KTW~K`_&z7Ja!cJk{zyLT1#0^$`|H+LOsd7d(v?3l!U$w8dOj zPkkQ_07Y-#@WCT4ei#Am5Z$%pnGWcgPC)x=PO?mGv)zIX47w%y^rUgt~4;BY7f)|X;B zR=X)Nsl$8q&uY1dlJ=6zSPz@1ur|f;h`R2Mc-jxgY*x?&K*V_skl1T!;asZHmiYeq zlu}SQ1`?lzWo6+N6*P{Hjv8&w)a2yk{~?(5YJCVKJf@Os>c&}|pmM@sfoj0B4w;UAT>fHV)B2%q zK!fCuS%^SM6y(Y$)N*PPqY2T?TEbr*WP7GWLY~*`MAZw5F%>2FnUV4AKCzaoXyLS9 zGMh%=YYb#e&eOwO*R-!%q-{i1UAzp|BFXQ*0VfXj*GO5CjtM*xHg-x(OuSwcpQ55Z&HN5Y4u`5f!2# zwNNixCnX(UvI^+2QxO!&%lQ_j2xZJl@aq5CC5&YMyW7V@WH8!pH?#7Li&|tx>hnt3 z$CxAI{sEs36?bxTj>(GaTuwdkxyycmr6;&KKVAQ=(2SOoGpKJqA;{whC|r$(D}xOV zpZ!gB3;6>r-mMZB8^x4-G5`Fg6+>PCnAF1y|J!;_r3tY9GGjx62 z$yi!hwVqeEk4#UeVR;%1&jLB)rC1^T}G|0>iz2ZB*>+~w&&7(7>lz0_T~Q}f=;i*yh3V7 zA>WxEtOr@v?gE|^Qhmv*t_Yv*IcRu>;f8RHY!I2k*fRN!)UAL(5@(%$@t}Fdo{~gKU)RC*M*I;KaiqUL1mcZob zs0g#)@vF!LNA^XceORg=Kp;Cw?g?#m5BG0WW+-rGEyVL2 zJ&HjtEu^~PeRG$ntmF32yTb(_VI8>8+Pd1U7;}1i2&}XyDJe_YbX>KRnkAkT;aUvd zBpm+=;B#6)UR5OO{uVPy5H-6pkM|1Y_R~=JRoDbE0 z{t0#LQ;7d6DN1NG;NNal^OkVY?7trqLGZys|=CvXx%2>nOTro)<2!CmSjP+kIG z9Yqa>FGO*tGs3c%2RSiJ-*y(bo`uW-pL65LbeN)X@Z|*%xbP9yUvR(rT^E#D0;l0U z$pQpP>+phP(;di|b=%|TIw{-Xqh8JO(ya8iqoqMAm1Lqd<0kV-uu)c9x-&iz^;l^@ zvy`GX%gBKy&trwgdQ=!JyxyrX7Dl4dhfiQCL-L4~MwArro2Vk*;S_+@4Mkio{3(2B zOGl=Ue$}d@){Ma3H-_(eSZd`;s?fjvM_##yJeK5i`~X)@2f9&IQowgp^>DVEAxkGY@OM7mf+HT+1s~tX4>>;nxQF4g z6y?*m*j^(y#}Q(Iolgntzz8Qf-0K%cOoN*tT{!vK2maswLXG#tYM%SJI45qgj{pp{Fw{*L5QtcD9Tw5wzJ1 zmyg=4DjG|}NpmsQMRyc3QmBShesj~rL@RRgW$9)i2hl!KHdEUt8%u-ZIWTWSdcJHg z4-%e)PcwLGt(f}z7sLXXk#boTScuMewwSB3S4KvsN%ssBsgUy&nW@FT$jSRE4Mzo~5hW_r!kqX%ZccN$tV$ z>Q{L$4&Yf!#EiTLr!$6@dA_*Uktr*T9mv405O$jz*-_g$%-d(O&AP0SJmC>lE0}ba zHHoD(;h(NsR-b=iZ_CYSELF6#SZ=-Bw6RBwQDt5p@Uv;IDQK4hW)qt;b`*cV7`+{f zE;duxaO)-9W-^-hA>0@Oe4@fTFX-#XR5Wj;NX7-$qSR_Ab`V)SEg4=#)-oAv_0>kt zxZcC%8WQ78$#!7tcGqc~kBAYw+^9E|VruSqqu&TcDaB_??06{}GOOm$J@qpp*eN3M z2RlAF!hkf+&l1{$_{F&4tXu=%GfxUgk+SE`IDL-L)aJ2T#0Q5>f9I|>t;x1TilKvH z5@}RZIo}Gcj}0$dx1YunqFIfA6@&q8M1k${k`me6vFClJ_#rC4ek4~}LAp+_Szas5 zMLc$5$ekX0r}WPBH^m>wb(s#n%O2Fs6ZvW=_!uZB)xVKa`M}dUKsPDBqdAJ`6BMx>Mpfe3 z@vXY+qOr^VEQfQRjp@2)f)_hSkp3YEpk398Bbyx<8E;ptFVCH`VJMudfL$jnYHG?X z`)Bd*Y1sZ3rHWRd0~xu~$;ZkqFs5~mojfu~Awkb>4nQnT@G4@bPSuE3*STX8Mo2F_MzuY&3+C8(1$hzYc3uc(H2H5O?OBMhjd> z_g%g-z6zskY3K=yHN@$ayAH(?hCME=!}xL#iPY#_)Ss-pP8e$tDEa$OpISe2W?F#P zsXyj_O%kyB)Q7EP@F`}nit4YWq_)0Wyty;pc3*qBbIK$d9KzRw*AS2HTV*{5l@eOI z2_M}QU5n$w9m>BCE z30kRckGOsXYdYydfAGd@U`xe3=B}{f@7rG$GJd2@5BjF4@xwx`pNw?>5dRO0%w`^I z@8LDR@UuThMm0rll{LO`5!x|W#Z_r@g&&%Be%A{JW({O)mg{NC@9s(GzPln-2nMrw z0_JvL_a4G-L*xbb%|_tgTv4wAagm8Py1@SmU>j~V(WGK688cf*#jzVMJ@4;ni!&+e zPBlGmA4cOW&+4d)=>BGYDi#%0hw*b@8M0-DSqs8)OOcX!7}PC$-!{vJAFEZE5LJX< z88YA7-i!vK)00EkgOK~#p>&(Bevc;7DAbO(1Ylr6RHV9*h2wGaF9^3sREfE$%1YfA0cV>hp| z$1v<1yzvK;;noZ~)I{1^=gWsyQLwvMx#h3sOR9!gj%u6%5&f&3^-Hz~nYhB)L(}81 z@4-WA5gVrZ+bl*04z{1W&wiw(?;yBatH3Tcs0$(@}?Gbr|1JqOUF$ ztan&pAK+^IMi9X;^j$y}r!A_O->-jxoN!K}uT=qUqYlH;lRIOWGnHG9=6av&$CnP%f#DpP?X4P8;I4F;JvjJ?>FbjyuhAA#TOx{x z;w$PuR(W~MhZ&)c0ZdPB@k?~Yo5z8w$i8Du?@Iw-^0whjt#%4Ek*z_^5p&yEc}t{% z`?OeZX^_bSF>IrK_F4@Taj_7jjrBv4nzy$jRc7qJL-^`&V()7-R@?*uYaU2o46meC zoOd8qR7tUwVdhF(C&nIze0ux5eS4tyNYa(Y$G?j2z)88m}^fW`fevv=HLUZ>t4EI1P2sveZ zT*o5?Hr{Q&>qC-#Q`PVsB*c(@%CCG7dOvx!(8%QC8OeabuR5M_ntMrdHfzVjLA(K z*`=SHZn)qi)fNaX6k~dmr_zNAswM%L&OaRB0`iEA148VJ@)R4f#^Zz3XcLY=V6*UA z%EfwCcwn<$Azp|>J_JaJZ8ZgbFXrCKJpZh`?))iGb=R55{zk84<+sIV3W|DD7;f$a zy>4b(P**1nAz%1G7F%E$jG?fjeM6oC-(QwgKjM8Zw>ql<@}BWqRiGijAOcNf*+SKm zEj59T!W)yqwMb@)Kv6TO9Tu=CV zK^@G~RGRV8qV`UQxu6#qZj*FsV1sb%Do-T|JC@6{bu=XV$GWg23W`b|r`8cBn#1U8 zsA5oF=yEez86@4YsquwFEs_YT7%3v6#omEQ+w@^^fww^2X$JPhu!bS#-N@$a<)L6l zj6aoj7ZFB6@*8~z2{}nDS6s^reA(+YX0akR&`Qy_P#t)d_OeqFSRWu=AGBSYY9E8Q z0ND;?RT@&G;CSr59j5&0+THb8|AI<54zxsKy&A)^{yzJ# z6g@TL%KQ#pz47g*RAbf37Jj~ncc-LU^eJdNk@eivSvSKwFUcRaz1m0QbB&beIO0+w z2ycJ+qvNh$O!{5)QxtXgOsu=Wqx6D&EU{FzQml!8Co&?&!q+pWOaPYn()$V}Ru0-T zvnK@5et0AdfGn~()oFPprn>$Lo36Y$2O{S@q)PNh61UHog(CV3U$6Su~3viy*5Dg7%Iu6Y7XZHEFNeAIB3pK zf3xkTL3Gh>4NkZ`t0?|d_+fV#BXH1g3OB#i?<$WHiey((p_5N@=;#=#HF}B`B)?tf zSjKiAx%#S$1HSiwZPjTJ3Mg{G?Tig z`-ZPtnk~4e`Dg={HqN4z0!+wf5SsHy-H4&zLR+T;E=fL3{`Nscz4_v4hDEoXx{@R) zDVid+8zFuQXb#KIyt2Fpl;SX$SSm(;ixyDD;aJHeaq(fxhr8Gna;!;LbNyQ?dVCLr z)NO9s1&c@=Z=n}Db>z#aqw{a)U#{dm&{&8;$7{ilM|kk6^#i#nf`skJ&cB20wg9Gn&aN#XKw?^AmZhh+}NODjqbQD(~NE{LEd&Eo# zv_eMNVlMx3H~cndywu%MRzI!q0xQ-wRoboakzuwN)-clOxW)uKcB7LeL^(@bA8T@C zwuB==0cEe@Ih%pHluWSth(f-`&xjz)s%)}Tm@n}@JtF?Zb~_QDiOT5K)_OnFPJ_qNy8P-~MP%*EW>uFTAs3)ODUbS1}p89?}T~0v#MNsF(rsDVri?V}t#h|C&kGH=?NIrL$76YvY0=Qwhik<3&?cO5` zV}hK09aW1HT57T$MQODFhsRpkFJAuc{&N-1PsJ|6>>uuDyXWmYk4}eNsK!^p)HNeZ z1jCVlzCypAzYaHx`bb$9SoUo&m{p~(mjWce?7{-0w65oeuxBYUZ?A3pxVdA>;2j-m z$^bgo{p<|js$fLRZ7m)Wkk9C9Og=Kq@1H`fu(zU<=>aWfu#k9qJAJgu*lv>s{|Io1 z7tI`OSKgGe*cwR~h;ZTKRwP$Kqe=cQ2?$nECS+&9%_vNja3KpFA{NCk7s}M$On%v= z-##L2t-k&ULF7?_r@12VWK@XtO$1_UF|#3ee9#;xf`w7ciyLcGuJBAbGv>g~=6o=E zxWS?YHi4;O)qFogT!(m))*=m|z>eg3*ruj%&*se4oS5KBH^MJxs4sio+(b-XQ-;2P zyj-`2!*|C>_9cfYIX2G`0%H99)6g6Q$Pq4@r-_ z1Aa&MN@wze=+99Rv5C_QbIzs}7_B0xQr+n`CIB1MiJuuaid}r<*yqJ_^6gjg^C{7G z&E&oLQ&%F+y;*I&CnOpRMF0qp#oeH#HBH(9XPllP$6elY?4t)o-IMEBrVP7hN54(e zCFflvPZbS9sT7Cq>owywb|mtKf-blw8fN-1gNRYe{SE|ChN-j0>WiJW_W)MEgE?iw z`~V09DGFzM*QPjSV)kYJQIfFr{!e6IBhIA);PzReFq`}M zP15_xkonyZq8OR+jJD8GUy=y#N$J#IX8qInVDoIQ&vSy?hO$(aDw_dVB+#J2_6ABE zOr00&)p`-CRtKHvtfF?XEL{!C2$lq~Tn8!n{n-tYfc5^QNz{tlk?d8r|M8YJL z9WUZ}d-?U*=-#XL2xFCJEOX(@c&|<9Nb==LFP-_AlE7op@`Rl#HD){Y?lcK+#^RSG zF2-UnO!f8iex80`ygiNi0Mfv|D{2Im5(y8gglNRtqM>J7DB5DOG!sMgRI!lF;^-h` zScmDF23erIT-jU<`&O`6)t>dIUT#I*&)+`azt@Ogn>0MNCLUO2M9&|}W@@RA;fAc; zK@I0C!xJH;Y`}$LkbesiIH1%&NNjpRF~^E7xYDC2g!deAXKrncZu!D;2B8FQF^NZ| zc7R4;8;L$arp~GoirGtXF`HTFG$l}5U?B-iao1HQ zemSXOlvqq9*S#AmNgN|Ql~42Zqmf#-oUgxt1EuA%`H<47{`6u$*mPg(HVmEyi4I~9 zr4;brncci8{CJkN1?_rSE`*v`^<<4xu+&>|iRJaXHUq!O#1 zQq1A^bp8!IDF+f25_ML~+hrAR5LumAxK_Xl-$`l)B5vMmC-Ji=l@HPQI`AVeor1c| z;G_R$YVhW&#QsXfk#P{^#7PZT7m6GiHiA{PWu>wDheQxo14wjuZ(V1EwoIw9H%njB z3#8~F1!@YuM8@)9_4GzGv%K+Vs^`LSD)Y?-ogzGOAV3NR8eMdze8zm`=GuB-fil;A zWsqsvID7t+FmlAmN`=ddlzXenGN0>21f3OPwx}7A#O_TDfJ3IJ4L7++9GdXgA$|!l zhaR!0)dtqBSOLFr+Ge1!3(7ARJZsaxa%(zJ-3=|Lloa`)NLlJ)qan)798Fme@b(xJ zea?~jH{f+=h7_C4#uc&a@{!d~Gq~K2x2f(l9D@+krh3n?SD|_9)|;~&kIMmCrtVWH zL7=M{Q3&~Eb7iB*w}T-KzG!xZyIyPeLHKn+-V{M8o*ibX8SLr217;Vw?YF^jtZTYHOA0=>;ug z5DTm#NGYjaSddgs$GLLPk!T|e^{lXErd~^<@Sy}|iVr9f6jWeIN&rOQ3Kmz&mQShf zQcy(YBg~b{j%AB9QIP zO*@jz4#2AwhO9Vcm6c?`i#DUjPA?{dyy7+t=G~nUWSD{E|BSpWay`b<< zEm-5{DMK&xW>v3ji!Gc8_=bxMXDd$l_u}g{Kp`MlT?vM+peb(-O}6Eo?s50_DB`yd zxAN`_5Ai3LkPdm}WN;o@!9ur4ulu+o$p{$o5NW>=oos_D=BLR;Cp@=$U29q%}(YQVR?U{X7Qz;~0-54)P zTr~3LvBM6TJb|C=4kkqSz~tD-ZA9um8U6LM!}Vy<`(t$2yU`CkVTcL0F|>ZnamjYI zNAwdUG1y`u(EpD*ojyI|;ozIu+mqDJEc}meFfyqdu~WQYBmDyApU5M;d!EhKt9{Me zzT+peMZn3i1K?o9PNEEhlIQ`e2#_^O`SV$OWV$+L(!&NlZbL@GrCM}V3~qTQnEq9F zQTMPW>cRdTAkHR4KI3EZGu48RjA69j;#El`mpI?`B*I;!i~yRpCwhn#>f!t4m@Vno z->{hLo>(ped%@@Xg6ovKsmca~&(3VdOfP6}Z+e}V?V`18x~ zBgSa!#&GCJ3=jZnYao59!5a@t1?+SqG4e_X*ELVmp~+ED$sk08pnhPF#FJcVY!mgE zH8Agw;k0bp0(;QZ^wfv%V%x@ZI2y-Wh@JsLnbjS7j5mfp;68VS8T@6R8|&unST`P{ zJOjAKQg!%T0_wnVgAR{-Y&J`DGKtuM>t_{ZWnoj(5+J>e100*LV8tB85c8?AIDdEM z{ixY%@1K}9={jqQq;ZahkqE%~)Wwr;pGWJ(O#XY+Rw6d^QL+v49`hd#UY z#s`C{ZQu$6(=M~eM&&6L%Mg$BmuUD{@N>04qis->IYF5PcXnYR>5|?0$WCA`=khO7 zGaN}AM|XVvG}C_l1=*bj;Q@5`0#AVeA31EV0Rx;h-@xq^mQ;*Q&iFn*uJLZCW$@D6#r_fiJn9hnmWatehr#S*O+-?YXb+} zTKyvV-g@9|RnmMRtzN>PiF0V*^nN=nJvci0X*2)>LPJY?`|5pgNT4lJ+&3!mUntfp zd^O}02n|E#h~+lsGOJc=t-V4%v?wxQG?k91?O$Fv`da_?od^b3Y?PWhN2gDvUvZlD zm)S~&rpXC!n|`rndUSEW_ilxCx{=Lu z+)1+fst!ht%zq}My8bwztL<;6fVLIW$U40>*;D3B6Lfg7))@u*3GIAR6&V|QG)7w^ znh031WYcTTns*uePeg< zDFUu}2sr%)^X;{lXd0iW8Y;vzy7H1)Mu!5Iza4E!Xd0Yc?-GzNK6HMJ|FR2R5OSn; z_!4Du{+n@Wp0EHf{*95!OATK_zFQjxR9zva;2v{H7;=;HS_Xi z3sCC&34k>gqHB9F)^C=s>|F`ppsx@5uDX%h{-eW(*P|jfq(mDXFqU z8Alx=x=~p%BZ|lL6yBRo6yxQBF*O}kJD6Tx!PVsPlV-7Zvt-n0XEmc=v!OIlchT0Y zYI~%cU7$ysoW#;Q#a&qbZ26M(jECa&Jt?a#VewQIhA~p$w ze6A*g6be8})Ft=^9i)>Xi0wPbCa*8PsXbnhxF-}5PqAE8BgV{e7|CR{5UP#wuW%Ng zOovdIojG`@Ei>yRH9&R0*HxT5C+Dz2rV-Uk7qmLWl+~vtOtA!dJ>wJx7h}x?o>|>l zB+H=hb<>yh>WPvL3_>QSrF%QBy9H9Jh*WD4GDSMX&js;)faZxkB~kIBw4x%ixR~r= z8Xr;!89O^~Y{%QeTBEPM@qFbvMR4Zs0%w|;)f6OzzP;u5mmFvG4?Uc3I6h;qCVm0b z2qw2H9U2;%bbcRhB_(K|W_IfpL1$;yjEsyNt*pM$!(uqeU%xO84-Yx%6JXYx?IcR)XB(~51!D_R)6&o)L?vZqKQI7$fR)=;?*O$=Ft9st zZZyDSvfmH#9WebQ>$ELJ7`*MkxSFKF{sfX^I>(_gLuO_`l>Dh^`+~8yPEdW+90c>&2&9ZmHvlSLLk5 z$mnSA{_V}Ax|8OPtU<|q-=`%w+A;cU87eUR$j!{$w7Tf}x{FZG%UB_7j@=?@s&L<5 zWSynVGWpjf%En^$Kk!+VpdK_`wT>`QsD6{YIleyQO9>;0#`lo|$F8(hr1U2L*s12DhzxU+_pOp=a>8F(}tXVGVy` z8Am2iF7uC)SzY(Gt%`TnsQ?1*c`vs=lgc+IIQ(~8U zkuq{p1wBBj+XR2=QProotoS>uO@`2`;B%H>8TefNXRshIbk$I4`wZiy#+Sw)m; zK~#Q99|r3D=1rP8%c-p9J2x}4wYX%G_b$n4D%kED>MD~xw#%|HbGAr=#d!qzDRud7 zjb|N3TzKI*wpWLMq)-V3*s)IJi1xEX8zQU=!$5V$G*+f`K zmkNB>TCLH^Nz;p!CPK+X016BQ5IP`&hRb>0L>um%!l%<1dju34z*Bii!eZ|zvpX6; z1%;7bp$~4UnZMNh7AB${T@8*eE>U?XU%*5D*Up#97>dO;uopsKg? zfwX%5Ka#F8s;aJAgCO1A-Hn8#bhmVOBi$|C-Q5aE3rI^h2-5k|A>GYgeE07W#*ls1 zo@>shri0Oc3kEU7ZZO#9I~n5^cHD$pgYmTd{Mlh)Fs&X!?euk&;A3YOm;2pphlLgw zS|=wbao(c+ll{1H3rYqZ6ginBabsihO3fMzAOkhCv~<}?)g(zHd);sv9v;qJLZ{Cm z0E$s`XvkoZ6Y95f|0V#1z%}qBM-@1OaM>Lrr=||g$qDv@U}k2{kf)KS!8|_)o)UBc z5>#A0M@aC6yd4W5AU#_!jW3)XsXGH-bDr;o3`icx=KivI8u0qa`f3u7t%=;XJ&aqG zhCpY)6fJeFao^+105?>)Kjv(G=EPLw6iT^L+Y$0EIx{wY_q0Uh4v2^CPxeMM|@n^-E7FB8FW=)b}Bt> zr6iYu;xoI7HycmT48g$bY8GV9La?^f&!fA4V&r@H*QWs)I&PN(`E+ELvCMXCJ!^9D5GE2)UAq}cNK_QyE14W;!b^i-YyCQiVtjw&in#=R&zkM4BjVP9;e z9RX}PkQXTHR-70pq#e+|iuYalE-o%~E>nx>HqSjB+g8;yH0u2x-7zpQGB~Y2XX_23 zLI;b?)fmRpMIqsT0I3c%wT>I2z*HojOf3U%I#89kpwO&~l9^Dx&)?W(_x-Tkr*}*mSCJWRhyN zm|THWpXYY!vYj; zw61lf)3px#kM=~~&rVhu>O32uW<-ipfa7n%hWB;_4v&s*rRmyloV1-<%>B@|Td4i& zGAWqe9SqxGxA2oQQ~G-h>xCWpv)I+*DY+6#`{T`EYa~0&roSdnYdB}5Ss2)IRT<^7?92?sP;%pn2WmIy8z@VUadESTR z+_&q#m*cb+iL_86fjR880n{-M(L^>X<>ujw6Wl9YsY4BYx4x9=u#W@m4P7rhyC1j?|u-}XY?KIlmwC zm?jU4gB}-@L!v|45^Qv)gH5fLnZ{yKjzo)Owy0f*Q zm`tAqBoaEjH}i>O2n!9=mZ=>X+)BHtP=NI9W%yWMT%@$Ghq7&f6KM$%Qirs3$aAWE zZJ2T;GTf4u|DD5Ts&Et4r}KA3k#oh9^;NWVCHgup=)2;B92+GIXQaz^Uex-sGJ z?}yqZo<3P^)Bb-a1-Ko4-Z(yI?C)Dm@Sh3;%<{Zrf?`pjz@2I5j^Y!yv zmJ-p~gsNM0-_`A0y~E+E4JV>WfWvlvE)?oW3tsxcTshFG-0@zMP%;$Vyf5BEP~|`Nwh*e)jRt z0BKvfS;2w~Q2zY;t_JP4>=yOH79=@qkru(SD0(%+GSktH&CZmRktx~4KyZsFfv3(k zGM%mL7t*TPym5dZZS4Avp%|e`Cz$&1TP$&a41l#h<)bDKgI*>X)g}F|e6MBI9sv;% zG@j}g23|J#Z5q`p zhwKs)`Bb-Qk0ey+-0kmm*V6r}+K~&mhEW=CdiOMQWC1zCkz|^gNF-M{uOvYRjj_OK zgTu|I0;!5wEHiDZLM&$`gCs&VT_)Dq;>Bgs7e22mE7t4mx|7MtntuV!#=${kW`G*_n2{AC*D*OD~XIB4&1e?Ow_@#n z>gyHf-IeJa-qua7Nvf^L`go+5{8jI7ceJ75@?>q=ytasG!lgLuAFHG9YLXQULR5Ga zgL{mW>`ot?rncbrB>yA)Pxw^jSGUL5yP`ODvhVNvb|Lx*0P9};{_)(tDm)^hl=xXy z*VU>B8L+p7O;+eg1ZptIb5rzIm^(`xrb+P4STv0RD59=J=;$NS(TlZCP>gd`Vo+N0 zG(8(^7L}m}9gouizafx>8ld!&$f&-S<^D{d6SbB>2jNRtxVT%_-ryR2bKz1^QN4AS z04PXJP5oiekyWMJYyy%Upub*Sxq@sgyidM7+S=M$RdXn(I#~YNZ|?sCKYCYw-1Rc0 zh3k74*V8E|u_vwl50S_Hik*g?X^C}P_dqLd8dp0b@7OuN&2*> zyj4x6v5HJ23pDV*S3hL5KAcJ`wc%jH%*;Uq8N}|xf0g~+QJ)mS7-H^uq2fYONPwE> z#&y>;M;R)S6Frj9$&jY+E&=aXhD^`YROjj-0r42}_TD+N)}*Ab2@;6CbfcyE*fNu| zOAp|$9$-)s;!BE>`+g!-v_$0p=rC~LK6)wW`w1j*@i*BojwNjLPFcC_bK3B*z4jWh z70mx*$BVu5^N+E>3~EDYNNtsV zef^Tjsj2-5#g!eMIodyX$Kk9&|)DNb7!|4)sKPzFmqs{vRiAR=+}-~5LP?BU5qZaSe0KELk1D`$ ztocr+ydXY?t*?@a8rCDO1V>M`xvUZI^M%XA1tL`}-rVX#p$=-Iq)-?M`*(RAH5z1Q zLmXq`^skQ4$$nIKGTFY`9bfZbU1I<&8Q_I?OoPvD-6E z)Jvrjp}x1r9=_MJ^JI}BPyWzm0SqG41yfd21W0#KOXAtm4XA{z3^n-G65b+hsOcumbqp9m7h&VZ|q-S6Q_ zBc5HDW!7)Y*9)h$0byib>}9d*jFCqI|CY?o6Ep1zGffD}J6j-J!ZwKhmEQ^nUVMd| zVgll863)z&w~58g@5b*)uM?8W<=fVw?;@(v+-km1PGbZ{0P2!x34Iti%$nO&tzYWl zy1!co+@4d;G&QX*CMrjSvAR=Mh!@f96dW4mi%Yt*%&#QGR+eUh8YO2+86-=Fm*T;^ z5h2ZnAz3_E*?Ba#6Vn>XU-=8AQRL&tbe*WUY7Fq<^wQaNm-MEFD*4SyW64Kn&%es+mW9T zyj=IRS9Ck3gF<#G{5CuLx8lel$xYteKnnyrxtfhLP+)-c`CI7$t#J7 zRyx#1_3)7r>P_UoR~sZf$e&uo1eqxAd-ofT@!I;0NM>GscuryVH2!=ynZ;1V`Y&q4 z@u;-BbHt*Z|1gsGBdY0G-Hs`AD#G$t*{c0t`6HpoJ-YYoMU^nmWAWQm?XtlIkCDzx zM^m*|@N~MYd)rw4jcs?H+Py!u_QYHgdT2B z0~{Y#n&JzLq6FSMuD|1*@~^ttIkPqLNza?@MyEJ)u6@?>|fTIT@1-oa8bpVRJI55pYS<-VnCU_z*0` zSBu*)Nu*BQej{a)+}397w74IQrXcIG0adjB+*1I6e1A*ja6!oiO&gO|ZD2_WmG|w* z>TaE&_m$D(&GA^CVD?+VV9{?Kf=c1;q+d>T&G$_*=x6`WHtid6T1{y6-cVccfM4wm z_Tuy!7E}|JHr!U5*k^}D4*6w8iMhk{oq7Lr+hZkBB=7A-M*iJEE)|@(0^A1!cx_X& z^Se85Is>XnloqE*y%sl!Mg^sr&=w%$V<+>`c>Xhp?gVS?>wybuaNKZ<73LUcKxe#i zDdA6>v5u|U7+H}o$4>>!rb$q${!G=4*b8B$g&|Stl>RCPS|uB~xgfEHWn6N!-J~QUgr#5fsPVm3GE+DghC58E zZ0r|10xnj&l8wg>s+_;XU$@p73qNR~Js3t>EGCDH)`(>fLo_-S^Z8M zoN0u=Gc$c)BHLeW`}mgbP(()&Ws?18HlCdzcEOH&WOVcmQA>bGwq`nqa*?5X4*K2_ zkir^;`RcWOPYRh%G-AELXei!DZ?0M zYq;>S^F1PIkf33A-yj(utlq4%X{n&X4TLRfD$IZ&#OY+&iez}@XMdR z%B?54pwS|*Gev9a`9I#aRT$kj)c?6^5naRAN;X;%tC!iv4G3BM`(FjC(Ic(0AvEPH zL|K0Ab?vud#X-Yob;UgX>Fq^2n6?~DeD7$cBA?E$GV!AUhEF_qx@cDy8M>+J;Vnu6 z0AS^8Z-JU3qwKER;0Z zvtY{pccPN|iMH`tN$TZSwZ2$Vui!^KGD<`z5nJ zy^ripT<{8quuDwcog)N=NK3gA!sr}hIELf!P7|YH0_#zkmFJe8aDoEGw@#Klv9eAS z)_i)Oh}3Eb3gYZ~D+NI5CV1dXqK08l_Wnosn~1hq7bF%9INlfC&4t#Bn9pee*~$;f zWb6BOxE_qDbl2{#X81<^cy0yzeasnA+d)q$z}1}0s)O#Yc(Mu8{)+S_Lz+tdd9&kJ z7qe6iH^p~oadPMbzN^SNl>Ex#pEGj>Dl#P~`A8iX8@c7q=iMzzqg}9%n%(u^wkw(< z<42*|V!rL#VT5jhBK!?a$n#yOC^^V$&EWn*u;GXYqCr7?fg&3o6&Av?864s^+gxFF zDe$wu!BNofAj7Usp{B|mhqEsVDsaPxF;Qs+7!1j-FWy_J_PUd3yAtvfx;tv@LX&hI z5xYje!W+J*;2;JQ>opq57_~8oTVH;XwDX11^@ZGy4URERa2Y_K$9tEv#%CuX_lM=R z*$$c>hRh`;=dTq*YOm^qtk(GtfhU_k$Uu1TC(|t5L=)N9Jz9XDnn6slmew`ArFqo& zqbjGkDDf*qQN7X1x;gH4tV8#E*5P7*+on;4aFLHWk4WwAO*!$9KYCTsetPTdrWY>% zl^@&DS3cmsU1wPNulW3VHN>V}OX?d&^)2z76gm0l*Kzxm%h9?RMe>Ijq8UCf1ace5 z#*@3(^tt_OfWA{eqEg)k%dG`1rjML@%+cLgaST+ zZq165;HM7rUakiTcXlkuf8ZoQ_PIUT0eQ7Wy3O_=S{EyNXMeq#kdQD_(1#l&@5<+Y zdNx9pkrn!NL+Ge6sxA*3LP-(X-g(Sru=s#rIz8a%KH&L@^@*rPC)U14$mlHA8H0a6 z>^qY?0=d2JEowy-#z*Ry|8B#W7C4j?7jX5c2>ESXLuUAFkZ??jo#CaPn4V5v_gkOk zdL{_w@Ldjf1KAU~* zzERq-zif9Jyb(cakzx6ub@NjXnfp%iYz_(|B-q*7=GkC@OUWP~ug9dmkW^QA>(KMi z;+V!lmLaRpH2?3$s+v?EHpNTYnpIKEOy@6nu~RF7Qe?PK@E#ISFVm6~{3{ug^5ico zBwZ1qZCrdXGAWK+Gse-e$z4S9{_vf59YhlxgTqC6D>av5lURy!pbQLyq=i>8D+(X5?#(fvIjOanYl_kSzUjXJTjHl^j|k<@Z&${HF2fHqum&wYiQYZYtikIWp3E71!l$5Ge4-&tc6fv7B2Rh{@0&o3yer3bY zK-f)ffHE72p5a65nhSy3Np)&f)L=Ze`X5tzG?JRl_a9=qx9{q*I0~038b&$dKG{NZn>J?B)5u*~REV`PV-7{zY;1!!Ab4!{OND@`+X)cB&4_zy58JrQaG; zR5Tz`(DiO>biUBwimfh~iRU(&)6XD|k2^1dF=jvBh$JPV04JfxBs-I|`va~5>9NFB z2wDum56cnwvx-eYyXA&J7B8`gyg%5gib#BI_#z2yLjf{BVL;8qS36mhcy&Q(q5P8Y zRD6~#fu4{o&qFkDdTYv^4Ie66_0FVD>lYx)m&b$wTL7pS6B82<2)(8-b)6*U;W0iR9683ci@CVwyXq_cdLr!~RR+;O)Ddy^Ww?3a z+TGW9y&Ed@$(iH7f`8^dXu-sTwd-{b{p(&J4lx~;G2`-D9n0sYc+N^YQ!v3nux-eC zNz7lZ)3@-%>1z%TD$fqvY)?SoIz28V4@y4cSG*u_Ih(^Rgp2vvk)sj5k&c9;$TmU`)s0U^@hmVq`{NU= zJ0(}J9JRjpmM77NQtJgn!HlG4(wh)@%tSr84nFugLL4(n+FOcY^Kxfhk3Au&$5~v?9y(cP(1WlVUC9o`u<8!!L)||{Cena z^V*PU)IvCT&r7wklzq2;+t1BS?4e3&C~%EOQ<=(9-Ep6Sh8lSHgXoC_xB8(KgpG%) z!kNQ5CxHeihEymI5(=i)=hj}MT2IEwiT!Vxf^Fk3nD5`e8+8Z0-`?3t%*Ys;E|nV| z8L@Hs0piJT*8?UGXDbhun^{c_+_aJA)wQFvUpIriqaEXnU~<6k$|8{jEIKK}ThwffTB;PN$WWo(&8 z_QY$`r^#TK`Fgj2kUl&_^b=~$OJ45cIQUhQA-z|)I&vY^=?StD_y1hDilRQaoRH?5 zY$mMr{X=2LL3-R_OOmCeoi}yaxljpK$g8lDOXZ*2Y8&m8yTNL4X&?2}diZ%X%=u!x z&;P6?6lZFA=~M5tv(upK!nz9?dB;6VE>rNqdWvTFnQX}{S^!u7uf|Gt1;b*O8>Z^X z1HZ*!xjKR^o38ZOvFPwFvMc_FCo@*54Xii+bqs)t|Ci?vrcj7CW_Qej0}zKY$uv_S z|L6^I9~uITljpg6+bWZ8Qw+xh&lcOc7VuPgxVQJVX1T2h-r2L9I6JlN%~kwdS$lP7 z$@k$sJ@wAdeq34Ea7*Wr%g}wo-{G1%0>(bHpgI_?NEBGa=k(g*xyigFg1iqB!zXp? zf-x3{x+PtgC=9_Qrapi7Y&?cbe=%TxA9q}AbN*})GerF}_OZY&)-DVW<@bbikMb$+pe2B=9u2iGKB{^)T_T zugo$jA=7Vj?w(`l@Nd4Gp>Gggq^svmL{AMNXy!x!t^I~yW^h=duy#FtfPzIS0Ye$k zV*rL4D4SN2M-E&8mcG0#ATG1rD@c0Lw?rGkK2U(6MwOlH6utsYDWda^d4JYBi5b>~ z!M!6lbJg-hQ3al$$0T8`+#Q1$rXs6-g`GRq zORn;Jni>^KJH$9wYhGtx``mfE`9PTxpms4>}eee zPqFaNY+;|d89IEZRRser%{81CUFbR%t-5gM!yzNk8p-SF5vhJt?6+vxaBsT@nU>$H zt1W=4D?GJaLsOH(=f)09t(tmH@4>JNq>O-f1L;>Lpu>_<_>hlM^6i@oP+QdbKYN48 z4D4|r1kxd!woS;|fLH&bqM{|&f|_cb@vgb&=r-8(SpMzZgKMSBrJH$2d)MiGhmtj2 zrB>$L+eyDJ1+9NU?TDTp^KHJW7vm}(RD<=z_fUkhICYEE=bBeaUlhT2)%xu+$35>V)+xd$>tlGT}>_5aVqek!D z1|fZXDF{09TAxe7ZUP9}Is`@{6jdOXDi8?B?0u_Z)z8Q zj}x=WJi(Ndls|v|IIc`MQ2}ib&~k5wfdE~LhC0`S&x`d%!~jZ(jE((5Nv2EKlw>?= z1j;zDG^Yt88q<%hm32^MVYJlU#(IhwP%V+D*35D6ec?nL#l_%@!v5c7r+>`wAyb6T zXzL;I0Bf#QCYqbw)CvZl-Gb&?o@%lB+ZeuarCW;LoUL3)1Bt*>U~K>> zmo`DLfZLS1-<@Ca3&o_U*B%83Iy+juHtnj)mpc^pVpA9W+HQKm)f0tlG@pL_NiQa( ze4#OTGS4%kYTd85PuD@Hv>)pCQACeXor?aiJu_B`4XlllV_{I6=M8+AA%JcHfJdFz zr@#x-sH32#r{3C*VKRDtxc)wC1BNt$SQr&7u(<;qQx4~C>CLTpvB_>FBkt@20947PvrrSE zkj`Pz$zXiSKdp7Mt65gO#cOf7>sdiOz+8t7Ui#f{s3%`llU62-Sy!gTS<$JbrZIjH#D^dg%TMQK@64gCrMuPhhI4=TMouomw|F$L+P7fl-7y{q*EX z&wW>LMohN!tII33k6)E9q=g|%wxpu}O98U1B^u=gT1BAT$&kDHXsU_fDn0FUcGQl5 z{0p?#;CqnV!5wgmy^J$4xI8Uy=S_pV{sUs01FpzwSX~{m;2YtIeGm{Jv87{7rC6;oufIJ3D&iY}BG14KFW#+v)`9 zQ8Kw~aX^_c1(b)-W5*pm(6WHm_&9AxHYtQI>I?J-Jox%@drS}1PC|(AZV^K}BvZs^g0dp$*vjNM z9ctEk>wEaBiIRqY|A4w|8o$`%Y?UL*U*{t#R{Y!P=}EZJTe&k*Icm`?WQ=BXs|gez ziw}NNLPm##u&V*#D-=Ff<6WBE5&~+0YC8LTGB4#TaHodwjD{(XvV3CqeD_b}`9~B- zUy}ABp#|%$VxBmD1SsTccK{3mf!r7N)>xtF}^@W+~xNa&Bx3Keu}6j#1q zUzk_^l+P2@gHl$^+Kkhnai}3GD98X|xC4T<39!gz%^TAHt(#e1e~|=5q2>8zSsN_5 z*~{}`Aq&8~eS8KG?%)9S(%(pVid*R$5%jD=STb5e0S}A^vScj`d%fCi_(& z$Wv8rR(-~Rg9a3!7OG4qOh1_jyQdcY$y{qy1}BaEi5~}{?MG*ehs-MT79koThWx#O zPlHg5B>ycUPfoKIl1h$u6C3By$DP!z-Zz;+qM{J?H&WIw7%cY8Kw2M@ur5wbpcRp7 zofYH=Wt?XU(4P%*9wkdH*+0UCy+VGF8lgD5!ID0NDi7ZF9qzB`hSdyOqY1ALac8Je zBUqGizvn=^5wYuRPnHqzonK<9wz+eJp}I+W--Cr{xyG1|^_c5?vQ^1Xe#?aSLc4v) z8i`HXF`B17+Ol5_D}AuEWo5}rtx`UFg&%;jDwWq=jV-5jJ#6P*qxF_9{nxzqL(fg~ zfD9&@3RcTuMI{>^l4pkj4vXGU2D>TP#AO6-Jg~P$SzTQMkQ>^yMvxK`5_&G?v@bQ__`Bmu7$_`iY^1%`&-Tjy-R)6(kxC7|8_2&jPa)Xb9fhcZI+hC69n zi#C5Aw?iVHRh~t$T-*G?Mdj=5b8yF&uvv z&mXb#PQ7tgI^(VkX1Tcp;LUW0ijvCkIshZm+-Sp@tqA~fZ+1uw_I9EnHF~Sh$T$bob zaPQVQZ<1)eye!-0-11tkyDnQZl7ts=5FEJeE}GpE4lVJLvcGmsHte)H;JKf3CSA4> zKSLmNAt)ZAY-ib4TX%TAoE_hT7;}zj9OfdhQVR&b&jM0^h_XpfZ4(FWqsRYaZZFl3+pu zJeS?l&vLFGSHXflJVf@yGK47MK#CxpB+f%5S*20@_W%tV5*wHZ27zr%e_4adPC4IEC%A0h z+&4bi2)dWdgW^%ai7PDSYD+ud~XY$k_9CdJJYL! z98QCOc3;>-ZP(7%e*4x5FQ{5Z^yY?~y| z=yYk9anrrAc@Bl&yQJoGtks&}=Ca3jFejA!aUWX!Yc3U&%2Z6p1B`c!V00?!_-=B* zijqbC3z9$p0{mj`lW3vi(%ZJO($DN(7u&t|^EQn!g!ArUy@jfGV-}ajld6@o;evbO zz{1^+t*O@yOX~akhjs%G@B|zzHqe5q7DK=l<;b(b5NF>^*t{IuZ6sVdyXoG>o++Op zU#3y_=il|03qnz!!gTky4Pc;4kYi~n89ydS4Lg}y)mCW?qSr6`xt3=mvvcW}{};o` z`me-`>cSPbs}1pNUN2+LOb|g;!I!OEH~AE%WHC+Lm*(e3(>CE-#O=EB&bFPTmEJbTAd_mFLIrZ=}cc)FqZ@T zdp~)_Jzm8xO}w!6|NaHL&AYc~x{cT=h+Qu=N?{_C;K3E%p_MCe?f9F^IjG&c@`#~* zUuAJxa1W_vq!NmAUgL81QfbP39}Xrj;9WMJ68Q(8Tgr}tkEOcYb?4cW+E=?wJ%woJ z+KUS&TWlFs5s7Rf`SFf)5ffH@R&^$|T2t9ocvTuP8iahw@Q}*ZocjtES2PXN&;AHS za6^-QR-7N~S9l(7jz=eQ`4^kM5dNF1qGVu*nwgoI>+7P6s=7}n6}9HejY~^HX&U zWFlxwtLC7w{>i}1txroc#dRF+hlpB}!Jv;qst=o{)lyS*%ZcY45+-R&v5>TrI~0H5 z|5bqSxwvx2S7MFF3vY&SZzQGVxQEi7Dyv4++X{R75mHNNDh(aN$89a@N%w z>L%od&;dIRH7byZ7O2SZd7Y^!C1VPH{!9_{xlz{DmGkoQ%05dWgR3&xxP8grBwUVBq*w~4D3nDX#_ z*_+yWO&RRgraBI!Kn6_Zf^iwO>!jPY%y;zwdbR{l&-d9HRjHUa7=!j)jZgHoG()SK zLdsoIEPLaGtZfG*^pj^$H)<{4Ob=8qPX7DT*G%^NSGXDb-8M|dO{blgNmOdT2+}C| zkK*%Q&y|u+VsCBcM&M80_v>wmN0dSzhIBPhC5g(Hg*I=O z=AA*jfCLX#->LhCN81^1uG;Zxf6m~mqMilL%`b6rcF{k3)ccp{7msl7-$6frda}pW z>G+yby?VfH!j#$ZSTvhYFn8BQ1E`A$xFBH*3mWjCs@mejB({{PWQu_0@3iYRZ{9E< zcVR&bSQoLev4PEm2n9A#It0A6$yr$=z<_WMd{IL~L;qD|Y9?k}{avqyhPFR)qYTh~ z!VRNB-8ho>^3Io4c9MCa7HD|EhoM0+v${z9M(`3SV)~o(Q+uz_R89o#wz2vw1NiZS z|Jp)`+?OskzRH$C$@sA|(e_{>fKV>;(VIB7T^3I_3f~u>jHk`+El4ZmdNeP>|2nKM z*jF2!r5R|XCLHc`x6S<21~&Y)UvPM7ryVT9_x#$jvm8Hivz7|7`)lPfkwGw+2RB z1>7Zx+1aCQr=4=Z3j(?=0I9S5`tKdUvH;*2LqN&bSiQRS@s99M#ZRqk4FDft^d^U! z^0GB}gE=`WEBcq;)G?9_NzcO1IwX0b1%kr|cKS3dcg`a+rm$t#YTP~?NB7;um97i_ zwahD&x&ahdn$s-A{8a``gB>LvPai5W{-Z3+?QUhK<8)@yFhOhnAgG}!Ot3P&!Sq0sdpuw2vpueN9`DXYt3(mi-ghy1`fXUbCLM8wsKURVBVf zf!C)?V4h`j+C;~wrv(cxY1Y8*%DK8Gz_dH^#E`)O-hD#o)wlC*6A|>NJbO7VcV*jz zc{;aP$@R2Oa{0OotG}{8XyUgKxYxD#bGYZ`Vpzh*zDU~f5#sI|45`1@B(~74|7FK+ zh~qaruoMdy^m~71YGr9pg9t_{d+^wHZ7` z5{T?Dq!86&>ArvYv@10Exy1)|%i#eEezC>s=W%pieA9buFnef4!!qIXqjHLn>vs+_GFys$*C#x%O zJ!H2kO#CW#r>ksFl3*Go2_Ga?%5|_x6+j(ex~mh7F-%bGn0mvnL7Q&ieL!7bU%x+J zLkgtefcXH+1HJ-B6u8B}4dysIR(ZBBsCPNbrS%i%30(-BSnjzmRNWbYz5kId=EQtF zGPjym9w-~VN109ZEk*f*VP))eZ}QT-6Oh4Wk%xB)eK6OQs_qiKdvU8CVCi|`_%^tC zslfhwsuWNcs6RyKp0WS0HRxZ4HXG)3A?|$|LWW-^Q5oNs!Q$s_X>n2K2-`;@x*XK0 z%z`(wXfV@uSUS+GT+#lhBVNdT9w}b| zarg=#uU}tWl$>INE%EJyx(W-c)q8#qfhsh_W@sen7>?1Y6e*o6n0q=amT>#;G^l_A z1D-OhHzEra8$|EFZg+G?=aHUS*$Zvn?;p8;6t+5|dKX86{$kCJ zhFdVYEb0T;*ap6~AOlpL4oE2z3>A9oKC05B)c`W~@<9Wy?{E<@6l8;$Mr=c%OBDv% z>td<+GT`+AQh+89U;&aBBBG*9!1~>i(o&U!>g|e}8j8`pe*ZoHBqo`sx^>dwTJr+c ze{41}5lNnEQ&#ah4$K~`ZP}kK=}}R963Au5mWV7Zw-oaXZaE*bZk_fD`0?|3JFh!y z@hlWKi1G|xImME~9AE!D)*c*R^n2Zu*2C#NNZNY1hksv2CR|T~mAr1(y2)BP!#Og9 z)C`z?@l7Cw`(<|eL!DJrHF9M*Z$+vpKOFb$>GIK(9nD6V(`ffv zz>B0FxUk_Dy(Vu}*YYFYk96jqu30QRo0k0iWu{$HFJ~J@*cBZy2Akg7xGp~8#UF@; zqH3tolcbp^RjT3Az5Wt9vEYVzn7Z6te|8|nwrs*CF<3BXqw>$y%GGr>+rg*1Bj?V zaS#Zo38v**^HCf?y+!t$kaKV(bJ@F%(Abn-By8ZcwL zIlMt;_sl~2;fkrh!u7D!GrJ)Y-7V1^P82P3XQ2tHh( z+6i8NUn3t#Vkz+;7Z)_wO0bsyj0=b|9V16((Hq)DZo*98my^EX%q{#S-*?S1CqeQz6Rb%bF@L*4C*@N&g7e z(?+wHfNJDDctbOlAzfOTWTVMtm=+L8yKk)8OA}*5`OPB7d;9VaUZ$Wkx2kiQ^2}#( zGNOXv`4B3(U%~difce!=ZV*Oj8UGL2BNJv2HDz4vgF&s8Z>K--G z{=BG$;vPL1`yq#PSS$mRoEsHs%BT7Dyi~eW-BqW=+vc)8!q(xiKuotwjA}c@Uv~SS zr7y~N15|8V3X`8FvC>vYL7EgGfzBYYCVxtz`&XOsMip5&g~>NANd(9^BY4u>jIlr& z%et-bRDB)c5=Xd(@tf#=sGF4D%9%tg9az+FvxGw=Y#B$8!F3;Jrqt>P!M4fb`C^W& z+P;3%pOUYeF8^eKCga_O%df*d*&n^^mhpRyq5>GU`HD*AN3PL|$CYnm;L8rk+%M;f7#!^RbwXsCXrnqZ9drlHE5_OR?T z=dyng{HtE!hVW4Fs`M7o%yhL_*J#^2AIM zX6iq-1PdNgVdDBvCsRrUGrAPShrC-Nos=Jj4pZT4?;O~+VuNW99u3goU8;OPe7@hQ z2vs;gXN9m@Y@S=XXF55hH^sfF$j>vydq}S-f2LhE~U?9MEM8|-(Z{TOY&VuQ6 zSZLeGYC*pgk0t-CvwS($A#>38DKY!!I(8j;xelxxX<*AYDQLRFk4T*q_1+Pu_~)fd z&tg>2H*#Stf7K@COI4z#r)o6+dQ+Iip703KJa%ksD#o* zL_(0k4*1Lp37NUn- zIKuUZC6NTwR`4r|(?dSDKWnXt+=G(^Aei9Oyz-&$S=t&=FwT zuAqffU6C65B|_do&ghi>xW5ggAk&Y)^Fu<7^GeVNVC2fPA!tXw6ZL=BJ#SfzZb(iI^0pwQ6@ag)X#(wWT95>hB_3898F=tKT{LEP)Y(sm;XQdSl%jY@Hn zROBNB`}rh3r%~FsIP+XMs6ZWVIZ`I|Bw{CCuX+cx3 zU|G$~TWUbH#qpI^I7gZGxjjDmtHOgq_ip-q6=_7*pO4?2 zEc+}E_tYg#RZ4me(KgXhWbCwchQp}a*mW9X^44N!NFG=1-ftw37a5Ab+@pWZ_Th8Q zhxVHYo#_s*fm_ax*+Bn0_`X?uj0{%4&clEo4Vzeu2d{6d)6*1Vm2hs zq~zih1kYtS_BZ-IY>TxEJ+j7hZp&VTAxV|q@2G1df`mm%1$`#AANM?~U_xm+Sp(T8|OcpZfIFC$UQ86RT$bUMUt!wbuIR>K403AykZ;=}@xQy% z@65R*(rpds4`Hm~FRQi>od6k6EMbzBqPEJaKg;4~NT#lYxN`gC84>dh<9T-|n9fg) zn`dIL_D}Jki2VJJa-$Bi%`w6&$s?7huCMy*q8mlh;XbD5E437xMD0RBc;y6@t1CB2 zSWydh5yWnICsapnjdt1(*kGtRT5C^4He@l`=f!1viL#Af_Q7MqQS3PM26-J57at9G zo&#P|oznFc@}0gB%LcPx;^#G4W&*B$%~~w?291yNFt# z>^+-v+v1K}9t0Q~4<4QlkqWU-kI}-BNrkEQmzld-BW7UYgQr5qK1?(5izQzvf9bc! zWmM+>QFYbvbiRLoy1Tn`V!D~(=$h{7n3!&+n_;?!O$@_ych_{c>26cc<@@{V@$!ck z$2sRd_jP@)PrTzH%m-d+@Vl8QjF>O3#e{1eMJaZTY4vezuzl~NfCPMNbZ-3K2Le_cH>Hkl*9KN}oF?PEJljURy%Ii7M5rLvLF4 zvx1tZC}TS3*`xBf!S=X+$G_9iuPzy`9u6n0(uituldcHq>E<|&{CKx<$5719PUrt1 z`5PWp^zblhH9dVJ%l7X2bt49al;SwL$pj}Mw>fc3U-a`dta8~j=fgu(KRJKuG(}4v zU-^hQav04C10t+2`M(@dI?m}cSiI$5phPmj%@QA(lW(kAON(LgcSq>EVsC=*8c~17SqJv zTFq#pvWuyi=>udfv%5sP?oXu>qiWm;#C0qnax96cHv49XJksft!!*(pxCBvKE+)i+ zrHaCo6JO^`wAlS_*Acv~4+lO&!64?EOoqA^^_bRk{6G!<*cyDt*Kwdm4TIxV_R$UNW#?ONIul6%!1`cF(Ph_&!hdo|f_MZ5fxU>C>{xTUM$mV1_ za0KB_6UVgn+#%~!VnFh5h8s7b!bLtis#{G+N$E@F$iMSDf`}SbhXYeL>LvvjAHtUH z_6NzLdFhIon88x}W5 zYqF3M>vbvE1>4Jq{mkqPs_qzHXChNWNHiMgC??NkbZ(vREL;;9zjcQ~N^5#dwB7 zs!;BdcG+|-&P22y43ptK&9o5m!V@~K5-B(&J4@Z`J*jOOP6BCr50NX*{e=+aIGJl? zY}1KabR>GN@u6EW;hhn(V`rcF64Z}d8FUP*kOnm&y))$IqEa8Pi1RF(Qaz%1|0Me!-5-HZLO-UG$xtRIM%Q4>(XuU zgbDH@b>b1Ibpo~O7|~7o3Ckr((yQsOrA9-wc&qPEA34>TdGd0;C(6LdW7St@~u9j&p}E-`{*B@Cg=JfaILv zEFKYD-Z9?VGbJt7ei8YE*wRNn|NX)$zSFq*S`*q3gIxN+QLMF-{pBDV7K>0o_u^Bnr!k1Ts*Hk5^; zl~_9A6v*_@Gez8rJt7%X6q;zbaEI<%q59sN5ORNsJSZ<*uk0&sTk}DM(1^dl|LH=QV|@K=pIDAdgx8-}bh#H!>I^zk)Wdzf== z8_NQR=71ux%f7xHw@MJJi^8SRDlQR2(CK>+Sb?{W157(O7xeD1KIV`d7d&i3sm^FemBP6NiNj6X4J zq;KNU5umv>G~W1a^LX27nh|NqbAgm`*!`qMR(I^d4f=hh9m>$EADWRfPK1Cbi?ppmD>?j*zZyAH_p`d&n{fbUCyJul=P}_tgYx% z(B_)O$dx!BHq*DWhdIrRS-hG|hay^-0>OgXh%3_VJS;)o-0 zKSk4E-mtF^I3Fsf)HVniobYpN^tZ%%N-)r-SEgSd6)JvS6xl6M%55!<*D~}$H&y2r zs$M2aOBzg$#OE}PS#pG6{PJ~1BgjtZAdEqujpOMyoFRG4w)aSqm3S!iX@-lLYh$EQ zS?}b{7qVFlAiD?smX|oTZg1C%OD5rIhbA#9K%nP@EcJ1J?ss$KHZh+nFPTkQqzdIQ z#)UiQd8fVE-Q{;}r#D%m$VrNEEQ7&8RPWf>#lsM!6=|*;iT5*x=C0=5ZMEw$%aq~_ zKSCUMO$QXH>1nd;C8V)4(*4>Sj+CcIp@pwCNfo_c_YQM7rsmll-r5tTwOoL6DBon! z0u$El*Hd=jU!zIWT|{jA`Ya3>>XBarl@B*~Y?+UUf?ez+^_MRr4rp0pOUuZdg z&G_wMumo{>#-!=;J@ntd!Oe!}PR@j{i#+YEnK%kqY;;?eIQWySTGF4r{b`_~^Dj2~ zl`FAXe$y05x-{Ht$Vi2{n38_|>@H7(#%}*EgFJ+dkJz=8+#nn6g?qktt&SZ5+x&9+ z66NzmQEZ=>Y$hywWI~NLH;C!@nyMB{eXkh)85gDVPm^j{O=0UcC{=;_pEv@2#U@?* zYjK(09cNRAvck$zT;(bOBcgG}` zV!p_Cn>W9()x58_2g9;EF~0Khj^7STu=wxk1SSNFF7$4ZICODZ5vJI&B7EzcLSgc_ z<4A74rp766KzdXiMtn~DJS*?40f*K!G*@W@SJM>9?>ch@INob6{~4`=afAUtJOU)e z1His4xlF!{eO^HD0PlCZm8kp@o)=)d_gv2F+8$%NyCsQ9NSGKI*MN}e77!1VDw&>W z^Y>@LAEH`SV}ewI?I@8Q%F3BI>l*Xl$^Nm)tCs~CTN9%gRynE^s_*T_9*A)Ac zW4Ou~1}vNL?Z4Oy%~utR@N)9;bV!al!6nXnocm8#?3c@4AJTbHRtxM1&o5-_50s*< z_cG(N#apNnciNaX7F2aIXSkqRAU(rc{t@p~6!qi}9)`xs$dp4V-NIFIIbRR!_^nFOW;n2>{MeVB%wcCti z%S-brl|qYe6ZxxaR_1u^-2lU(UY`vc`&#oi;E}5t8I1v^ zxHNz*@hyFVNilFE0^(ccvr%$COxiy~9j!2sqHliU2jH5*$>MOtc-*ecNUvl*AtYJO zlPTWBUz2%4$xbt$5o-Mqec54y&}{13|M3}bJjPS0NC#}4Q8r|Gr1*5jp3OHm9via_ zWr^Fn*xw)Nv;BN_!}w>|^5Bn9hCML5y0%rtJyp%47kM5-Qj z+&~@$GZMcNA)_K#Kb2$6s;FyuA{yL&Y!Z2!xOjE^Vwq8{`0*jgXnQcsL(4m;h-6(p z;-t4pLXz^aAycvHtw(X_3NYA2qHD7CNNlF^EjjP0hSx(no zOx}L~?Zi5xAD0yVxdL<~Ev)cSLrTr0)N~>^E%OR$=5Tb1BUAq#2o2B=^CA~6g@2Ly zAghJObtWf-%9TmZDB}xW`OD!-0rr1++mm1-gR;H5a}HomY8m;uHXY&M;EW$!y$FBK z9l3!R95S}E33hg`eO;CPvf$Ihj@ZTJq;me^B+Q4pMbG`>!cY99K>jS3^>8{V{)^7c zVK=+%_kEuFE^Pl{1xf>dR1J|ILmh@_kL;1d586%?-_Zl02q>;{SrX}09jB)e6S_Zc z{?u-0NTQc!jf?Ybwp*ZjTTuXi^6!dv)n9BQJJtJJ5v-Oj4C^GaW3pc)V{vh!s1#kg zjDm3DUm;?~Qs)G-@m!544;W*3i#eI!yW>Bl0uvOp6I73|R235<>R)?}H+~?l6Igz6Y7f@KfdZ}5D#61__-Nm zv_7ofg^RRi>iOP%O#=7Bq7Np~A(NXd zm%K!`^|c|Dwf#Yzo_8$`r-2`>t%^2&587!S8wS?rawaC!fD|CLcmM|n=h3nPV}Ct9J}!JU zrS<@{0Ra#Z&;`RO5mAY-&%^|eswZFMdqJuVkdaWBI5=G}7{agGQP`W`QexiPu^q!g z=n$LJb7DkrAq>c{bZN&0rqvEcTzs5UOuXz90fsf?7QSwOa^<8u-&kQ5oNZ!0irc24 zORf^*AL_Q(_?G{0SX#NmI-V{Wzd4k>4DlYa^c8Yn8!nO7{5T;0gpya3|NT<-vXPKk zFKFrf7~YMqsgjsm8%c+Bi%gcD5o~H5(fY(L+z! zlvrKBE|-&4%*TS7;`C)A0Q2M*S(}JwTTl>tA&OBpZ zN1#01aN2&5m+#N}11ih6!j4Q9CX@M})CD6}RwTc5M)2Ox2~A)5ntpmxiD{)w?l2v- zLGdMv`1ckwhEin{BcXODml~39A?L##mo!tkxWm9Q)c=S6&8wCeU3}CdI1>sd*Z7LL zi49#%P2Nuux}=FF!z?x4K*lUtxGWMa`_&3hh(a94y*sH6n zB@j=62xLNfI&!=BNlPD4-d{{`JeVyn1n@|}2U6A1k+rvHyVxEv1itVQ>dMkMCv`U_s&>U3kuNA78BUhG5d?xlvC-iG z5=Eiq*-?(~*JhZf15L<-Li108GFKuW4oj5RtE;k4_39AC6Nt%-XoHY!!C8zQ_mhK= zD$WE)PD1dfFZ6vhxr$Mpf^;bN37m)Wa^p9VJ#ON4H2Bi`S@?ZNA$XasZV1o)@_+Nb z%j6flG9#%TF1Grh+LA>bm!ikDIEFXIbLji&O8~6ucH}K1`H12>R>VxKD!x*I_M*S7 znY4yW%WoTIl`$sIir3!X9_?~N#nU2*W6H?Ld^lce04nuP0DSuDOLB5A$bFV;?I+efCXuB-bS=gc5uJ&wwaC-F zjz9AkVGA35Q0W_3%V_iLu-B>|f;)Zt z7^7Xb_u%QIY%fql635QAb#UaWu$K|6i<`zv8R9*5IP$mrvHKAPg|XBX+jPQY{G*l2 zy{C&KRV6*^4!X?W31#Xi=iZ~`QtRxNj5fX>QuZvliqK_aUVUer}Z7s~klI6yd zC3bjlqH5_=re)@(I_Jx=HlDh)4~91OiscXQ_|Ayw?1+&GB(i)oV%sXx=5+4DYRS{3Ps;Y35@lgerAX~LRf%76v#y?6#oBm90^$yIb>`N_&}_W7p+{>L8*rtpT-y4M!Va6Klxs; zGH0N5waeTzkfKhU9y$}kqkXIKPJL?!!#2xHE5OOMY&C4ZI9sIQIKY(VQ{Rg1PC5Kq z3zdqsH+-aMJ&$Uyxz2)b68<}9tG94SjS{U+CqaUTzam@|ua0*r{*nr(f?1csh z8HU@*?XJ-@R;1?lPp{%J_4Izmb8W^gL+R$KsX)ge_d)J#teD~yj`TQ5AkRi-u@D5N z*RN@Ck>l&){{D+ctf~I<@2sy|-;*v-E`$_W^`@WZw$6dU^jJTyO8xe68u$2CD6go? zY8a9qVecM2tWATYD#)u*&FOr*((#@zcI@x|xDLybc9*NeubyveSctOWfo~~U95?I3 zJwV>6eL0+8pOGQ6Hy@Uq&TNYmR$Dx~k9J8elFfr6O6|R-QhF9f5DRmy%tKWq3Ef0S z=tUuX#=5T&tm`Y8vxw%eio!~9C^_{Lw8=s})tQZ(D8%XVVvjtsOa2_0%O(cxcs+Ur zWj`2;x!W(w#ZQiToS{WVB@B!-A*iY+>@lDK${4B0`YWGjmdi|N*G1~CJa_}gt;g#% zz_SC32yiNf(|Mw*t7ApR1@Zln^1}a?_Um8G++9?%>q{1{&VWlLr zr4rbCfQijjMf%=HSZs4v>n(zr;9=`X*lk(SJMiTueg=4o8dNqi7GS8ERDwmVYD@`YCy`#B3C2>mnxGRLv%I&+_K>dsnL3(z+;4fT>T598{dVFDuDHW=#XeWD-@|t+p ze1eN@dzQoHG8Fp3dXYD|#>10FEa+)pUb|@zVzNn(gTu9<<*6L98AnZtEUhe0q+S-# zq1DmIw^n^0`*TMk*&JkxqB(IuzBw9*8Ri~+PV%fT#!8xw6r{&ayeZWoDPAIOon;vK zAj2SZuuYTG<+s{a??XUQTDH}b7L@jf=C_XeIx=()oTYx-DTA%!@u#w8)gzwjMI%3XEBe?9B^x&eZaI8b4ag?d$|;k%^8 zcQ@Vl5xb^m21^2CcM};3Y{#4c6am8g86j%;w1J@k!aI2USh5I^yVfI9ZB+pe7)Yx{ifWB4+yi+Y5e21uP&`+$%2T_{|UtI82ND z9~?PXZ`qS4$G%+!c}^|4Q&>Mg@%`+Yn|p|s!|jjec|WlR-HwBJF%|-+aFo(503(>( zH9#aBjihDR_nU7A8~K*V>l!Ys#kq9O)dV4wzDG|Yyh zLusGZpJ+jd@*EN6b{?2YoWGR{tZ2fcDqiDb!#{qtAb`mPA^e&>f|R(8Oz2_!0_ z0WwMS&sUt-cz`Oj%=V-QB*voQn+=Dym~(md{Iee^GWCzV@5y~WFZC*$AMF)l z31TA4|K6Y1H%C|tGl-euZx(hw`GwWo`G<~ zR&(2uv`yR!f-Q}wM6ljtDumWlfColmmH#km=oGJpI-NTlo~HxC-l2=Jpzu0SiFOK8 z$Gj-78f-3_FmqC)B&s4f+sSJtg*h5d^OaXOcp8&K-z}fog}u@+S{hS{9n5VWD*4`! zM?-Qlgpg}1C&;%}MH1>MS>tkgs7SBhtKDOfkFiOrMro3C9Y@ZF$B>0h3!c_zhjpGw zVPS?SN&I+E<~kP3OBd4JbW-qd%wK%9iXpjJO~!v+?5mL<94kQW4G*VD3y0Q^sU78w z@SqM>#FO}6a@>497E$?FbGvLGE_BNqvdS)f-PmAuEz!M2x{p{j$c>6^Q*8s;;41$opq^Y1EZlm3>8PW-@zz^0t_wQ}u}c~@wZe4bx|sS=>B zz)O1Wu^R3$8x}a;`T5C56;^UxOX@u2lTQ`NSfk*^uL0={FQ378n+^>`b`Zgus^@@K zX$%)nPyRODKvKkZEZ8l}K&l7}i?}}^r0rTYw&}6 zXH+fr4=qQxHr$mbYd#^HtY#|7)hN39)D=YJ=o&n5v4>bkjQ6=@h2@P>Y?aUp z$rERlI@WW9L$?gW z+rMCQpB_+23^DZFx>bK>*<7Cc%-iFm&h-vG>?+gQD(i;oX~V$ky&84a3K^r&v^BM2 zVD?+Y6fv1$j{0^lQuGO9pT>rc?XPD(2Lbz(^)wO@tkJtAbP zed`PWzpk}zuw!clIIhIaGW}#HtDEKg9~_t)Voe9P6~Ex}<;+s2hZ996`Fm{Wb@L0D z5M$7c#+{@>_f)iw3X*av*Wv0 z2&kqB=4pQ&Mt)S*#!ft35G7`h{*ag~`gm1gqlHtJO-bK^|FItKu&EeNY*an zvjlP*IrQADmfs_iEH zjd30BCv82W6m2_8O~+CW_7n+vk7wS8x-ALOQ4Z#tH=+qc8Mjuxbn}k%PMagqhvZ|| zBL^Psei+S%f%a&Zy{_=R7vbjhmp42%l96vY3~!T9`N#kV=f2@Y5Qxgb zkxwXmD>A%&9LYW-3xH?HZOnn1A+9F$1s$^Cy_@$t^qwuj#KbB(Ww-8!l-uVtS|6Z_ zZi3x)o5iDbc{Wsor~Dqw$VpnY+o!}4*`P3zmru%|JdvwE^%+srs} z6|OO3a4>R4rdZ`pkgQ6MbHjK1p$}c=r~ODB-AttMjzWE=e`J-L1?eVT_nj+1ogA)z zNPh6O@?m<-8bgrSZ%tB;O0N@%ck zMp~`m$H4reysw)^Xekiv@sN}}!-GnEN{>Gj(87k0|8w8}_CX`lX)pybeD_LL?s${- z9oQDDur)`wtGGUkbu?jg>Xnr;@J+mYZ!XckWAMkr?YMWQ zV^`T08rjORCAl}sYU>lWFN0FSeK^M}RE0PT4M!TU8kszeI9*&1V{4A`uRR=R)dYJ~ zN3T8#&Mjz>!2LA%9L-#?`N$E#i4YE*ehY(wIDw}7OF0e!U6f(RlDd(3EwNMmAGH3L zIZ*}8?W?Muzw5a{^=HW`ZGp>`0pc*Loo4&>;stW%^q5xdJ7u)IZVF*HYWy53Aeeve zd0cic+T2v_>!nUt2q&|mhMm(npq}||?p+qNTlnqa1iT&a;7*Uc*V|lM9K<=`VLC%-*{Z`)C;K zKeR4DeHA^_@lhql`oqJ@Q=Cz|H_yl|Z3L6!Q3g`m#|W2`xabwJG1|#L6~Uk8q+PEN zz84rqj;BhwUJ2VA;D1Sc^p}*z5Z(3Ob2-~}k=;cjb9d6@cl_@%q@0>mrKD*Pg2L%> z0t;Z?t({hR=&=@Fhwm(sUFmZP?u1j7rsusA^*yJW@VYWSP1_+qz1ZL>VR~2>8oeK~ zb-DkFS-2RzRj704r-gI!sNNA0q~_`3CFaFIvEbCxRGD4D_1ROQ&(gkrc812gix7dq z)gwn@oDlI0qCvw~z<^n@?CouZ24|e1K?6=N18QUj3Q(tJRu^sJoc>yUTU1#oLDw9^ zCRXQL5&uEk#8Z_9KPb8CAH_=S51%23sQ0zwZv<3}jT*lE`7zPvXG1CW#i`lUI6~+{C#xT9KCx;Y9|5+__4=(F z-*)@}*saY!eW8$-L?05ORp}BOW^l@n<_68AqHx@`O?pMu5FZn|4TP90tku2W86zZo zd6N_zTIvOhSXdF_aPBRD$g^j>F8^ z;SZPyUebTPjot_J_#zjKEs!`%08aR}a5AuHT35BZ8i$pUJx~;duCNvdoY?}LFNiX& zO;xr7%-J5w6*Xka)bXs+oto692eIQE4Caw$|e+W~A-hcYa&-I8v~z(CkC znIwHmHs-b%Z@E8wG;G0&tIO=|bto=-{JOAkhZq7@^ur@#rpHUg zFkwP05ZK-J6!KPzu5wo2cMdOH47Xt6n2u+m{ky2_lAo75HfxyDd#w`|v+*{H{J#E5 zh5B>B;Kx|9%RB9d4X*x#?x~EEw~ zCWS(*s>&pDk|ZiRqMWViuFI)o!VAPBuufjLq%XI=@_+qvV=36`xL>ZbRCAmZ^~^3Y zh(g!EM|ZY!^GA*Ff=EnM0>)uB`}ytM-npaJj3LzG@%h=wx!3WaQbAmUsX$tgF}GPl zyl7PPrOxd`4L@l{#o}y$Gf8oAO0Ft}(FG)A6{vRNDj9-a6$<;|p90{iH2aHKj-SPp zi%fLh(4aav?(YX$!<2%~`UjX0EO&eda9LFrTsQC}&gGNa|&+ezNAGO<}iE=|p#g6)E#L{mQ zJZqa{=X*4054fY3%O%~jmL-}sB^;b3wZVDVIAv-ozk~@p zzl403eMT1|7OxSkp8TH`pOq<^at;F|_%|{9C0mTlQ~Fj0EyqMUjsr(Bo%=+(3&?WP z)}NdZ7R+!uMHORW*@v)dm`f9@XaurFK>cMknYR#uAPKt_e{AiuytNhuxraK*#e;iF zP3xaW8edXT8NDfEdw0IFt_!6DW-oSXhy+u0zlSz=a_x();u0T+pBOhBo$^cH;x=-x zf52?(Q*>6uYnmyw0BFjAeX;P#@+P9BMqj`0fZ{op>l+*mKX3HwRNT!+%#iLQRL-lXJ5#^J!mWSGHEP%e4xG|Q8xa@e!>taOXt zy7Dv~b(~lfJ1FxovC{j`GNU6RsPDCE{k8Z#K{b#ozhPrzwmEC0QsYa)%A)}ox!Av* zKskShn(j3UwNd%9T}3Y#2O5^sd?ufbt05G1&SxnCh&;F9qFmH6PBJecNg{{vayd5B z2Z0gDg`0{C?7_3vnbCeS?99DULxLW=99}zsAcWYnH~B&xujoXG;1(Et#ZtvY98%f8 za(45%Jgb)sS!DvT#D%FDT}Us*cLa=I$1_;B&_qIvca#{YxDXOq6SUoUc_wtVf*^%` znC7`wBg}rOH*77^ah4BBv$^|k+hnEVVT=!rzUCcRNM&FR{D3_WhW(k@tvqmY>S$PM z)n1@_*5)UmJtd8fQVbU`aHdIrk|ta~xw(07!H@<|2923)-fWDt%rPvhx+@4a_#g9A z!NXzyL!+r=T#l{Mi(L^fVZkb*@@4V68Aa}6wjXX<9I>H-X0i}SEdSWh8=GDQfFb!? zA0<8XKuzoZB~{)FVv!}!8B6!qYBE38#!XH;R6qZ?Vgr~RIFHP{mlUU5{MJK+p8sy` ze2QPGGJV&6NiV4Y9}fHoQvZ-NrpjHx4~gY}u3knK=4qZ6+m()+kd2LvQ~_stBqXHf zvtAM)n(~S=lDcl$jU*z%yGebv+-U6K?gH3}y_j7A2j4$HO&qAw0O4$X&%a4qe_fZN zq;n52(ZkN>^#1I-yfie&<4O=cmSKJU6lw|c5)&fEhAq~%s=^h=x=)yFUL*_;jrEB)3 zr{B5h;I2`28vrXQxtu52l%i@rS-Nq0dY_X)e{zOE@7ObLmkTj_R!bA>N=f2g=TE}Y zlA_ZLalY910b{^zFlgB>F3jElV{A(>Km&SU=MNWSrYnU4rM0!mFAi!M4y|cfLhk2N zYRWG$)>_gDZrrsF%URWZsxqD&+sGK_nq9t9Y1BR= zZbfyYhL{*yyjGy)GQwsx5&Mhz{-gN*gLrR`Hb3i3=M~)F`Yz*@qnsh)ri}TX>4*IK z$l3F%#e`iVc(UictbtP2A6Ak9VNHd(|B`-I;wmZq55qxV;DYrEaEKK%c%i#PB|$rS+C{Q|;j^TX0ZitFo& z#Cweh9|ROYLJtoQS8wI;lxP9DOnU?UM9y0nspi(|u|E7m8HsrQJMNzxXo{VGgz#N6 zbL}9_ZBt4Y2tpCWiJ_ib>qi>sy>Ch){8ZuDmehx*Zg)jWP_(rjcJ4J95=hhFRxuGB z4ZMVZsSqa0g&5)znQ_Gbn0@~j1M{nS_5EdWTsZHAjUTU;9TSyO{`Q!_K$sw02%Ajd z&Rfubyah5cU1fDE3z`SVKo6nP1P(*wQl-g`i;Ig$*y9brH}L3|B?B_`;t>Q_s@i-1 z>dH$*G^gASOgSqSk8XWLg~^qhyu9E-j7Xl@gjx;UY+mdB}85<88pJS zP^k9fj`P#LPoA!}3i!-#t(E0EB zzSk~I_6Rb0JSKoN-fca-q@&$IQJZw;<;SZ^R&@RT)_2kx8hD`s?N~besF0(@y2(F( z+I1|m;M&0!>ju(Q3z{f7{!aoxuzSgAh_(6txC!8isyr_&*xDYP0MaaxUU4G;ft*rM zFul21*a?F}n~@Op$zrzf+p7=6@$bG3N?kl7gD!3?vs_e>mXs!;4U_;a4I(si3I0Kv zfMkqvUrR3e$qYm|g9MYCI`j!?_csgWP`zIfB!f7_+=M>NMUr+R2_1`Jx$UUbOx*YN zrYXyVfKXZmi`o$e^}uOgG^|q8Q7Yb0zZI9m&6sg-ufdX@eh-ew#5m*Ye{70{zup4- zKhXz2v4FRq>+9=lXB`^%=o{TnR<0k{I)Yv_a$W)9N?P##frLpExro=RH$bor=$im# z=V(F>Xw_nTReo`mH14_4q5P{vgL+MMYI#mXN%C0OFY_fZc{=h_sp@$U>DG5>-`dzMUE1^4S&p_=IB6LZ`=@}55e!KDVqYG-kY%?N z*_1HH;+b z-=5S)PBQQQgi{U#>-pYB>YdqIwf|v&KbRs_SM~O-f5$4i^*MO(Xa4$M#ogZ^i}XEB zszrS$sgHct;B%I;VcF@Ho*bLtU|6;sI=F4?MDAS}Zk`oO`7RM$^W<=E|MO8?l<-d_ zM<+7Y3ZUErAKVmuCt1s!!6(wm_wPN`wN!4=u&!*TIJ~N@EnIZ)tBWnF_f!f0^_mKm zlQ?Q!QK=jioYj=wzRt9}FShgIj(|wEAYT-zFMYJJe)@oraNk~WNPt$>th3x*)xufN z$M>Ua0}2yJQ+%AFLVn}Atn{QeJqOH{EJtO-Ey5P1EBoBPRrViUq01s0`}GL+7~Xf#ZUvfj<^%6RMyzGW=z`_5e## zzO(bm+=c}sY3kE#EHdWUQZzY`2f{w`}?9T9rE-wt^4`k z-&5DbAidg@N#Z5F4lDIyewvv<@0t-<+Pyh;&GqjPEKIl%99-3M$e&`&DFh7Vd-)^E z&z>$eda&N+QBllBpGmmT#r%E!eG-O&Vd7`nNkT-`t!tVAFkq*Dn&k_tapS`{{Apb` zHeKxa)f>h~d(lZ@w(!$=K4N%aCsOzp+WP#|=Sm~tz{bZub#%FEpZ5x&6Pg-I`%Q7> z;|&0}A`WsI@d?E4KPoWC_j!N6ux}$J-%TEbgoVwhnEjnxHF?Q*S!}J$GbR^nm4C^C z7pfdy(da2jLi6Yxl>1(i`@dK6uL}YtcBR7-lo*P-AV6o*Y(*0_hpN(@E9e;L*_fMTCbe3?R7I7X9@F3gK-!ivk*u(R zVP6PI)LpK|K!gh&t{61%bPXh?f>9+OSAIG87n8Z>Vfl`w1|2d{B0|gF zyOiJ4Ml|4|u~<8OP>D#@pspn5;UMN25JdAz3KycKLV3k0q6lw4EVFhZrQyQ2mzsth zhA6y16GN2Y=BOqv5O*a*V^|{25V@3))(&E(u>Sk5;Sst_V$=1wG^ER@eUGb@&VdNf zK|^Ms+$R*Ny|}P4s6ZJjfM>zs;y{z7ojUAlDL|KKtA}}~hw1oP`OinDD|o7~t9m>ym!9 zXs6BBx!y*e=>}heqfl8>@8WH!_)wHD=UweUM-(2KenoC-8k6I&Toj!|d35( zIay(^E+Gj{Pu@SxY_{^vpalzXg=b-x9urp!LC5)@&j0s*q6hPOoOIzASWuqGSc~Ah z0V>-=CP>OsJ5Lnz`w~ezgRFi6sr(Q2cIUrWU%ehsD+qO__c#98nSZyc*;R*LsTjW6 zJBzMP)0x=)U~dis!wSjXjgM8#&r4zAH2U64v8BA|u10})u2MHuI~}H+@F}<=#ILsLSnx<@Np~|B%P&=;n=mN zE|l8zr}Em->*7AvC7Y-hdlVX>H$X&URZ=~9Mtmmz$QAd+$$d7ym@SqtsGRZT5I;_(}?_csP6Zu2TB!<23PT$cQ_u((#fi z=*b8Obqtc}$jbEFy5DIZd^&S;_$Qt!p;s)BITDa!7&-1X? z9wbS27#7|XNhb3M)#rZ8v_9jtACj}RtoN{qhLdT>LRLFGyU>w%J$C$SGr!RydpE7) zLEQ7DsNVZ6C|c~W-nxW4DlhDJv5n`a_Y#Y5vnGaY*Am5-Ps8Z)Zqg{VNf1rmM};Zc zl)&-}QKeVm$lKzFBv^-!262tM}tldRlnVFuuT^3NaEMu3sOaWM!7eEA_iT$&$E-Lo19B zo%s#hlKP+52p&b>lXd;SFdKa#aGRxZ{ZJthsWkNZPDgk`B*w=7OB65OMVOsASfY3C z<*3{0+@Jblkpug9b>HRUfNEQO-8~ld(B=O0m_y{DIX7v%w&j{3hp*acej8iqvG|K# zU5eT|+N+9+)+8_nXmI<;#PjruNs#pde#;4zOBn`+E5GdD9j{qY)=+*j%!_fq-{Ew4 zOAbluH_wn~yuMA%$muz^U~oSv6>h)MTJQevbXgUM383WSOZjAJ43{%gr*e!|tzp(; zG&xLx;%2H=k{VAc5y8-&b`W_h?T80nFYnbVuTX=d-pjvLG#;1)Jn6X`L49je` z;^3MDL`FOL^>9a3Qrif$&I(H_|Q*HK6zpq z_?T600gsZ#_yByECgGICKGGLb$plJ@oBvKZ5G6 z`u~akag5FEVnV~OnS(Sg4yp79TrIUejLs83;ZJ_%U} z@-}np*N%R9F-RP$wvJx1<2Hn5Jdb(KIB@mUJh2KvvHpiYOK4 z-rqjy(E#h)Y}XaFlK4&yLao!gX_>r>7y?FZX>e$6ZjCM62weaGu4)sno|PPcPm@C5 z$GKo3y8kc~(XQvv5~5qSogmM)cc?a>WBSjW`87ya*up_)*>v|d#)u&@Ix1qLglhRL zlyvrLO)z-sJ+$_E1-rF;t z!1*Cs&zIU+4!o9t=rkFGJ#x3lNX+#oKJ57jJopA1brbXzKuM+=wT3L~^M1B-Uy~0E zzKw~LqGC1*E+yYzrqfX9uh+jMAt2vT{H@gWY);(8YlF%il;VUuHn=Y+e!W`Tj*}ZTUERUwryH^NYH9)DD$O@5-ahw7 zb-=6#nD?e}n8IU--ckZMIPhM5<8yn8!y*3T+qX}ol|MC>GUMAXB}w9vXHHYH@Dw7e z&OP(9QkaSBMMYYxlq==`fKm<-8f586dwY8ipm8#s#uWyPX+BLV@Kjg8GK zpb5{wz_7#DEC=^e++B04!9b< zG!p7oygn{|jERZC#lzz;?hJNYY}981thR>JPLzhVKoo7@@C&N#A@;UR1agnhg*krd z!0-FLfx&WZT-W*|~n)D{w1rxuj5V&*Po3ZHEtX`tX=4Z|NfZkAGThq?47} zI=WpKKico9TR(xFcn6^Ro?G$K;ZciCOZq^+xW(&=T2vIf57-QM?_7XJx4F4Fs3ZZX zvK>HM17{CVUu^;?WmR{``$nemSvwOa6& zbdnb~<}gy^Z5zc`uP2ISFBx)tuz{5m`0(B;DWUs6Y*IkY0T1jHBR|i@+7ivOyzk$G z#UA&RGhJtNmOOW|UcK02YnN$~d+kyI;rn>nh!-7r;FKEFYRUNhfr^RoVMP@QT5Y3L zA+lxy*dLc`RgH1%~AW;+QW2 zvVCXwkGn37U7W&cL?IDbamyoQFfv840!h39&X3Y9g#Zan6p%yf=_^C^XwzMZmhO+hnyyTw%f+6bt#8rc7> zWH)g9>&wFU`b!WdD*THtdJsI?{-N3)u9=Pnkc$T-&Tm70Ux0n)+mn@+@x8`vkExLi zzQn}Dpn2Qlm&XAIlf`%12EnGf0iX^3a)-VJSN!ju2{qb&Ybz~{juU$#6vwOw+X={Q zzFGsmVjqqcYA%l!Qvf5P&hEaq7XqAOu4)@-f$-_e%FA-OZp&Q@z+8+M zD5Jts=Lc&6V;-=Axh#gQoZp7_Z}kIPde9o0EL3I?5oxJXdL{s!vcdb4Hf5l;Zkg!_ zAB2Db|6#e&6}Sj%f%y1o-aMs7Kokb>FwQ~0I8U>r0oX(V@xQ&&$>$!>dfOh!kk-(^ z18zRX!>Jslx;2pwt#|O^n7};>pv@UN$2&||5I`F?B3~~k=)wfNLLp!?>x(6Oxy(VcNfDO;yztqB zzd#9_2KeR#2g6#7Wh?TNTC6mCvKX~yBH_?~iH{#=i?HEL;|C-kfrHX2;1f~+K4qNp zwG=8@Y+yko=GBsI*y6=;B4lFpLIe^L`p2R(X52`Eq`MUYkaVW{tN8GAuL*A7H0>*V--4 zL|FQpjMKq^eF7}JR7$3c)S1C^V!;N0Gw)T~>R2Czzk;u?ZxihAjt?Jz?JFuN|CgB= zP2jkb_upL^n=K!=!jy^!PPhQ93FKf!LC+ix1z*Jwq{rBbxIMM2X}V>=CgJY8QpKVz z@1>Urb5bN=xq?RJ%clsu-g}}5)U>td>?J?wzB7{1M4|rUJi-ut~oa`Z9}n( zY@i756CO@4r=p?JLhisR9&uUwNi!B}30AMeVa598**C!)J#u95Qo?D_+_$H*H*c?S zyA~hI=dUNLiYfRG(BGbh&G}86ja~IT#2j|p%1kGf@A_d-L4z)h>g!7xsE>ii`HI61 z$Oh0QuosQ)E%gNwWh6?GISRx2422mB`@=OOym+z49g+Rn&?fgn`MN_w;Wk7@>O`M( z*>28f1OyZVeeG0cNJM=scB91LCLXeSCvX1OdxM5M((O0 z%_We_u-qU|uPZx}dr3tSv>nAtJL8$#k03IVT+3*6x5XbvsVJq&84BCVT8X+qZIoZG zxg)qeX|YdCio$@O=eDo}*fvqtsP0oiH<_TbQv?{)Lti!V#x5&q5*Sr&4_KC?b;Bv=h4 z{QONLzm@|T3Aayz20*g{MO+-2#(6N6e9y7u2L*{6t7~CB$k4L(_IAtLV;kr1hnigTR+Yj%H14}54!86&;H zqCRVqV$^PhKpuX>&e#IrCMMF*MdaO@)};l5k|I&BivB)BR$ni+Si>TVgxT_&!+Hvk5Q5Qznx7~PU;Hux?)ibz1J$B!nga+kf^kC0Mi-37L-6vP z3F*n%ysq?-j zXR%HqD))@9`Uy+zpo=*ZRTV6Uwb&HVbYJq$U7pi$csvFMGZXPS_Nq@PMNy&)HFAv>mi5xv3Xa$AnJ zy%6^b?cK}|^s7_R%pn5<#mhO^J@scYQG4_67t3l+l}Kd5)0B=XNFHG^knw zvVG;1m1}O}jQeqMN!bk!u0ZXB&os^*I&LypsL?@t6TjGtkfrruyq3 zDdWTP8&Mcl=$@pPXOO`|eC_@EH64{Os|8R3pY8%ckS}@o>@TJ7c6!)x#*f3{WF*}~ zp_6AB?iYFaUH({_rLu1T>P6UCU8qZ&?u%WDG5yt5rE5+gKK})--e|gDHH?Ao#q)>d z`s+!~NE`U3&OynBQNs+UR4NTUB7yHWdn3``xLiz*6mCUhiPmx&f4qvoVXXY0+*r#0 X^NaJ8=cb-+CmfuZgVC*#f(!ovY#MoA literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/index.rst b/doc/source/handling_examples/index.rst new file mode 100644 index 000000000..a02235a9e --- /dev/null +++ b/doc/source/handling_examples/index.rst @@ -0,0 +1,37 @@ +:orphan: + +Handling +======== + +Examples about warping, reprojecting, resampling cropping, as well as rasterizing and polygonizing of georeferenced data. + + + +.. raw:: html + +

    + + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-gallery + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download all examples in Python source code: handling_examples_python.zip ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download all examples in Jupyter notebooks: handling_examples_jupyter.zip ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_raster.ipynb b/doc/source/handling_examples/read_raster.ipynb new file mode 100644 index 000000000..72e6c4623 --- /dev/null +++ b/doc/source/handling_examples/read_raster.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# Loading and understanding Rasters\n\nThis is (right now) a dummy example for showing the functionality of :class:`geoutils.Raster`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n\nimport geoutils as gu" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Example raster:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img = gu.Raster(gu.examples.get_path(\"everest_landsat_b4\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Info:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(img)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A plot:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img.show(cmap=\"Greys_r\")\nplt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/examples/read_raster.py b/doc/source/handling_examples/read_raster.py similarity index 100% rename from examples/read_raster.py rename to doc/source/handling_examples/read_raster.py diff --git a/doc/source/handling_examples/read_raster.py.md5 b/doc/source/handling_examples/read_raster.py.md5 new file mode 100644 index 000000000..cff3da644 --- /dev/null +++ b/doc/source/handling_examples/read_raster.py.md5 @@ -0,0 +1 @@ +3aad6d3dc8ef4c2558a827cad52379c2 \ No newline at end of file diff --git a/doc/source/handling_examples/read_raster.rst b/doc/source/handling_examples/read_raster.rst new file mode 100644 index 000000000..cdad8a617 --- /dev/null +++ b/doc/source/handling_examples/read_raster.rst @@ -0,0 +1,131 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "handling_examples/read_raster.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_handling_examples_read_raster.py: + + +Loading and understanding Rasters +================================= + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.Raster`. + +.. GENERATED FROM PYTHON SOURCE LINES 7-12 + +.. code-block:: default + + + import matplotlib.pyplot as plt + + import geoutils as gu + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 13-14 + +Example raster: + +.. GENERATED FROM PYTHON SOURCE LINES 14-16 + +.. code-block:: default + + img = gu.Raster(gu.examples.get_path("everest_landsat_b4")) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 17-18 + +Info: + +.. GENERATED FROM PYTHON SOURCE LINES 18-21 + +.. code-block:: default + + print(img) + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + not_loaded + + + + +.. GENERATED FROM PYTHON SOURCE LINES 22-23 + +A plot: + +.. GENERATED FROM PYTHON SOURCE LINES 23-25 + +.. code-block:: default + + img.show(cmap="Greys_r") + plt.show() + + + +.. image-sg:: /handling_examples/images/sphx_glr_read_raster_001.png + :alt: read raster + :srcset: /handling_examples/images/sphx_glr_read_raster_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.096 seconds) + + +.. _sphx_glr_download_handling_examples_read_raster.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: read_raster.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: read_raster.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_raster_codeobj.pickle b/doc/source/handling_examples/read_raster_codeobj.pickle new file mode 100644 index 0000000000000000000000000000000000000000..e14e3920dd11f0a434a8bea90b63165eeec55f7b GIT binary patch literal 975 zcmb7CJx{|h5JhS!sX{@ZGQi4GmQ4Hsq%KTI-BIKwCbr~!D7HlzkQf`u8|EMJ!??ux zaHO;fS#mnxyZ7$ieVzXNPEM*%xk1Vp4g^gTu9f?ehd9UKng&9D>xYeADTl_9$4UCG zU-jw}64M057-~0&3!IxLHH*_wGDzP>IF*7j?m>{767$pQ+!XoHEf-ojO(}$w`vF6o z>q5B@<6XvRK!q+OP?EDg8=0%?2HVkS;#w`qq97NC&>b^z$xU5~)iRPE-k~^SxRfva z3<*+?4H}#`nN*w7Ai&v5$^?b2L5sT{8+5OsPL5~=Ec;OcQe@)Py literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/read_satimg.ipynb b/doc/source/handling_examples/read_satimg.ipynb new file mode 100644 index 000000000..856d04062 --- /dev/null +++ b/doc/source/handling_examples/read_satimg.ipynb @@ -0,0 +1,108 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n# SatelliteImage class basics\n\nThis is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n\nimport geoutils as gu" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Example raster:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img = gu.Raster(gu.examples.get_path(\"everest_landsat_b4_cropped\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Info:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(img)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A plot:\n\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "img.show(cmap=\"Greys_r\")\nplt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.9" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/examples/read_satimg.py b/doc/source/handling_examples/read_satimg.py similarity index 100% rename from examples/read_satimg.py rename to doc/source/handling_examples/read_satimg.py diff --git a/doc/source/handling_examples/read_satimg.py.md5 b/doc/source/handling_examples/read_satimg.py.md5 new file mode 100644 index 000000000..2a522a091 --- /dev/null +++ b/doc/source/handling_examples/read_satimg.py.md5 @@ -0,0 +1 @@ +4692bb52ad05dc058226c3b82fad0246 \ No newline at end of file diff --git a/doc/source/handling_examples/read_satimg.rst b/doc/source/handling_examples/read_satimg.rst new file mode 100644 index 000000000..93524a7b8 --- /dev/null +++ b/doc/source/handling_examples/read_satimg.rst @@ -0,0 +1,130 @@ + +.. DO NOT EDIT. +.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. +.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: +.. "handling_examples/read_satimg.py" +.. LINE NUMBERS ARE GIVEN BELOW. + +.. only:: html + + .. note:: + :class: sphx-glr-download-link-note + + Click :ref:`here ` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_handling_examples_read_satimg.py: + + +SatelliteImage class basics +=========================== + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.SatelliteImage`. + +.. GENERATED FROM PYTHON SOURCE LINES 7-11 + +.. code-block:: default + + import matplotlib.pyplot as plt + + import geoutils as gu + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 12-13 + +Example raster: + +.. GENERATED FROM PYTHON SOURCE LINES 13-15 + +.. code-block:: default + + img = gu.Raster(gu.examples.get_path("everest_landsat_b4_cropped")) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 16-17 + +Info: + +.. GENERATED FROM PYTHON SOURCE LINES 17-20 + +.. code-block:: default + + print(img) + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + not_loaded + + + + +.. GENERATED FROM PYTHON SOURCE LINES 21-22 + +A plot: + +.. GENERATED FROM PYTHON SOURCE LINES 22-24 + +.. code-block:: default + + img.show(cmap="Greys_r") + plt.show() + + + +.. image-sg:: /handling_examples/images/sphx_glr_read_satimg_001.png + :alt: read satimg + :srcset: /handling_examples/images/sphx_glr_read_satimg_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.077 seconds) + + +.. _sphx_glr_download_handling_examples_read_satimg.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: read_satimg.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: read_satimg.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_satimg_codeobj.pickle b/doc/source/handling_examples/read_satimg_codeobj.pickle new file mode 100644 index 0000000000000000000000000000000000000000..0891c1f725007f02407d3e86fa6e29872bb0029a GIT binary patch literal 991 zcmb7CO-sW-5EWW$N^C)~h+e(aOHTd)p_iV7dX}sX|(^8EonxV6CQ&3GWRnx)*g?eK(ge` zyqNI^Cb}yUQ>>j42{dz0x)&!)B`|#x(L@0kGQc2nCGKbRxhtAuw+xK-%2EuuoQHx) zX|~$On66X7Lk?!EkdmFX*;rp+me`ucFu&1~4Yq8!4_z}epN)!As;?py&^3uuL9Kk4 zrvz9rHfr!*)hgeVK>^-EaRD5+hAnPeY}mbkV%` + to download the full example code + +.. rst-class:: sphx-glr-example-title + +.. _sphx_glr_handling_examples_read_vector.py: + + +Loading and understanding Vectors +================================= + +This is (right now) a dummy example for showing the functionality of :class:`geoutils.Vector`. + +.. GENERATED FROM PYTHON SOURCE LINES 7-12 + +.. code-block:: default + + + import matplotlib.pyplot as plt + + import geoutils as gu + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 13-14 + +Example raster: + +.. GENERATED FROM PYTHON SOURCE LINES 14-16 + +.. code-block:: default + + glaciers = gu.Vector(gu.examples.get_path("everest_rgi_outlines")) + + + + + + + + +.. GENERATED FROM PYTHON SOURCE LINES 17-18 + +Info: + +.. GENERATED FROM PYTHON SOURCE LINES 18-21 + +.. code-block:: default + + print(glaciers) + + + + + + +.. rst-class:: sphx-glr-script-out + + .. code-block:: none + + Filename: /home/atom/code/devel/libs/geoutils/examples/data/Everest_Landsat/15_rgi60_glacier_outlines.gpkg + Coordinate System: EPSG:4326 + Number of features: 86 + Extent: [86.70393205700003, 27.847778695000045, 87.11409458600008, 28.14549759700003] + Attributes: ['RGIId', 'GLIMSId', 'BgnDate', 'EndDate', 'CenLon', 'CenLat', 'O1Region', 'O2Region', 'Area', 'Zmin', 'Zmax', 'Zmed', 'Slope', 'Aspect', 'Lmax', 'Status', 'Connect', 'Form', 'TermType', 'Surging', 'Linkages', 'Name', 'geometry'] + RGIId ... geometry + 0 RGI60-15.03410 ... POLYGON ((86.92842 27.92877, 86.92866 27.92930... + 1 RGI60-15.03411 ... POLYGON ((86.94077 27.92375, 86.94059 27.92295... + 2 RGI60-15.03412 ... POLYGON ((86.85344 27.94752, 86.85340 27.94752... + 3 RGI60-15.03413 ... POLYGON ((86.84776 27.94845, 86.84776 27.94849... + 4 RGI60-15.03414 ... POLYGON ((86.84567 27.96906, 86.84567 27.96910... + .. ... ... ... + 81 RGI60-15.10070 ... POLYGON ((86.94112 28.11455, 86.94129 28.11452... + 82 RGI60-15.10075 ... POLYGON ((86.95861 28.04669, 86.95847 28.04701... + 83 RGI60-15.10077 ... POLYGON ((86.96454 28.07843, 86.96445 28.07814... + 84 RGI60-15.10078 ... POLYGON ((86.96302 28.04915, 86.96304 28.04920... + 85 RGI60-15.10079 ... POLYGON ((86.97074 28.09496, 86.97039 28.09488... + + [86 rows x 23 columns] + + + + +.. GENERATED FROM PYTHON SOURCE LINES 22-23 + +A plot: + +.. GENERATED FROM PYTHON SOURCE LINES 23-26 + +.. code-block:: default + + for _, glacier in glaciers.ds.iterrows(): + plt.plot(*glacier.geometry.exterior.xy) + plt.show() + + + +.. image-sg:: /handling_examples/images/sphx_glr_read_vector_001.png + :alt: read vector + :srcset: /handling_examples/images/sphx_glr_read_vector_001.png + :class: sphx-glr-single-img + + + + + + +.. rst-class:: sphx-glr-timing + + **Total running time of the script:** ( 0 minutes 0.114 seconds) + + +.. _sphx_glr_download_handling_examples_read_vector.py: + +.. only:: html + + .. container:: sphx-glr-footer sphx-glr-footer-example + + + .. container:: sphx-glr-download sphx-glr-download-python + + :download:`Download Python source code: read_vector.py ` + + .. container:: sphx-glr-download sphx-glr-download-jupyter + + :download:`Download Jupyter notebook: read_vector.ipynb ` + + +.. only:: html + + .. rst-class:: sphx-glr-signature + + `Gallery generated by Sphinx-Gallery `_ diff --git a/doc/source/handling_examples/read_vector_codeobj.pickle b/doc/source/handling_examples/read_vector_codeobj.pickle new file mode 100644 index 0000000000000000000000000000000000000000..cbb1c890478979841887b2f4ff6d94a354f57788 GIT binary patch literal 2764 zcmbVOTWi!n6jrRg7+ZI1ckw~+wSq+Otsn?xy&<;sf`U-i*<`viI-7)<#O@-Bk6jFc z7!iCGeDTG<;6L+BGRe-QX*-&yVdi}2`_7z`bLPX@uYb;+%D>d{LdLKc(=ZTO>TGi# zbL{u17iV9xtB20N=m940Yk!iB{f zb$Y%y?F$m}I0J9W29{LxJ%)tHrl|vKJdPOkXq-(Gpdkwt16k5w#3606JS8jB1gC4m z#KmJYiWrupaX&&aQ98lF;$9Nup=Hv=AfYU#5GJ_jDi;M2h*D6nYnKWu&j4uLEmbT= zEOsHjeH-R02J>7FldK~YM=XpP9k|g%u5#9f-I8^!%!}&_m&Ta$aKFqE#NN4!!yby! z9S+%=odZ=D0*p}LqdZSO*z8Fg$+uJ;Wrob7o#_aNTr*Hzkg$^zZ6aZ6SgDBI0i4N{ zT(`xVls636YIDeK6|Wa^>{ih>?H0FNl*JLw9L<>&zG0(az5q%4n}&UBt9Lj>7c~l~ z>i~1=N$9PUp>=heCqp!Cw+-v|b|fCsF%69R>FDAR=V-#{>pX?`EYNj*4=ga6z%9eP zvy+$C_TUwC8$)DE7g?6rBTH~y<5NqVrt`T0>D~`~4EH!Ze4A^!P^!#iu3y-CsH@$z z1!{6%nwTEtThJ03l5faGo`;2i831xUTMu=)d$vGLE>#$u=Djy>m%I6NI=1Tv2Gv~A zW&fx5E~wr+Yxwg|;2*^>Tb9yQJ)=TxJI&jAyU|?K@93=|TrhDSK& z6PRQ$A85$k@nj~swG*<*Fbt|wDgKfmD8g(q!$yS_iTntZkd>yphIYB$PtO1UueNQsV1?BTh79`X`{apsxoISnE8Y|5;q-3Bs=69KSNS(aN{hqTrqTPfA9GL;Ml0s%6V00P2+ z2bFEJy6yY!i+O>$n@5LIqt$YB+&QW|E5DrfC&?#A*J*My%jV0Y${?FAlPQ&N(&6&zlVNh54w7U0QLUuY zbeZ@`#-a=gro-LMl<7DxSZzjpfV6pheVS1S?mY>wR-EOPaOd6wl zqfP6b&R}#@nJ4255cE-P;@(8Sba-`3XD$BbWoTTSx zH`!=ZhXv-@Vv)_$%XIpi`o2jn)Xd`92$W>wba>o7Jw5S8-xnMg^Zsq)KA@1C_TMx9Zs-;E?Ipp2SfJ0wovqV_r~&BocXH%#ZrV3}T`=ZeL#YmzA63qw%<+ zHVf)PxI zpB$~3xL!7sr9+!6=7Yj4ZLM?iGb;P}GjBcE`_R?e$3OqF5(^zzdnKGN2AIx6zW}&p zmW>yO){hvyuiy%Nb_d=1X}#HPp4OAD>~8H0!`p8UF9v7rvu^jaJs2d-TJ0VJ3N<7y zzeWGaz4+wlmH%>mh6`hFPsCVTRanxW53cf$--Mt3^}>EV`0W&fkbn8|B3mtUcO~*x zU!}dvgn#jKHd-=&?p<8AVnC&)AdJl}cb)3D*}{b~#Qe zFQ@(S9llX?o2Y~IJ&PXb^NVbF=e)g`-Qf=55sX2qar?55!w0W_criR)W`pBt|2n+E zdoX|X>{MQpy8HAh8N9v7ZWClU9>2wBG-69OaXAEbI zdj|kslm67gQN>Z&>(O_5X?~>3-FiuXp7xK&{R???-+SN12eEf^@Kh)Em0c(EasRFo z{%U*xc~rskM0m?x2KgMO*YVrybg@Fa-yL72!(lQ#dhuYl&!6!bSbApg?L${OJ)S{c zFgP%U_xrG=CPcD`tK#<-OvNt0?_WT(Y*hIa*G|sQUWBPx>m^_;@8)oPJ=pnHw0+6M z3H?^VNQk$V+02ntB20fe%~sO^Gl1!`A~sLv$LTVeRHoT%p5g|hbe$ljkG$T_>Shew zB9uPVWUQYur8*vsS1I1MY{CUFB=fu@|8mEtx8oqbNM@<+d3KYR%M!Vuy(x=QsWvhi zr7VurfLSnjadwoR`=lwp?DfLrvea)GqH;}e&j?EZxbz&G8-9!?2yHxPdh4QHu27Ma zdVGTi8MW$%d7p+U(^EET{R3jIYOx&~(Kx%z9NYHO?8MQFIQc}u)qJ3#o#)T`5<$Ry z@f>EI#I`gdGYFL-6ik?7j?>GjG0ft401H9KN*{+go1&Y_rUUJKl|gJIz#xB~#x_%s zSQ+&f{6916d7x(@Qn~d@<`!H>w?r zk*x18lI4<$6=DJNWiq7tGu79)X8g0t)xO3TIGIOlc$^atfS2pT!C4QoV(2*Fy>o#AJm$-kblQw2Ii%4EWe}EtL&z?%zEjR;SvNeSI9d3GP{ul z4lf?J(_dEU;H`o!^JS^W|Ef|)`$l)qpREXdJ%4OUf1dQ0t9i0`XA)Pg`bhazIvzsV zgXt~r8pPwDmXPo{o;Ncntic$#0;Qw82hNUUE}R}o)n@5&ElU5kHydt~#RHz3XW?$k zmT!zL3iT8;6F^+gvndjN6J$5)sOYFL3XxCBcMGf54PU%FKtc>-(l;5(8dX}tTLo_; zFRzT|*`)Gae}RCdS%r8`1&#iizkMM!M{5+JM}ln9Uk2RYU#s-E5c?5cg@DIG zEbsn6Xl8dzBg3@9uwSXXS@CQPXP6YgC# z2Vq1Np9CLLKJN<~3nCDc%ZKsd68|JZ%_2iKzpr}!$;$!P zec|8dAvGRUJ-qqNs;A+y_Fh$wDBA_mi7GrjSelOY z%*)>iTv6@mLDWLUPT*AG>A@Pz%bMyQhn?{L>47q@Q!&1s2+M0dJ%H0}FFC!+(}T9S z>d)5admBpQ#A-Y}U{^_cc~uU_-Dfth^7NqDp0u63W;LE3umLMXyHRE}-V;39T*;xPaj>pnfO#Ibh*XmRc>QKNUB+3)g4*x|rl zx}9)&m8ZwoP@d>+(7ejigO)BVI+?7;CpYaxUhBO8h&!B)()}RDdQT6ef{HsiPZgdX ztl^yX%lS^gyvoyq#-5$oPQX;*>A{*Onzl1T-w9h@>*)cUW#hX`7E^xR&2L4u_WZA_-|3!h*ozP!rQgT^k&o>oS70v9X2AMg&CbPK@uHSuPD6+1Df z`=e0hicgQpd&||8oP3Fo!m3xMoE=_woq^Y6qU zP?oB`7f^5aO9xPTt)~a@_O71(yc301a>ym+~r458C1?9qpC} ztMT-JN&S0Fq8FwednUQtK6rYt&Cy)=GUiShDOY@YFj*hd?(&}PXxa(qTFs{iH0IIHtM zfSV?ldx5h$?*q8WYP=UXtMfj9Th3Pr#|!oXnAdwBK#s-=gd6~6QSp6%Nql!N9<9#% z0B*+NhJDJ#>bwu&l3(`1rYi3Pv}v*zG*x*YppBP%K~t6Y0@{A8jo%a2F85Exd1~N_CAJ;V6_uVGytux$3%Xe634!zd+1r6C?cIJF z#t*GAdOE{p)!6Ys*v$@I`xhf;;7$5yU5 zcgf;=QLh0l9_@N?r^9Bgr^jHvg<&TK^IA_2;9$2yY<)<0PLVF-Ll4z%eV zsforM_^-^;Tk=nZbCiA(a{%IPxJvV2h^Wa{vyE|$|H%Crd}V4 zXGb4csiopk)(=RnV;DytgoJhXvVk3aAbw?-<#XP7H@%*c25lhr-hos^z;Xt9Dxq)^ z97zp{#qt5#!zMefjf~U{Mw%X57X&sebFo5(rZ@x}RZ@YpFnxkBvcY>V-?}&+6iY)FjWoAgfZE zeTjyAb~{Wa8tm559>@+G!+m2JlsT$tDNwTc-8&BWLdm5Z`fURy&Cr@a!J@0An7KuZN#;d5JPMmaD8Kp86|Ysa-U3x~M2MTrQ0F zI9p(;ZfPu$X6sgG4fM@)Wiee1lp~8qVIOj`_dJpH;+bdfnG8>sB#(MWN}HOAY@H9< zd#a35k{CXM=vzB^7O|Mv>H%G1xz&_OEZCcq$uQ=PFyj`874lG-<%vaQwB6SUmO$_I zaP+ZA`i}Zw4Q;QLnNMcz^TyWf? zrw~-{x}C{$Bhtt{smcw3C4aQcjZ}LCz8R+rw0oY}$&s*OG9tL{=6IIDJQt+c2>a&g zL(4WE8+|+(9ygRqjSMgq?MU7*?d9rJ3AEFnUip6l2IU zQyL-47EU?cLW(+ix}Tx$by@wO9*S&mB=Ss^_fjnc2=#eI*#I%f>kCJnl+m&@&VP0fl(%JZUdOCDD{C!tmMeSl%s8^g1xuEwf03m zHpJn9x~gxItB1}1sO!RHZ@$&<=U4R?xdwov`T1~b1^;YqN)leqV7Z|mwtn;f>x^Kx zr0^a~0X~M#*h(#nUu@-%l0qqexutkpkz~S5&6;q8WTmBP}a#c z(DRFNHhBBXia1U5b&^?X_eBV3N&53yo~*ZmVkNKZ^97l2r%~4186OGDcU^88sb6o)(xXhL#yIH5nBaDvd15T)d?oRb&Orqi=nuc&Ywk`$A$(Cc|W>xiI9 zGWr51Syq)hkd%+y@WWLuGp0UE8*GXCJ012<#af{JE?pl%*!5e zWObngNq5-_eV0^jviV!tDrG3n=0qbL!M1)eTn*BTRM{*IYYIjhw!TN{d_h)5XiAb^QZ936L&9l_^=x_K=!cWzt-0%#74MomiU3cEeM9=Y z%E#mMZ6b_uIS%RBl%Ti5^#hGRmkGHx9Y%p!l3(SYR#c7o|4gA$G3RehR=Wmb$Xy(g z5`VFtkvIq2(-G6xmb9w66Csz?d~@(&IfvKt4VySx{@pFzkZ$pJcJtv4S9Bbwi(Dxk zz4)6h?LdgX2v)bTVS^(j2Q0feMYuZ;b- zVZw7E+thIzRt_XQv#k4hpTAto`TQ7!-_p&8A*D3xgWJZISA=0F7kv`sB0cCPZgDw! z@ygRtOT(zjyNSBBp9_tpx~zO$>h8~v!e)nzbVk7UamPlB>mz%Lv509krJ1J$G|0}l zHYRE9^2-uHkHfk5lZTfQhd&0-Zg%jYq*bY>kA3yq=B3xHng|WbHJ^Zi?-5(oS!dPX zn8S6=2AG-p&QkBLlh{3#cSsd`zZ+i~-RLHatwg}zZvgJ$6jwC(O2_6&NgHUsZ#lG! zY}s}dLv*TQMMJU zP!3=c;SUdg!uCbrBBy*rs4aZ;CMWh?5)I$y9_>Y3=N(-j=`a`;A`E}b@O2-Z zjbHr3Ib@Bz#9mrpgFvB@!Q+*bt}(KfD*Z9gU1ZbMf^aPUGjPE|eOvJ}%SReu&%JBM z;c%sb8~qo3a}G+~tFFBEuURzlueTV*ch)rPJ8sv>S;9=44;?oV=y$lqbK~p6$YOkc6mXG5Wim+8wi_s73{pF;PJX$yOdz(bp(&s zr*F;IR`-~zMGEVodj}q`-_2_Vwq7vsc-^JNy#ZmX&a0wCN*{H1|0aP_|7FqM!d(YW zEZj$0&)Dr=%)KkhhonXdAP9*4o*^IvCh_n#ky!M*uC+~AcSn;(1>xjD6&^5f%A zSryiDu!sF}dH(gsP!k)Sd^o}%J?3i1jQh;xgt4uaCA66L{piK#ysz{LGV)$-7VlY* zi|l#%mE7m9KCp>yve0nM5HwV*G{Ny zWGajL?{QOo+3!6#wco`g{J9uDf>&GQ`d`cQ{}_T6oBq%Z^U;fFCAEh0u94etMZt%g z^Q29M|CoKr&?0>N`zYL=l-cIdem-b^2dZYOdCn<|- z8GBm3_J>Eo)@CP)yv>r#)M9ca9gd@C>pUKD^LJc|rFVJkz644cwQG9dSy&=vX`#GW zN;CJjo@jOS)xEtZ#k$9qrO++y{MNP4Vw=E%6+yQD*(!(@Jd2)lpZDIv&idunL>5@- zic5)$B^RZ$qnxi5x+PGPD;r4?9Yo1Jv^RobO7yFy!rbHG4!^dGG_;xL-TN)yq`G5_ z;qFpx&e%TEz#2JEl()}o0e;H@{>B!R=g+iezrK-#8)9|BAfi2N;Ex-Q;_bTY->5fR z=C|E8ujk2inynUC+4lY;+j3wv z%S-p$t{#p2Y8`7o5)5d#&@QA9=_&fvam`S{D&_0eMHMpTgq3SGX^YmXs{gQBdauP$ zcXKfkb7)@_8*}b$^y6xC{HU-aJ?vQ&4fCsetp)2?eREx{$W+jwMD6lpY22l=7+R@b zzhQZU?Aku&^$GA9^w#}f?s9!q`KzzYv&R1XVs2M)1_=L?5~2PBo0VpFHXN--*Q(n- zJn=8rvAQ<0(oo{IxX9+ESNS6l7z?s%Kb^5tK-;QWmF$ni(RSVY3s%Z)f&Q=GeywF& zuA{5iE{AY>ga+7|&&QR;e85HM>_i}b{>;l3&t`WP2wlw3-M7bRdeU|Oy!gjq%65h2 zC$(<3+p0B_#;D$C({ZOWuwA(RHQUk!-!pA}LoGg2X37`;{p3IVf8am={vZFx|NVdX zIR5$n;^CU>?*IDjr$2oC**D+6{QlLq-}JtHtv_!npH%+M*yM-rzUqDV?YG~b+YN!* z-f_ZFvCqbKb@^ZJUJXA!THEi#wV>K(vN-y)>b+e0I$rYeM@Em;eB7HO5Fh=yYOVj% zXWzY{yYtFNrzh;0IQ__rzk2!27eBoG;xo#>$)-st{q)nD)mMN0>^tgE1 zZ_rX_rWfZw9<8hGqw10EL}LF8C*1n-8Nvw#y_u!_TliP}usCojI2wy=7mHu88;i`c z7eDfgOJh+U>Wl?>csUk$;Fn)puZxhoz3UzK9C1zjj*m}@qz=2Q#q0_@Yvq&5hidsG zXZPEakhpGtt^9UeiDj>M;0GZu&EDEdBDJ?tkwk27Rb}JZUx{o&dn+j$(*8<^v}75^ z_BLe+=l0f8W~&1#k$i7&Ez2#ox0YoP+*>P8Mz^P?XDHuODa4o_5GdzvPO~W!|DKlQ zpzN)}ElG#eiI?Z>tGh!NM9|rkvU_l{b^EH=O&$9x*)=kIDm|2NbI)hReRz8+ZaW!% zz$Y;s`ktzO?bn`4mf?6`9ZmVzEVG+3W^0ngfy`j@j(rW{vc}sQ_I6I)Fb&*SHQv&* zzne;O`NQyLfO}#Cs^4?LbB&0U!Jkl zF?(x1>@EjrNmR9`k>6rfLZiF1aj3FOgM})4^+>4adW2*|dj?W==i?s2`!l5bZwRFg zd)^5>Jn){i2^PB_et&K2R9oLKE`dV1L*(}B#DOTXzK@uKIQQZjiF>m;EmrtaF%`8C z<_oL*C~dgpSJ1c?W6OVrC2W5C81jZ3 zo%SrIQxt+af;!3a=)89SWvZ|c3kloa&1OgEt$Uw1Mz+-dxqsdF0MRxun3G;@EB8&F zN9T=u9dYht+XuwX%f$pUxmQ_!gAKbp5WTuilN&9itK4gxx6b}v)k*yA^LUQgLz4%l z@+X4Hrt@B{n9=+1%}IT!v^1|Tl^QbFe&@6MQfWTHFO}tU{8Cxi-(@3saHb!|@bG&} zd+7sxxKiPk>}@YsD0^$>s$y5o@63NWphiq@zhfw&EoyE=0S;^4K8hJ?59!9TP9IXS zhzJ}6iKX#9q+Lrzd`Lw_2M+4mQbHfnqD2V~YSJPF2i3Gx*@txQ>G}_;=~>GUspMrDu#P#_5-SD!a?on-sMA@qh6dy9#Wsk!a>y*2h>u8;gD*6 zAL#zUgbQo;S9~biZ~!nx#NmL-+x)`=KZ}uv1FDC6$@dT2Z(%y1o<|`L=z1d(anKi| z?4W9;h{U1wLsa6B>M=5LSlt+%IH0y76bDq3Uvo&c>_8b=i&h-a@L{e&Fueyk2hrr0 zA{Ph1@teE}E~DPhyozERQY~H=c|eQ#os5UHt6g{p)zj@>2XxMC=YVQ?bmOqPr3lAi zAB21F_QOlsNRMZe}^ZxEP zmDc^Zhff*3rTcG7QIpWy19d#=u)EH+MNb~Ab3cL-tF9k!xA|ykrh6}z5HR?+Gtv629~D z#BJe`z-Z@n<6^g3=#)!0rnS~}_g;m#n9b+))6!d8yjEd(z{S*5t<{S`_qYkqF;Opu zO<_dOS*hg$YxM#XO7Y_(C0Ev)gXuo#=I>8hxme>~t&!qOV- zte3^vmAxxQ7um0QS`}F8Ujb(tZPxMzYz{T)LuqG;yib4AtC}pkF9QH7W(C4jU!ds`6JqUidd zps2+c^EFJpD=^`)M^uHPsCv~ZdXF;!D?cljN3Y-B6l1nENHx;QX<`w36?OCP!}1q} zX9e%BdFg8Qtl;2v-E&rqHMe5+K{4Q=fKy zXr4}io%Pi+KD_Y;3f9`sTz%{+jKQ5RcU6PuWD!2SQLydg^(r|ZqQBmIn7@N z3jOrc-{naNaJdqvknG5|?8L<&PhH{ruL1R_me=4M+*PKvh)^Z`rcOPt!|2I;wbUM< zP%AH?UL&v9kK0U(ibauwXjMB+AXt}wTh;DawF`HgLcr4|pq~m;rB92OR5-H=r&g!@Nv+#z z)atFH^R^mjHQKe)ZlfK4ckAtTqnZD%wOXh3ZoPZfQ4KY7bSi`i3hSWJtN{doQQoY9 zhxiBz;v*l#$7XnJw%YBp##y^&?Fy3S*;zNfY@f!4n%x@x+Sko)J@#avH2J7y!vh&S zp9wyDIBhi@UbWjkI!9{xd)jVv+s(5uSXFHE?nj}uEmXBTwFq6i(~K~8o1IPopxf-y zhryzUA4L;=DQ1)l5eb1>f{M@xWr4t-Rk4vGXDv_^johwR+t3oa@~`TPqEt7k3Za3j z8jvHpW^{B03AL;Q=uZX1DFbxi^cZlaN5&|S^%w@C$AD@&4p`#}im{H#L+ecJVaTkV zN{j?V4jh=_7xieaqrbGJzalt0i=38bWyLWF#xM>wGH5Q>@mo5t?tOsOQV}Tgy9N2_1mbQcElZ~6da^RH#F1` zmedI_n$YL`*rb(Q4b+;{FGygDz`jzi_E3)x5dr9!@QS9zEzMTwv)O8P+pTU}48m8& zAkDM*vTKyoIKY;;DsY4F1EY4t8K{Mb+>(Ze;KE(W9~cnbA%#wBZMT*)|$m8E@6TgltW8C@(P9F%YTNsh`yfJy`2l zCu&EE$GYp}fObI4X`_w=H5$$&)azPH`l8DVy3LheO?t|~E6~=Bzyo@&x9CSk2SwGC zax%2$Itdg@*Vi4ZC{vznH+mr z;JJFEUTc?)VO?Up264kWp~1Yf61&x<$EY53cC^{j7v9fV&`$L;QCb5&6Dj?{^$aCI z<0W+N4>ao!3``IFk&M(47%!kTh+#A|x^XRo^ckA>fPqq`WSOd4-!Qn znKjYb?3IMg#t}H8A?mv00{3jb11`isTE_flQ`!v-3J!-U9-c9|YN7-4LE}bFsz&`x z%Ftdo^ECKO<5F*-BYjcgp@`sy{zJ`jEi2nz=&$sJQ3bIABEgMAQWB*`iL%mMD9%Rv zw1YY+yeC|#bzyFF>QSo-ot?8z^Rxq>NB67IJ?(ZeJEbPN^=9j=?7%d-xtk^=plvShva3zH(fiVXLB(0S*(rC{hH_@K zOL%U!u_GmVHbt-woTFAdwN9k($TCVE-IR;$w(b#ZSm1|wd26B0Oo(#O$P5v!GcoSk*>jB@Uf;VG+| zGZ|qha~1@_$8aV}=Oa3+V*zR%dAg^vw7fVvLv$qg8?tj2S^kV>1w~8#X=t>GeJPXX ztTaBDLH|JrO0?fnq6&ffC>aMGjsRO{9S%&EdG{<45QZRqC5lH!LvAgfm^TosFePc? zFLd$CqD^j}Ho7=6t+Ll3Z^+xM)jI{tAf=#WeNxk|=Q9J;V!I9vbC*a3QvIO5Eu88v zKLr%jYPH;vM8I(`@WONY8O|fAvD;!c2d7o9bsM!7ekC{-iP|j?qZVjh?ZOGnkV;L# zHI!2ctJ*;@e8y(2br_}ef2Ve){d<}MA!`O^N4(KNykX}~9Ve}Zv*#L#L3N}M6I>k0`FPvtkIS|6~^V@7Gl%I zi8^cN>Kf#9L7ia(ChLaLb>Z6lu_F~L=s?_AzzvUaz6EUo7$t95o(_(t7kJI|8MzXcAsp%tc=!ZF@QE|_Yq z+a;B`8wmeNE2e3qZKnqRF;U@eLsG2cq|2d1Uno%GmQKFr1uJ}S0f8qjLOeP@gHWU( z<>^N%cTxDI${xiz|FK>@lXpR9I6B=b@kruWM60?Dg{1360I-*9TD3(s}kH*Fo0eSB87VBji>;PxTB>mG6y=S z=Nd)aa5f3-$whD^*r!HYBa#&kQ!)ej*oLgNi;J zD*U||r2q|tL_rwfbqF_z7I~8-EcA`hHk06Uxx4(2=A`=dSrNKg3v|$m5a<^P0B($) z05VRWT2p*+9ibwJHlvEH8yYw>56DmJi(+Or(UM z#GKkj)12SnJy_1MLM*CN6=HHKH>z!%Mfea5MXoOs9c&SEE+NdAfT6RbAnt>fJg>=jd>%`PfVSSEr{VGCB;{M&GEubTNQyBOnF^v;c?n8Cb)l+&SE(Z-f0}51Rkb%ggCd$^w+~ z%o+FKPX6UitQpZ{Ab+0CRnyP_Zx*of<*QL-dI!!GhrGegXx{Y(7YZ8ll+r!?i~*(- z$FjAf!JE`s9hQ$BbWt*zA>OdADi&5t$yGA=1=;Tpf98H1P=>f@jKk~xbO5&dTpK=E zo}+7c0{_4bx3i2HSct!ZO*v$Ysb&4TaGjOh^yfqPyPr-htYt_~xo3GkB^1*iUuKqr zolZ@?n3ti(#_1&;86>i{hV$`Q&7wz3lkTuMyU}ArHx~)Kh*<43`$0x z?r=-`jol^jGs#y>gGxr4YvV(e7Jc(#lXB7EzPV;+y^IBBO=% zBW9r=8uqdxrAW^%z~(U1{WpG+D!%$v_~?2^V(e)cKt6i0Vy>{*ZveG&wT0G=o}4HI z;6SrR8nu|AVL)#{2<96GC{a&U?%-;$G9^L37<3j!XA&;0 zJV%n%=2l~r8lSQ73BpAqms{g#FUJrZ?G04$6gy|i4C@69qCIvdFU(zBI_cVcaINA+fc>teH0!m$lV-RNzr+eC8vs6t*&Xk*}G1nII#)62n4P-v3@C)nkQf0A1iY2v)SrR-p7706@;Alyz$cG^XCWg& zC4xozlAKd;Ib|naA~#LfwoSP=u4qmlFd89(zaqYkTf<GCE8D}oUtbusQw-}ba4 z$wsET`|N?+ENsj~6{6OF)XleNw)3VVtPAh4YvFw~7gsCKs4+CK07^jS2Sd$)i&M-;P zGsYXt*5ou5yt-6Ea)ja$#1`N zc`uu`)Rdk~g)2I|0*ezAXqG(ZXy!SH4vpa`oU*iG%6CNCp;2= zGQmXz-0hKR5-LgHH<#)RP5Ad*2#FYWt4skK{KJ8I5M(yfvB|+4wN~d{g4hLjUp@!S zW0GWKVq!l;huCB+%!)9<1DqPZi8FIx7^kvb^aQYI1_m|Gq(j|OI=NH=28|KM*$RN8 z1|LlNGgI)^BF$yWIVfGAgYdM1V=he$KS|Fbk?_8#CEg4Eh-&uOxF#mblKH@9Cz5bU zJUKPJwJ{o375E!zpl9_jdf2twPz-MeezaWd1Kl}}|iTu0dWT=4`fO$VAs7=_UJeIsNx9i^ApBoQ^6PUSy|8%YSs zW?NK(J1DzGrwguje-b+u9*|p7gx@ys!W#^!3*~CI?)V+MQ~a%bWKT90rZ-i zYtpj~j_c&McO}i_;%c&{v}bhS8r3$)i!(3{G;I1f7%i9EXtX3`=znOy=uu2&q7+RC zJ3fr%H2ZK7!bHj0RJsPouRBn<#C-2Vn@?re>CsS&5h$sN+!W9NR~fRUMw4#)v6z{8 zmwmMvpQ$^%0>Ti;K5&J2ga-nM^Ln5NWC!3)=~;aaPb*$>PFwa7TcoGICiBd=q;#~} zIc&D-#ir?4;{|*TYi+`JfQj`uZDCnaa|jk_1-T*kQ5e^f%2$uvXDjAVw^9~x6nL|! zt$33*FpT*5un)W(G`!?b)ujj2Kw>F&+-Z|0P?^+^2#j_b+tOMQ`mz{; z7&&h-o3YC`pJkZiBIK+t)DXybSzSR#P1w+i3aU^lni=B{&l$mxR4>S+&P)^xvlX*= zuR9`~8=-JTz2PssFP}t2T(dhC#t9LAcZY*U61IZlC18Bshj{6S&(16W$PiTm?qtdM z0Nf+7Dr2*~sa|Omdm-fCKqVPKW_Ljl2x6Su}dH=H;D29pPqXadf zKV;ewnyGFRm{7zjIc$7Z9VYUOU=WfHi$JneMj?K$`0d&ctwku(^#bjflUnE(IK$pb zkLQd)iF^Q!wM3T0^X3>sqQ1h2Z6>Xb7bylqe^Jzo$>n>< zb#$8h>9mWZNsCi(w9M?9Go}u3PbPfKYc8U8#Q@#iy2k#vpxOWuj6pEMt<}!+W_^VF zw9hTz6FeD3k|Te~mgqOK>fW`$G@-W_#3oTl3gDS7b8Qa1R6*B+hBEV{mm*#y0J)i_ zxXz4Qq~-(I2XR0-4DFRUCs-KXXx^b>Y2|Z_fJ0*veT+bp@K(S8_5n{9%g3 z^A*+OTsKT~kxkQvr3TccE;GbGq;Wlqwa|7va+f%a3!55v3b~eL*xx6;6~_n}Vrd9m zKNw6esn1DoBo0Wg@l0jS^#n>-aci&uLQ-PBMRY|qabUP$KFp`BQNL|qM+8@-1QS-3T#g|ZbTqdN(epq8Sh3k5w#)fP z90yS)+9f1$gS_&jOAE3W zl&Ow>Wqhg{G^wjmDZI+AbY$nrS#D?iz<@w} z9+{OJ0_?FrWRkPcDz2&9&?a??7n}>w$_8yo;*10NYok(Qp<Cya0ui zaSa)L0W|G#;emNDNHey-1Nw9YLXw9$fgtjB!ZYnK2csai41BrpOD#Xat$q` z{=`D@W{F3A4x}RB&FaWB=!ofxbPE03N-@kX$dUxZ-Q_Rg=71=25?eUP0|M}xk`g)< zU;t38z;^VZZ06;kmd_C z3qrw>Q9}5nz+=oPG7yR`3aHUL7!@CpzX_^lBuDU^*~BO%S%nbd6GO~~S#!KNDZStg zD7qfSag^Q)o%ri;QXxd6F%m+?z0boq<;2fW5G-ks}@GQhW(;hB- z{~XU_glD7%__GjFFi(CRxVe*Fm<)uBrU#4joyMU0qhzEg8 zb3it(_+OG!jM!`8KW?)5TMc=hBC{wzB#O-$q6=<}++45-HI47!RMa106c$X8;9MSj zhKN-cCDSi2&Fz83n23pU*}rEd&{a*ViFH0iP)k_4-(1L6LoUf|bJ`Xv9?%lgDC{79T8a})0$kHRbMrdp9)s6Nj~`mHjrHA zX!r&=7Mz;HgQAy){bY`3e5OXxU;3aJ%rYH~Kw?cb^Na!Kcx?REwRzDH$$tc%+@mn} zQO(4`8NL~qVvjflNL+kt#Le?!Xr`*xmp$OJy%-W?c}Rt`V&KuX+C<0F7k{o+gdT8t zt*%DO3{%rp{guY$quflQ3S#h6bCPHywZ)RY;3zlj_`~6lA@iL`WHI;{yPaKWJ((Ug zpYf7$b}5{W(}@;T`QkFeeba7{%h_8rF9jA2lP z7kISR)Pn@g46si@3fzj}`KZS<@0@p+Y_HIEp-UqkS`|q$-E&DqyyUAth}0|X1Q9u< zFcp>H8P`YyX2*fXqXbM4${$P@7)zFT$5J$57K$vi#?;_yBYBgKwrv#Q+xeCt82J9H zuqVjg32OicO|Gy~wrYcIV3FLWp2#^QXYf1MU&AKexbiB7uu!GN3XDlN9VwET3PC_Jkj6_`k}pf)B)2rLlP z=M{>`>1I4=0>uXzGow@fFqfw%9?T1i+Ml1AZc14gQTSjs5aGBgOHSL>a_Ra>M!>ZZ z94G4B4d-l;>XVlO{J4QmIZ`+RYV=14U0=1 zWDNC!%7%l3mO=3KfoxBkC%z04V7F67duDbV?y@PRAy2PuY9Q7Q3QJ9Ssf;G#3tB}x zP|J(CHeCt={Ze8TDTZ_^579Sj0-%T4DD=6Z8yE%T*rZ_FD-oA;>T@8PGD3z9tW=BD zsZEKfg_z4nhgCf4VCi{Em;s4#|2vowT0oBNM36shyhYnWkaCNLH0>sER)B!SWt8|E zKh45>3|Sv(9;KE=Cw>uQL-q@lOr_LSaWq~94mQ6E%$0ap$TN#5HK&=ByHFOa!89WS zHq*|lVL0J|DK^??Yw6UOT%M88B{ors+~P>L@JfUsS_tX9qnv!aw|4{|8Ik;7o8p*k zTEDhvX-sZ`5!Y2}RW0(8dH7Q6nBy%7 zo<6oE2zgKLIOlIz(1+@L5)`fyDP1BXsvvl@pze6*Ne=FHvbZ8b-ST%{Vs5k5l|qPG zzzUcfijQ-)m+~;IDiqG@g2G)^5rZcXLZ7obu6lSAQa%-6kVwhe6pY4jw8a&h^eo>l zve0rRs+m#ZADq#2YOx4Wmc7qLHg{qfyG8CIQi@9k;yzCn5{M!`Yi~e?=jVz8=xQe@ANqe8?13B z01_u7=^Rc8dNk)dM0%Wz#F**~F$39jDQ6l55VZXmXOCz8Tq=U5C);P#vaBSaZdSwS(urh2vr5)8HCzP;0@#ie3b^Kj9W`_ow!fJr zw7Rwg6YIUjVAAc_!e$188P7Q75NETG+4Qa~FPI^?!?3YHzU4dy%-woKy+}|=XqEc| zW!9%IT1{f!Nps^#|JHifk~{;@?2Hc0cyo}6wIMA_`5HOQg{vDZHxmzc>}g5IA2c(w zr55|td?RsJ#s)00hHo5`)=-fMsL-^2UCK^jd}&Yg3pztv-*c`(8etRM8O9SxrV*LF z4g|rUW-y8vgYRE&G({x{0E5u#Pt^&a3A6HN&Cup%aKb7C!r7n-n~3xW_H#a#Zw!RG zgG4J%MHtaQq^%(Ps^D8zfX(YGz@xUDbC7DrJ?T#_hy-?dm_qTk{(>~y{bkInD!!fF zy9$_~P;=)~y|w7ML#K5e+Ytr#+qjT4Wf~QRY!YWG(S&_VxE=2n&2By}V(GGD3@-5% zo^0UU1B2>>eNmV-Ydv`L;7AquUl2Lo$T)owzY_!Y2XeX!;3fSjEutH?+k-OduFBz2S5O0_P=@3+M(LBoiYt zXk{j6tF96&=q5BoRH)KjZ=5R@X%+kOj^L!}yF#$+mEw3X6A?fcJ;|-yGs!Mp#Sp6& ztrR<=D{+Wc%n?(;C|Y;L;E`V?QgoghXkK#>{#wGdf}fV2@sKw%Q|OA0xk_;d;H z!8Rqgt4cvF!>C z0o6?L)0YKh0v3VEZ8vE1(1qAr<1ldeD^?;H8Wox|eyNJTW+nrs2qEatY}|Wm(-yMO z#ihQ9&hPnxjOfYHW{;lOlh&*?QYVcNVY$VO2=#<7O&_3gHH-tFT!|t^oxK<3cf;ozWmHYm8NxT~LF!uX=urpv5z- zYC$7VlHU?qI_ob`+J1Ir8kZ*|0?@_S8-|J+Ny>`S-J^wwCZSSW9M-jw1T*VKFsLv! zk1%OdyZh&8FR-uQntIw)MB>FhNIVbQHWK)5ss$7LayJPi>($EURFMT7F7Z=%W~s$~ z+=Q?*YJ*eFJw=6_M}$l1soxf+JNK_I%Q~ z?VOMVj0bVc%Iy>JrU`*4fm(^*8@crUmm_yEk`QAM~BC}8w0eFWDCRZAdY zg7pR)+0tjW@(6>XHd*9_c5=)0GvsD$z7i`Ta#(5@oC*n91~7qJTx?dzSiT2h_FB@Sorn=X3MC6OPr5$MXwFT)#144|rn#40|YDhy0(3HQim^7C7kV<=0v*L8 zXxlhcArU>2T`UrI51&>u3&XUk8K+eFEVJ!b4gIzKe~K(4wKR!?n{gr(IIAoJ2qI&e zjHO8|B7cI|=!Yi%$gKL27Y_vDdL82rszlI$blytw3MOF!=5@;n6Pb)4V~`Jfi+uZp z#8`ceaK`d*AG-mN3nF*Oox8MjYAd6~hV?jjPU+hKf{8I#N$OOlg}`@CA`@wV!?dt7 z-cP&=P@W~;L6Uum*eOuAg<_vkFp&XzNKuMJ1!{z02nBQ5P2+CHYzEdBGPLhlON+i= zOV4a}^G?Ac6fu`WEr3GULB=3kU`;_q+RM!D>k-hOr1Y9{HSLQiKI?UcVV1~B_M)5|3%Ve}`(PQfqs$O$B!Y_pl4 z6==7L9#9TKh8$G!w9Kvz8JxM2K+KR`>7}UbL_Q;k7QI|$Is(CR+`bHJXi-e_82MW> z))Xy@iH=C7%pPH43g=qGbmin7o4TMGT)9GGXiD2>K?s9k@r1RqYKULYII06$!VF9P znzTnGn!F_Rw(4{Qp|=g{baH+u`M{jF@nOD8cZrews<+H`qG(#2NQfS zF9u+v1#}Z&WKU4g2uthAjF`u&fVCIQVb3!*&*W-dM3>*TV4j_RF?Md51^hjfn!l)U?4lu&3HXOz1xqfCB za+Vk=kMcr=WtljBvgKSDl08wFva6uoOq-0fK9^{- zwG9PgBT(LMcY$j-WO~DC8O$WMln9jV2a|(=rpYq^+4^G000ig2p!6Y%vSQ<_py=pe zVJ+k*`7vIDZKXbQVoH6APWli6)bzxR-fgFVl|Bnb)nBE{JKe*9$#rRpcb0@g*8qNt zidSIQKNwxj;RRuf{WwyYIVo%plR&uRG#wJy;wk6jSWB%r-?m0T2i8%n7>AvB=FoW1U1I~+YEHSTSMV_ugvZC>FXRzOL+ImKstq~;6c&ig zeFv&Adu-T4^mR0{4Ognun0DQSkw)g=4>Y}cjqWcclA@{N?Cgk$uw zpq*GFto#*2Q#)GSC z5f~E~a&L)BU^4<7>dJ!mjz+?mLcG7k2zjPBKP84s8h+le_NEO4A}qc(3X+MGk&Ry* z3a_hN+qBh=Wh&QnnFp;s^Wc$D3)YyMEcM~LdYA-6MP49Ea7YEYeiaJikkicWb-`tU zSF;NX>Iv609T-VL4o!)DY6saPi*w!!oo`!M!KmSqZtPqLzvnCM*;UfLV!>xn~K0KSPionmdz>Utk_OGi=4+F?ASszP#z*SR<12RV3td?lAe@Zd=Boq zzhJ)t+^1LbRLA)uedvYAB+4K0DE+#k@5Y|@4Bl9TobjVRs1S+zM+a?8}V`QRW4^5giJRPJ9+D<>T zDXH*p%?XF$^p|Lv=Vx0a*iTuO5LvSL5x~K$#;G#A3_i|beeR^-4nTkbRE+azZs)~K z;v#7T0pqApiS9QQe#+9Y&+Zo-Ye?7VLA^&$)lOL$)V>yp&^vaE6cmKN#BNvIm`VhB zTkYYIcfS69fjV&!jah5+s+Oc3}ZsWRqyKbzg(k5jtxW@c0ttI4_Jr=7KAw2 z*kP>CXXMv4i^GE@`&-zqLD*Cn#Bu_KG_{H?I_PtP1z8bH0eJ!q(9Ov3JU~g*+7enC zIYe$PE~gN$FLlN!FT%VQ(E*9&piVRcu(?ExS%ljI*w|z(ScQ9`9Uc8(6c##@GA?*k z@+Tx~aQU$}&ax72fU?@}tbGYW3g0k0k?(-w&0RqMEnX|pS@sET)fcXJbaaWxmo+IR zD2AW0pFml5?(gzhu=qxR8Q~qkVZ;K}b5_AxVBc2V0BzAY30uYZ4xy zoO6)PzO$teX|E;WJKuZ(gDx#n7wC#xqgc0gv1yyA{Z%$ZBt_0KzvX&vy*k9j;86n_}ZB)UdaH~N9>v1#Vah^Gc!XRt|+hVEC@_O3P6j_iUn@Ono7beWK zCbbZ=Z^C*WsK(>Q7BJH_Cr0|X9H@pnAc#$%;f2!bQUs3qD0sAA?)Z^;Hvf@ZMD~2h= zG6q%`7BP_FT(Rr{iQqaJxz&Z5X3M_4(F_tEAvcrGvf2=|KM$I*g8_l%&jgNwDeM7G z))tmvpM`Ax&)k3u9FaF%@9pfwE3{TAl3 z%PS)x7+musz%kT9&DtW1qd>k&bW|j85VBB=Vl`p=%2F@MX9@<5Pl8=l4gL>zY^>hv zCpOI3$X@!XV;{Uemo%0dQ62UiYuaV4t6wQuHDJ#+$uOmAtR{>r`mX6}$$!IG$?n3P za6uf2v5m-)X)T6f#+xx6<7?Mp#-cD;GLmFNWt<`WV?L0cSU6TIz86+jGi;l&PS9Nz z1@{OfR4^k@AS#>nv@x;AVXPL7gkS7ZuwUUMlChOFr@hCz&9WGdu$^f$hlYZjTfSk$ zmuIEg1;BVsyt^R2Oe6j20(;2(2bXS>K5A4!2yCu(Ag5RWKxg`1S=Ix028_)@-DM2tWQYXMLj4n!Doe;s@w9cHPW@??V~=%AtA*u>VDnjz%N(dI{R#)vBZ zMMzj6uk%ZeAUcUrjx=CZD23wFAojVgwQPqvHBi}!IP9a%$4wJYMXy?EO|=bvW&9AThTU2Fcp>3N*T+-P90%{aGe|_ z#;_uRnMLSGOr^LYYC)Y5CHAd20nFqvj6jSapQR}p{p;pLMCw{@6@pp#|dg4$4vTFoUirG4`n zmKFKlvbe5s%|7%-H3q9gv^O#~QAh8v6z$RMLbArx(7)=HR*}5K7+P8n4ri}9scaZu zLgsN8N|MO4DL}EU36q5{usVo`8AG)XS$MAn8a_^mzJn|+?DI=o;Gpfbwo&RYSqa&N zY?0jSjMN;Nx32xL)MoB%K2x)LgLeZK=}4#WWwhZ-Wz!YTjB3&%KHcCaTn***Q@S8j zCa%ENdU%yDu*}hkfF+~k0r*WaCNytVAt>cGT~s1A-x3ocqUYO#%G-Qvov*yu8x1 z?S|v5+DQV!&}ed(RKm6pI)Hety@(jt&~yeqkilNz92r7!Ds(-04{|POu z%fXJ8TUGTS4{MMrx9|tGjQBb?ry^~sLe6gBFb=s2P}@GO)|l}}cVAeCdVC2^ZB0;Q zL=WbAUt>~Ffy-}uP!s_V9vWjoY1yc6?HIXy)G!4AX0kie!>w)Ib{$J-#B?lZy0wkP z_lwzeATQ@utm9+K^#vc@)j7ThH{9N77N9%}2M)&u@l*9K4B#%&(Stk9N-UOPIKH!# z7!;UI^&&egdAEHCciV?>$GJ@?dlyD>$F>0>N(LV~NUmT;N<~F8Lw8%p`l}$cjEjCa z{io`$Om974LA08mf(k7B%BFVz3nO;V{*H6L#F}!~_e&BUEGu6m*$-I71-Ea%3<>=! zzN6fVUcaiub+Lphe<4tzW64up{{6L`7dW<)!If`Tlh=0@JQ((^;P~?T(~z+g#aPi= zWaT?s&rP!m&WZYc&ELLy`JDDF?*SQ6`1#5Y7m}MSsVuV!C)AJaeCfhI^eok?77mJ5 zIBs-T(F!mMjqBwKC*l_;8sFyc z6Rk$yD-7FGbfx>o_b2%tg?n#oQBF&6driTx_c<2kZ?LY7do8%vO!{M#%y|88bfj4Xg zdf*L_n+M+L-ld0-1IIJx*@6R0HnPTTPWcn_akjsh-EC>e601H`-V3LRx4qxs~q>s2V2? zOhawy!m|Nvd6E8Hzund?OUC5M+BUH8W(UG`0()Cm(wN~e@AigyA6E6YeZJ^P^KBhH zhi3gu%+|O1Y)gYWw6U$&vbJn_;d@+@o@wYU@3*VQz*k$9V|X|B-9N@LTiV^BJx|yw zu;mM_=WZ*8M>Lsy;FWJN-}X`~c3aC?uiILl-E1k25wC3!^Nu|OG5OW@kImWI#;bX# zY0G!(GpcP}n;X34OY^bHprh~60Rb^{oo+lwh!$R zgI@5VdtoQ_mWrih&0F8DoAA*4U|scv(tVmHRNIv9P`oKkp?E9bhj-5R8?Hor$EF_B zc8uyvyeF2TE=%6pSMCFun^eSsRL5mXgkGmt#a1>dcEgF@KHP#spgQvFR$4*AuTxZ4 zWnX4Da@9N`!bIdwDR$D<^OlMKl#@qW&(2K_}{S)Ma|2ft=Q4&{cJQU2Ezw;@yru5tTf_2@o;PP#t=M5o_$X+fIXZey{YR-Hz9ImX^&M2JvH@YWx5{GJYt<=hwwH`O|Sc+Hk)8&xG z=tZt)rKs2DxKaQo)Fh;N4W*-+)_${)E1NZZ&t=Wx`a_PqQp$ytwRPo9)mp7R*s8a{ z4oOI9mhDanFD*BxI8GkF25DOqJ+MUjLnUBzwjxA=n1&3AmJU!NY5`UZg?SA$$D?QH zYw0nX4?plxVm98{4ia_fVR&Lv6vkk;&MRnB zH{!>(9J{gULIEgSk!Sfxp2rz>80<(BmnG`jOoAFGi7rsv8Xbb1l>!P9-o87nbtWAmlP3Q2UvMwsCi%1k2$Ky zNhS{i@g;T86zgpPID1lNDD~q(D?u%>64(sdij{!g9%8;F!rcCwC1$a+{m}eOJG)4x zN={*k34HT3iTZUrg7R~2Z!cGshkDP>L6JmTHEdWq_#m-+Mh>%VgQ16}*!oDI) z1FjG&0Wm3oO6A^yZ(r`q59$YxdK@3p**bpO`8GAc$9{ZGOpzZ`vtu(6XlgJs)fs5vE_m|s+0HomY6+{VYs2&? zr3r%dV+Vlvj-B5&M-~bcS&yrU|2wNG?I@BAnw{dWp5?H?&puc@CoU0;L)Uf9?9)GI zm7;WrM5iC@N)OXQK>+fA0f@BK5Y~T02k%4eCw}%IY@BCXUfe=n>?RMp#-**a#AmJ~ zEg(O>Tb!W$I_$K|TA`mkKuznpIJ6!%6{ENTgo@hftL2TD+0bShNN^YdzMF4E)5b8{ zRmR4^xb;hQaC^1mI>#v5qM6|Ib)$CY?|=XEe?R%3sxcQu literal 0 HcmV?d00001 diff --git a/doc/source/handling_examples/searchindex.dir b/doc/source/handling_examples/searchindex.dir new file mode 100644 index 000000000..38a7775ad --- /dev/null +++ b/doc/source/handling_examples/searchindex.dir @@ -0,0 +1,3 @@ +'/home/atom/code/devel/libs/geoutils/doc/build/index.html', (0, 35132) +'/home/atom/code/devel/libs/geoutils/doc/build/_static/documentation_options.js', (35328, 440) +'/home/atom/code/devel/libs/geoutils/doc/build/searchindex.js', (35840, 48486) diff --git a/doc/source/handling_examples/sg_execution_times.rst b/doc/source/handling_examples/sg_execution_times.rst new file mode 100644 index 000000000..426de5f9d --- /dev/null +++ b/doc/source/handling_examples/sg_execution_times.rst @@ -0,0 +1,16 @@ + +:orphan: + +.. _sphx_glr_handling_examples_sg_execution_times: + +Computation times +================= +**00:00.286** total execution time for **handling_examples** files: + ++-----------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_handling_examples_read_vector.py` (``read_vector.py``) | 00:00.114 | 0.0 MB | ++-----------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_handling_examples_read_raster.py` (``read_raster.py``) | 00:00.096 | 0.0 MB | ++-----------------------------------------------------------------------+-----------+--------+ +| :ref:`sphx_glr_handling_examples_read_satimg.py` (``read_satimg.py``) | 00:00.077 | 0.0 MB | ++-----------------------------------------------------------------------+-----------+--------+ diff --git a/doc/source/index.md b/doc/source/index.md index 68119783c..dc53edabe 100644 --- a/doc/source/index.md +++ b/doc/source/index.md @@ -40,7 +40,9 @@ proj_tools :caption: Examples :maxdepth: 2 -auto_examples/index.rst +io_examples/index +handling_examples/index +analysis_examples/index ``` ```{toctree} diff --git a/doc/source/io_examples/images/sphx_glr_read_raster_001.png b/doc/source/io_examples/images/sphx_glr_read_raster_001.png new file mode 100644 index 0000000000000000000000000000000000000000..a04171162487e0202a3a79e5acfd07f988727f6d GIT binary patch literal 213447 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