Skip to content

Commit de05c79

Browse files
committedDec 19, 2024·
WIP: add query for trino for the attribute completeness
1 parent 833172d commit de05c79

File tree

1 file changed

+140
-12
lines changed
  • ohsome_quality_api/indicators/attribute_completeness

1 file changed

+140
-12
lines changed
 

‎ohsome_quality_api/indicators/attribute_completeness/indicator.py

+140-12
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import logging
2+
import time
23
from string import Template
34

4-
import dateutil.parser
55
import plotly.graph_objects as go
6+
import requests
67
from geojson import Feature
8+
from shapely import to_wkt
9+
from shapely.geometry import shape
710

811
from ohsome_quality_api.attributes.definitions import (
912
build_attribute_filter,
1013
get_attribute,
1114
)
1215
from ohsome_quality_api.indicators.base import BaseIndicator
13-
from ohsome_quality_api.ohsome import client as ohsome_client
1416
from ohsome_quality_api.topics.models import BaseTopic as Topic
1517

1618

@@ -69,17 +71,143 @@ def __init__(
6971
)
7072

7173
async def preprocess(self) -> None:
72-
# Get attribute filter
73-
response = await ohsome_client.query(
74-
self.topic,
75-
self.feature,
76-
attribute_filter=self.attribute_filter,
74+
75+
TRINO_HOST = ""
76+
TRINO_PORT =
77+
TRINO_USER = ""
78+
TRINO_CATALOG = ""
79+
TRINO_SCHEMA = ""
80+
81+
URL = f"http://{TRINO_HOST}:{TRINO_PORT}/v1/statement"
82+
83+
HEADERS = {
84+
"X-Trino-User": TRINO_USER,
85+
"X-Trino-Catalog": TRINO_CATALOG,
86+
"X-Trino-Schema": TRINO_SCHEMA,
87+
}
88+
89+
AUTH = None
90+
91+
QUERY_TEMPLATE = """
92+
SELECT
93+
SUM(
94+
CASE
95+
WHEN ST_Within(ST_GeometryFromText(a.geometry), b.geometry) THEN length
96+
ELSE CAST(st_length(ST_Intersection(ST_GeometryFromText(a.geometry), b.geometry)) AS integer)
97+
END
98+
) AS total_road_length,
99+
100+
SUM(
101+
CASE
102+
WHEN element_at(tags, 'name') IS NULL THEN 0
103+
WHEN ST_Within(ST_GeometryFromText(a.geometry), b.geometry) THEN length
104+
ELSE CAST(st_length(ST_Intersection(ST_GeometryFromText(a.geometry), b.geometry)) AS integer)
105+
END
106+
) AS total_road_length_with_name,
107+
108+
(
109+
SUM(
110+
CASE
111+
WHEN element_at(tags, 'name') IS NULL THEN 0
112+
WHEN ST_Within(ST_GeometryFromText(a.geometry), b.geometry) THEN length
113+
ELSE CAST(st_length(ST_Intersection(ST_GeometryFromText(a.geometry), b.geometry)) AS integer)
114+
END
115+
)
116+
/
117+
SUM(
118+
CASE
119+
WHEN ST_Within(ST_GeometryFromText(a.geometry), b.geometry) THEN length
120+
ELSE CAST(st_length(ST_Intersection(ST_GeometryFromText(a.geometry), b.geometry)) AS integer)
121+
END
77122
)
78-
timestamp = response["ratioResult"][0]["timestamp"]
79-
self.result.timestamp_osm = dateutil.parser.isoparse(timestamp)
80-
self.result.value = response["ratioResult"][0]["ratio"]
81-
self.absolute_value_1 = response["ratioResult"][0]["value"]
82-
self.absolute_value_2 = response["ratioResult"][0]["value2"]
123+
) AS ratio
124+
125+
FROM contributions a, (VALUES {aoi_values}) AS b(id, geometry)
126+
WHERE 'herfort' != 'kwakye'
127+
AND status = 'latest'
128+
AND element_at(a.tags, 'highway') IS NOT NULL
129+
AND a.tags['highway'] IN (
130+
'motorway', 'trunk', 'motorway_link', 'trunk_link', 'primary', 'primary_link',
131+
'secondary', 'secondary_link', 'tertiary', 'tertiary_link', 'unclassified', 'residential'
132+
)
133+
AND (bbox.xmax >= 8.629761 AND bbox.xmin <= 8.742371)
134+
AND (bbox.ymax >= 49.379556 AND bbox.ymin <= 49.437890)
135+
AND ST_Intersects(ST_GeometryFromText(a.geometry), b.geometry)
136+
GROUP BY b.id
137+
"""
138+
139+
def extract_geometry(feature):
140+
geometry = feature.get("geometry")
141+
if not geometry:
142+
raise ValueError("Feature does not contain a geometry")
143+
geom_shape = shape(geometry)
144+
return to_wkt(geom_shape)
145+
146+
def format_aoi_values(geom_wkt):
147+
return f"('AOI', ST_GeometryFromText('{geom_wkt}'))"
148+
149+
def execute_query(query):
150+
try:
151+
response = requests.post(URL, data=query, headers=HEADERS, auth=AUTH)
152+
response.raise_for_status()
153+
return response.json()
154+
except requests.exceptions.RequestException as e:
155+
print(f"Error submitting query: {e}")
156+
return None
157+
158+
def poll_query(next_uri):
159+
"""Poll the query's nextUri until results are ready."""
160+
results = []
161+
while next_uri:
162+
try:
163+
response = requests.get(next_uri, headers=HEADERS, auth=AUTH)
164+
response.raise_for_status()
165+
data = response.json()
166+
167+
state = data["stats"]["state"]
168+
print(f"Query state: {state}")
169+
170+
if state == "FINISHED":
171+
if "data" in data:
172+
results.extend(data["data"])
173+
print("Query completed successfully!")
174+
break
175+
elif state in {"FAILED", "CANCELLED"}:
176+
print(f"Query failed or was cancelled: {data}")
177+
break
178+
179+
next_uri = data.get("nextUri")
180+
except requests.exceptions.RequestException as e:
181+
print(f"Error polling query: {e}")
182+
break
183+
time.sleep(1)
184+
185+
return results
186+
187+
188+
geom_wkt = extract_geometry(self.feature)
189+
190+
aoi_values = format_aoi_values(geom_wkt)
191+
192+
query = QUERY_TEMPLATE.format(aoi_values=aoi_values)
193+
194+
initial_response = execute_query(query)
195+
if not initial_response:
196+
return
197+
next_uri = initial_response.get("nextUri")
198+
if not next_uri:
199+
print("No nextUri found. Query might have failed immediately.")
200+
print(initial_response)
201+
return
202+
203+
response = poll_query(next_uri)
204+
205+
206+
# timestamp = response["ratioResult"][0]["timestamp"]
207+
# self.result.timestamp_osm = dateutil.parser.isoparse(timestamp)
208+
self.absolute_value_1 = response[0][0]
209+
self.absolute_value_2 = response[0][1]
210+
self.result.value = self.absolute_value_2 / self.absolute_value_1
83211

84212
def calculate(self) -> None:
85213
# result (ratio) can be NaN if no features matching filter1

0 commit comments

Comments
 (0)
Please sign in to comment.