Issues (77)

osmalchemy/util/db.py (10 issues)

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
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 BinaryExpression imported from sqlalchemy.sql.elements
Loading history...
Unused BooleanClauseList imported from sqlalchemy.sql.elements
Loading history...
Unused BindParameter imported from sqlalchemy.sql.elements
Loading history...
34 1
from sqlalchemy.sql.annotation import AnnotatedColumn
0 ignored issues
show
The name AnnotatedColumn does not seem to exist in module sqlalchemy.sql.annotation.
Loading history...
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
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