From f138bc8a094cfcd5326886d32c6bafd8e0c4a5d0 Mon Sep 17 00:00:00 2001 From: Robert Metcalf <metcalfr@cardiff.ac.uk> Date: Tue, 23 Nov 2021 12:27:23 +0000 Subject: [PATCH] add interactive map and lookup from address to latitude and longitude --- database.py | 21 ++++++++++++++++++--- import_database.py | 7 +++++-- main.py | 10 ++++++++++ static/scripts/map.js | 27 +++++++++++++++++++++++++++ static/style.css | 17 +++++++++++++++++ templates/map.html | 14 ++++++++++++++ 6 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 static/scripts/map.js create mode 100644 templates/map.html diff --git a/database.py b/database.py index 44e9e7a..a120ee9 100644 --- a/database.py +++ b/database.py @@ -1,6 +1,6 @@ import os import sqlite3 -from typing import List +from typing import List, Tuple DATABASE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "database.db") @@ -50,8 +50,25 @@ with Connection() as conn: url text NOT NULL, workspace_id integer NOT NULL, FOREIGN KEY (workspace_id) REFERENCES Workspaces(id))''') + conn.execute('''CREATE TABLE IF NOT EXISTS AddressToLatLong ( + id integer NOT NULL PRIMARY KEY AUTOINCREMENT, + address text NOT NULL, + latlong text NOT NULL)''') conn.commit() +def lookup_address(address: str): + with Connection() as conn: + conn.execute("SELECT latlong FROM AddressToLatLong WHERE address = ?", (address,)) + res = conn.cursor.fetchone()[0] + (lat, long) = res.split(",") + return (float(lat), float(long)) + +def set_address_latlong(address: str, latlong: Tuple[float, float]): + with Connection() as conn: + latlong_str = f"{ repr(latlong[0]) },{ repr(latlong[1]) }" + conn.execute("INSERT INTO AddressToLatLong (address, latlong) VALUES (?, ?)", (address, latlong_str)) + conn.commit() + class Workspace: def __init__(self, name: str, address: str, main_photo: str, additional_photos: List[str], description: str, @@ -96,5 +113,3 @@ def get_workspaces(): with Connection() as conn: conn.execute("SELECT * FROM Workspaces") return [Workspace.from_query(conn, x) for x in conn.cursor.fetchall()] - -print(get_workspaces()) \ No newline at end of file diff --git a/import_database.py b/import_database.py index deceed4..0bdd5d3 100644 --- a/import_database.py +++ b/import_database.py @@ -1,6 +1,6 @@ import database -print(database.add_workspace(database.Workspace( +workspace = database.Workspace( "CodeBase", "CodeBase Edinburgh, 37a Castle Terrace, Edinburgh, EH1 2EL", "https://images.squarespace-cdn.com/content/v1/55439320e4b0f92b5d6c4c8b/1505921023376-PAHUDHVOOKIYF4XQPHOO/5951229048_3e3d50fcb1_o.jpg?format=750w", @@ -11,4 +11,7 @@ print(database.add_workspace(database.Workspace( "+44 131 560 2003", "Monday - Friday, 9am - 5pm", "Tramsched members should contact Hannah using the email address listed." -))) +) + +database.set_address_latlong(workspace.address, (55.9471623, -3.203928)) +print(database.add_workspace(workspace)) diff --git a/main.py b/main.py index dc32f70..d94babe 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,7 @@ from flask import Flask, request, render_template import database +import json +import re app = Flask(__name__) @@ -9,5 +11,13 @@ def home(): workspaces = database.get_workspaces() return render_template("home.html", workspaces = workspaces) +@app.route("/map", methods = ["GET"]) +def map(): + if request.method == "GET": + workspaces = database.get_workspaces() + data = [{"id": x.id, "name": x.name, "address": x.address, "phoneNumber": x.phone_number, "email": x.email, "website": x.website, "latlong": database.lookup_address(x.address)} for x in workspaces] + dumped = json.dumps(data) + return render_template("map.html", json = re.sub(r"(?i)\</script\>", "<\/script>", dumped)) # escape </script> + if __name__ == "__main__": app.run(debug = True) diff --git a/static/scripts/map.js b/static/scripts/map.js new file mode 100644 index 0000000..3ca7fbd --- /dev/null +++ b/static/scripts/map.js @@ -0,0 +1,27 @@ +var map = L.map("map").setView([54.004, -2.55], 5); + +L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { + attribution: "© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors" +}).addTo(map); + +for (let workspace of mapData) { + L.marker(workspace.latlong).addTo(map) + .bindPopup(`<div class="popup"><div><a href="/workspace/${ encodeURIComponent(workspace.id) }" class="popup-title">${ + escapeHTML(workspace.name) + }</a></div><div>Phone: <a href="tel:${ escapeHTML(workspace.phoneNumber) }">${ + escapeHTML(workspace.phoneNumber) + }</a></div><div>Email: <a href="mailto:${ escapeHTML(workspace.email) }">${ + escapeHTML(workspace.email) + }</a></div><div>Website: <a href="${ escapeHTML(workspace.website) }">${ + escapeHTML(workspace.website) + }</a></div><div>${ escapeHTML(workspace.address) }</div></div>`); +} + +function escapeHTML(html) { + let lookup = Object.create(null); + lookup["<"] = "<" + lookup[">"] = ">" + lookup["\""] = """ + lookup["&"] = "&"; + return html.replace(/[<>"&]/g, v => lookup[v] || ""); +} \ No newline at end of file diff --git a/static/style.css b/static/style.css index b9a1f78..099283c 100644 --- a/static/style.css +++ b/static/style.css @@ -127,3 +127,20 @@ nav li a:hover:last-child { color: #666; font-weight: 700; } + +#map { + height: 600px; +} + +#map .popup { + font-family: CircularXX,sans-serif; + font-size: 0.8rem; + font-weight: 400; + line-height: 1.5; +} + +#map .popup-title { + color: inherit; + font-size: 1rem; + font-weight: 500; +} diff --git a/templates/map.html b/templates/map.html new file mode 100644 index 0000000..af4c384 --- /dev/null +++ b/templates/map.html @@ -0,0 +1,14 @@ +{% extends "main.html" %} +{% block title %}Map{% endblock %} +{% block mainBlock %} +<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" + integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" + crossorigin=""/> + <!-- Make sure you put this AFTER Leaflet's CSS --> +<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" + integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" + crossorigin=""></script> +<script>var mapData = {{ json|safe }};</script> +<div id="map"></div> +<script src="/static/scripts/map.js"></script> +{% endblock %} \ No newline at end of file -- GitLab