Skip to content

Commit 4d0a3d3

Browse files
Gigaszimatthiasschaub
authored andcommitted
WIP: add query for trino for the attribute completeness
1 parent e0cae09 commit 4d0a3d3

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

Lines changed: 140 additions & 12 deletions
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

@@ -74,17 +76,143 @@ def __init__(
7476
)
7577

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

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

0 commit comments

Comments
 (0)