ajout connexion Liazo
This commit is contained in:
parent
b05cf14bf3
commit
b9df2de12a
9 changed files with 263 additions and 147 deletions
|
@ -1,2 +1,2 @@
|
|||
[DB]
|
||||
path = /path/to/ipe.sqlite
|
||||
axione_ipe_path = /path/to/ipe.sqlite
|
|
@ -0,0 +1,2 @@
|
|||
from .axione import *
|
||||
from .liazo import *
|
|
@ -1,8 +1,86 @@
|
|||
# const AXIONE_ETAT_DEPLOYE =
|
||||
# "DEPLOYE"
|
||||
# const AXIONE_ETAT_DEPLOIEMENT = "EN COURS DE DEPLOIEMENT"
|
||||
# const AXIONE_ETAT_ABANDONNE = "ABANDONNE"
|
||||
# const AXIONE_ETAT_CIBLE = "CIBLE"
|
||||
# const AXIONE_ETAT_SIGNE = "SIGNE"
|
||||
# const AXIONE_ETAT_RAD_DEPLOIEMENT = "RAD EN COURS DE DEPLOIEMENT"
|
||||
# const AXIONE_ETAT_RACCORDABLE_DEMANDE = "RACCORDABLE DEMANDE"
|
||||
from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus
|
||||
from ipe_fetcher.sqlite_connector.cursor import getCursorWithSpatialite
|
||||
from os.path import exists
|
||||
|
||||
AXIONE_ETAT_DEPLOYE = "DEPLOYE"
|
||||
AXIONE_ETAT_DEPLOIEMENT = "EN COURS DE DEPLOIEMENT"
|
||||
AXIONE_ETAT_ABANDONNE = "ABANDONNE"
|
||||
AXIONE_ETAT_CIBLE = "CIBLE"
|
||||
AXIONE_ETAT_SIGNE = "SIGNE"
|
||||
AXIONE_ETAT_RAD_DEPLOIEMENT = "RAD EN COURS DE DEPLOIEMENT"
|
||||
AXIONE_ETAT_RACCORDABLE_DEMANDE = "RACCORDABLE DEMANDE"
|
||||
|
||||
|
||||
class Axione:
|
||||
def __init__(self, db_axione_ipe_path: str):
|
||||
self.db_axione_ipe_path = db_axione_ipe_path
|
||||
# Check at least that the file exists
|
||||
if not exists(self.db_axione_ipe_path):
|
||||
raise ValueError(f"File {self.db_axione_ipe_path} does not exist")
|
||||
|
||||
def getAreaBuildings(
|
||||
self, areaCoordinates: AreaCoordinates, existing_buildings: dict
|
||||
) -> dict:
|
||||
cur = None
|
||||
# Try to get cursor on Axone database
|
||||
try:
|
||||
cur = getCursorWithSpatialite(self.db_axione_ipe_path)
|
||||
except Exception as err:
|
||||
print("Error while connecting to DB: ", err)
|
||||
raise "Could not get Axione data"
|
||||
# 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))
|
||||
""",
|
||||
areaCoordinates,
|
||||
)
|
||||
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))
|
||||
""",
|
||||
areaCoordinates,
|
||||
)
|
||||
if not existing_buildings:
|
||||
existing_buildings = dict()
|
||||
buildings = existing_buildings
|
||||
for b in cur.fetchall():
|
||||
etatImm = b[3]
|
||||
idImm = b[2]
|
||||
isEligible = etatImm == AXIONE_ETAT_DEPLOYE
|
||||
aquilenetEligStatus = FAIEligibilityStatus(
|
||||
isEligible=isEligible,
|
||||
ftthStatus=etatImm,
|
||||
reasonNotEligible=None if isEligible else "Pas encore deploye",
|
||||
)
|
||||
if buildings.get(idImm):
|
||||
buildings[idImm]["aquilenetEligStatus"] = aquilenetEligStatus
|
||||
else:
|
||||
building = Building(
|
||||
x=b[0],
|
||||
y=b[1],
|
||||
idImm=idImm,
|
||||
numVoieImm=b[4],
|
||||
typeVoieImm=b[5],
|
||||
nomVoieImm=b[6],
|
||||
aquilenetEligStatus=aquilenetEligStatus,
|
||||
)
|
||||
buildings[idImm] = building
|
||||
return buildings
|
||||
else:
|
||||
raise ValueError("The requested area is too wide, please reduce it")
|
||||
|
|
41
webapp/ipe_fetcher/liazo.py
Normal file
41
webapp/ipe_fetcher/liazo.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import http.client as httplib
|
||||
from ipe_fetcher.model import AreaCoordinates, Building, FAIEligibilityStatus
|
||||
import json
|
||||
|
||||
class Liazo:
|
||||
def __init__(self):
|
||||
self.https_conn = httplib.HTTPSConnection("vador.fdn.fr")
|
||||
|
||||
def getAreaBuildings(
|
||||
self, center_lat: float, center_lng: float, existing_buildings: dict
|
||||
) -> dict:
|
||||
c = self.https_conn
|
||||
req = "/souscription/gps-batiments.cgi?etape=gps_batiments&lat1=%f&lat2=%f&lon1=%f&lon2=%f" % (center_lat-0.0011, center_lat+0.0011, center_lng-0.0022, center_lng+0.0022)
|
||||
req = req.replace(" ", "%20")
|
||||
print("Req FDN with: ", req)
|
||||
c.request("GET", req)
|
||||
r = c.getresponse()
|
||||
if r.status < 200 or r.status >= 300:
|
||||
print("Erreur de serveur chez FDN. Merci de nous faire remonter le numéro de téléphone qui provoque cette erreur")
|
||||
return
|
||||
d = r.read()
|
||||
c.close()
|
||||
v = json.loads(d.decode("utf-8"))
|
||||
if not existing_buildings:
|
||||
existing_buildings = dict()
|
||||
buildings = existing_buildings
|
||||
for building in v:
|
||||
idImm=building.get('ref')
|
||||
if not buildings.get(idImm):
|
||||
|
||||
building = Building(
|
||||
y=building.get('lat'),
|
||||
x=building.get('lon'),
|
||||
idImm=idImm,
|
||||
numVoieImm="",
|
||||
typeVoieImm="",
|
||||
nomVoieImm=""
|
||||
)
|
||||
print("add building ", building)
|
||||
buildings[idImm] = building
|
||||
return buildings
|
|
@ -3,7 +3,6 @@ from typing import TypedDict
|
|||
|
||||
class FAIEligibilityStatus(TypedDict):
|
||||
isEligible: bool
|
||||
ftthDeployer: str
|
||||
ftthStatus: str
|
||||
reasonNotEligible: str
|
||||
|
||||
|
@ -18,3 +17,10 @@ class Building(TypedDict):
|
|||
aquilenetEligStatus: FAIEligibilityStatus
|
||||
ffdnEligStatus: FAIEligibilityStatus
|
||||
othersEligStatus: FAIEligibilityStatus
|
||||
|
||||
|
||||
class AreaCoordinates(TypedDict):
|
||||
swx: float
|
||||
swy: float
|
||||
nex: float
|
||||
ney: float
|
||||
|
|
1
webapp/ipe_fetcher/sqlite_connector/__init__.py
Normal file
1
webapp/ipe_fetcher/sqlite_connector/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# from .cursor import *
|
8
webapp/ipe_fetcher/sqlite_connector/cursor.py
Normal file
8
webapp/ipe_fetcher/sqlite_connector/cursor.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
import sqlite3
|
||||
|
||||
def getCursorWithSpatialite(db_path: str = None) -> sqlite3.Cursor:
|
||||
db = sqlite3.connect(db_path)
|
||||
cur = db.cursor()
|
||||
db.enable_load_extension(True)
|
||||
cur.execute('SELECT load_extension("mod_spatialite")')
|
||||
return cur
|
|
@ -4,30 +4,37 @@ from typing import TypedDict
|
|||
import configparser
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
from ipe_fetcher import Liazo,Axione
|
||||
class Config(TypedDict):
|
||||
dbPath: str
|
||||
axione_ipe_path: str
|
||||
|
||||
|
||||
def parseConfig() -> Config:
|
||||
cfg_path = os.environ.get("CONFIG", "/etc/ftth-ipe-map/conf.ini")
|
||||
cfg = configparser.ConfigParser()
|
||||
with open(cfg_path, "r") as f:
|
||||
cfg.read_file(f)
|
||||
return {'dbPath':cfg.get("DB","path")}
|
||||
return {"axione_ipe_path": cfg.get("DB", "axione_ipe_path")}
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
cfg:Config = parseConfig()
|
||||
cfg: Config = parseConfig()
|
||||
|
||||
axione = Axione(cfg.get("axione_ipe_path"))
|
||||
liazo = Liazo()
|
||||
|
||||
|
||||
@app.route("/", methods=["GET"])
|
||||
def getMap():
|
||||
return render_template("map.html")
|
||||
|
||||
|
||||
@app.route("/eligdata", methods=["GET"])
|
||||
def getEligData():
|
||||
args = request.args
|
||||
valid_args = True
|
||||
processed_args = {}
|
||||
for k in ['swx', 'swy', 'nex', 'ney']:
|
||||
for k in ["swx", "swy", "nex", "ney", "centerlat", "centerlng"]:
|
||||
valid_args = valid_args and k in args
|
||||
if valid_args:
|
||||
try:
|
||||
|
@ -35,43 +42,14 @@ def getEligData():
|
|||
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
|
||||
buildings = dict()
|
||||
try:
|
||||
buildings = axione.getAreaBuildings(processed_args, buildings)
|
||||
except ValueError as err:
|
||||
print("Could not get Axione data for this area:", err)
|
||||
|
||||
buildings = liazo.getAreaBuildings(processed_args["centerlat"], processed_args["centerlng"], buildings)
|
||||
|
||||
return {"buildings": buildings}
|
||||
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
|
||||
|
|
|
@ -7,7 +7,7 @@ function initMap(btn) {
|
|||
let y = parseFloat(params.get('y'));
|
||||
let z = parseInt(params.get('z'));
|
||||
let map = L.map('map');
|
||||
if(x && y && z) {
|
||||
if (x && y && z) {
|
||||
map.setView([y, x], z);
|
||||
fetchEligData(map);
|
||||
displayBtn(btn);
|
||||
|
@ -35,7 +35,7 @@ function initAddrSearch(map) {
|
|||
const autocompleteOptions = {
|
||||
debounceTime: 300,
|
||||
search: async (query) => {
|
||||
if(query.length > 2) {
|
||||
if (query.length > 2) {
|
||||
const mapCenter = map.getCenter();
|
||||
const reqUri = `https://photon.komoot.io/api/?q=${encodeURI(query)}&lat=${mapCenter.lat}&lon=${mapCenter.lng}&limit=20&lang=fr`;
|
||||
const source = await fetch(reqUri);
|
||||
|
@ -47,7 +47,7 @@ function initAddrSearch(map) {
|
|||
},
|
||||
renderResult: (res, props) => {
|
||||
const p = res.properties;
|
||||
if(p.name && p.postcode && p.city && p.county && res.geometry.coordinates && res.geometry.coordinates.length === 2)
|
||||
if (p.name && p.postcode && p.city && p.county && res.geometry.coordinates && res.geometry.coordinates.length === 2)
|
||||
return `<li ${props}>${p.name} - ${p.postcode} ${p.city}, ${p.county}</li>`;
|
||||
else
|
||||
return "";
|
||||
|
@ -57,7 +57,7 @@ function initAddrSearch(map) {
|
|||
const p = res.properties;
|
||||
searchInput.value = `${p.name} - ${p.postcode} ${p.city}, ${p.county}`;
|
||||
// We already filtered out the result not having strictly 2 coordinates at item display
|
||||
map.setView([res.geometry.coordinates[1],res.geometry.coordinates[0]], 19);
|
||||
map.setView([res.geometry.coordinates[1], res.geometry.coordinates[0]], 19);
|
||||
fetchEligData(map);
|
||||
}
|
||||
};
|
||||
|
@ -68,7 +68,8 @@ function initAddrSearch(map) {
|
|||
function updateEligData(map, eligData) {
|
||||
markers.map(marker => map.removeLayer(marker));
|
||||
let buildings = eligData.buildings;
|
||||
markers = buildings.map(building => {
|
||||
console.log(buildings)
|
||||
markers = Object.values(buildings).map(building => {
|
||||
const latlng = new L.latLng(building.y, building.x);
|
||||
const addrImm = `${building.numVoieImm} ${building.typeVoieImm} ${building.nomVoieImm}`
|
||||
const marker = new L.marker(latlng)
|
||||
|
@ -86,10 +87,11 @@ function updateUrl(map) {
|
|||
async function fetchEligData(map) {
|
||||
const zoom = map.getZoom();
|
||||
if (zoom >= minZoomForRequest) {
|
||||
const mc = map.getCenter();
|
||||
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 reqUri = encodeURI(`eligdata?swx=${sw.lng}&swy=${sw.lat}&nex=${ne.lng}&ney=${ne.lat}¢erlat=${mc.lat}¢erlng=${mc.lng}`);
|
||||
const source = await fetch(reqUri);
|
||||
const eligData = await source.json();
|
||||
updateEligData(map, eligData);
|
||||
|
|
Loading…
Reference in a new issue