summaryrefslogtreecommitdiff
path: root/sublab_project/matekarte
diff options
context:
space:
mode:
Diffstat (limited to 'sublab_project/matekarte')
-rw-r--r--sublab_project/matekarte/__init__.py0
-rw-r--r--sublab_project/matekarte/models.py3
-rw-r--r--sublab_project/matekarte/osmux.py186
-rw-r--r--sublab_project/matekarte/tasks.py49
-rw-r--r--sublab_project/matekarte/templates/matekarte/matekarte.html191
-rw-r--r--sublab_project/matekarte/tests.py16
-rw-r--r--sublab_project/matekarte/urls.py6
-rw-r--r--sublab_project/matekarte/views.py1
8 files changed, 452 insertions, 0 deletions
diff --git a/sublab_project/matekarte/__init__.py b/sublab_project/matekarte/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sublab_project/matekarte/__init__.py
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 <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_)
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 %}
+<h2>Mate</h2>
+<p>
+ Diese Karte markiert alle Orte in Leipzig (bzw. sogar ganz Deutschland:
+ einfach herauszoomen) an denen Club Mate zu haben ist und die bei
+ <a href="http://www.openstreetmap.org">OpenStreetMap</a> mit dem Tag
+ <i>drink:club-mate=*</i> versehen wurden. Ein paar mehr Details findet
+ Ihr <a href="http://bitfehler.net/mate">hier</a>. Die Daten werden jede
+ Nacht aktualisiert.
+</p>
+<div id="spacer" style="margin-left: 20px;">
+<div id="mapdiv"></div>
+</div>
+<p style="font-size: 10px;">Javascript required. Click an icon for details. Map data
+<a style="font-size: 10px;" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a> 2011
+<a style="font-size: 10px;" href="http://www.openstreetmap.org">OpenStreetMap</a> contributors.</p>
+
+<script type="text/javascript" src="http://www.openlayers.org/api/OpenLayers.js"></script>
+<script defer="defer" type="text/javascript">
+<!--
+
+var obj, map, pois, styleMap, selectControl, selectedFeature, loadingPopup;
+var numDots = 3;
+
+function resizeMap() {
+ document.getElementById("mapdiv").style.width = "100px"
+ document.getElementById("mapdiv").style.height = "100px";
+ var c = document.getElementsByClassName("content")[0];
+ var w = c.offsetWidth;
+ var h = c.offsetHeight;
+ w = Math.round(w * 0.90);
+ document.getElementById("mapdiv").style.width = w + "px"
+ document.getElementById("mapdiv").style.height = "500px";
+}
+
+function onPopupClose(evt) {
+ // 'this' is the popup.
+ selectControl.unselect(this.feature);
+}
+
+// Pretty-print an amenity tag (and others actually)
+function amenityPp(amenity) {
+ var words = amenity.split("_");
+ var result = "";
+ for (w in words) {
+ if (result != "") {
+ result += " ";
+ }
+ result += words[w].charAt(0).toUpperCase() + words[w].slice(1);
+ }
+ return result;
+}
+
+function popupText(feature) {
+ var title = "";
+ if ("name" in feature.attributes) {
+ title = feature.attributes["name"];
+ }
+ else {
+ title = "*no name*";
+ }
+ if ("website" in feature.attributes) {
+ title = '<a href="' + feature.attributes["website"] + '" rel="external">' + title + '</a>';
+ }
+
+ var desc = "";
+ if ("amenity" in feature.attributes) {
+ desc = '<p style="font-size: smaller">' + amenityPp(feature.attributes["amenity"]) + '</p>';
+ }
+ else if ("leisure" in feature.attributes) {
+ desc = '<p style="font-size: smaller">' + amenityPp(feature.attributes["leisure"]) + '</p>';
+ }
+ else if ("shop" in feature.attributes) {
+ desc = '<p style="font-size: smaller">' + amenityPp(feature.attributes["shop"]) + '</p>';
+ }
+
+ var addr = "";
+ if ("addr:street" in feature.attributes) {
+ addr += feature.attributes["addr:street"]
+ if ("addr:housenumber" in feature.attributes) {
+ addr += " " + feature.attributes["addr:housenumber"];
+ }
+ addr += "</br>";
+ }
+ if ("addr:city" in feature.attributes) {
+ if ("addr:postcode" in feature.attributes) {
+ addr += feature.attributes["addr:postcode"] + " ";
+ }
+ addr += feature.attributes["addr:city"];
+ }
+
+ var open = "";
+ if ("opening_hours" in feature.attributes) {
+ open += '<p style="font-size: smaller">Open: ' + feature.attributes["opening_hours"] + '</p>';
+ }
+
+ return "<h2>" + title + "</h2>" + desc + "<p>" + addr + "</p>" + open;
+}
+
+function onFeatureSelect(feature) {
+ var lst = "";
+ for (var prop in feature.attributes) {
+ lst += prop + " ";
+ }
+ popup = new OpenLayers.Popup.FramedCloud(
+ "featurePopup",
+ feature.geometry.getBounds().getCenterLonLat(),
+ new OpenLayers.Size(100,100),
+ popupText(feature),
+ null, true, onPopupClose
+ );
+ feature.popup = popup;
+ popup.feature = feature;
+ map.addPopup(popup);
+}
+function onFeatureUnselect(feature) {
+ if (feature.popup) {
+ popup.feature = null;
+ map.removePopup(feature.popup);
+ feature.popup.destroy();
+ feature.popup = null;
+ }
+}
+
+function dataCallback() {
+ map.removePopup(loadingPopup);
+}
+
+resizeMap();
+window.onresize = resizeMap;
+
+map = new OpenLayers.Map("mapdiv");
+layer = new OpenLayers.Layer.OSM();
+layer.attribution = 0;
+
+
+var styleMap = new OpenLayers.Style({
+ externalGraphic: '/static/img/mate_icon.png',
+ graphicWidth: 32,
+ graphicHeight: 32,
+ graphicXOffset: 0,
+ graphicYOffset: -32
+});
+
+var pois = new OpenLayers.Layer.Vector("Marker", {
+ styleMap: styleMap,
+ eventListeners: {"featuresadded": dataCallback },
+ strategies: [new OpenLayers.Strategy.Fixed],
+ projection:map.displayProjection,
+ protocol: new OpenLayers.Protocol.HTTP({
+ url: "{{ MEDIA_URL }}mate.xml",
+ format: new OpenLayers.Format.OSM,
+ })
+});
+
+map.addLayer(layer);
+map.addLayer(pois);
+
+//Set start centrepoint and zoom
+var lonLat = new OpenLayers.LonLat(12.3903, 51.342).transform(
+ new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984
+ map.getProjectionObject() // to Spherical Mercator Projection
+);
+map.setCenter(lonLat, 12);
+
+var lonLatPopup = new OpenLayers.LonLat(12.34, 51.36).transform(
+ new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984
+ map.getProjectionObject() // to Spherical Mercator Projection
+);
+
+loadingPopup = new OpenLayers.Popup("loadingPopup",
+ lonLatPopup,
+ new OpenLayers.Size(300,200),
+ '<p style="font-weight: bold; text-align: center; margin-top: 60px">Loading OpenStreetMap data.<br>Please wait.<br><br><span id="dots">...</span></p>', false
+);
+loadingPopup.opacity = 0.9;
+map.addPopup(loadingPopup);
+
+selectControl = new OpenLayers.Control.SelectFeature(pois, {
+ onSelect: onFeatureSelect,
+ onUnselect: onFeatureUnselect
+});
+map.addControl(selectControl);
+selectControl.activate();
+-->
+</script>
+{% 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.