Skip to content

Commit

Permalink
Change type of attribute bpolys to Feature
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasschaub committed Jun 29, 2021
1 parent ffffe88 commit 99bd795
Show file tree
Hide file tree
Showing 31 changed files with 199 additions and 223 deletions.
35 changes: 21 additions & 14 deletions workers/ohsome_quality_analyst/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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()
Expand Down
9 changes: 4 additions & 5 deletions workers/ohsome_quality_analyst/base/indicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions workers/ohsome_quality_analyst/base/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -39,15 +39,15 @@ 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,
):
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] = []
Expand Down
26 changes: 10 additions & 16 deletions workers/ohsome_quality_analyst/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,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,
Expand All @@ -143,7 +143,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,
Expand All @@ -153,12 +153,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)
Expand Down Expand Up @@ -190,7 +187,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,
Expand All @@ -208,7 +205,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,
Expand All @@ -218,12 +215,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)
Expand Down
19 changes: 9 additions & 10 deletions workers/ohsome_quality_analyst/geodatabase/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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"]
Expand Down Expand Up @@ -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.
Expand All @@ -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)
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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)
11 changes: 5 additions & 6 deletions workers/ohsome_quality_analyst/indicators/last_edit/indicator.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit 99bd795

Please sign in to comment.