summaryrefslogtreecommitdiff
path: root/sublab_project/matekarte/osmux.py
diff options
context:
space:
mode:
Diffstat (limited to 'sublab_project/matekarte/osmux.py')
-rw-r--r--sublab_project/matekarte/osmux.py186
1 files changed, 186 insertions, 0 deletions
diff --git a/sublab_project/matekarte/osmux.py b/sublab_project/matekarte/osmux.py
new file mode 100644
index 0000000..69cefe5
--- /dev/null
+++ b/sublab_project/matekarte/osmux.py
@@ -0,0 +1,186 @@
+'''
+
+OSMux - A query multiplexer for the Overpass OpenStreetMap-API
+
+Version 2011-10-22
+
+(c) 2011 - Conrad Hoffmann <ch@bitfehler.net>
+
+This software is public domain and comes without warranty of any kind.
+
+This script splits queries for large areas into several smaller bounding boxes, sends them to them
+to the server and merges the results into a single file (the result for the original query).
+
+Additionally, ways are returned as nodes, so they can be displayed in map overlays. Actually, for
+a matching way the script returns the first node of the way - but with all the tags that were
+originally attached to the way.
+
+Basic usage instructions available via the --help switch.
+
+See http://code.bitfehler.net/osmux for more details.
+
+'''
+
+from __future__ import print_function
+
+from os import close
+from os import remove
+from sys import stderr
+from time import sleep
+from tempfile import mkstemp
+from argparse import ArgumentParser
+import xml.etree.ElementTree as etree
+
+try:
+ from urllib.request import urlretrieve
+except ImportError:
+ from urllib import urlretrieve
+
+QUERY_TEMPLATE = 'http://www.overpass-api.de/api/xapi?%s[bbox=%f,%f,%f,%f][%s=%s]'
+
+def log(msg):
+ ''' Print a message to the console, unless the 'quiet' flag was set. '''
+
+ if 'params' not in globals():
+ return
+
+ if not params.quiet:
+ print(msg)
+
+def extract(xmlfile, tagname):
+ ''' Extract nodes from a query result.
+
+ Regular nodes are just added to the result. For ways, the ways tags are copied to the first
+ node of the way and this first node is added to the result.
+
+ '''
+ tree = etree.parse(xmlfile)
+ root = tree.getroot()
+ result = {}
+ nodes = {}
+
+ for child in root:
+ if child.tag == 'node':
+ # Save all nodes in this temp structure, if we are parsing ways we need them later on.
+ nodes[child.attrib['id']] = child
+ # If the node also contains the tag we are looking for, add it to the result.
+ for tag in child:
+ if tag.attrib['k'] == tagname:
+ result[child.attrib['id']] = child
+ elif child.tag == 'way':
+ # Create a new custom node that contains all of the ways tags and add it to result. If
+ # a way is returned, it must contain the tag we are looking for, so no need to check.
+ node = nodes[child[0].attrib['ref']]
+ for sub in child:
+ if sub.tag == 'tag':
+ etree.SubElement(node, 'tag', sub.attrib)
+ result[node.attrib['id']] = node
+ elif child.tag == 'remark':
+ # This usually means the query timed out or ran out of memory.
+ print('Potential error: %s' % child.text, file=stderr)
+
+ return result
+
+def osmux(tag, north, south, east, west, value='*', pause=1, step=1.0):
+ (handle, tmpfile) = mkstemp(suffix='.xml', prefix='osm')
+ close(handle)
+ log("Using temporary file %s, step %f and pause %d." % (tmpfile, step, pause))
+
+ result = {}
+
+ cur_lat = south
+ cur_lon = west
+
+ while cur_lat < north:
+
+ # While we are in this loop, there are more squares to fetch.
+
+ # We know which square to go for, get its bbox:
+ bbox_lon = [cur_lon, min(cur_lon + step, east)]
+ bbox_lat = [cur_lat, min(cur_lat + step, north)]
+
+ nquery = QUERY_TEMPLATE % ('node', bbox_lon[0], bbox_lat[0], bbox_lon[1], bbox_lat[1], tag, value)
+ wquery = QUERY_TEMPLATE % ('way', bbox_lon[0], bbox_lat[0], bbox_lon[1], bbox_lat[1], tag, value)
+
+ log("Fetching %s" % nquery)
+ urlretrieve(nquery, tmpfile)
+ result.update(extract(tmpfile, tag))
+
+ log("Fetching %s" % wquery)
+ urlretrieve(wquery, tmpfile)
+ result.update(extract(tmpfile, tag))
+
+ # Calculate the next sub-bbox:
+ if cur_lon + step < east:
+ # Proceed with next square in current "row":
+ cur_lon += step
+ else:
+ # Reset longitude, continue with next "row", if any:
+ cur_lon = west
+ cur_lat += step
+
+ # Wait a moment - might help the server.
+ sleep(pause)
+
+
+ log("Removing temporary data file...")
+ remove(tmpfile)
+
+ log("Generating data...")
+ buffer_ = (('''<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6" generator="OSMux (http://code.bitfehler.net/osmux">
+ <bound box="%f,%f,%f,%f" origin="Osmosis SNAPSHOT-rexported"/>
+''' % (west, south, east, north)).encode('utf-8'))
+ for nid in result:
+ node = result[nid]
+ buffer_ += etree.tostring(node, encoding='utf-8')
+ buffer_ += '</osm>'.encode('utf-8')
+ return buffer_
+
+if __name__ == '__main__':
+ # Parse command line arguments.
+ parser = ArgumentParser(description='Generate an OpenLayers OSM file using the Overpass API.',
+ epilog='See http://code.bitfehler.net for more details.')
+ parser.add_argument('-q', '--quiet', dest='quiet', action='store_true', help='suppress progress messages')
+ parser.add_argument('-S', '--step', dest='step', metavar='D', default=1.0, type=float, help='split into steps of D degrees')
+ parser.add_argument('-p', '--pause', dest='pause', metavar='P', default=1, type=int, help='pause for P seconds between requests to reduce server load (default: 1, must be >= 0)')
+ parser.add_argument('-t', '--tag', dest='tag', metavar='<tag>', type=str, required=True, help='return only elements with this tag')
+ parser.add_argument('-v', '--value', dest='value', metavar='<value>', default='*', type=str, help='return only elements with this value (omit for \'*\')')
+ parser.add_argument('-s', '--south', dest='south', metavar='S', type=float, required=True, help='latitude of the southern border of the bounding box')
+ parser.add_argument('-w', '--west', dest='west', metavar='W', type=float, required=True, help='longitude of the western border of the bounding box')
+ parser.add_argument('-n', '--north', dest='north', metavar='N', type=float, required=True, help='latitude of the northern border of the bounding box')
+ parser.add_argument('-e', '--east', dest='east', metavar='E', type=float, required=True, help='longitude of the eastern border of the bounding box')
+ parser.add_argument('outfile', metavar='<output file>', type=str, help='the output file for the merged OSM data')
+ params = parser.parse_args()
+
+ # Sanity checks for the arguments.
+ if params.pause < 0:
+ print('Value for --pause must be >= 0. Exiting.', file=stderr)
+ exit(1)
+ if params.step <= 0:
+ print('Value for --pause must be > 0. Exiting.', file=stderr)
+ exit(1)
+ try:
+ open(params.outfile, mode='ab')
+ except IOError as e:
+ (errno, strerror) = e.args
+ print('Failed to open output file for writing: %s. Exiting.' % strerror, file=stderr)
+ exit(1)
+ if min(params.south, params.north) != params.south:
+ print('Value for --south must be less then --north. Use negative numbers for southern hemisphere. Exiting.', file=stderr)
+ exit(1)
+ if min(params.east, params.west) != params.west:
+ print('Value for --west must be less then --east. Use e.g. --east 187.5 if you need to cover the 180th meridian. Exiting.', file=stderr)
+ exit(1)
+
+ buffer_ = osmux(params.tag,
+ params.north,
+ params.south,
+ params.east,
+ params.west,
+ value=params.value,
+ pause=params.pause,
+ step=params.step)
+
+ with open(params.outfile, mode='wb') as outfile:
+ outfile.write(buffer_)