Skip to content

Commit

Permalink
Improve challenge generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Robbendebiene committed Oct 14, 2024
1 parent 0fc6bd9 commit 8f63e21
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 87 deletions.
60 changes: 22 additions & 38 deletions challenges/large_stop_area_bbox/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,6 @@

## Functions specific to this challenge

# Use this function to determine if a task needs to be created for a given element
# use this function for filtering things that overpass cannot filter, maybe by using a function from a different file that you specifically implemented for this task
# if your overpass query already returns all elements that need to be fixed, make this function return True
def needsTask(e):
# An element needs a task if either the vertical or the horizontal bbox edge is longer than a predefined value in meters
max_distance = 1000
# Calculate the length of the longitude difference of the bbox
if len(e['geometry']['coordinates']) == 1:
e['geometry']['coordinates'] = e['geometry']['coordinates'][0]

e['bounds'] = {}
e['bounds']['minlat'] = e['geometry']['coordinates'][0][1]
e['bounds']['minlon'] = e['geometry']['coordinates'][0][0]
e['bounds']['maxlat'] = e['geometry']['coordinates'][2][1]
e['bounds']['maxlon'] = e['geometry']['coordinates'][2][0]

# Calculate the length of the longitude difference of the bbox
point1 = Point((e['bounds']['minlon'], e['bounds']['minlat']))
point2 = Point((e['bounds']['maxlon'], e['bounds']['minlat']))
lon_diff = distance(point1, point2) * 1000 # Convert kilometers to meters

# Calculate the length of the latitude difference of the bbox
point3 = Point((e['bounds']['minlon'], e['bounds']['maxlat']))
lat_diff = distance(point1, point3) * 1000 # Convert kilometers to meters

# If either the longitude or the latitude difference is longer than 1000 meters, return True
if lon_diff > max_distance or lat_diff > max_distance:
return True



opQuery = """
[out:json][timeout:250];
area(id:3600051477)->.searchArea;
Expand All @@ -51,22 +20,37 @@ def needsTask(e):
}
"""

# Use this function to determine if a task needs to be created for a given element
# use this function for filtering things that overpass cannot filter, maybe by using a function from a different file that you specifically implemented for this task
# if your overpass query already returns all elements that need to be fixed, make this function return True
def needsTask(e):
# An element needs a task if either the vertical or the horizontal bbox edge is longer than a predefined value in meters
max_distance = 1000
bbox = e['geometry']['coordinates'][0]
point1 = Point((bbox[0][0], bbox[0][1]))
point2 = Point((bbox[1][0], bbox[1][1]))
point3 = Point((bbox[2][0], bbox[2][1]))
# Calculate the length of the longitude/latitude difference of the bbox
lon_diff = distance(point1, point3) * 1000 # Convert kilometers to meters
lat_diff = distance(point1, point2) * 1000 # Convert kilometers to meters
# If either the longitude or the latitude difference is longer than 1000 meters, return True
if lon_diff > max_distance or lat_diff > max_distance:
return True

op = mrcb.Overpass()
resultElements = op.queryElementsAsGeoJSON(opQuery, forceGeomType="Polygon")
resultElements = op.queryElementsAsGeoJSON(opQuery)

challenge = mrcb.Challenge()

