_dom_to_relation()   F
last analyzed

Complexity

Conditions 9

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 9.0046

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 9
c 5
b 0
f 0
dl 0
loc 43
ccs 25
cts 26
cp 0.9615
crap 9.0046
rs 3
1
# ~*~ coding: utf-8 ~*~
2
#-
3
# OSMAlchemy - OpenStreetMap to SQLAlchemy bridge
4
# Copyright (c) 2016 Dominik George <[email protected]>
5
#
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be included in all
14
# copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
# SOFTWARE.
23
#
24
# Alternatively, you are free to use OSMAlchemy under Simplified BSD, The
25
# MirOS Licence, GPL-2+, LGPL-2.1+, AGPL-3+ or the same terms as Python
26
# itself.
27
28 1
""" Utility code for the OSMAlchemy database OSMAlchemy. """
29
30 1
import operator
0 ignored issues
show
Unused Code introduced by
The import operator seems to be unused.
Loading history...
31 1
import xml.dom.minidom as minidom
32 1
import dateutil.parser
33 1
from sqlalchemy.sql.elements import BinaryExpression, BooleanClauseList, BindParameter
0 ignored issues
show
Unused Code introduced by
Unused BinaryExpression imported from sqlalchemy.sql.elements
Loading history...
Unused Code introduced by
Unused BooleanClauseList imported from sqlalchemy.sql.elements
Loading history...
Unused Code introduced by
Unused BindParameter imported from sqlalchemy.sql.elements
Loading history...
34 1
from sqlalchemy.sql.annotation import AnnotatedColumn
0 ignored issues
show
Bug introduced by
The name AnnotatedColumn does not seem to exist in module sqlalchemy.sql.annotation.
Loading history...
Unused Code introduced by
Unused AnnotatedColumn imported from sqlalchemy.sql.annotation
Loading history...
35
36 1
def _import_osm_dom(oa, dom, session=None):
0 ignored issues
show
Coding Style Naming introduced by
The name oa does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
37
    """ Import a DOM tree from OSM XML into an OSMAlchemy model.
38
39
    Not called directly; used by _import_osm_xml and _import_osm_file.
40
    """
41
42 1
    # Get session
43 1
    if session is None:
44 1
        session = oa.session
45 1
46 1
    oa.logger.debug("Started import of data from DOM.")
47 1
48 1
    def _dom_attrs_to_any(xml_element, osm_element):
49 1
        if "version" in xml_element.attributes.keys():
50 1
            osm_element.version = int(xml_element.attributes["version"].value)
51 1
        if "changeset" in xml_element.attributes.keys():
52 1
            osm_element.changeset = int(xml_element.attributes["changeset"].value)
53 1
        if "user" in xml_element.attributes.keys():
54 1
            osm_element.user = xml_element.attributes["user"].value
55
        if "uid" in xml_element.attributes.keys():
56 1
            osm_element.uid = int(xml_element.attributes["uid"].value)
57
        if "visible" in xml_element.attributes.keys():
58 1
            osm_element.visible = True if xml_element.attributes["visible"].value == "true" else False
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
59
        if "timestamp" in xml_element.attributes.keys():
60
            osm_element.timestamp = dateutil.parser.parse(xml_element.attributes["timestamp"].value)
61
62 1
    def _dom_tags_to_any(xml_element, osm_element):
63
        # Remove all tags previously associated with element
64 1
        for key in list(osm_element.tags.keys()):
65
            del osm_element.tags[key]
66 1
67 1
        # Iterate over all <tag /> nodes in the DOM element
68
        for xml_tag in xml_element.getElementsByTagName("tag"):
69 1
            # Append data to tags
70
            osm_element.tags[xml_tag.attributes["k"].value] = xml_tag.attributes["v"].value
71
72 1
    def _dom_to_node(xml_element):
73 1
        with oa.session.no_autoflush:
74 1
            # Get mandatory node id
75
            id_ = int(xml_element.attributes["id"].value)
76
            oa.logger.debug("Importing OSM node with id %i." % id_)
77 1
78 1
            # Find object in database and create if non-existent
79
            node = oa.session.query(oa.node).filter_by(id=id_).scalar()
80
            if node is None:
81 1
                node = oa.node(id=id_)
82 1
83
            # Store mandatory latitude and longitude
84
            node.latitude = xml_element.attributes["lat"].value
85 1
            node.longitude = xml_element.attributes["lon"].value
86 1
87
            # Store other attributes and tags
88 1
            _dom_attrs_to_any(xml_element, node)
89 1
            _dom_tags_to_any(xml_element, node)
90
91 1
        # Add to session
