Test Failed
Push — master ( decfdb...a24d69 )
by Dominik
02:28
created

OSMRelationsElements.role_tuple()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
1
# ~*~ coding: utf-8 ~*~
2
3
""" Simple representation of OpenStreetMap's conceptual data model.
4
(cf. http://wiki.openstreetmap.org/wiki/Elements)
5
6
This implementation of the model assumes that only current data is used,
7
not historic data.
8
"""
9
10
import datetime
11
from sqlalchemy import Column, ForeignKey, Integer, Float, String, DateTime
12
from sqlalchemy.ext.declarative import declarative_base
13
from sqlalchemy.ext.associationproxy import association_proxy
14
from sqlalchemy.ext.orderinglist import ordering_list
15
from sqlalchemy.orm import relationship, backref
16
from sqlalchemy.orm.collections import attribute_mapped_collection
17
18
class OSMAlchemy(object):
19
    """ Wrapper class for the OSMAlchemy model """
20
21
    def __init__(self, base=None, prefix="osm_"):
22
        """ Initialise the table definitions in the wrapper object
23
24
        This function generates the OSM element classes as SQLAlchemy table
25
        declaratives. If called without an argument, it uses a newly created
26
        declarative base.
27
28
        The base argument, if provided, can be either a declarative base or
29
        a Flask-SQLAlchemy object.
30
        """
31
32
        # Check what we got as declarative base
33
        if base is None:
34
            # Nothing, so create one
35
            base = declarative_base()
36
        elif hasattr(base, "Model"):
37
            # Unwrap Flask-SQLAlchemy object if we got one
38
            base = base.Model
39
40
        class OSMElement(base):
41
            """ Base class for all the conceptual OSM elements. """
42
43
            __tablename__ = prefix + "elements"
44
45
            element_id = Column(Integer, primary_key=True)
46
            updated = Column(DateTime, default=datetime.datetime.now,
47
                             onupdate=datetime.datetime.now)
48
            type = Column(String(256))
49
            tags = association_proxy(prefix+"elements_tags", "tag_value",
50
                                     creator=lambda k, v: OSMElementsTags(tag_key=k, tag_value=v))
51
52
            __mapper_args__ = {
53
                'polymorphic_identity': prefix + 'elements',
54
                'polymorphic_on': type,
55
                'with_polymorphic': '*'
56
            }
57
58
        class OSMNode(OSMElement):
59
            """ An OSM node element.
60
61
            A node hast a latitude and longitude, which are non-optional,
62
            and a list of zero or more tags.
63
            """
64
65
            __tablename__ = prefix + "nodes"
66
67
            element_id = Column(Integer, ForeignKey(prefix + 'elements.element_id'),
68
                                primary_key=True)
69
            latitude = Column(Float, nullable=False)
70
            longitude = Column(Float, nullable=False)
71
72
            __mapper_args__ = {
73
                'polymorphic_identity': prefix + 'nodes',
74
            }
75
76
            def __init__(self, latitude=0.0, longitude=0.0, **kwargs):
77
                """ Initialisation with two main positional arguments. """
78
79
                self.latitude = latitude
80
                self.longitude = longitude
81
82
                # Pass rest on to default constructor
83
                OSMElement.__init__(self, **kwargs)
84
85
        class OSMWaysNodes(base):
86
            """ Secondary mapping table for ways and nodes """
87
88
            __tablename__ = prefix + "ways_nodes"
89
90
            map_id = Column(Integer, primary_key=True)
91
            way_id = Column(Integer, ForeignKey(prefix + 'ways.element_id'))
92
            node_id = Column(Integer, ForeignKey(prefix + 'nodes.element_id'))
93
            position = Column(Integer)
94
95
            way = relationship("OSMWay", foreign_keys=[way_id])
96
            node = relationship("OSMNode", foreign_keys=[node_id])
97
98
        class OSMWay(OSMElement):
99
            """ An OSM way element (also area).
100
101
            Contains a list of two or more nodes and a list of zero or more
102
            tags.
103
            """
