diff --git a/workers/ohsome_quality_analyst/api.py b/workers/ohsome_quality_analyst/api.py index e19e0b13d..032307158 100644 --- a/workers/ohsome_quality_analyst/api.py +++ b/workers/ohsome_quality_analyst/api.py @@ -63,29 +63,32 @@ def int_or_str_param_type(value: str) -> Union[int, str]: raise ValueError("Given parameter is not a valid integer or string") -async def load_bpolys(bpolys: str) -> Union[Polygon, MultiPolygon]: +async def load_bpolys(bpolys: str) -> Feature: """Load as GeoJSON object, validate and check size of bpolys""" # TODO: Return API response with error message + + async def check_geom_size(geom): + if await db_client.get_area_of_bpolys(geom) > GEOM_SIZE_LIMIT: + raise ValueError( + "Input GeoJSON Object is too big. " + + "The area should be less than {0} sqkm.".format(GEOM_SIZE_LIMIT) + ) + bpolys = geojson.loads(bpolys) if bpolys.is_valid is False: raise ValueError("Input geometry is not valid") - - if isinstance(bpolys, Feature): - bpolys = bpolys["geometry"] - - if isinstance(bpolys, (Polygon, MultiPolygon)) is False: + elif isinstance(bpolys, Feature): + await check_geom_size(bpolys.geometry) + return bpolys + elif isinstance(bpolys, (Polygon, MultiPolygon)): + await check_geom_size(bpolys) + return Feature(geometry=bpolys) + else: raise ValueError( "Input GeoJSON Objects have to be of type Feature, Polygon or MultiPolygon" ) - if await db_client.get_area_of_bpolys(bpolys) > GEOM_SIZE_LIMIT: - raise ValueError( - "Input GeoJSON Object is too big. " - + "The area should be less than {0} sqkm.".format(GEOM_SIZE_LIMIT) - ) - return bpolys - @app.get("/indicator/{name}") async def get_indicator( @@ -184,7 +187,11 @@ async def _fetch_report( feature_id = int_or_str_param_type(feature_id) report = await oqt.create_report( - name, bpolys=bpolys, dataset=dataset, feature_id=feature_id, fid_field=fid_field + name, + feature=bpolys, + dataset=dataset, + feature_id=feature_id, + fid_field=fid_field, ) response = empty_api_response() diff --git a/workers/ohsome_quality_analyst/base/indicator.py b/workers/ohsome_quality_analyst/base/indicator.py index d0d225ce4..5577d55d5 100644 --- a/workers/ohsome_quality_analyst/base/indicator.py +++ b/workers/ohsome_quality_analyst/base/indicator.py @@ -7,11 +7,11 @@ from dataclasses import dataclass from datetime import datetime from io import StringIO -from typing import Dict, Literal, Optional, Union +from typing import Dict, Literal, Optional import matplotlib.pyplot as plt from dacite import from_dict -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.utils.definitions import ( INDICATOR_LAYER, @@ -63,11 +63,10 @@ class BaseIndicator(metaclass=ABCMeta): def __init__( self, layer_name: str, - bpolys: Union[Polygon, MultiPolygon], + feature: Feature, data: Optional[dict] = None, ) -> None: - - self.bpolys = bpolys + self.feature = feature self.data = data # setattr(object, key, value) could be used instead of relying on from_dict. diff --git a/workers/ohsome_quality_analyst/base/report.py b/workers/ohsome_quality_analyst/base/report.py index aab90fa7b..968f0f3f7 100644 --- a/workers/ohsome_quality_analyst/base/report.py +++ b/workers/ohsome_quality_analyst/base/report.py @@ -2,10 +2,10 @@ from abc import ABCMeta, abstractmethod from dataclasses import dataclass from statistics import mean -from typing import Dict, List, Literal, NamedTuple, Optional, Tuple, Union +from typing import Dict, List, Literal, NamedTuple, Optional, Tuple from dacite import from_dict -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.indicator import BaseIndicator from ohsome_quality_analyst.utils.definitions import get_metadata @@ -39,7 +39,7 @@ class BaseReport(metaclass=ABCMeta): def __init__( self, - bpolys: Union[Polygon, MultiPolygon, None] = None, + feature: Feature = None, dataset: Optional[str] = None, feature_id: Optional[int] = None, fid_field: Optional[str] = None, @@ -47,7 +47,7 @@ def __init__( self.dataset = dataset self.feature_id = feature_id self.fid_field = fid_field - self.bpolys = bpolys + self.feature = feature # Definies indicator+layer combinations self.indicator_layer: Tuple[IndicatorLayer] = [] diff --git a/workers/ohsome_quality_analyst/cli.py b/workers/ohsome_quality_analyst/cli.py index cdacd3f1a..9426d7f3b 100644 --- a/workers/ohsome_quality_analyst/cli.py +++ b/workers/ohsome_quality_analyst/cli.py @@ -137,7 +137,7 @@ def create_indicator( oqt.create_indicator( indicator_name, layer_name, - bpolys=feature.geometry, + feature=feature, feature_id=feature_id, dataset=dataset_name, fid_field=fid_field, @@ -156,7 +156,7 @@ def create_indicator( oqt.create_indicator( indicator_name, layer_name, - bpolys=None, + feature=None, feature_id=feature_id, dataset=dataset_name, fid_field=fid_field, @@ -166,12 +166,9 @@ def create_indicator( if outfile: if fid_field is None: fid_field = DATASETS[dataset_name]["default"] - feature_collection = asyncio.run( - db_client.get_bpolys_from_db(dataset_name, feature_id, fid_field) - ) - for feature in feature_collection.features: - feature = update_features_indicator(feature, indicator) - write_geojson(outfile, feature_collection) + feature = asyncio.run(db_client.get_region_from_db(feature_id, fid_field)) + feature = update_features_indicator(feature, indicator) + write_geojson(outfile, feature) else: click.echo(indicator.metadata) click.echo(indicator.result) @@ -203,7 +200,7 @@ def create_report( report = asyncio.run( oqt.create_report( report_name, - bpolys=feature.geometry, + feature=feature, dataset=dataset_name, feature_id=feature_id, fid_field=fid_field, @@ -221,7 +218,7 @@ def create_report( report = asyncio.run( oqt.create_report( report_name, - bpolys=None, + feature=None, dataset=dataset_name, feature_id=feature_id, fid_field=fid_field, @@ -231,12 +228,9 @@ def create_report( if outfile: if fid_field is None: fid_field = DATASETS[dataset_name]["default"] - feature_collection = asyncio.run( - db_client.get_bpolys_from_db(dataset_name, feature_id, fid_field) - ) - for feature in feature_collection.features: - feature = update_features_report(feature, report) - write_geojson(outfile, feature_collection) + feature = asyncio.run(db_client.get_region_from_db(feature_id, fid_field)) + feature = update_features_report(feature, report) + write_geojson(outfile, feature) else: click.echo(report.metadata) click.echo(report.result) diff --git a/workers/ohsome_quality_analyst/geodatabase/client.py b/workers/ohsome_quality_analyst/geodatabase/client.py index 439d093b5..4c265a9f8 100644 --- a/workers/ohsome_quality_analyst/geodatabase/client.py +++ b/workers/ohsome_quality_analyst/geodatabase/client.py @@ -21,7 +21,7 @@ import asyncpg import geojson -from geojson import FeatureCollection, MultiPolygon, Polygon +from geojson import Feature, FeatureCollection, MultiPolygon, Polygon from ohsome_quality_analyst.utils.definitions import DATASETS @@ -148,22 +148,21 @@ async def get_area_of_bpolys(bpolys: Union[Polygon, MultiPolygon]): return result["area_sqkm"] -# TODO: Return GeoJSON object of type Polygon or MultiPolygon not FeatureCollection -async def get_bpolys_from_db( - dataset: str, feature_id: Union[int, str], fid_field: str -) -> Union[Polygon, MultiPolygon]: - """Get bounding polygon from the geodatabase as a GeoJSON FeatureCollection.""" - logging.info("(dataset, fid_field, id): " + str((dataset, fid_field, feature_id))) +async def get_region_from_db(feature_id: Union[int, str], fid_field: str) -> Feature: + """Get regions from geodatabase as a GeoJSON Feature object""" + logging.info("(fid_field, id): " + str((fid_field, feature_id))) # Safe against SQL injection because of predefined values # (See oqt.py and definitions.py) - query = "SELECT ST_AsGeoJSON(geom) FROM {dataset} WHERE {fid_field} = $1".format( - fid_field=fid_field, dataset=dataset + query = ( + "SELECT ST_AsGeoJSON(geom) " + + "FROM regions " + + "WHERE {0} = $1".format(fid_field) ) async with get_connection() as conn: result = await conn.fetchrow(query, feature_id) - return geojson.loads(result[0]) + return Feature(geometry=geojson.loads(result[0])) async def get_available_regions() -> FeatureCollection: diff --git a/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_buildings/indicator.py b/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_buildings/indicator.py index 1ac97a0c5..4e7d226ea 100644 --- a/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_buildings/indicator.py +++ b/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_buildings/indicator.py @@ -1,12 +1,11 @@ import logging from io import StringIO from string import Template -from typing import Union import matplotlib.pyplot as plt import numpy as np from asyncpg import Record -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.indicator import BaseIndicator from ohsome_quality_analyst.geodatabase import client as db_client @@ -19,11 +18,11 @@ class GhsPopComparisonBuildings(BaseIndicator): def __init__( self, layer_name: str, - bpolys: Union[Polygon, MultiPolygon], + feature: Feature, ) -> None: super().__init__( layer_name=layer_name, - bpolys=bpolys, + feature=feature, ) # Those attributes will be set during lifecycle of the object. self.pop_count = None @@ -45,14 +44,16 @@ def yellow_threshold_function(self, pop_per_sqkm) -> float: return 0.75 * np.sqrt(pop_per_sqkm) async def preprocess(self) -> bool: - pop_count, area = await self.get_zonal_stats_population(bpolys=self.bpolys) + pop_count, area = await self.get_zonal_stats_population() if pop_count is None: pop_count = 0 self.area = area self.pop_count = pop_count - query_results = await ohsome_client.query(layer=self.layer, bpolys=self.bpolys) + query_results = await ohsome_client.query( + layer=self.layer, bpolys=self.feature.geometry + ) if query_results is None: return False self.feature_count = query_results["result"][0]["value"] @@ -172,7 +173,7 @@ def create_figure(self) -> bool: plt.close("all") return True - async def get_zonal_stats_population(self, bpolys: dict) -> Record: + async def get_zonal_stats_population(self) -> Record: """Derive zonal population stats for given GeoJSON geometry. This is based on the Global Human Settlement Layer Population. @@ -198,6 +199,6 @@ async def get_zonal_stats_population(self, bpolys: dict) -> Record: st_setsrid(public.ST_GeomFromGeoJSON($3), 4326) ) """ - data = tuple(map(str, [bpolys] * 3)) + data = tuple(map(str, [self.feature.geometry] * 3)) async with db_client.get_connection() as conn: return await conn.fetchrow(query, *data) diff --git a/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_roads/indicator.py b/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_roads/indicator.py index b0df7daf6..053ec9e19 100644 --- a/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_roads/indicator.py +++ b/workers/ohsome_quality_analyst/indicators/ghs_pop_comparison_roads/indicator.py @@ -1,12 +1,11 @@ import logging from io import StringIO from string import Template -from typing import Union import matplotlib.pyplot as plt import numpy as np from asyncpg import Record -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.indicator import BaseIndicator from ohsome_quality_analyst.geodatabase import client as db_client @@ -19,12 +18,9 @@ class GhsPopComparisonRoads(BaseIndicator): def __init__( self, layer_name: str, - bpolys: Union[Polygon, MultiPolygon], + feature: Feature, ) -> None: - super().__init__( - layer_name=layer_name, - bpolys=bpolys, - ) + super().__init__(layer_name=layer_name, feature=feature) # Those attributes will be set during lifecycle of the object. self.pop_count = None self.area = None @@ -47,14 +43,16 @@ def yellow_threshold_function(self, pop_per_sqkm) -> float: return 5 async def preprocess(self) -> bool: - pop_count, area = await self.get_zonal_stats_population(bpolys=self.bpolys) + pop_count, area = await self.get_zonal_stats_population() if pop_count is None: pop_count = 0 self.area = area self.pop_count = pop_count - query_results = await ohsome_client.query(layer=self.layer, bpolys=self.bpolys) + query_results = await ohsome_client.query( + layer=self.layer, bpolys=self.feature.geometry + ) if query_results is None: return False # results in meter, we need km @@ -169,7 +167,7 @@ def create_figure(self) -> bool: plt.close("all") return True - async def get_zonal_stats_population(self, bpolys: dict) -> Record: + async def get_zonal_stats_population(self) -> Record: """Derive zonal population stats for given GeoJSON geometry. This is based on the Global Human Settlement Layer Population. @@ -195,6 +193,6 @@ async def get_zonal_stats_population(self, bpolys: dict) -> Record: st_setsrid(public.ST_GeomFromGeoJSON($3), 4326) ) """ - data = tuple(map(str, [bpolys] * 3)) + data = tuple(map(str, [self.feature.geometry] * 3)) async with db_client.get_connection() as conn: return await conn.fetchrow(query, *data) diff --git a/workers/ohsome_quality_analyst/indicators/last_edit/indicator.py b/workers/ohsome_quality_analyst/indicators/last_edit/indicator.py index 780c80393..8dd020889 100644 --- a/workers/ohsome_quality_analyst/indicators/last_edit/indicator.py +++ b/workers/ohsome_quality_analyst/indicators/last_edit/indicator.py @@ -1,12 +1,11 @@ import logging from io import StringIO from string import Template -from typing import Union import matplotlib.patches as mpatches import matplotlib.pyplot as plt from dateutil.relativedelta import relativedelta -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.indicator import BaseIndicator from ohsome_quality_analyst.ohsome import client as ohsome_client @@ -16,12 +15,12 @@ class LastEdit(BaseIndicator): def __init__( self, layer_name: str, - bpolys: Union[Polygon, MultiPolygon], + feature: Feature, time_range: str = None, ) -> None: super().__init__( layer_name=layer_name, - bpolys=bpolys, + feature=feature, ) self.time_range = time_range # TODO: thresholds might be better defined for each OSM layer @@ -41,13 +40,13 @@ async def preprocess(self) -> bool: query_results_contributions = await ohsome_client.query( layer=self.layer, - bpolys=self.bpolys, + bpolys=self.feature.geometry, time=self.time_range, endpoint="contributions/latest/centroid", ) query_results_totals = await ohsome_client.query( layer=self.layer, - bpolys=self.bpolys, + bpolys=self.feature.geometry, ) if query_results_contributions is None or query_results_totals is None: return False diff --git a/workers/ohsome_quality_analyst/indicators/mapping_saturation/indicator.py b/workers/ohsome_quality_analyst/indicators/mapping_saturation/indicator.py index a70fe6355..538aa216d 100644 --- a/workers/ohsome_quality_analyst/indicators/mapping_saturation/indicator.py +++ b/workers/ohsome_quality_analyst/indicators/mapping_saturation/indicator.py @@ -1,11 +1,10 @@ import logging from io import StringIO from string import Template -from typing import Union import matplotlib.pyplot as plt import pandas as pd -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.indicator import BaseIndicator from ohsome_quality_analyst.indicators.mapping_saturation.sigmoid_curve import ( @@ -26,12 +25,12 @@ class MappingSaturation(BaseIndicator): def __init__( self, layer_name: str, - bpolys: Union[Polygon, MultiPolygon], + feature: Feature, time_range: str = "2008-01-01//P1M", ) -> None: super().__init__( layer_name=layer_name, - bpolys=bpolys, + feature=feature, ) self.time_range = time_range # Those attributes will be set during lifecycle of the object. @@ -42,7 +41,7 @@ def __init__( async def preprocess(self) -> bool: """Get data from ohsome API and db. Put timestamps + data in list""" query_results = await ohsome_client.query( - layer=self.layer, bpolys=self.bpolys, time=self.time_range + layer=self.layer, bpolys=self.feature.geometry, time=self.time_range ) if query_results is None: return False diff --git a/workers/ohsome_quality_analyst/indicators/poi_density/indicator.py b/workers/ohsome_quality_analyst/indicators/poi_density/indicator.py index d14cbccb6..28503c35a 100644 --- a/workers/ohsome_quality_analyst/indicators/poi_density/indicator.py +++ b/workers/ohsome_quality_analyst/indicators/poi_density/indicator.py @@ -1,11 +1,10 @@ import logging from io import StringIO from string import Template -from typing import Union import matplotlib.pyplot as plt import numpy as np -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.indicator import BaseIndicator from ohsome_quality_analyst.geodatabase.client import get_area_of_bpolys @@ -19,11 +18,11 @@ class PoiDensity(BaseIndicator): def __init__( self, layer_name: str, - bpolys: Union[Polygon, MultiPolygon], + feature: Feature, ) -> None: super().__init__( layer_name=layer_name, - bpolys=bpolys, + feature=feature, ) self.threshold_yellow = 30 self.threshold_red = 10 @@ -39,12 +38,14 @@ def yellow_threshold_function(self, area): async def preprocess(self) -> bool: query_results_count = await ohsome_client.query( - layer=self.layer, bpolys=self.bpolys + layer=self.layer, bpolys=self.feature.geometry ) if query_results_count is None: return False - self.area_sqkm = await get_area_of_bpolys(self.bpolys) # calc polygon area + self.area_sqkm = await get_area_of_bpolys( + self.feature.geometry + ) # calc polygon area self.count = query_results_count["result"][0]["value"] self.density = self.count / self.area_sqkm return True diff --git a/workers/ohsome_quality_analyst/indicators/tags_ratio/indicator.py b/workers/ohsome_quality_analyst/indicators/tags_ratio/indicator.py index 236df8027..f77e80015 100644 --- a/workers/ohsome_quality_analyst/indicators/tags_ratio/indicator.py +++ b/workers/ohsome_quality_analyst/indicators/tags_ratio/indicator.py @@ -1,25 +1,20 @@ import logging from io import StringIO from string import Template -from typing import Union import matplotlib.patches as mpatches import matplotlib.pyplot as plt -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.indicator import BaseIndicator from ohsome_quality_analyst.ohsome import client as ohsome_client class TagsRatio(BaseIndicator): - def __init__( - self, - layer_name: str, - bpolys: Union[Polygon, MultiPolygon], - ) -> None: + def __init__(self, layer_name: str, feature: Feature) -> None: super().__init__( layer_name=layer_name, - bpolys=bpolys, + feature=feature, ) self.threshold_yellow = 0.75 self.threshold_red = 0.25 @@ -30,7 +25,7 @@ def __init__( async def preprocess(self) -> bool: query_results_count = await ohsome_client.query( - layer=self.layer, bpolys=self.bpolys, ratio=True + layer=self.layer, bpolys=self.feature.geometry, ratio=True ) if query_results_count is None: return False diff --git a/workers/ohsome_quality_analyst/oqt.py b/workers/ohsome_quality_analyst/oqt.py index 43fe29e53..c3fef840a 100644 --- a/workers/ohsome_quality_analyst/oqt.py +++ b/workers/ohsome_quality_analyst/oqt.py @@ -6,7 +6,7 @@ from typing import Optional, Union from asyncpg.exceptions import UndefinedTableError -from geojson import MultiPolygon, Polygon +from geojson import Feature import ohsome_quality_analyst.geodatabase.client as db_client from ohsome_quality_analyst.base.indicator import BaseIndicator @@ -17,7 +17,7 @@ async def create_indicator( indicator_name: str, layer_name: str, - bpolys: Union[Polygon, MultiPolygon, None] = None, + feature: Optional[Feature] = None, dataset: Optional[str] = None, feature_id: Union[int, str, None] = None, fid_field: Optional[str] = None, @@ -63,11 +63,11 @@ async def from_database(dataset, feature_id) -> bool: logging.info("Layer name:\t" + layer_name) # from scratch - if bpolys is not None and dataset is None and feature_id is None: - indicator = indicator_class(layer_name=layer_name, bpolys=bpolys) + if feature is not None and dataset is None and feature_id is None: + indicator = indicator_class(layer_name=layer_name, feature=feature) await from_scratch() # from database - elif bpolys is None and dataset is not None and feature_id is not None: + elif feature is None and dataset is not None and feature_id is not None: # Support only predefined datasets and id field. # Otherwise creation of arbitrary relations or SQL injections are possible. if not db_client.sanity_check_dataset(dataset): @@ -81,9 +81,9 @@ async def from_database(dataset, feature_id) -> bool: logging.info("Feature id:\t" + str(feature_id)) logging.info("Feature id field:\t" + str(fid_field)) - bpolys = await db_client.get_bpolys_from_db(dataset, feature_id, fid_field) + feature = await db_client.get_region_from_db(feature_id, fid_field) - indicator = indicator_class(layer_name=layer_name, bpolys=bpolys) + indicator = indicator_class(layer_name=layer_name, feature=feature) success = await from_database(dataset, feature_id) if not success or force: await from_scratch() @@ -128,7 +128,7 @@ async def create_all_indicators(force: bool = False) -> None: async def create_report( report_name: str, force: bool = False, - bpolys: Union[Polygon, MultiPolygon, None] = None, + feature: Optional[Feature] = None, dataset: Optional[str] = None, feature_id: Union[int, str, None] = None, fid_field: Optional[str] = None, @@ -143,14 +143,14 @@ async def create_report( """ report_class = name_to_class(class_type="report", name=report_name) report = report_class( - bpolys=bpolys, dataset=dataset, feature_id=feature_id, fid_field=fid_field + feature=feature, dataset=dataset, feature_id=feature_id, fid_field=fid_field ) report.set_indicator_layer() for indicator_name, layer_name in report.indicator_layer: indicator = await create_indicator( indicator_name, layer_name, - bpolys=report.bpolys, + feature=report.feature, dataset=report.dataset, feature_id=report.feature_id, fid_field=fid_field, diff --git a/workers/ohsome_quality_analyst/reports/jrc_requirements/report.py b/workers/ohsome_quality_analyst/reports/jrc_requirements/report.py index b8c5322ca..10bb304e9 100644 --- a/workers/ohsome_quality_analyst/reports/jrc_requirements/report.py +++ b/workers/ohsome_quality_analyst/reports/jrc_requirements/report.py @@ -1,6 +1,6 @@ -from typing import Optional, Union +from typing import Optional -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.report import BaseReport, IndicatorLayer @@ -8,7 +8,7 @@ class JrcRequirements(BaseReport): def __init__( self, - bpolys: Union[Polygon, MultiPolygon, None] = None, + feature: Feature = None, dataset: Optional[str] = None, feature_id: Optional[int] = None, fid_field: Optional[str] = None, @@ -16,7 +16,7 @@ def __init__( """Create a list of indicator objects.""" super().__init__( - bpolys=bpolys, dataset=dataset, feature_id=feature_id, fid_field=fid_field + feature=feature, dataset=dataset, feature_id=feature_id, fid_field=fid_field ) def set_indicator_layer(self) -> None: diff --git a/workers/ohsome_quality_analyst/reports/map_action_poc/report.py b/workers/ohsome_quality_analyst/reports/map_action_poc/report.py index dacb7f91a..05ebe982a 100644 --- a/workers/ohsome_quality_analyst/reports/map_action_poc/report.py +++ b/workers/ohsome_quality_analyst/reports/map_action_poc/report.py @@ -1,6 +1,6 @@ -from typing import Optional, Union +from typing import Optional -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.report import BaseReport, IndicatorLayer @@ -8,7 +8,7 @@ class MapActionPoc(BaseReport): def __init__( self, - bpolys: Union[Polygon, MultiPolygon, None] = None, + feature: Feature = None, dataset: Optional[str] = None, feature_id: Optional[int] = None, fid_field: Optional[str] = None, @@ -16,7 +16,7 @@ def __init__( """Create a list of indicator objects.""" super().__init__( - bpolys=bpolys, dataset=dataset, feature_id=feature_id, fid_field=fid_field + feature=feature, dataset=dataset, feature_id=feature_id, fid_field=fid_field ) def set_indicator_layer(self) -> None: diff --git a/workers/ohsome_quality_analyst/reports/remote_mapping_level_one/report.py b/workers/ohsome_quality_analyst/reports/remote_mapping_level_one/report.py index 646a54e1f..6a92ee763 100644 --- a/workers/ohsome_quality_analyst/reports/remote_mapping_level_one/report.py +++ b/workers/ohsome_quality_analyst/reports/remote_mapping_level_one/report.py @@ -1,6 +1,6 @@ -from typing import Optional, Union +from typing import Optional -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.report import BaseReport, IndicatorLayer @@ -8,7 +8,7 @@ class RemoteMappingLevelOne(BaseReport): def __init__( self, - bpolys: Union[Polygon, MultiPolygon, None] = None, + feature: Feature = None, dataset: Optional[str] = None, feature_id: Optional[int] = None, fid_field: Optional[str] = None, @@ -16,7 +16,7 @@ def __init__( """Create a list of indicator objects.""" super().__init__( - bpolys=bpolys, dataset=dataset, feature_id=feature_id, fid_field=fid_field + feature=feature, dataset=dataset, feature_id=feature_id, fid_field=fid_field ) def set_indicator_layer(self) -> None: diff --git a/workers/ohsome_quality_analyst/reports/simple_report/report.py b/workers/ohsome_quality_analyst/reports/simple_report/report.py index 8abbffdad..df3239c75 100644 --- a/workers/ohsome_quality_analyst/reports/simple_report/report.py +++ b/workers/ohsome_quality_analyst/reports/simple_report/report.py @@ -1,6 +1,6 @@ -from typing import Optional, Union +from typing import Optional -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.report import BaseReport, IndicatorLayer @@ -8,13 +8,13 @@ class SimpleReport(BaseReport): def __init__( self, - bpolys: Union[Polygon, MultiPolygon] = None, + feature: Feature = None, dataset: Optional[str] = None, feature_id: Optional[int] = None, fid_field: Optional[str] = None, ) -> None: super().__init__( - bpolys=bpolys, dataset=dataset, feature_id=feature_id, fid_field=fid_field + feature=feature, dataset=dataset, feature_id=feature_id, fid_field=fid_field ) def set_indicator_layer(self): diff --git a/workers/ohsome_quality_analyst/reports/sketchmap_fitness/report.py b/workers/ohsome_quality_analyst/reports/sketchmap_fitness/report.py index 051820c17..0dc8c325e 100644 --- a/workers/ohsome_quality_analyst/reports/sketchmap_fitness/report.py +++ b/workers/ohsome_quality_analyst/reports/sketchmap_fitness/report.py @@ -1,6 +1,6 @@ -from typing import Optional, Union +from typing import Optional -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.base.report import BaseReport, IndicatorLayer @@ -8,13 +8,13 @@ class SketchmapFitness(BaseReport): def __init__( self, - bpolys: Union[Polygon, MultiPolygon, None] = None, + feature: Feature = None, dataset: Optional[str] = None, feature_id: Optional[int] = None, fid_field: Optional[str] = None, ) -> None: super().__init__( - bpolys=bpolys, dataset=dataset, feature_id=feature_id, fid_field=fid_field + feature=feature, dataset=dataset, feature_id=feature_id, fid_field=fid_field ) def set_indicator_layer(self): diff --git a/workers/tests/integrationtests/fixtures/heidelberg_altstadt.geojson b/workers/tests/integrationtests/fixtures/heidelberg_altstadt.geojson deleted file mode 100644 index 8dddc8ede..000000000 --- a/workers/tests/integrationtests/fixtures/heidelberg_altstadt.geojson +++ /dev/null @@ -1 +0,0 @@ -{"type":"Polygon","coordinates":[[[8.674092292785645,49.40427147224242],[8.695850372314453,49.40427147224242],[8.695850372314453,49.415552187316095],[8.674092292785645,49.415552187316095],[8.674092292785645,49.40427147224242]]]} diff --git a/workers/tests/integrationtests/fixtures/niger-kanan-bakache.geojson b/workers/tests/integrationtests/fixtures/niger-kanan-bakache.geojson index 4b14ce259..ff2a91900 100644 --- a/workers/tests/integrationtests/fixtures/niger-kanan-bakache.geojson +++ b/workers/tests/integrationtests/fixtures/niger-kanan-bakache.geojson @@ -1,29 +1,32 @@ { - "type": "MultiPolygon", - "coordinates": [ - [ + "type": "Feature", + "geometry": { + "type": "MultiPolygon", + "coordinates": [ [ [ - 7.818124294281006, - 13.859122207420464 - ], - [ - 7.835311889648438, - 13.859122207420464 - ], - [ - 7.835311889648438, - 13.872538264248497 - ], - [ - 7.818124294281006, - 13.872538264248497 - ], - [ - 7.818124294281006, - 13.859122207420464 + [ + 7.818124294281006, + 13.859122207420464 + ], + [ + 7.835311889648438, + 13.859122207420464 + ], + [ + 7.835311889648438, + 13.872538264248497 + ], + [ + 7.818124294281006, + 13.872538264248497 + ], + [ + 7.818124294281006, + 13.859122207420464 + ] ] ] ] - ] + } } diff --git a/workers/tests/integrationtests/test_api_indicator.py b/workers/tests/integrationtests/test_api_indicator.py index 8aec4fc01..20a04099d 100644 --- a/workers/tests/integrationtests/test_api_indicator.py +++ b/workers/tests/integrationtests/test_api_indicator.py @@ -30,7 +30,7 @@ def setUp(self): infile = os.path.join( os.path.dirname(os.path.abspath(__file__)), "fixtures", - "heidelberg_altstadt.geojson", + "heidelberg-altstadt-feature.geojson", ) with open(infile, "r") as f: self.bpolys = geojson.load(f) diff --git a/workers/tests/integrationtests/test_api_report.py b/workers/tests/integrationtests/test_api_report.py index a10b3af00..03b7ae1e4 100644 --- a/workers/tests/integrationtests/test_api_report.py +++ b/workers/tests/integrationtests/test_api_report.py @@ -31,7 +31,7 @@ def setUp(self): infile = os.path.join( os.path.dirname(os.path.abspath(__file__)), "fixtures", - "heidelberg_altstadt.geojson", + "heidelberg-altstadt-feature.geojson", ) with open(infile, "r") as f: self.bpolys = geojson.load(f) diff --git a/workers/tests/integrationtests/test_geodatabase.py b/workers/tests/integrationtests/test_geodatabase.py index e201da13d..a48f2d63c 100644 --- a/workers/tests/integrationtests/test_geodatabase.py +++ b/workers/tests/integrationtests/test_geodatabase.py @@ -16,8 +16,8 @@ def setUp(self): self.dataset = "regions" self.feature_id = 11 # Algeria Touggourt self.fid_field = "ogc_fid" - self.bpolys = asyncio.run( - db_client.get_bpolys_from_db(self.dataset, self.feature_id, self.fid_field) + self.feature = asyncio.run( + db_client.get_region_from_db(self.feature_id, self.fid_field) ) def test_get_connection(self): @@ -35,7 +35,7 @@ def test_save_and_load(self): # save self.indicator = GhsPopComparisonBuildings( layer_name="building_count", - bpolys=self.bpolys, + feature=self.feature, ) asyncio.run(self.indicator.preprocess()) self.indicator.calculate() @@ -48,7 +48,7 @@ def test_save_and_load(self): # load self.indicator = GhsPopComparisonBuildings( - layer_name="building_count", bpolys=self.bpolys + layer_name="building_count", feature=self.feature ) result = asyncio.run( db_client.load_indicator_results( @@ -73,29 +73,20 @@ def test_get_feature_ids(self): asyncio.run(db_client.get_feature_ids("foo", self.fid_field)) def test_get_area_of_bpolys(self): - result = asyncio.run(db_client.get_area_of_bpolys(self.bpolys)) + result = asyncio.run(db_client.get_area_of_bpolys(self.feature.geometry)) self.assertIsInstance(result, float) - def test_get_bpolys_from_db(self): + def test_get_region_from_db(self): result = asyncio.run( - db_client.get_bpolys_from_db(self.dataset, self.feature_id, self.fid_field) + db_client.get_region_from_db(self.feature_id, self.fid_field) ) self.assertTrue(result.is_valid) # GeoJSON object validation with self.assertRaises(UndefinedColumnError): - asyncio.run( - db_client.get_bpolys_from_db(self.dataset, self.feature_id, "foo") - ) + asyncio.run(db_client.get_region_from_db(self.feature_id, "foo")) with self.assertRaises(DataError): - asyncio.run( - db_client.get_bpolys_from_db(self.dataset, "foo", self.fid_field) - ) - - with self.assertRaises(UndefinedTableError): - asyncio.run( - db_client.get_bpolys_from_db("foo", self.feature_id, self.fid_field) - ) + asyncio.run(db_client.get_region_from_db("foo", self.fid_field)) def test_get_available_regions(self): regions = asyncio.run(db_client.get_available_regions()) diff --git a/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_buildings.py b/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_buildings.py index b8cae4748..ab57199dc 100644 --- a/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_buildings.py +++ b/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_buildings.py @@ -13,14 +13,13 @@ class TestIndicatorGhsPopComparisonBuildings(unittest.TestCase): def setUp(self): - dataset = "regions" - feature_id = 3 # Heidelberg - self.bpolys = asyncio.run( - db_client.get_bpolys_from_db(dataset, feature_id, "ogc_fid") + # Heidelberg + self.feature = asyncio.run( + db_client.get_region_from_db(feature_id=3, fid_field="ogc_fid") ) self.layer_name = "building_count" self.indicator = GhsPopComparisonBuildings( - bpolys=self.bpolys, layer_name=self.layer_name + feature=self.feature, layer_name=self.layer_name ) @oqt_vcr.use_cassette() @@ -42,9 +41,7 @@ def test(self): @oqt_vcr.use_cassette() def test_get_zonal_stats_population(self): - result = asyncio.run( - self.indicator.get_zonal_stats_population(self.indicator.bpolys) - ) + result = asyncio.run(self.indicator.get_zonal_stats_population()) self.assertIsInstance(result, Record) diff --git a/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_roads.py b/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_roads.py index 6eaefd8ef..44031111d 100644 --- a/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_roads.py +++ b/workers/tests/integrationtests/test_indicator_ghs_pop_comparison_roads.py @@ -11,13 +11,12 @@ class TestIndicatorGhsPopComparisonRoads(unittest.TestCase): def setUp(self): - dataset = "regions" - feature_id = 3 # Heidelberg - bpolys = asyncio.run( - db_client.get_bpolys_from_db(dataset, feature_id, "ogc_fid") + # Heidelberg + feature = asyncio.run( + db_client.get_region_from_db(feature_id=3, fid_field="ogc_fid") ) self.indicator = GhsPopComparisonRoads( - bpolys=bpolys, layer_name="major_roads_length" + feature=feature, layer_name="major_roads_length" ) def test(self): @@ -37,9 +36,7 @@ def test(self): self.assertIsNotNone(self.indicator.result.svg) def test_get_zonal_stats_population(self): - result = asyncio.run( - self.indicator.get_zonal_stats_population(self.indicator.bpolys) - ) + result = asyncio.run(self.indicator.get_zonal_stats_population()) self.assertIsInstance(result, Record) diff --git a/workers/tests/integrationtests/test_indicator_last_edit.py b/workers/tests/integrationtests/test_indicator_last_edit.py index bd667dda5..d6817b9a1 100644 --- a/workers/tests/integrationtests/test_indicator_last_edit.py +++ b/workers/tests/integrationtests/test_indicator_last_edit.py @@ -14,13 +14,11 @@ class TestIndicatorLastEdit(unittest.TestCase): @oqt_vcr.use_cassette() def test(self): # Heidelberg - bpolys = asyncio.run( - db_client.get_bpolys_from_db( - dataset="regions", feature_id=3, fid_field="ogc_fid" - ) + feature = asyncio.run( + db_client.get_region_from_db(feature_id=3, fid_field="ogc_fid") ) - indicator = LastEdit(bpolys=bpolys, layer_name="major_roads_count") + indicator = LastEdit(feature=feature, layer_name="major_roads_count") asyncio.run(indicator.preprocess()) indicator.calculate() self.assertIsNotNone(indicator.result.label) @@ -52,9 +50,9 @@ def test_no_amenities(self): "niger-kanan-bakache.geojson", ) with open(infile, "r") as f: - bpolys = geojson.load(f) + feature = geojson.load(f) - indicator = LastEdit(layer_name="amenities", bpolys=bpolys) + indicator = LastEdit(layer_name="amenities", feature=feature) asyncio.run(indicator.preprocess()) self.assertEqual(indicator.total_features, 0) diff --git a/workers/tests/integrationtests/test_indicator_mapping_saturation.py b/workers/tests/integrationtests/test_indicator_mapping_saturation.py index dee990573..7783f0f92 100644 --- a/workers/tests/integrationtests/test_indicator_mapping_saturation.py +++ b/workers/tests/integrationtests/test_indicator_mapping_saturation.py @@ -15,16 +15,14 @@ class TestIndicatorMappingSaturation(unittest.TestCase): def setUp(self): # Heidelberg - self.bpolys = asyncio.run( - db_client.get_bpolys_from_db( - dataset="regions", feature_id=3, fid_field="ogc_fid" - ) + self.feature = asyncio.run( + db_client.get_region_from_db(feature_id=3, fid_field="ogc_fid") ) @oqt_vcr.use_cassette() def test(self): indicator = MappingSaturation( - layer_name="major_roads_length", bpolys=self.bpolys + layer_name="major_roads_length", feature=self.feature ) asyncio.run(indicator.preprocess()) @@ -39,7 +37,7 @@ def test(self): # TODO: Should an error get raised here? @oqt_vcr.use_cassette() def test_float_division_by_zero_error(self): - indicator = MappingSaturation(layer_name="building_count", bpolys=self.bpolys) + indicator = MappingSaturation(layer_name="building_count", feature=self.feature) asyncio.run(indicator.preprocess()) indicator.calculate() indicator.create_figure() @@ -52,9 +50,9 @@ def test_cannot_convert_nan_error(self): "niger-kanan-bakache.geojson", ) with open(infile, "r") as f: - bpolys = geojson.load(f) + feature = geojson.load(f) - indicator = MappingSaturation(layer_name="building_count", bpolys=bpolys) + indicator = MappingSaturation(layer_name="building_count", feature=feature) asyncio.run(indicator.preprocess()) indicator.calculate() indicator.create_figure() diff --git a/workers/tests/integrationtests/test_indicator_poi_density.py b/workers/tests/integrationtests/test_indicator_poi_density.py index ab3effc90..b383cc85b 100644 --- a/workers/tests/integrationtests/test_indicator_poi_density.py +++ b/workers/tests/integrationtests/test_indicator_poi_density.py @@ -14,11 +14,11 @@ def setUp(self): infile = os.path.join( os.path.dirname(os.path.abspath(__file__)), "fixtures", - "heidelberg_altstadt.geojson", + "heidelberg-altstadt-feature.geojson", ) with open(infile, "r") as f: - bpolys = geojson.load(f) - self.indicator = PoiDensity(bpolys=bpolys, layer_name="poi") + feature = geojson.load(f) + self.indicator = PoiDensity(feature=feature, layer_name="poi") @oqt_vcr.use_cassette() def test(self): diff --git a/workers/tests/integrationtests/test_indicator_tags_ratio.py b/workers/tests/integrationtests/test_indicator_tags_ratio.py index 4296cd728..d658693d8 100644 --- a/workers/tests/integrationtests/test_indicator_tags_ratio.py +++ b/workers/tests/integrationtests/test_indicator_tags_ratio.py @@ -13,16 +13,14 @@ class TestIndicatorRatio(unittest.TestCase): def setUp(self): # Heidelberg - self.bpolys = asyncio.run( - db_client.get_bpolys_from_db( - dataset="regions", feature_id=3, fid_field="ogc_fid" - ) + self.feature = asyncio.run( + db_client.get_region_from_db(feature_id=3, fid_field="ogc_fid") ) @oqt_vcr.use_cassette() def test(self): indicator = TagsRatio( - bpolys=self.bpolys, + feature=self.feature, layer_name="jrc_health_count", ) asyncio.run(indicator.preprocess()) @@ -43,9 +41,9 @@ def test_no_features(self): "niger-kanan-bakache.geojson", ) with open(infile, "r") as f: - bpolys = geojson.load(f) + feature = geojson.load(f) - indicator = TagsRatio(layer_name="jrc_health_count", bpolys=bpolys) + indicator = TagsRatio(layer_name="jrc_health_count", feature=feature) asyncio.run(indicator.preprocess()) self.assertEqual(indicator.count_all, 0) @@ -57,7 +55,7 @@ def test_no_features(self): def test_no_filter2(self): """Layer with no filter2 for ratio endpoint""" with self.assertRaises(ValueError): - TagsRatio(layer_name="major_roads_length", bpolys=self.bpolys) + TagsRatio(layer_name="major_roads_length", feature=self.feature) if __name__ == "__main__": diff --git a/workers/tests/integrationtests/test_oqt.py b/workers/tests/integrationtests/test_oqt.py index 713cddf2a..7437dae9c 100644 --- a/workers/tests/integrationtests/test_oqt.py +++ b/workers/tests/integrationtests/test_oqt.py @@ -9,20 +9,23 @@ class TestOqt(unittest.TestCase): def setUp(self): + # Heidelberg self.dataset = "regions" - self.feature_id = 3 # Heidelberg + self.feature_id = 3 self.fid_field = "ogc_fid" - self.bpolys = asyncio.run( - db_client.get_bpolys_from_db(self.dataset, self.feature_id, self.fid_field) + self.feature = asyncio.run( + db_client.get_region_from_db( + feature_id=self.feature_id, fid_field=self.fid_field + ) ) @oqt_vcr.use_cassette() - def test_create_indicator_bpolys(self): + def test_create_indicator_feature(self): """Test creating indicator from scratch.""" # From scratch indicator = asyncio.run( oqt.create_indicator( - "GhsPopComparisonBuildings", "building_count", bpolys=self.bpolys + "GhsPopComparisonBuildings", "building_count", feature=self.feature ) ) self.assertIsNotNone(indicator.result.label) @@ -111,13 +114,13 @@ def test_create_indicator_from_database_invalid_arguments(self): "building_count", dataset=self.dataset, feature_id=self.feature_id, - bpolys=self.bpolys, + feature=self.feature, ) ) @oqt_vcr.use_cassette() - def test_create_report_bpolys(self): - report = asyncio.run(oqt.create_report("SimpleReport", bpolys=self.bpolys)) + def test_create_report_feature(self): + report = asyncio.run(oqt.create_report("SimpleReport", feature=self.feature)) self.assertIsNotNone(report.result.label) self.assertIsNotNone(report.result.value) self.assertIsNotNone(report.result.description) diff --git a/workers/tests/unittests/test_api.py b/workers/tests/unittests/test_api.py index c2d680a77..cad2c22a7 100644 --- a/workers/tests/unittests/test_api.py +++ b/workers/tests/unittests/test_api.py @@ -2,7 +2,7 @@ import os import unittest -from geojson import MultiPolygon, Polygon +from geojson import Feature from ohsome_quality_analyst.api import int_or_str_param_type, load_bpolys @@ -31,7 +31,7 @@ def test_load_bpolys_size(self): bpolys = file.read() bpolys = asyncio.run(load_bpolys(bpolys)) self.assertTrue(bpolys.is_valid) - self.assertIsInstance(bpolys, (Polygon, MultiPolygon)) + self.assertIsInstance(bpolys, Feature) # Error should be raised infile = os.path.join( @@ -65,7 +65,7 @@ def test_load_bpolys_feature(self): bpolys = f.read() bpolys = asyncio.run(load_bpolys(bpolys)) self.assertTrue(bpolys.is_valid) - self.assertIsInstance(bpolys, (Polygon, MultiPolygon)) + self.assertIsInstance(bpolys, Feature) def test_load_bpolys_geometry(self): infile = os.path.join( @@ -77,7 +77,7 @@ def test_load_bpolys_geometry(self): bpolys = f.read() bpolys = asyncio.run(load_bpolys(bpolys)) self.assertTrue(bpolys.is_valid) - self.assertIsInstance(bpolys, (Polygon, MultiPolygon)) + self.assertIsInstance(bpolys, Feature) def test_int_or_str_param_type(self): self.assertIsInstance(int_or_str_param_type("1"), int) diff --git a/workers/tests/unittests/test_get_default_figure.py b/workers/tests/unittests/test_get_default_figure.py index 425bfc91f..0e28866b0 100644 --- a/workers/tests/unittests/test_get_default_figure.py +++ b/workers/tests/unittests/test_get_default_figure.py @@ -16,9 +16,9 @@ def test_get_default_figure(self): "heidelberg-altstadt-geometry.geojson", ) with open(infile, "r") as f: - bpolys = geojson.load(f) + feature = geojson.load(f) indicator = GhsPopComparisonBuildings( - bpolys=bpolys, layer_name="building_count" + feature=feature, layer_name="building_count" ) self.assertIsInstance(indicator.result.svg, str) # TODO: Validate SVG