92
        oa.session.add(node)
93
        oa.session.commit()
94 1
95 1
    def _dom_to_way(xml_element):
96 1
        with oa.session.no_autoflush:
97
            # Get mandatory way id
98
            id_ = int(xml_element.attributes["id"].value)
99 1
            oa.logger.debug("Importing OSM way with id %i." % id_)
100
101 1
            # Find object in database and create if non-existent
102 1
            way = oa.session.query(oa.way).filter_by(id=id_).scalar()
103
            if way is None:
104 1
                way = oa.way(id=id_)
105
106
            # Find all related nodes
107 1
            for node in xml_element.getElementsByTagName("nd"):
108 1
                # Get node id and find object
109
                ref = int(node.attributes["ref"].value)
110
                new_node = oa.session.query(oa.node).filter_by(id=ref).one()
111 1
                # Append to nodes in way
112 1
                way.nodes.append(new_node)
113
114 1
            # Store other attributes and tags
115 1
            _dom_attrs_to_any(xml_element, way)
116
            _dom_tags_to_any(xml_element, way)
117 1
118
        # Add to session
119
        oa.session.add(way)
120 1
        oa.session.commit()
121 1
122 1
    def _dom_to_relation(xml_element):
123
        with oa.session.no_autoflush:
124
            # Get mandatory way id
125 1
            id_ = int(xml_element.attributes["id"].value)
126
            oa.logger.debug("Importing OSM relation with id %i." % id_)
127 1
128 1
            # Find object in database and create if non-existent
129
            relation = oa.session.query(oa.relation).filter_by(id=id_).scalar()
130 1
            if relation is None:
131 1
                relation = oa.relation(id=id_)
132
133
            # Find all members
134 1
            for member in xml_element.getElementsByTagName("member"):
135 1
                # Get member attributes
136
                ref = int(member.attributes["ref"].value)
137 1
                type_ = member.attributes["type"].value
138 1
139 1
                if "role" in member.attributes.keys():
140 1
                    role = member.attributes["role"].value
141 1
                else:
142 1
                    role = ""
143
                element = oa.session.query(oa.element).filter_by(id=ref, type=type_).scalar()
144 1
                if element is None:
145 1
                    # We do not know the member yet, create a stub
146
                    if type == "node":
147 1
                        element = oa.node(id=ref)
148
                    elif type == "way":
149
                        element = oa.way(id=ref)
150 1
                    elif type == "relation":
151 1
                        element = oa.relation(id=ref)
152
                    # We need to commit here because element could be repeated
153
                    oa.session.add(element)
154 1
                    oa.session.commit()
155 1
                # Append to members
156
                relation.members.append((element, role))
157
158 1
            # Store other attributes and tags
159
            _dom_attrs_to_any(xml_element, relation)
160
            _dom_tags_to_any(xml_element, relation)
161 1
162
        # Add to session
163 1
        oa.session.add(relation)
164 1
        oa.session.commit()
165 1
166 1
    # Get root element
167 1
    osm = dom.documentElement
168 1
169
    # Iterate over children to find nodes, ways and relations
170
    for xml_element in osm.childNodes:
171 1
        # Determine element type
172
        if xml_element.nodeName == "node":
173 1
            _dom_to_node(xml_element)
174
        elif xml_element.nodeName == "way":
175
            _dom_to_way(xml_element)
176
        elif xml_element.nodeName == "relation":
177
            _dom_to_relation(xml_element)
178
179
        # Rmove children
180
        xml_element.unlink()
181
182
def _import_osm_xml(oa, xml, session=None):
0 ignored issues
show
Coding Style Naming introduced by
The name oa does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
183
    """ Import a string in OSM XML format into an OSMAlchemy model.
184
185
      oa - reference to the OSMAlchemy model instance
186 1
      xml - string containing the XML data
187
    """
188
189
    # Get session
190
    if session is None:
191
        session = oa.session
192
193
    # Parse string into DOM structure
194
    dom = minidom.parseString(xml)
195 1
196
    return _import_osm_dom(oa, dom, session=session)
197 1
198
def _import_osm_file(oa, file, session=None):
0 ignored issues
show
Coding Style Naming introduced by
The name oa does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
199
    """ Import a file in OSM XML format into an OSMAlchemy model.
200
201
      oa - reference to the OSMAlchemy model instance
202
      path - path to the file to import or open file object
203
    """
204
205
    # Get session
206
    if session is None:
207
        session = oa.session
208
209
    # Parse document into DOM structure
210
    dom = minidom.parse(file)
211
212
    return _import_osm_dom(oa, dom, session=session)
213