104
105
            __tablename__ = prefix + "ways"
106
107
            element_id = Column(Integer, ForeignKey(prefix + 'elements.element_id'),
108
                                primary_key=True)
109
            _nodes = relationship('OSMWaysNodes', order_by="OSMWaysNodes.position",
110
                                  collection_class=ordering_list("position"))
111
            nodes = association_proxy("_nodes", "node",
112
                                      creator=lambda _n: OSMWaysNodes(node=_n))
113
114
            __mapper_args__ = {
115
                'polymorphic_identity': prefix + 'ways',
116
            }
117
118
        class OSMElementsTags(base):
119
            """ Secondary mapping table for elements and tags """
120
121
            __tablename__ = prefix + "elements_tags"
122
123
            map_id = Column(Integer, primary_key=True)
124
            element_id = Column(Integer, ForeignKey(prefix + 'elements.element_id'))
125
            tag_id = Column(Integer, ForeignKey(prefix + 'tags.tag_id'))
126
127
            element = relationship("OSMElement", foreign_keys=[element_id],
128
                                   backref=backref(prefix+"elements_tags",
129
                                                   collection_class=attribute_mapped_collection("tag_key"),
130
                                                   cascade="all, delete-orphan"))
131
            tag = relationship("OSMTag", foreign_keys=[tag_id])
132
            tag_key = association_proxy("tag", "key")
133
            tag_value = association_proxy("tag", "value")
134
135
        class OSMRelationsElements(base):
136
            """ Secondary mapping table for relation members """
137
138
            __tablename__ = prefix + "relations_elements"
139
140
            map_id = Column(Integer, primary_key=True)
141
            relation_id = Column(Integer, ForeignKey(prefix + 'relations.element_id'))
142
            element_id = Column(Integer, ForeignKey(prefix + 'elements.element_id'))
143
            position = Column(Integer)
144
            role = Column(String(256))
145
146
            relation = relationship("OSMRelation", foreign_keys=[relation_id])
147
            element = relationship("OSMElement", foreign_keys=[element_id])
148
149
            @property
150
            def role_tuple(self):
151
                return (self.element, self.role)
152
153
        class OSMRelation(OSMElement):
154
            """ An OSM relation element.
155
156
            Contains zero or more members (ways, nodes or other relations)
157
            with associated, optional roles and zero or more tags.
158
            """
159
160
            __tablename__ = prefix + "relations"
161
162
            element_id = Column(Integer, ForeignKey(prefix + 'elements.element_id'),
163
                                primary_key=True)
164
            _members = relationship("OSMRelationsElements",
165
                                    order_by="OSMRelationsElements.position",
166
                                    collection_class=ordering_list("position"))
167
            members = association_proxy("_members", "role_tuple",
168
                                        creator=lambda _m: OSMRelationsElements(element=_m[0],
169
                                                                                role=_m[1]))
170
171
            __mapper_args__ = {
172
                'polymorphic_identity': prefix + 'relations',
173
            }
174
175
        class OSMTag(base):
176
            """ An OSM tag element.
177
178
            Simple key/value pair.
179
            """
180
181
            __tablename__ = prefix + "tags"
182
183
            tag_id = Column(Integer, primary_key=True)
184
            key = Column(String(256))
185
            value = Column(String(256))
186
187
            def __init__(self, key="", value="", **kwargs):
188
                """ Initialisation with two main positional arguments. """
189
190
                self.key = key
191
                self.value = value
192
193
                # Pass rest on to default constructor
194
                base.__init__(self, **kwargs)
195
196
197
        # Set the classes as members of the wrapper object
198
        self.Node = OSMNode #pylint: disable=invalid-name
199
        self.Way = OSMWay #pylint: disable=invalid-name
200
        self.Relation = OSMRelation #pylint: disable=invalid-name
201
        self.Tag = OSMTag #pylint: disable=invalid-name
202
203
        # Store generation attributes
204
        self._base = base
205
        self._prefix = prefix
206