for element in resultElements:
if needsTask(element):
mainFeature = mrcb.GeoFeature.withId(
osmType="relation",
osmType="relation",
osmId=element["properties"]["@id"],
geometry=element["geometry"],
geometry=element["geometry"],
properties={})
t = mrcb.Task(
task = mrcb.Task(
mainFeature=mainFeature)
challenge.addTask(t)
challenge.addTask(task)

challenge.saveToFile("large_stop_area_bbox.json")


35 changes: 22 additions & 13 deletions challenges/stop_area_names_from_platform_names/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys
sys.path.append('shared')
from pathlib import Path
sys.path.append(str(Path(__file__).parent.parent.parent / "shared"))
from turfpy.measurement import center, bbox, bbox_polygon
import challenge_builder as mrcb

opQuery = """
Expand All @@ -13,16 +15,16 @@
// and to get members of type relation (single > only returns ways and nodes)
.stop_area >>;
nwr._["public_transport"="platform"];
if (u(t["name"]) != "< multiple values found >" && u(t["name"]) != "") {
make StopArea
name=u(t["name"]),
// get stop are relation id
::id=stop_area.u(id()),
// group geometries into one
::geom=hull(gcat(geom()));
out center tags;
out geom tags;
}
}
"""
Expand All @@ -33,20 +35,27 @@
challenge = mrcb.Challenge()

for element in resultElements:
if element["geometry"]["type"] == "Polygon":
geometry=element["geometry"]
elif element["geometry"]["type"] == "LineString":
geometry=bbox_polygon(bbox(element["geometry"])).geometry
else:
geometry=center(element["geometry"]).geometry

mainFeature = mrcb.GeoFeature.withId(
osmType="relation",
osmType="relation",
osmId=element["properties"]["@id"],
geometry=element["geometry"],
geometry=geometry,
properties={})
suggestedName = element["properties"]["name"]
cooperativeWork = mrcb.TagFix(
osmType="relation",
osmId=element["properties"]["@id"],
osmType="relation",
osmId=element["properties"]["@id"],
tags={"name": suggestedName})
t = mrcb.Task(
mainFeature=mainFeature,
additionalFeatures=[],
task = mrcb.Task(
mainFeature=mainFeature,
additionalFeatures=[],
cooperativeWork=cooperativeWork)
challenge.addTask(t)
challenge.addTask(task)

challenge.saveToFile("stop_area_names_from_platform_names.json")
challenge.saveToFile("stop_area_names_from_platform_names.json")
61 changes: 25 additions & 36 deletions shared/challenge_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from turfpy.measurement import distance, bbox, centroid



@dataclass
class GeoFeature:
def __init__(self, geometry, properties={}):
Expand Down Expand Up @@ -43,13 +42,13 @@ def __init__(self, osmType, osmId, tags):
raise ValueError("tagsToDelete must be a list, e.g. ['tag1', 'tag2']")
if not isinstance(self.tagsToSet, dict):
raise ValueError("tagsToSet must be a dict e.g. {'tag1': 'value1', 'tag2': 'value2'}")

def toGeoJSON(self):
return {"meta": {"version": 2, "type": 1},
return {"meta": {"version": 2, "type": 1},
"operations": [
{"operationType": "modifyElement",
{"operationType": "modifyElement",
"data": {
"id": f"{self.osmType}/{self.osmId}",
"id": f"{self.osmType}/{self.osmId}",
"operations": [
{"operation": "setTags", "data": self.tagsToSet},
{"operation": "unsetTags", "data": self.tagsToDelete}
Expand Down Expand Up @@ -95,41 +94,33 @@ def queryElementsRaw(self, overpass_query):
raise ValueError("Invalid return data")
return response.json()["elements"]

def queryElementsAsGeoJSON(self, overpass_query, forceGeomType=None):
def queryElementsAsGeoJSON(self, overpass_query):
rawElements = self.queryElementsRaw(overpass_query)
featureList = list(map(lambda element: geojson.Feature(
geometry=self.geoJSONGeometryFromOverpassElement(element, forceGeomType),
geometry=self.geoJSONGeometryFromOverpassElement(element),
properties=self.geoJSONPropertiesFromOverpassElement(element)
), rawElements))
return featureList


def geoJSONPropertiesFromOverpassElement(self, element):
if 'tags' in element:
tags = element['tags']
else:
tags = {}
tags = element.get('tags', {})
tags["@type"] = element["type"]
tags["@id"] = element["id"]
return tags;
return tags;

def geoJSONGeometryFromOverpassElement(self, element, forceGeomType=None):
def geoJSONGeometryFromOverpassElement(self, element):
# returns a geojson depending on element; either Point(), LineString() or Polygon()
# frist, asses the geometry type we want to give back based on the element if ForceGeomType is None
if forceGeomType is None:
if 'lat' in element or 'center' in element:
geomType = "Point"
elif 'bounds' in element:
geomType = "Polygon"
elif 'geometry' in element:
if element['geometry']['type'] in ["Point", "LineString", "Polygon"]:
geomType = element['geometry']['type']
else:
raise ValueError("No handalable coordinates found for element")
if 'lat' in element or 'center' in element:
geomType = "Point"
elif 'geometry' in element:
if element['geometry']['type'] in ["Point", "LineString", "Polygon"]:
geomType = element['geometry']['type']
else:
raise ValueError("No handalable coordinates found for element")
else:
geomType = forceGeomType
raise ValueError("No handalable coordinates found for element")

# now, create the geojson object
if geomType == "Point":
if 'geometry' in element:
Expand All @@ -139,18 +130,16 @@ def geoJSONGeometryFromOverpassElement(self, element, forceGeomType=None):
else:
return geojson.Point([element['lon'], element['lat']])
elif geomType == "LineString":
return geojson.LineString([[point['lon'], point['lat']] for point in element['geometry']])
if 'coordinates' in element['geometry']:
return geojson.LineString(element['geometry']['coordinates'])
else:
return geojson.LineString([[point['lon'], point['lat']] for point in element['geometry']])
elif geomType == "Polygon":
if 'bounds' in element:
return geojson.Polygon([
[[element['bounds']['minlon'], element['bounds']['minlat']],
[element['bounds']['minlon'], element['bounds']['maxlat']],
[element['bounds']['maxlon'], element['bounds']['maxlat']],
[element['bounds']['maxlon'], element['bounds']['minlat']],
[element['bounds']['minlon'], element['bounds']['minlat']]]
])
if 'coordinates' in element['geometry']:
return geojson.Polygon([element['geometry']['coordinates']])
if len(element['geometry']['coordinates']) == 1:
return geojson.Polygon(element['geometry']['coordinates'])
else:
return geojson.Polygon([element['geometry']['coordinates']])
else:
return geojson.Polygon([[[point['lon'], point['lat']] for point in element['geometry']]])
raise ValueError("No handalable coordinates found for element")
raise ValueError("No handalable coordinates found for element")

0 comments on commit 8f63e21

Please sign in to comment.