Skip to content

Commit

Permalink
git - Merge pull request #54 from DinoTools/relation_member_attributes
Browse files Browse the repository at this point in the history
Add geometry information to relation members
  • Loading branch information
phibos committed Nov 27, 2016
2 parents 65147a8 + 98fd849 commit 7b0e0d9
Show file tree
Hide file tree
Showing 7 changed files with 2,228 additions and 18 deletions.
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

0 comments on commit 7b0e0d9

Please sign in to comment.