From cfab2cec623f6b277fb562b06b9ce9d8c6d68bbb Mon Sep 17 00:00:00 2001 From: Christian Franke Date: Mon, 23 Jan 2012 21:49:39 +0100 Subject: add matekarte for now --- sublab_project/matekarte/__init__.py | 0 sublab_project/matekarte/models.py | 3 + sublab_project/matekarte/osmux.py | 186 ++++++++++++++++++++ sublab_project/matekarte/tasks.py | 49 ++++++ .../matekarte/templates/matekarte/matekarte.html | 191 +++++++++++++++++++++ sublab_project/matekarte/tests.py | 16 ++ sublab_project/matekarte/urls.py | 6 + sublab_project/matekarte/views.py | 1 + sublab_project/sublab_monitor/tasks.py | 4 - 9 files changed, 452 insertions(+), 4 deletions(-) create mode 100644 sublab_project/matekarte/__init__.py create mode 100644 sublab_project/matekarte/models.py create mode 100644 sublab_project/matekarte/osmux.py create mode 100644 sublab_project/matekarte/tasks.py create mode 100644 sublab_project/matekarte/templates/matekarte/matekarte.html create mode 100644 sublab_project/matekarte/tests.py create mode 100644 sublab_project/matekarte/urls.py create mode 100644 sublab_project/matekarte/views.py diff --git a/sublab_project/matekarte/__init__.py b/sublab_project/matekarte/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/sublab_project/matekarte/models.py b/sublab_project/matekarte/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/sublab_project/matekarte/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. 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 + +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_ = ((''' + + +''' % (west, south, east, north)).encode('utf-8')) + for nid in result: + node = result[nid] + buffer_ += etree.tostring(node, encoding='utf-8') + buffer_ += ''.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='', type=str, required=True, help='return only elements with this tag') + parser.add_argument('-v', '--value', dest='value', metavar='', 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='', 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_) diff --git a/sublab_project/matekarte/tasks.py b/sublab_project/matekarte/tasks.py new file mode 100644 index 0000000..bc1018b --- /dev/null +++ b/sublab_project/matekarte/tasks.py @@ -0,0 +1,49 @@ +"""Celery tasks. +""" +from celery.task import PeriodicTask +from celery.schedules import crontab +from django.conf import settings +import os + +from osmux import osmux + +class OSMFetcher(object): + """ + A mixin to get and store OpenStreetmap Data + """ + + @property + def bbox(self): + raise NotImplementedError + + @property + def store_name(self): + raise NotImplementedError + + @property + def tag(self): + raise NotImplementedError + + value = '*' + + def run(self, **kwargs): + + fn = os.path.join(settings.MEDIA_ROOT, self.store_name) + fn_new = os.path.join(settings.MEDIA_ROOT, 'new-%s' % self.store_name) + + with open(fn_new, 'wb') as output: + output.write(osmux( + self.tag, + self.bbox[0], + self.bbox[1], + self.bbox[2], + self.bbox[3], + self.value)) + os.rename(fn_new, fn) + + +#class MateFetcher(OSMFetcher, PeriodicTask): +# run_every = crontab(minute=0, hour=3) +# bbox = (55.26, 46.52, 15.26, 5.22) +# store_name = 'mate.xml' +# tag = 'drink:club-mate' diff --git a/sublab_project/matekarte/templates/matekarte/matekarte.html b/sublab_project/matekarte/templates/matekarte/matekarte.html new file mode 100644 index 0000000..7172b46 --- /dev/null +++ b/sublab_project/matekarte/templates/matekarte/matekarte.html @@ -0,0 +1,191 @@ +{% extends 'base.html' %} + +{% block title %}Mate{% endblock %} + +{% block content %} +

Mate

+

+ Diese Karte markiert alle Orte in Leipzig (bzw. sogar ganz Deutschland: + einfach herauszoomen) an denen Club Mate zu haben ist und die bei + OpenStreetMap mit dem Tag + drink:club-mate=* versehen wurden. Ein paar mehr Details findet + Ihr hier. Die Daten werden jede + Nacht aktualisiert. +

+
+
+
+

Javascript required. Click an icon for details. Map data +CC-BY-SA 2011 +OpenStreetMap contributors.

+ + + +{% endblock content %} diff --git a/sublab_project/matekarte/tests.py b/sublab_project/matekarte/tests.py new file mode 100644 index 0000000..501deb7 --- /dev/null +++ b/sublab_project/matekarte/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/sublab_project/matekarte/urls.py b/sublab_project/matekarte/urls.py new file mode 100644 index 0000000..f15aaa7 --- /dev/null +++ b/sublab_project/matekarte/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls.defaults import patterns, url +from django.views.generic.simple import direct_to_template + +urlpatterns = patterns('', + url(r'^$', direct_to_template, {'template': 'matekarte/matekarte.html' }), +) diff --git a/sublab_project/matekarte/views.py b/sublab_project/matekarte/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/sublab_project/matekarte/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/sublab_project/sublab_monitor/tasks.py b/sublab_project/sublab_monitor/tasks.py index 20f8ad8..681ce5f 100644 --- a/sublab_project/sublab_monitor/tasks.py +++ b/sublab_project/sublab_monitor/tasks.py @@ -22,10 +22,6 @@ class NetworkStatus(PeriodicTask): run_every = timedelta(minutes=2) ignore_result = True - def __init__(self, *args, **kwargs): - PeriodicTask.__init__(self, *args, **kwargs) - - @staticmethod def host_alive(host): rc = subprocess.call(['ping', '-c', '2', '-W', '1', host]) -- cgit v1.2.1