diff --git a/webapp/config.ini.sample b/webapp/config.ini.sample
new file mode 100644
index 0000000..2e3a82b
--- /dev/null
+++ b/webapp/config.ini.sample
@@ -0,0 +1,2 @@
+[DB]
+ path = /path/to/ipe.sqlite
\ No newline at end of file
diff --git a/webapp/main.py b/webapp/main.py
index 1d7efd5..85123cb 100644
--- a/webapp/main.py
+++ b/webapp/main.py
@@ -1,19 +1,77 @@
from flask import Flask, request, render_template
-app = Flask(__name__)
+from typing import TypedDict
+import configparser
+import sqlite3
+import os
-@app.route("/", methods=['GET'])
+class Config(TypedDict):
+ dbPath: str
+
+def parseConfig() -> Config:
+ cfg_path = os.environ.get("CONFIG", "/etc/ipe-ftth-elig/conf.ini")
+ cfg = configparser.ConfigParser()
+ with open(cfg_path, "r") as f:
+ cfg.read_file(f)
+ return {'dbPath':cfg.get("DB","path")}
+
+app = Flask(__name__)
+cfg:Config = parseConfig()
+
+@app.route("/", methods=["GET"])
def getMap():
return render_template("map.html")
-@app.route("/eligdata", methods=['GET'])
+@app.route("/eligdata", methods=["GET"])
def getEligData():
args = request.args
- if 'swx' in args and 'swy' in args and \
- 'nex' in args and 'ney' in args:
- return { "buildings": [
- {"x": args['swx'], "y":args['swy'], "label": "dummy elig val" },
- {"x": args['nex'], "y":args['ney'], "label": "dummy elig val 2" }
- ]}
+ valid_args = True
+ processed_args = {}
+ for k in ['swx', 'swy', 'nex', 'ney']:
+ valid_args = valid_args and k in args
+ if valid_args:
+ try:
+ processed_args[k] = float(args[k])
+ except ValueError:
+ valid_args = False
+ if valid_args:
+ cur = cursorWithSpatialite()
+ # Let's first see how big is the area we're about to query.
+ # If it's too big, abort the request to prevent a server DOS.
+ cur.execute('''
+ SELECT Area(BuildMBR(:swx,:swy,:nex,:ney,4326))
+ ''',processed_args)
+ req_area = cur.fetchone()[0]
+ if req_area <= 0.08:
+ cur.execute('''
+ SELECT
+ X(ImmeubleGeoPoint),
+ Y(ImmeubleGeoPoint),
+ IdentifiantImmeuble,
+ EtatImmeuble,
+ NumeroVoieImmeuble,
+ TypeVoieImmeuble,
+ NomVoieImmeuble
+ FROM ipe
+ WHERE ROWID IN (
+ SELECT ROWID FROM SpatialIndex
+ WHERE f_table_name = 'ipe' AND
+ search_frame = BuildMBR(:swx, :swy, :nex, :ney, 4326))
+ ''',processed_args)
+ buildings = [ {
+ 'x':b[0], 'y':b[1], 'idImm':b[2],
+ 'etatImm':b[3], 'numVoieImm': b[4],
+ 'typeVoieImm': b[5], 'nomVoieImm': b[6]
+ } for b in cur.fetchall()]
+ return { "buildings": buildings}
+ else:
+ return "The requested area is too wide, please reduce it", 400
else:
- return "Missing coordinates", 400
+ return "Invalid bounding box coordinates", 400
+
+def cursorWithSpatialite():
+ db = sqlite3.connect(cfg['dbPath'])
+ cur = db.cursor()
+ db.enable_load_extension(True)
+ cur.execute('SELECT load_extension("mod_spatialite")')
+ return cur
diff --git a/webapp/run-dev-server b/webapp/run-dev-server
index 2439d28..4438644 100755
--- a/webapp/run-dev-server
+++ b/webapp/run-dev-server
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
-TEMPLATES_AUTO_RELOAD=true FLASK_ENV=development FLASK_APP=main poetry run flask run --reload
+CONFIG=./config.ini TEMPLATES_AUTO_RELOAD=true FLASK_ENV=development FLASK_APP=main poetry run flask run --reload
diff --git a/webapp/templates/app.js b/webapp/templates/app.js
index 43bb25f..c21977d 100644
--- a/webapp/templates/app.js
+++ b/webapp/templates/app.js
@@ -1,9 +1,23 @@
+const minZoomForRequest = 17;
let markers = [];
-function initMap() {
+function initMap(btn) {
let map = L.map('map').setView([46.710, 3.669], 6);
L.tileLayer('https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
+ map.on("zoom", () => {
+
+ console.log(map.getZoom());
+ /* We only want to enable the search button when we reached a sufficient zoom level */
+ if (btn.disabled && map.getZoom() >= minZoomForRequest) {
+ btn.disabled = false;
+ btn.title = "Rechercher les données d'éligibilité pour cette zone."
+ }
+ if (!btn.disabled && map.getZoom() < minZoomForRequest) {
+ btn.disabled = true;
+ btn.title = "Veuillez zoomer plus la carte avant de lancer une recherche d'éligibilité.";
+ }
+ });
return map;
}
@@ -46,24 +60,35 @@ function updateEligData(map, eligData) {
let buildings = eligData.buildings;
markers = buildings.map(building => {
const latlng = new L.latLng(building.y, building.x);
- const marker = new L.marker(latlng).bindPopup(building.label);
+ const addrImm = `${building.numVoieImm} ${building.typeVoieImm} ${building.nomVoieImm}`
+ const marker = new L.marker(latlng)
+ .bindPopup(`${addrImm}
Etat: ${building.etatImm}
Code Immeuble: ${building.idImm}`);
map.addLayer(marker);
return marker
});
}
async function fetchEligData(map) {
- const bounds = map.getBounds();
- const sw = bounds.getSouthWest();
- const ne = bounds.getNorthEast();
const zoom = map.getZoom();
- const reqUri = encodeURI(`/eligdata?swx=${sw.lng}&swy=${sw.lat}&nex=${ne.lng}&ney=${ne.lat}`);
- const source = await fetch(reqUri);
- const eligData = await source.json();
- updateEligData(map, eligData);
+ if (zoom >= minZoomForRequest) {
+ const bounds = map.getBounds();
+ const sw = bounds.getSouthWest();
+ const ne = bounds.getNorthEast();
+ const reqUri = encodeURI(`/eligdata?swx=${sw.lng}&swy=${sw.lat}&nex=${ne.lng}&ney=${ne.lat}`);
+ const source = await fetch(reqUri);
+ const eligData = await source.json();
+ updateEligData(map, eligData);
+ }
}
-const map = initMap();
-const addrSearch = initAddrSearch(map);
+function initBtn() {
+ const btn = document.getElementById("btn-load-elig-data");
+ btn.disabled = true;
+ btn.title = "Veuillez zoomer plus la carte avant de lancer une recherche d'éligibilité.";
+ btn.onclick = () => fetchEligData(map);
+ return btn;
+}
-document.getElementById("btn-load-elig-data").onclick = () => fetchEligData(map);
+const btn = initBtn();
+const map = initMap(btn);
+const addrSearch = initAddrSearch(map);