Skip to content

Commit

Permalink
merge main into #140
Browse files Browse the repository at this point in the history
  • Loading branch information
mvexel committed Feb 13, 2024
1 parent 72a45ad commit ca2d57e
Show file tree
Hide file tree
Showing 8 changed files with 758 additions and 842 deletions.
9 changes: 5 additions & 4 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
[[source]]
url = "https://pypi.python.org/simple"
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
geojson = ">=3.0.1"
pytest = "7.4.0"
osm2geojson = ">=0.1.30"
requests = ">=2.31.0"
shapely = ">=2.0.1"

[dev-packages]
geojson = ">=1.3.1"
pytest = "6.0.0"
requests-mock = "*"

[requires]
105 changes: 13 additions & 92 deletions overpass/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@
from math import ceil
from typing import Optional

import geojson
import requests
from shapely.geometry import Point, Polygon
from osm2geojson import json2geojson

from .errors import (
MultipleRequestsError,
Expand Down Expand Up @@ -131,7 +130,7 @@ def get(self, query, responseformat="geojson", verbosity="body", build=True, dat
return response

# construct geojson
return self._as_geojson(response["elements"])
return json2geojson(response)

@staticmethod
def _api_status() -> dict:
Expand Down Expand Up @@ -275,94 +274,16 @@ def _get_from_overpass(self, query):

self._status = r.status_code

if self._status == 200:
r.encoding = "utf-8"
return r
elif self._status == 400:
raise OverpassSyntaxError(query)
elif self._status == 429:
raise MultipleRequestsError()
elif self._status == 504:
raise ServerLoadError(self._timeout)
else:
if self._status != 200:
if self._status == 400:
raise OverpassSyntaxError(query)
elif self._status == 429:
raise MultipleRequestsError()
elif self._status == 504:
raise ServerLoadError(self._timeout)
raise UnknownOverpassError(
f"The request returned status code {self._status}"
"The request returned status code {code}".format(code=self._status)
)

def _as_geojson(self, elements):
ids_already_seen = set()
features = []
geometry = None
for elem in elements:
try:
if elem["id"] in ids_already_seen:
continue
ids_already_seen.add(elem["id"])
except KeyError:
raise UnknownOverpassError("Received corrupt data from Overpass (no id).")
elem_type = elem.get("type")
elem_tags = elem.get("tags", {})
elem_nodes = elem.get("nodes", None)
elem_timestamp = elem.get("timestamp", None)
elem_user = elem.get("user", None)
elem_uid = elem.get("uid", None)
elem_version = elem.get("version", None)
if elem_nodes:
elem_tags["nodes"] = elem_nodes
if elem_user:
elem_tags["user"] = elem_user
if elem_uid:
elem_tags["uid"] = elem_uid
if elem_version:
elem_tags["version"] = elem_version
elem_geom = elem.get("geometry", [])
if elem_type == "node":
# Create Point geometry
geometry = geojson.Point((elem.get("lon"), elem.get("lat")))
elif elem_type == "way":
# Create LineString geometry
geometry = geojson.LineString([(coords["lon"], coords["lat"]) for coords in elem_geom])
elif elem_type == "relation":
# Initialize polygon list
polygons = []
# First obtain the outer polygons
for member in elem.get("members", []):
if member["role"] == "outer":
points = [(coords["lon"], coords["lat"]) for coords in member.get("geometry", [])]
# Check that the outer polygon is complete
if points and points[-1] == points[0]:
polygons.append([points])
else:
raise UnknownOverpassError("Received corrupt data from Overpass (incomplete polygon).")
# Then get the inner polygons
for member in elem.get("members", []):
if member["role"] == "inner":
points = [(coords["lon"], coords["lat"]) for coords in member.get("geometry", [])]
# Check that the inner polygon is complete
if not points or points[-1] != points[0]:
raise UnknownOverpassError("Received corrupt data from Overpass (incomplete polygon).")
# We need to check to which outer polygon the inner polygon belongs
point = Point(points[0])
for poly in polygons:
polygon = Polygon(poly[0])
if polygon.contains(point):
poly.append(points)
break
else:
raise UnknownOverpassError("Received corrupt data from Overpass (inner polygon cannot "
"be matched to outer polygon).")
# Finally create MultiPolygon geometry
if polygons:
geometry = geojson.MultiPolygon(polygons)
else:
raise UnknownOverpassError("Received corrupt data from Overpass (invalid element).")

if geometry:
feature = geojson.Feature(
id=elem["id"],
geometry=geometry,
properties=elem_tags
)
features.append(feature)

return geojson.FeatureCollection(features)
else:
r.encoding = "utf-8"
return r
1 change: 1 addition & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pytest>=7.4.0
requests-mock[fixture]
tox>=4.6.3
geojson>=1.3.1
4 changes: 1 addition & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
geojson>=3.0.1
pytest
osm2geojson>=0.1.30
requests>=2.31.0
shapely>=2.0.1
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
"Topic :: Scientific/Engineering :: GIS",
"Topic :: Utilities",
],
install_requires=["requests>=2.3.0", "geojson>=1.0.9", "shapely>=1.6.4"],
extras_require={"test": ["pytest", "requests-mock[fixture]"]},
install_requires=["requests>=2.3.0", "osm2geojson"],
extras_require={"test": ["pytest", "requests-mock[fixture]", "geojson>=1.0.9"]},
)
Loading

0 comments on commit ca2d57e

Please sign in to comment.