From 78a8a1b65ed30824c5e482b241f713f3e229eefb Mon Sep 17 00:00:00 2001 From: Adrian Schmutzler Date: Sat, 14 Oct 2017 16:42:13 +0200 Subject: [PATCH] fff-map: Add layer for KeyExchange v2 Draft version. On my local setup, I had to create CSVs manually with mkcsv.py and copy them to /var/... Signed-off-by: Adrian Schmutzler --- ffmap/mapnik/hoodsv2.xml | 28 ++++++++ ffmap/mapnik/mkcsv.py | 129 ++++++++++++++++++++++-------------- ffmap/mapnik/run.sh | 3 +- ffmap/mapnik/tilestache.cfg | 10 +++ ffmap/maptools.py | 129 +++++++++++++++++++++++------------- ffmap/web/application.py | 1 + ffmap/web/static/js/map.js | 4 +- install.sh | 6 +- 8 files changed, 210 insertions(+), 100 deletions(-) create mode 100644 ffmap/mapnik/hoodsv2.xml diff --git a/ffmap/mapnik/hoodsv2.xml b/ffmap/mapnik/hoodsv2.xml new file mode 100644 index 0000000..bb86599 --- /dev/null +++ b/ffmap/mapnik/hoodsv2.xml @@ -0,0 +1,28 @@ + + + + + + + hoodborder + + csv + csv/hoodsv2.csv + + + + hoodpoint + + csv + csv/hood-points-v2.csv + + + diff --git a/ffmap/mapnik/mkcsv.py b/ffmap/mapnik/mkcsv.py index 919639b..5dd008b 100755 --- a/ffmap/mapnik/mkcsv.py +++ b/ffmap/mapnik/mkcsv.py @@ -4,62 +4,26 @@ import math import numpy as np from scipy.spatial import Voronoi +import urllib.request, json + from pymongo import MongoClient client = MongoClient() db = client.freifunk -with open("csv/routers.csv", "w") as csv: - csv.write("lng,lat,status\n") - for router in db.routers.find({"position.coordinates": {"$exists": True}}): - csv.write("%f,%f,%s\n" % ( - router["position"]["coordinates"][0], - router["position"]["coordinates"][1], - router["status"] - )) +EARTH_RADIUS = 6378137.0 -with open("csv/links.csv", "w") as csv: - csv.write("WKT,quality\n") - for router in db.routers.find({"position.coordinates": {"$exists": True}, "neighbours": {"$exists": True}}): - for neighbour in router["neighbours"]: - if "position" in neighbour: - csv.write("\"LINESTRING (%f %f,%f %f)\",%i\n" % ( - router["position"]["coordinates"][0], - router["position"]["coordinates"][1], - neighbour["position"]["coordinates"][0], - neighbour["position"]["coordinates"][1], - neighbour["quality"] - )) +def merc_sphere(lng, lat): + x = math.radians(lng) * EARTH_RADIUS + y = math.log(math.tan(math.pi/4 + math.radians(lat)/2)) * EARTH_RADIUS + return (x,y) -with open("csv/hood-points.csv", "w", encoding="UTF-8") as csv: - csv.write("lng,lat,name\n") - for hood in db.hoods.find({"position": {"$exists": True}}): - csv.write("%f,%f,\"%s\"\n" % ( - hood["position"]["coordinates"][0], - hood["position"]["coordinates"][1], - hood["name"] - )) - -with open("csv/hoods.csv", "w") as csv: - EARTH_RADIUS = 6378137.0 - - def merc_sphere(lng, lat): - x = math.radians(lng) * EARTH_RADIUS - y = math.log(math.tan(math.pi/4 + math.radians(lat)/2)) * EARTH_RADIUS - return (x,y) - - def merc_sphere_inv(x, y): - lng = math.degrees(x / EARTH_RADIUS) - lat = math.degrees(2*math.atan(math.exp(y / 6378137.0)) - math.pi/2) - return (lng,lat) - - csv.write("WKT\n") - hoods = [] - for hood in db.hoods.find({"position": {"$exists": True}}): - # convert coordinates info marcator sphere as voronoi doesn't work with lng/lat - x, y = merc_sphere(hood["position"]["coordinates"][0], hood["position"]["coordinates"][1]) - hoods.append([x, y]) +def merc_sphere_inv(x, y): + lng = math.degrees(x / EARTH_RADIUS) + lat = math.degrees(2*math.atan(math.exp(y / EARTH_RADIUS)) - math.pi/2) + return (lng,lat) +def draw_voronoi_lines(csv, hoods): points = np.array(hoods) vor = Voronoi(points) #mp = voronoi_plot_2d(vor) @@ -96,3 +60,72 @@ with open("csv/hoods.csv", "w") as csv: lng1, lat1 = merc_sphere_inv(vor.vertices[i,0], vor.vertices[i,1]) lng2, lat2 = merc_sphere_inv(far_point[0], far_point[1]) csv.write("\"LINESTRING (%f %f,%f %f)\"\n" % (lng1, lat1, lng2, lat2)) + + +with open("csv/routers.csv", "w") as csv: + csv.write("lng,lat,status\n") + for router in db.routers.find({"position.coordinates": {"$exists": True}}): + csv.write("%f,%f,%s\n" % ( + router["position"]["coordinates"][0], + router["position"]["coordinates"][1], + router["status"] + )) + +with open("csv/links.csv", "w") as csv: + csv.write("WKT,quality\n") + for router in db.routers.find({"position.coordinates": {"$exists": True}, "neighbours": {"$exists": True}}): + for neighbour in router["neighbours"]: + if "position" in neighbour: + csv.write("\"LINESTRING (%f %f,%f %f)\",%i\n" % ( + router["position"]["coordinates"][0], + router["position"]["coordinates"][1], + neighbour["position"]["coordinates"][0], + neighbour["position"]["coordinates"][1], + neighbour["quality"] + )) + +with open("csv/hood-points.csv", "w", encoding="UTF-8") as csv: + csv.write("lng,lat,name\n") + for hood in db.hoods.find({"position": {"$exists": True}}): + csv.write("%f,%f,\"%s\"\n" % ( + hood["position"]["coordinates"][0], + hood["position"]["coordinates"][1], + hood["name"] + )) + +with open("csv/hoods.csv", "w") as csv: + csv.write("WKT\n") + hoods = [] + for hood in db.hoods.find({"position": {"$exists": True}}): + # convert coordinates info marcator sphere as voronoi doesn't work with lng/lat + x, y = merc_sphere(hood["position"]["coordinates"][0], hood["position"]["coordinates"][1]) + hoods.append([x, y]) + draw_voronoi_lines(csv, hoods) + +with open("csv/hood-points-v2.csv", "w", encoding="UTF-8") as csv: + csv.write("lng,lat,name\n") + with urllib.request.urlopen("http://keyserver.freifunk-franken.de/v2/hoods.php") as url: + data = json.loads(url.read().decode()) + for hood in data: + if not ( 'lon' in hood and 'lat' in hood ): + continue + csv.write("%f,%f,\"%s\"\n" % ( + hood["lon"], + hood["lat"], + hood["name"] + )) + +with open("csv/hoodsv2.csv", "w") as csv: + csv.write("WKT\n") + hoods = [] + with urllib.request.urlopen("http://keyserver.freifunk-franken.de/v2/hoods.php") as url: + data = json.loads(url.read().decode()) + + for hood in data: + if not ( 'lon' in hood and 'lat' in hood ): + continue + # convert coordinates info marcator sphere as voronoi doesn't work with lng/lat + x, y = merc_sphere(hood["lon"], hood["lat"]) + hoods.append([x, y]) + + draw_voronoi_lines(csv, hoods) diff --git a/ffmap/mapnik/run.sh b/ffmap/mapnik/run.sh index efd096b..9cae649 100755 --- a/ffmap/mapnik/run.sh +++ b/ffmap/mapnik/run.sh @@ -1,6 +1,7 @@ #!/bin/bash liteserv.py links_and_routers.xml --processes=5 & -liteserv.py hoods.xml -p 8001 --processes=5 +liteserv.py hoods.xml -p 8001 --processes=5 & +liteserv.py hoodsv2.xml -p 8002 --processes=5 killall liteserv.py diff --git a/ffmap/mapnik/tilestache.cfg b/ffmap/mapnik/tilestache.cfg index e7dee56..d35081c 100644 --- a/ffmap/mapnik/tilestache.cfg +++ b/ffmap/mapnik/tilestache.cfg @@ -23,6 +23,16 @@ }, "metatile": {"buffer": 128}, "cache lifespan": 300 + }, + "tiles/hoodsv2": { + "provider": { + "class": "dynmapnik:DynMapnik", + "kwargs": { + "mapfile": "/usr/share/ffmap/hoodsv2.xml" + } + }, + "metatile": {"buffer": 128}, + "cache lifespan": 300 } }, "logging": "info" diff --git a/ffmap/maptools.py b/ffmap/maptools.py index 8430260..bfd553c 100644 --- a/ffmap/maptools.py +++ b/ffmap/maptools.py @@ -10,16 +10,69 @@ import math import numpy as np from scipy.spatial import Voronoi +import urllib.request, json + db = FreifunkDB().handle() CONFIG = { "csv_dir": "/var/lib/ffmap/csv" } +EARTH_RADIUS = 6378137.0 + def touch(fname, times=None): with open(fname, 'a'): os.utime(fname, times) +def merc_sphere(lng, lat): + x = math.radians(lng) * EARTH_RADIUS + y = math.log(math.tan(math.pi/4 + math.radians(lat)/2)) * EARTH_RADIUS + return (x,y) + +def merc_sphere_inv(x, y): + lng = math.degrees(x / EARTH_RADIUS) + lat = math.degrees(2*math.atan(math.exp(y / EARTH_RADIUS)) - math.pi/2) + return (lng,lat) + +def draw_voronoi_lines(csv, hoods): + points = np.array(hoods) + vor = Voronoi(points) + #mp = voronoi_plot_2d(vor) + #mp.show() + + lines = [vor.vertices[line] for line in vor.ridge_vertices if -1 not in line] + + for line in lines: + x = [line[0][0], line[1][0]] + y = [line[0][1], line[1][1]] + for i in range(len(x)-1): + # convert mercator coordinates back into lng/lat + lng1, lat1 = merc_sphere_inv(x[i], y[i]) + lng2, lat2 = merc_sphere_inv(x[i+1], y[i+1]) + csv.write("\"LINESTRING (%f %f,%f %f)\"\n" % (lng1, lat1, lng2, lat2)) + + ptp_bound = np.array(merc_sphere(180, 360)) + center = vor.points.mean(axis=0) + + for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices): + simplex = np.asarray(simplex) + if np.any(simplex < 0): + i = simplex[simplex >= 0][0] # finite end Voronoi vertex + + t = vor.points[pointidx[1]] - vor.points[pointidx[0]] # tangent + t /= np.linalg.norm(t) + n = np.array([-t[1], t[0]]) # normal + + midpoint = vor.points[pointidx].mean(axis=0) + direction = np.sign(np.dot(midpoint - center, n)) * n + far_point = vor.vertices[i] + direction * ptp_bound.max() + + # convert mercator coordinates back into lng/lat + lng1, lat1 = merc_sphere_inv(vor.vertices[i,0], vor.vertices[i,1]) + lng2, lat2 = merc_sphere_inv(far_point[0], far_point[1]) + csv.write("\"LINESTRING (%f %f,%f %f)\"\n" % (lng1, lat1, lng2, lat2)) + + def update_mapnik_csv(): with open(os.path.join(CONFIG["csv_dir"], "routers.csv"), "w") as csv: csv.write("lng,lat,status\n") @@ -82,64 +135,46 @@ def update_mapnik_csv(): )) with open(os.path.join(CONFIG["csv_dir"], "hoods.csv"), "w") as csv: - EARTH_RADIUS = 6378137.0 - - def merc_sphere(lng, lat): - x = math.radians(lng) * EARTH_RADIUS - y = math.log(math.tan(math.pi/4 + math.radians(lat)/2)) * EARTH_RADIUS - return (x,y) - - def merc_sphere_inv(x, y): - lng = math.degrees(x / EARTH_RADIUS) - lat = math.degrees(2*math.atan(math.exp(y / 6378137.0)) - math.pi/2) - return (lng,lat) - csv.write("WKT\n") hoods = [] for hood in db.hoods.find({"position": {"$exists": True}}): # convert coordinates info marcator sphere as voronoi doesn't work with lng/lat x, y = merc_sphere(hood["position"]["coordinates"][0], hood["position"]["coordinates"][1]) hoods.append([x, y]) + draw_voronoi_lines(csv, hoods) - points = np.array(hoods) - vor = Voronoi(points) - #mp = voronoi_plot_2d(vor) - #mp.show() + with open(os.path.join(CONFIG["csv_dir"], "hood-points-v2.csv"), "w", encoding="UTF-8") as csv: + csv.write("lng,lat,name\n") + + with urllib.request.urlopen("http://keyserver.freifunk-franken.de/v2/hoods.php") as url: + data = json.loads(url.read().decode()) + + for hood in data: + if not ( 'lon' in hood and 'lat' in hood ): + continue + csv.write("%f,%f,\"%s\"\n" % ( + hood["lon"], + hood["lat"], + hood["name"] + )) - lines = [vor.vertices[line] for line in vor.ridge_vertices if -1 not in line] - - for line in lines: - x = [line[0][0], line[1][0]] - y = [line[0][1], line[1][1]] - for i in range(len(x)-1): - # convert mercator coordinates back into lng/lat - lng1, lat1 = merc_sphere_inv(x[i], y[i]) - lng2, lat2 = merc_sphere_inv(x[i+1], y[i+1]) - csv.write("\"LINESTRING (%f %f,%f %f)\"\n" % (lng1, lat1, lng2, lat2)) - - ptp_bound = np.array(merc_sphere(180, 360)) - center = vor.points.mean(axis=0) - - for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices): - simplex = np.asarray(simplex) - if np.any(simplex < 0): - i = simplex[simplex >= 0][0] # finite end Voronoi vertex - - t = vor.points[pointidx[1]] - vor.points[pointidx[0]] # tangent - t /= np.linalg.norm(t) - n = np.array([-t[1], t[0]]) # normal - - midpoint = vor.points[pointidx].mean(axis=0) - direction = np.sign(np.dot(midpoint - center, n)) * n - far_point = vor.vertices[i] + direction * ptp_bound.max() - - # convert mercator coordinates back into lng/lat - lng1, lat1 = merc_sphere_inv(vor.vertices[i,0], vor.vertices[i,1]) - lng2, lat2 = merc_sphere_inv(far_point[0], far_point[1]) - csv.write("\"LINESTRING (%f %f,%f %f)\"\n" % (lng1, lat1, lng2, lat2)) + with open(os.path.join(CONFIG["csv_dir"], "hoodsv2.csv"), "w") as csv: + csv.write("WKT\n") + hoods = [] + with urllib.request.urlopen("http://keyserver.freifunk-franken.de/v2/hoods.php") as url: + data = json.loads(url.read().decode()) + + for hood in data: + if not ( 'lon' in hood and 'lat' in hood ): + continue + # convert coordinates info marcator sphere as voronoi doesn't work with lng/lat + x, y = merc_sphere(hood["lon"], hood["lat"]) + hoods.append([x, y]) + draw_voronoi_lines(csv, hoods) # touch mapnik XML files to trigger tilelite watcher touch("/usr/share/ffmap/hoods.xml") + touch("/usr/share/ffmap/hoodsv2.xml") touch("/usr/share/ffmap/links_and_routers.xml") if __name__ == '__main__': diff --git a/ffmap/web/application.py b/ffmap/web/application.py index 47f7965..1d35687 100755 --- a/ffmap/web/application.py +++ b/ffmap/web/application.py @@ -27,6 +27,7 @@ db = FreifunkDB().handle() tileurls = { "links_and_routers": "/tiles/links_and_routers", "hoods": "/tiles/hoods", + "hoodsv2": "/tiles/hoodsv2", } @app.route('/') diff --git a/ffmap/web/static/js/map.js b/ffmap/web/static/js/map.js index b4cd0e8..954f54f 100644 --- a/ffmap/web/static/js/map.js +++ b/ffmap/web/static/js/map.js @@ -28,13 +28,15 @@ var overlay_config = { var links_and_routers = new L.TileLayer(tileurls.links_and_routers + '/{z}/{x}/{y}.png', overlay_config).addTo(map); var hoods = new L.TileLayer(tileurls.hoods + '/{z}/{x}/{y}.png', overlay_config); +var hoodsv2 = new L.TileLayer(tileurls.hoodsv2 + '/{z}/{x}/{y}.png', overlay_config); layersControl = new L.Control.Layers({ "openstreetmap.org": tilesosmorg, "openstreetmap.de": tilesosmde, "Thunderforest Outdoors": tilestfod }, { "Links & Routers": links_and_routers, - "Hoods": hoods + "Hoods": hoods, + "Hoods v2": hoodsv2 }); map.addControl(layersControl); diff --git a/install.sh b/install.sh index 9186d4a..c1b55f6 100755 --- a/install.sh +++ b/install.sh @@ -5,9 +5,9 @@ mkdir -vp /var/lib/ffmap/csv chown -R www-data:www-data /var/lib/ffmap mkdir -vp /usr/share/ffmap -cp -v ffmap/mapnik/{hoods,links_and_routers}.xml /usr/share/ffmap -sed -i -e 's#>csv/#>/var/lib/ffmap/csv/#' /usr/share/ffmap/{hoods,links_and_routers}.xml -chown www-data:www-data /usr/share/ffmap/{hoods,links_and_routers}.xml +cp -v ffmap/mapnik/{hoods,hoodsv2,links_and_routers}.xml /usr/share/ffmap +sed -i -e 's#>csv/#>/var/lib/ffmap/csv/#' /usr/share/ffmap/{hoods,hoodsv2,links_and_routers}.xml +chown www-data:www-data /usr/share/ffmap/{hoods,hoodsv2,links_and_routers}.xml cp -v ffmap/mapnik/tilestache.cfg /usr/share/ffmap cp -rv ffmap/web/static /usr/share/ffmap