Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 103 additions & 18 deletions overpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,7 @@ class RelationMember(object):
Base class to represent a member of a relation.
"""

def __init__(self, ref=None, role=None, result=None):
def __init__(self, attributes=None, geometry=None, ref=None, role=None, result=None):
"""
:param ref: Reference Id
:type ref: Integer
Expand All @@ -1138,6 +1138,8 @@ def __init__(self, ref=None, role=None, result=None):
self.ref = ref
self._result = result
self.role = role
self.attributes = attributes
self.geometry = geometry

@classmethod
def from_json(cls, data, result=None):
Expand All @@ -1160,7 +1162,35 @@ def from_json(cls, data, result=None):

ref = data.get("ref")
role = data.get("role")
return cls(ref=ref, role=role, result=result)

attributes = {}
ignore = ["geometry", "type", "ref", "role"]
for n, v in data.items():
if n in ignore:
continue
attributes[n] = v

geometry = data.get("geometry")
if isinstance(geometry, list):
geometry_orig = geometry
geometry = []
for v in geometry_orig:
geometry.append(
RelationWayGeometryValue(
lat=v.get("lat"),
lon=v.get("lon")
)
)
else:
geometry = None

return cls(
attributes=attributes,
geometry=geometry,
ref=ref,
role=role,
result=result
)

@classmethod
def from_xml(cls, child, result=None):
Expand All @@ -1185,7 +1215,33 @@ def from_xml(cls, child, result=None):
if ref is not None:
ref = int(ref)
role = child.attrib.get("role")
return cls(ref=ref, role=role, result=result)

attributes = {}
ignore = ["geometry", "ref", "role", "type"]
for n, v in child.attrib.items():
if n in ignore:
continue
attributes[n] = v

geometry = None
for sub_child in child:
if sub_child.tag.lower() == "nd":
if geometry is None:
geometry = []
geometry.append(
RelationWayGeometryValue(
lat=Decimal(sub_child.attrib["lat"]),
lon=Decimal(sub_child.attrib["lon"])
)
)

return cls(
attributes=attributes,
geometry=geometry,
ref=ref,
role=role,
result=result
)


class RelationNode(RelationMember):
Expand All @@ -1208,6 +1264,15 @@ def __repr__(self):
return "<overpy.RelationWay ref={} role={}>".format(self.ref, self.role)


class RelationWayGeometryValue(object):
def __init__(self, lat, lon):
self.lat = lat
self.lon = lon

def __repr__(self):
return "<overpy.RelationWayGeometryValue lat={} lon={}>".format(self.lat, self.lon)


class RelationRelation(RelationMember):
_type_value = "relation"

Expand Down Expand Up @@ -1235,7 +1300,7 @@ class OSMSAXHandler(handler.ContentHandler):
#: Tuple of opening elements to ignore
ignore_start = ('osm', 'meta', 'note', 'bounds', 'remark')
#: Tuple of closing elements to ignore
ignore_end = ('osm', 'meta', 'note', 'bounds', 'remark', 'tag', 'nd', 'member', 'center')
ignore_end = ('osm', 'meta', 'note', 'bounds', 'remark', 'tag', 'nd', 'center')

def __init__(self, result):
"""
Expand All @@ -1245,6 +1310,8 @@ def __init__(self, result):
handler.ContentHandler.__init__(self)
self._result = result
self._curr = {}
#: Current relation member object
self.cur_relation_member = None

def startElement(self, name, attrs):
"""
Expand Down Expand Up @@ -1392,11 +1459,21 @@ def _handle_start_nd(self, attrs):
:param attrs: Attributes of the element
:type attrs: Dict
"""
try:
node_ref = attrs['ref']
except KeyError:
raise ValueError("Unable to find required ref value.")
self._curr['node_ids'].append(int(node_ref))
if isinstance(self.cur_relation_member, RelationWay):
if self.cur_relation_member.geometry is None:
self.cur_relation_member.geometry = []
self.cur_relation_member.geometry.append(
RelationWayGeometryValue(
lat=Decimal(attrs["lat"]),
lon=Decimal(attrs["lon"])
)
)
else:
try:
node_ref = attrs['ref']
except KeyError:
raise ValueError("Unable to find required ref value.")
self._curr['node_ids'].append(int(node_ref))

def _handle_start_relation(self, attrs):
"""
Expand Down Expand Up @@ -1429,7 +1506,10 @@ def _handle_start_member(self, attrs):
:param attrs: Attributes of the element
:type attrs: Dict
"""

params = {
# ToDo: Parse attributes
'attributes': {},
'ref': None,
'result': self._result,
'role': None
Expand All @@ -1439,13 +1519,18 @@ def _handle_start_member(self, attrs):
if attrs.get('role', None):
params['role'] = attrs['role']

if attrs['type'] == 'area':
self._curr['members'].append(RelationArea(**params))
elif attrs['type'] == 'node':
self._curr['members'].append(RelationNode(**params))
elif attrs['type'] == 'way':
self._curr['members'].append(RelationWay(**params))
elif attrs['type'] == 'relation':
self._curr['members'].append(RelationRelation(**params))
else:
cls_map = {
"area": RelationArea,
"node": RelationNode,
"relation": RelationRelation,
"way": RelationWay
}
cls = cls_map.get(attrs["type"])
if cls is None:
raise ValueError("Undefined type for member: '%s'" % attrs['type'])

self.cur_relation_member = cls(**params)
self._curr['members'].append(self.cur_relation_member)

def _handle_end_member(self):
self.cur_relation_member = None
8 changes: 8 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ relation-03 (2016-11-23)
out center;
```

relation-04 (2016-11-24)
------------------------

```
(rel["ref"="A 555"];);
out geom;
```

way-03.xml (2016-11-22)
-----------------------

Expand Down
28 changes: 28 additions & 0 deletions tests/base_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,34 @@ def _test_relation03(self, result):
assert relation.center_lat == Decimal("50.8176646")
assert relation.center_lon == Decimal("7.0208539")

def _test_relation04(self, result):
assert len(result.nodes) == 0
assert len(result.relations) == 1
assert len(result.ways) == 0

relation = result.relations[0]

assert isinstance(relation, overpy.Relation)
assert isinstance(relation.id, int)
assert relation.id == 23092

assert isinstance(relation.tags, dict)
assert len(relation.tags) == 10

way = relation.members[2]

assert isinstance(way, overpy.RelationWay)
assert len(way.attributes) == 0
assert isinstance(way.attributes, dict)

assert isinstance(way.geometry, list)
assert len(way.geometry) == 2
assert isinstance(way.geometry[0], overpy.RelationWayGeometryValue)
assert isinstance(way.geometry[0].lat, Decimal)
assert isinstance(way.geometry[0].lon, Decimal)
assert way.geometry[0].lat == Decimal("50.8137408")
assert way.geometry[0].lon == Decimal("6.9813352")


class BaseTestWay(object):
def _test_way01(self, result):
Expand Down
Loading