Webapp: add real elig data query
When a acceptable zoom level is reached, the frontend is going to query the DB for the eligibility data associated to the region the user is viewing. When requesting too much data, the frontend gets pretty slow. In order to prevent that, we require a minimal zoom level before triggering the actual request.
This commit is contained in:
parent
925d93fc58
commit
38c67ee588
4 changed files with 108 additions and 23 deletions
2
webapp/config.ini.sample
Normal file
2
webapp/config.ini.sample
Normal file
|
@ -0,0 +1,2 @@
|
|||
[DB]
|
||||
path = /path/to/ipe.sqlite
|
|
@ -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 "Missing coordinates", 400
|
||||
return "The requested area is too wide, please reduce it", 400
|
||||
else:
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> 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}<br/>Etat: ${building.etatImm}<br/>Code Immeuble: ${building.idImm}`);
|
||||
map.addLayer(marker);
|
||||
return marker
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchEligData(map) {
|
||||
const zoom = map.getZoom();
|
||||
if (zoom >= minZoomForRequest) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
const map = initMap();
|
||||
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;
|
||||
}
|
||||
|
||||
const btn = initBtn();
|
||||
const map = initMap(btn);
|
||||
const addrSearch = initAddrSearch(map);
|
||||
|
||||
document.getElementById("btn-load-elig-data").onclick = () => fetchEligData(map);
|
||||
|
|
Loading…
Reference in a new issue