diff --git a/.gitignore b/.gitignore index b6e4761..4a010d7 100644 --- a/.gitignore +++ b/.gitignore @@ -103,6 +103,7 @@ celerybeat.pid # Environments .env +.env.b64 .venv env/ venv/ diff --git a/pyproject.toml b/pyproject.toml index 45934bb..0940782 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta" [project] name = "geoapis" -version = "0.2.9" +version = "0.3.0" description = "A package for downloading geospatial data from web APIs." readme = "README.md" authors = [{ name = "Rose pearson", email = "rose.pearson@niwa.co.nz" }] diff --git a/src/geoapis/__init__.py b/src/geoapis/__init__.py index 689ad13..78b78b1 100644 --- a/src/geoapis/__init__.py +++ b/src/geoapis/__init__.py @@ -4,4 +4,4 @@ @author: pearsonra """ -__version__ = "0.2.9" +__version__ = "0.3.0" diff --git a/src/geoapis/geometry.py b/src/geoapis/geometry.py index 6c6e78a..e3e4f2f 100644 --- a/src/geoapis/geometry.py +++ b/src/geoapis/geometry.py @@ -23,13 +23,13 @@ def __init__( self._set_up() def _set_up(self): - """Set CRS and select all tiles partially within the catchment, and look up the file column name""" + """Set CRS and select all tiles partially within the catchment, and look up the + file column name""" if self.catchment_polygon is not None: self._tile_info = self._tile_info.to_crs(self.catchment_polygon.crs) self._tile_info = geopandas.sjoin(self._tile_info, self.catchment_polygon) self._tile_info = self._tile_info.reset_index(drop=True) - # Try workout the name of the column containing file name information. column_names = self._tile_info.columns column_name_matches = [ diff --git a/src/geoapis/lidar.py b/src/geoapis/lidar.py index 3168e4a..59cca11 100644 --- a/src/geoapis/lidar.py +++ b/src/geoapis/lidar.py @@ -162,7 +162,8 @@ def query_for_datasets_inside_catchment(self): return response.json() def download_dataset(self, dataset_prefix, client): - """Download all files within an optional search polygon of a given dataset_prefix""" + """Download all files within an optional search polygon of a given + dataset_prefix""" if self.verbose: print(f"Check files in dataset {dataset_prefix}") diff --git a/src/geoapis/raster.py b/src/geoapis/raster.py new file mode 100644 index 0000000..43e8a95 --- /dev/null +++ b/src/geoapis/raster.py @@ -0,0 +1,241 @@ +# -*- coding: utf-8 -*- +""" +Created on Fri Nov 7 10:10:55 2022 + +@author: pearsonra +""" + +import urllib +import requests +import numpy +import geopandas +import abc +import typing +import pathlib +import logging +import time +import io +import zipfile + + +class KoordinatesExportsQueryBase(abc.ABC): + """An abstract class to manage fetching Raster data using the Koordinates exports + API. Downloads the GeoTiff specified in the run routine. + + API details at: https://help.koordinates.com/site-admin-apis/export-api/ + + Parameters + ---------- + + key: str + The API key. Must have Search, view and download exported data + permissions. + + cache_path: pathlib.Path + The location to download all GeoTiffs queried in the run method. + + crs: int + The CRS EPSG code for the GeoTifss to be downloaded as. + + bounding_polygon: geopandas.geodataframe.GeoDataFrame + An option geometry to clip the downloaded GeoTiffs within. + + """ + + @property + @abc.abstractmethod + def NETLOC_API(): + """This should be instantiated in the base class. Provide the netloc of the data + service.""" + + raise NotImplementedError("NETLOC_API must be instantiated in the child class") + + SCHEME = "https" + PATH = "services/api/v1" + PATH_API_END = "/exports" + K_CRS = "EPSG:4326" + + def __init__( + self, + key: str, + cache_path: typing.Union[str, pathlib.Path], + crs: int = None, + bounding_polygon: geopandas.geodataframe.GeoDataFrame = None, + ): + """Load in the wfs key and CRS/bounding_polygon if specified. Specify the layer + to import during run.""" + + self.key = key + self.cache_path = pathlib.Path(cache_path) + self.bounding_polygon = bounding_polygon + self.crs = crs + + self.base_url = urllib.parse.urlunparse( + ( + self.SCHEME, + self.NETLOC_API, + self.PATH, + "", + "", + "", + ) + ) + + self._set_up() + + def _set_up(self): + """Ensure the bouding_polygon and CRS are in agreement.""" + + # Set the crs from the bounding_polygon if it's not been set + if self.crs is None and self.bounding_polygon is not None: + logging.info("The download CRS is being set from the bounding_polygon") + self.crs = self.bounding_polygon.crs.to_epsg() + # Set the bounding_polygon crs from the crs if they differ + if ( + self.bounding_polygon is not None + and self.crs != self.bounding_polygon.crs.to_epsg() + ): + logging.info( + "The bounding_polygon is being transformed to the specified " + "download CRS" + ) + self.bounding_polygon.to_crs(self.crs) + # Enforce the bounding_polygon must be a single geometry if it exists + if self.bounding_polygon is not None: + assert ( + len(self.bounding_polygon) == 1 + ), "The bounding polygon must be a single geometry" + assert ( + len(numpy.array(self.bounding_polygon.exterior.loc[0].coords)) < 1000 + ), "The bounding polygon must be lass than 1000 points" + + def run(self, layer: int) -> pathlib.Path: + """Query for a specified layer and return a geopandas.GeoDataFrame of the vector + features. If a polygon_boundary is specified, only return vectors passing + through this polygon.""" + + headers = {"Authorization": f"key {self.key}"} + + # Create the initial request + api_query = { + "crs": f"EPSG:{self.crs}", + "formats": {"grid": "image/tiff;subtype=geotiff"}, + "items": [{"item": f"{self.base_url}/layers/{layer}/"}], + } + if self.bounding_polygon is not None: + exterior = self.bounding_polygon.to_crs(self.K_CRS).exterior.loc[0] + api_query["extent"] = { + "type": self.bounding_polygon.type.loc[0], + "coordinates": [list(exterior.coords)], + } + logging.info("Send initial request to download image") + response = requests.post( + url=f"{self.base_url}/exports/", headers=headers, json=api_query + ) + query_id = response.json()["id"] + + # Check the state of your exports until the triggered raster exports completes + logging.info("Check status of download request") + while True: + response = requests.get( + f"{self.base_url}/exports/", + headers=headers, + ) + # find the triggered export + element = [ + element for element in response.json() if element["id"] == query_id + ][0] + logging.info(f"/texport state is {element['state']}") + if element["state"] == "processing": + logging.info("Not complete - check again in 20s") + time.sleep(20) + continue + elif element["state"] == "complete": + logging.info("/tCompleted - move to download") + break + else: + logging.warning( + f"Could not download raster. Ended with status {element['state']}" + ) + return + # Download the completed export + logging.info(f"Downloading {element['download_url']} to {self.cache_path}") + with requests.get( + element["download_url"], + headers={"Authorization": f"key {self.key}"}, + stream=True, + ) as response: + response.raise_for_status() + zip_object = zipfile.ZipFile(io.BytesIO(response.content)) + zip_object.extractall(self.cache_path / f"{layer}") + # Return the file names of the downloaded rasters + rasters = [] + for file_name in (self.cache_path / f"{layer}").iterdir(): + if file_name.suffix == ".tif": + rasters.append(file_name) + return rasters + + +class Linz(KoordinatesExportsQueryBase): + """A class to manage fetching Vector data from LINZ. + + LIRS data service can be accessed at: https://https://data.linz.govt.nz/ + + Note that only rasters supporting the grid image/tiff geotiff are supported + """ + + NETLOC_API = "data.linz.govt.nz" + + +class Lris(KoordinatesExportsQueryBase): + """A class to manage fetching Vector data from LRIS. + + LIRS data service can be accessed at: https://lris.scinfo.org.nz/ + + Note that only rasters supporting the grid image/tiff geotiff are supported + """ + + NETLOC_API = "lris.scinfo.org.nz" + + +class StatsNz(KoordinatesExportsQueryBase): + """A class to manage fetching Vector data from the Stats NZ datafinder. + + Stats NZ data service can be accessed at: datafinder.stats.govt.nz + + Note that only rasters supporting the grid image/tiff geotiff are supported + """ + + NETLOC_API = "datafinder.stats.govt.nz" + + +class KoordinatesQuery(KoordinatesExportsQueryBase): + """A class to manage fetching Vector data from any generic data portal supporting + WFS. + + Note that the 'geometry_name' used when making a WFS 'cql_filter' queries can vary + between layers. You will need to specify the 'geometry_name' of the layers you want + to download. + """ + + def __init__( + self, + key: str, + netloc_url: str, + crs: int = None, + bounding_polygon: geopandas.geodataframe.GeoDataFrame = None, + ): + """Set NETLOC_API and instantiate the KoordinatesExportsQueryBase""" + + self.netloc_url = netloc_url + + # Setup the WfsQueryBase class + super(KoordinatesQuery, self).__init__( + key=key, crs=crs, bounding_polygon=bounding_polygon + ) + + @property + def NETLOC_API(self): + """Instantiate the entered netloc of the data service.""" + + return self.netloc_url diff --git a/tests/test_lidar_subfolders/test_lidar_subfolders.py b/tests/test_lidar_subfolders/test_lidar_subfolders.py index afbbdf5..d5e228b 100644 --- a/tests/test_lidar_subfolders/test_lidar_subfolders.py +++ b/tests/test_lidar_subfolders/test_lidar_subfolders.py @@ -17,22 +17,28 @@ class OpenTopographyTestSubfolders(unittest.TestCase): - """A class to test the basic lidar.OpenTopography functionality by downloading files from - OpenTopography within a small region. All files are deleted after checking their names and size. + """A class to test the basic lidar.OpenTopography functionality by downloading files + from OpenTopography within a small region. All files are deleted after checking + their names and size. Tests run include: - 1. test_correct_datasets - Test that the expected dataset is downloaded from OpenTopography - 2. test_correct_files_downloaded - Test the downloaded LIDAR files have the expected names - 3. test_correct_file_sizes - Test the downloaded LIDAR files have the expected file sizes + 1. test_correct_datasets - Test that the expected dataset is downloaded from + OpenTopography + 2. test_correct_files_downloaded - Test the downloaded LIDAR files have the + expected names + 3. test_correct_file_sizes - Test the downloaded LIDAR files have the expected + file sizes """ - # The expected datasets and files to be downloaded - used for comparison in the later tests + # The expected datasets and files to be downloaded - used for comparison in the + # later tests DATASETS = [ "NZ18_Banks", "NZ18_AmuriCant", "NZ18_Canterbury", "Chch_Selwn_2015", "Chch_Selwn_2015/NZ_Christchurch", + "NZ20_Canterbury", ] FILE_SIZES = { DATASETS[0]: {f"{DATASETS[0]}_TileIndex.zip": 134113}, @@ -43,12 +49,16 @@ class OpenTopographyTestSubfolders(unittest.TestCase): }, DATASETS[3]: {f"{DATASETS[3]}_TileIndex.zip": 221422}, DATASETS[4]: {"ot_CL2_BX24_2015_1000_2520.laz": 10761065}, + DATASETS[5]: { + "CL2_BX24_2020_1000_2520.laz": 25891330, + f"{DATASETS[5]}_TileIndex.zip": 120930, + }, } @classmethod def setUpClass(cls): - """Create a cache directory and CatchmentGeometry object for use in the tests and also download the files used - in the tests.""" + """Create a cache directory and CatchmentGeometry object for use in the tests + and also download the files used in the tests.""" # load in the test instructions file_path = pathlib.Path().cwd() / pathlib.Path( @@ -56,13 +66,13 @@ def setUpClass(cls): ) with open(file_path, "r") as file_pointer: instructions = json.load(file_pointer) - # define cache location - and catchment dirs cls.cache_dir = pathlib.Path( instructions["instructions"]["data_paths"]["local_cache"] ) - # ensure the cache directory doesn't exist - i.e. clean up from last test occurred correctly + # ensure the cache directory doesn't exist - i.e. clean up from last test + # occurred correctly cls.tearDownClass() cls.cache_dir.mkdir(exist_ok=True) @@ -72,31 +82,23 @@ def setUpClass(cls): y0 = 5172000 y1 = 5172200 catchment = shapely.geometry.Polygon([(x0, y0), (x1, y0), (x1, y1), (x0, y1)]) - catchment = geopandas.GeoSeries([catchment]) - catchment = catchment.set_crs(instructions["instructions"]["projection"]) - - # save faked catchment boundary - catchment_dir = cls.cache_dir / "catchment" - catchment.to_file(catchment_dir) - shutil.make_archive( - base_name=catchment_dir, format="zip", root_dir=catchment_dir + catchment = geopandas.GeoDataFrame( + geometry=[catchment], crs=instructions["instructions"]["projection"] ) - shutil.rmtree(catchment_dir) - # create a catchment_geometry - catchment_dir = pathlib.Path(str(catchment_dir) + ".zip") - catchment_polygon = geopandas.read_file(catchment_dir) - catchment_polygon.to_crs(instructions["instructions"]["projection"]) + # save faked catchment boundary + catchment.to_file(cls.cache_dir / "catchment.geojson") # Run pipeline - download files runner = lidar.OpenTopography( - cache_path=cls.cache_dir, search_polygon=catchment_polygon, verbose=True + cache_path=cls.cache_dir, search_polygon=catchment, verbose=True ) runner.run() @classmethod def tearDownClass(cls): - """Remove created cache directory and included created and downloaded files at the end of the test.""" + """Remove created cache directory and included created and downloaded files at + the end of the test.""" if cls.cache_dir.exists(): shutil.rmtree(cls.cache_dir) @@ -110,9 +112,10 @@ def test_correct_datasets(self): self.assertEqual( len(list(self.cache_dir.glob("*/**"))), len(dataset_dirs), - "There should be " - + f"{len(dataset_dirs)} dataset folders created (including nested folders) instead there are" - + f" {len(list(self.cache_dir.glob('*/**')))} created: {list(self.cache_dir.glob('*/**'))}", + f"There should be {len(dataset_dirs)} dataset folders created (including " + "nested folders) instead there are " + f"{len(list(self.cache_dir.glob('*/**')))} created: " + f"{list(self.cache_dir.glob('*/**'))}", ) self.assertEqual( diff --git a/tests/test_raster_linz_in_bounds/__init__.py b/tests/test_raster_linz_in_bounds/__init__.py new file mode 100644 index 0000000..60b194b --- /dev/null +++ b/tests/test_raster_linz_in_bounds/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Jun 29 14:33:10 2021 + +@author: pearsonra +""" diff --git a/tests/test_raster_linz_in_bounds/instruction.json b/tests/test_raster_linz_in_bounds/instruction.json new file mode 100644 index 0000000..6a8a1b7 --- /dev/null +++ b/tests/test_raster_linz_in_bounds/instruction.json @@ -0,0 +1,16 @@ +{ + "instructions": + { + "projection": 2193, + "data_paths": { + "local_cache": "tests/test_raster_linz_in_bounds/data" + }, + "apis": { + "linz": { + "1": { + "layers": 51768 + } + } + } + } +} diff --git a/tests/test_raster_linz_in_bounds/test_raster_linz_in_bounds.py b/tests/test_raster_linz_in_bounds/test_raster_linz_in_bounds.py new file mode 100644 index 0000000..5a7c2e7 --- /dev/null +++ b/tests/test_raster_linz_in_bounds/test_raster_linz_in_bounds.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jun 30 11:11:25 2021 + +@author: pearsonra +""" + +import unittest +import json +import pathlib +import shapely +import geopandas +import shutil +import dotenv +import os +import logging + +from src.geoapis import raster + + +class LinzRasterTest(unittest.TestCase): + """A class to test the basic raster.Linz functionality by downloading files from + the dataservice within a small region. + + Tests run include (test_#### indicates the layer tested): + * test_51768 - Test the specified layer features are correctly downloaded within + the specified bbox + See the associated description for keywords that can be used to search for the layer + in the data service. + """ + + # Datasets and files to be downloaded - used for comparison in the later tests + RASTER_1 = {"size": 1791242, "name": "MG.tif", "number": 1} + + @classmethod + def setUpClass(cls): + """Create a cache directory and CatchmentGeometry object for use in the tests + and also download the files used in the tests.""" + + # load in the test instructions + file_path = pathlib.Path().cwd() / pathlib.Path( + "tests/test_raster_linz_in_bounds/instruction.json" + ) + with open(file_path, "r") as file_pointer: + cls.instructions = json.load(file_pointer) + # Load in environment variables to get and set the private API keys + dotenv.load_dotenv() + linz_key = os.environ.get("LINZ_API", None) + cls.instructions["instructions"]["apis"]["linz"]["key"] = linz_key + + # define cache location - and catchment dirs + cls.cache_dir = pathlib.Path( + cls.instructions["instructions"]["data_paths"]["local_cache"] + ) + + # makes sure the data directory exists but is empty + if cls.cache_dir.exists(): + shutil.rmtree(cls.cache_dir) + cls.cache_dir.mkdir() + + # create fake catchment boundary + x0 = 1477354 + x1 = 1484656 + y0 = 5374408 + y1 = 5383411 + catchment = shapely.geometry.Polygon([(x0, y0), (x0, y1), (x1, y1), (x1, y0)]) + catchment = geopandas.GeoSeries([catchment]) + catchment = catchment.set_crs(cls.instructions["instructions"]["projection"]) + + # save faked catchment file + catchment.to_file(cls.cache_dir / "catchment.geojson") + + # Run pipeline - download files + cls.runner = raster.Linz( + key=cls.instructions["instructions"]["apis"]["linz"]["key"], + crs=None, + bounding_polygon=catchment, + cache_path=cls.cache_dir, + ) + + @classmethod + def tearDownClass(cls): + """Remove created cache directory.""" + + if cls.cache_dir.exists(): + shutil.rmtree(cls.cache_dir) + + def compare_to_benchmark( + self, + file_names: list, + benchmark: dict, + description: str, + ): + """Compare the various attributes of the raster (total number of files, name, + file size) against those recorded in a benchmark.""" + + # check various raster attributes match those expected + self.assertEqual( + len(file_names), + benchmark["number"], + f"The number of the downloaded rasters {len(file_names)} does not match the" + f" expected benchmark number of {benchmark['number']}", + ) + self.assertEqual( + file_names[0].name, + benchmark["name"], + f"The name of the downloaded raster {file_names[0].name} does not match the" + f" expected benchmark name of {benchmark['name']}", + ) + self.assertEqual( + file_names[0].stat().st_size, + benchmark["size"], + f"The size of the downloaded raster {file_names[0].stat().st_size} does not" + f" match the expected benchmark name of {benchmark['size']}", + ) + + def test_1(self): + """Test expected features of layer loaded""" + + logging.info("In test_1") + print(self.instructions) + file_names = self.runner.run( + self.instructions["instructions"]["apis"]["linz"]["1"]["layers"], + ) + benchmark = self.RASTER_1 + description = "nz 8m DEM 2012" + # check various shape attributes match those expected + self.compare_to_benchmark(file_names, benchmark, description) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_raster_lris_in_bounds/__init__.py b/tests/test_raster_lris_in_bounds/__init__.py new file mode 100644 index 0000000..60b194b --- /dev/null +++ b/tests/test_raster_lris_in_bounds/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +""" +Created on Tue Jun 29 14:33:10 2021 + +@author: pearsonra +""" diff --git a/tests/test_raster_lris_in_bounds/instruction.json b/tests/test_raster_lris_in_bounds/instruction.json new file mode 100644 index 0000000..1224f06 --- /dev/null +++ b/tests/test_raster_lris_in_bounds/instruction.json @@ -0,0 +1,16 @@ +{ + "instructions": + { + "projection": 2193, + "data_paths": { + "local_cache": "tests/test_raster_lris_in_bounds/data" + }, + "apis": { + "lris": { + "1": { + "layers": 48081 + } + } + } + } +} diff --git a/tests/test_raster_lris_in_bounds/test_raster_lris_in_bounds.py b/tests/test_raster_lris_in_bounds/test_raster_lris_in_bounds.py new file mode 100644 index 0000000..102d0a2 --- /dev/null +++ b/tests/test_raster_lris_in_bounds/test_raster_lris_in_bounds.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +""" +Created on Wed Jun 30 11:11:25 2021 + +@author: pearsonra +""" + +import unittest +import json +import pathlib +import shapely +import geopandas +import shutil +import dotenv +import os +import logging + +from src.geoapis import raster + + +class LrisRasterTest(unittest.TestCase): + """A class to test the basic raster.Linz functionality by downloading files from + the dataservice within a small region. + + Tests run include (test_#### indicates the layer tested): + * test_51768 - Test the specified layer features are correctly downloaded within + the specified bbox + See the associated description for keywords that can be used to search for the layer + in the data service. + """ + + # Datasets and files to be downloaded - used for comparison in the later tests + RASTER_1 = {"size": 4630, "name": "lenz-slope.tif", "number": 1} + + @classmethod + def setUpClass(cls): + """Create a cache directory and CatchmentGeometry object for use in the tests + and also download the files used in the tests.""" + + # load in the test instructions + file_path = pathlib.Path().cwd() / pathlib.Path( + "tests/test_raster_lris_in_bounds/instruction.json" + ) + with open(file_path, "r") as file_pointer: + cls.instructions = json.load(file_pointer) + # Load in environment variables to get and set the private API keys + dotenv.load_dotenv() + lris_key = os.environ.get("LRIS_API", None) + cls.instructions["instructions"]["apis"]["lris"]["key"] = lris_key + + # define cache location - and catchment dirs + cls.cache_dir = pathlib.Path( + cls.instructions["instructions"]["data_paths"]["local_cache"] + ) + + # makes sure the data directory exists but is empty + if cls.cache_dir.exists(): + shutil.rmtree(cls.cache_dir) + cls.cache_dir.mkdir() + + # create fake catchment boundary + x0 = 1477354 + x1 = 1484656 + y0 = 5374408 + y1 = 5383411 + catchment = shapely.geometry.Polygon([(x0, y0), (x0, y1), (x1, y1), (x1, y0)]) + catchment = geopandas.GeoSeries([catchment]) + catchment = catchment.set_crs(cls.instructions["instructions"]["projection"]) + + # save faked catchment file + catchment.to_file(cls.cache_dir / "catchment.geojson") + + # Run pipeline - download files + cls.runner = raster.Lris( + key=cls.instructions["instructions"]["apis"]["lris"]["key"], + crs=None, + bounding_polygon=catchment, + cache_path=cls.cache_dir, + ) + + @classmethod + def tearDownClass(cls): + """Remove created cache directory.""" + + if cls.cache_dir.exists(): + shutil.rmtree(cls.cache_dir) + + def compare_to_benchmark( + self, + file_names: pathlib.Path, + benchmark: dict, + description: str, + ): + """Compare the various attributes of the raster (total number of files, name, + file size) against those recorded in a benchmark.""" + + # check various raster attributes match those expected + self.assertEqual( + len(file_names), + benchmark["number"], + f"The number of the downloaded rasters {len(file_names)} does not match the" + f" expected benchmark number of {benchmark['number']}", + ) + self.assertEqual( + file_names[0].name, + benchmark["name"], + f"The name of the downloaded raster {file_names[0].name} does not match the" + f" expected benchmark name of {benchmark['name']}", + ) + self.assertEqual( + file_names[0].stat().st_size, + benchmark["size"], + f"The size of the downloaded raster {file_names[0].stat().st_size} does not" + f" match the expected benchmark name of {benchmark['size']}", + ) + + def test_1(self): + """Test expected features of layer loaded""" + + logging.info("In test_1") + file_names = self.runner.run( + self.instructions["instructions"]["apis"]["lris"]["1"]["layers"], + ) + benchmark = self.RASTER_1 + description = "nz slopes" + # check various shape attributes match those expected + self.compare_to_benchmark(file_names, benchmark, description) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_vector_linz/test_vector_linz.py b/tests/test_vector_linz/test_vector_linz.py index 46c16b5..a38a41e 100644 --- a/tests/test_vector_linz/test_vector_linz.py +++ b/tests/test_vector_linz/test_vector_linz.py @@ -37,9 +37,9 @@ class LinzVectorsTest(unittest.TestCase): "id": [1775717, 1775718, 1775719, 1778938, 1778939], } PASTURAL_LEASE = { - "area": 13463923602.50064, + "area": 13278778764.94074, "geometryType": "MultiPolygon", - "length": 15868456.954778664, + "length": 15648834.84743058, "columns": ["geometry", "id", "lease_name"], "id": [12511, 12653, 12658, 12797, 12461], } diff --git a/tests/test_vector_linz_in_bounds/test_vector_linz_in_bounds.py b/tests/test_vector_linz_in_bounds/test_vector_linz_in_bounds.py index fd08785..41f76fa 100644 --- a/tests/test_vector_linz_in_bounds/test_vector_linz_in_bounds.py +++ b/tests/test_vector_linz_in_bounds/test_vector_linz_in_bounds.py @@ -41,9 +41,9 @@ class LinzVectorsTest(unittest.TestCase): # The expected datasets and files to be downloaded - used for comparison in the later tests LAND = { - "area": 150539796136.69177, + "area": 150539776091.31247, "geometryType": "Polygon", - "length": 6004326.598544762, + "length": 6002892.54900315, "columns": [ "geometry", "name",