diff --git a/.gitignore b/.gitignore index bc6b2de..e9f1dbe 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ api2/ ssh/ .venv/ +__pycache__/ # +++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/scripts_v2/config.ini b/scripts_v2/config.ini new file mode 100644 index 0000000..05cc4ec --- /dev/null +++ b/scripts_v2/config.ini @@ -0,0 +1,14 @@ + +[redadeg_database] +host=localhost +port=55432 +db=redadeg +user=redadeg +passwd=redadeg + +[osm_database] +host=localhost +port=55432 +db=osm +user=redadeg +passwd=redadeg diff --git a/scripts_v2/logguer.py b/scripts_v2/logguer.py new file mode 100644 index 0000000..5273f46 --- /dev/null +++ b/scripts_v2/logguer.py @@ -0,0 +1,32 @@ + +################################################################################ +################ FONCTION LOG_VERIF ################# +################################################################################ + + +def log_verif(nom_fichier, txt, purger): + + # répertoire courant + import os + script_dir = os.path.dirname(os.path.abspath(__file__)) + + if (purger == 1): + # création d'un nouveau fichier texte contenant les logs + log = open(script_dir + "\\logs\\" + nom_fichier + ".log", "w+") + # remplir ce fichier avec le txt que l'on souhaite, nombre de lignes avant et après MàJ + log.write(txt + "\n") + print(txt) + # puis fermeture du fichier + log.close + + else: + # ouverture du fichier sans supprimer les logs précédents + log = open(script_dir + "\\logs\\" + nom_fichier + ".log", "a") + # remplir ce fichier avec le txt que l'on souhaite, nombre de lignes avant et après MàJ + log.write(txt + "\n") + print(txt) + # puis fermeture du fichier + log.close + + + diff --git a/scripts_v2/phase_3_prepare.py b/scripts_v2/phase_3_prepare.py new file mode 100644 index 0000000..72a419c --- /dev/null +++ b/scripts_v2/phase_3_prepare.py @@ -0,0 +1,291 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# +# Libraries +# + +import os +import sys +import datetime +import time +import configparser +import psycopg2 +#from logguer import log_verif + + +# ============================================================================== + +# répertoire courant +script_dir = os.path.dirname(os.path.abspath(__file__)) + +# jour courant +date = datetime.datetime.now() + +# lecture du fichier de configuration qui contient les infos de connection +config = configparser.ConfigParser() +config.read( script_dir + '/config.ini') + + + +#fichier_log = str(date.year)+str(date.month)+str(date.day)+"_"+str(date.hour)+str(date.minute) +fichier_log = str(date.year)+str(date.month)+str(date.day) + + +# ============================================================================== + +def initConnRedadegDB(): + + # connexion à la base postgresql + try: + global db_redadeg_pg_conn + db_redadeg_pg_conn = psycopg2.connect(db_redadeg_conn_str) + db_redadeg_pg_conn.set_session(autocommit=True) + + print(" Connexion à la base "+db_redadeg_db+" sur "+db_redadeg_host+" réussie ") + + + except Exception as err: + print(" Connexion à la base "+db_redadeg_db+" sur "+db_redadeg_host+ " impossible ") + try: + err.pgcode + print(" PostgreSQL error code : " + err.pgcode) + sys.exit() + except: + print(" " + str(err),0) + sys.exit() + + +# ============================================================================== + +def closeConnRedadegDB(): + + try: + db_redadeg_pg_conn.close() + print(" Fermeture de la connexion à la base "+db_redadeg_db+" sur "+db_redadeg_host) + except: + pass + + +# +# Functions +# + +# +# Start processing +# + +startTime = time.perf_counter() + +# on récupère les arguments passés +list_of_args = sys.argv + +millesime="" +secteur="" +typemaj="" + +# et on fait des tests + +try: + if len(list_of_args[1]) != 4: + print("Pas de millésime en argument") + sys.exit() + else: + millesime = list_of_args[1] + + # millesime ok : on passe au secteur + if len(list_of_args[2]) != 3: + print("Pas d'id secteur en argument") + sys.exit() + else: + secteur = list_of_args[2] + + # ok : tout est bon on peut commencer + # sortie des tests + + +except SystemExit: + print("Erreur dans les arguments --> stop") + sys.exit() +except: + print("oups : vérifiez vos arguments passés au script !") + print("stop") + sys.exit() + + + +print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") +print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") +print("") +print(" Préparation des données phase 3 du secteur "+str(secteur)+" pour le millésime "+str(millesime)) +print("") + +print(" Lecture du fichier de configuration ") +print("") + +# BD Ar Redadeg +global db_redadeg_host +global db_redadeg_db +db_redadeg_host = config.get('redadeg_database', 'host') +db_redadeg_port = config.get('redadeg_database', 'port') +db_redadeg_db = config.get('redadeg_database', 'db')+"_"+str(millesime) +db_redadeg_user = config.get('redadeg_database', 'user') +db_redadeg_passwd = config.get('redadeg_database', 'passwd') +# chaîne de connection +global db_redadeg_conn_str +db_redadeg_conn_str = "host="+db_redadeg_host+" port="+db_redadeg_port+" dbname="+db_redadeg_db+" user="+db_redadeg_user+" password="+db_redadeg_passwd + + +initConnRedadegDB() + +# le cursor +db_redadeg_cursor = db_redadeg_pg_conn.cursor() + +print("") + +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +try: + + # longueur de découpage des tronçons de la phase 2 + longueur_densification = str(10) + + # ------------------------------------------------------ + print(" Suppression des données du secteur "+secteur) + sql_delete = "DELETE FROM phase_3_troncons_pgr WHERE secteur_id = "+secteur +" ;" + db_redadeg_cursor.execute(sql_delete) + print(" fait") + print("") + + + # ------------------------------------------------------ + print(" Chargement de tronçons découpés tous les "+longueur_densification+" m depuis la couche des tronçons phase 2") + + # on charge, pour le secteur concerné des tronçons courts découpés tous les x mètres + # (densification avec ST_LineSubstring ) + + sql_insert = """ +INSERT INTO phase_3_troncons_pgr (secteur_id, osm_id, highway, type, oneway, ref, name_fr, name_br, the_geom) + SELECT + secteur_id, osm_id, highway, type, oneway, ref, name_fr, name_br, + ST_LineSubstring(the_geom, """+longueur_densification+"""*n/length, + CASE + WHEN """+longueur_densification+"""*(n+1) < length THEN """+longueur_densification+"""*(n+1)/length + ELSE 1 + END) As the_geom +FROM + ( + SELECT + secteur_id, osm_id, highway, type, oneway, ref, name_fr, name_br, + ST_Length(the_geom) AS length, + the_geom + FROM phase_2_trace_troncons + WHERE secteur_id = """+secteur+""" + ) AS t +CROSS JOIN generate_series(0,10000) AS n +WHERE n*"""+longueur_densification+"""/length < 1;""" + + db_redadeg_cursor.execute(sql_insert) + + + # calcul des attributs de support du calcul pour PGR + sql_update_costs = """ +UPDATE phase_3_troncons_pgr +SET cost = round(st_length(the_geom)::numeric), reverse_cost = round(st_length(the_geom)::numeric) +WHERE secteur_id = """+secteur +""" ;""" + + db_redadeg_cursor.execute(sql_update_costs) + + # optimisation + #db_redadeg_cursor.execute("VACUUM FULL phase_3_troncons_pgr ;") + + print(" fait") + print("") + + + # ------------------------------------------------------ + print(" Création / maj de la topologie pgRouting pour les tronçons nouvellement créés") + + sql_create_pgr_topology = "SELECT pgr_createTopology('phase_3_troncons_pgr', 0.001, rows_where:='true', clean:=true);" + db_redadeg_cursor.execute(sql_create_pgr_topology) + + print(" fait") + print("") + + + # ------------------------------------------------------ + print(" Récupération id des nœuds de début et fin du secteur") + + # on commence par supprimer le secteur puis on y remet avec les infos de la table des secteurs + sql_init_secteurs = """ +DELETE FROM phase_3_secteurs WHERE secteur_id = """+secteur+""" ; +INSERT INTO phase_3_secteurs (secteur_id, nom_br, nom_fr, longueur_km_redadeg) + SELECT id, nom_br, nom_fr, km_redadeg + FROM secteur + WHERE id = """+secteur+""" + ORDER BY id ;""" + db_redadeg_cursor.execute(sql_init_secteurs) + + + # récupération id node début et fin de secteur + secteurs_in_clause = secteur+","+str(int(secteur)+100) + sql_get_nodes = """ + SELECT v.id, s.the_geom + FROM phase_2_pk_secteur s, phase_3_troncons_pgr_vertices_pgr v + WHERE s.secteur_id IN ("""+secteurs_in_clause+""") AND ST_INTERSECTS(s.the_geom, v.the_geom) + ORDER BY s.secteur_id;""" + + db_redadeg_cursor.execute(sql_get_nodes) + + # fetchone() fait passer d'un enregistrement à un autre + # donc : nœud de début du secteur + node_start = db_redadeg_cursor.fetchone() + node_start_id = node_start[0] + node_start_geom = node_start[1] + # nœud de fin du secteur + node_end = db_redadeg_cursor.fetchone() + node_end_id = node_end[0] + node_end_geom = node_end[1] + + + # on maj les infos dans la table phase_3_secteurs + sql_update_nodes_infos = "UPDATE phase_3_secteurs SET node_start = "+str(node_start_id)+", node_stop = "+str(node_end_id)+" WHERE secteur_id = "+secteur +" ;" + db_redadeg_cursor.execute(sql_update_nodes_infos) + print(" fait : "+str(node_start_id)+" -> "+str(node_end_id)) + + + print("") + print("") + +except Exception as err: + print(" ERREUR : " + str(err)) + closeConnRedadegDB() + sys.exit() + + + + +# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +db_redadeg_cursor.close() + +closeConnRedadegDB() + + +# pour connaître le temps d'exécution +print("") +print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") +print("F I N") +print("") + +stopTime = time.perf_counter() + +# version simple en secondes +#print(f"Exécuté en {stopTime - startTime:0.4f} secondes") + +# version en h min s +hours, rem = divmod(stopTime - startTime, 3600) +minutes, seconds = divmod(rem, 60) +print("Exécuté en {:0>2}:{:0>2}:{:05.2f}".format(int(hours),int(minutes),seconds)) + + diff --git a/scripts_v2/sql/create_tables.sql b/scripts_v2/sql/create_tables.sql index 7afe160..ff2c7c7 100644 --- a/scripts_v2/sql/create_tables.sql +++ b/scripts_v2/sql/create_tables.sql @@ -534,6 +534,51 @@ ALTER TABLE phase_2_tdb OWNER TO redadeg; ========================================================================== */ +-- cette table contient les infos de découpage des tronçons +DROP TABLE IF EXISTS phase_3_secteurs CASCADE ; +CREATE TABLE phase_3_secteurs +( + secteur_id integer, + nom_br text, + nom_fr text, + longueur_km_redadeg integer, + node_start integer, + node_stop integer, + pk_start integer, + pk_stop integer, + CONSTRAINT phase_3_secteurs_pkey PRIMARY KEY (secteur_id) +); +ALTER TABLE phase_3_secteurs OWNER TO redadeg; + + +-- la table qui va accueillir une couche support de calcul itinéraire phase 3 +-- à savoir les tronçons phase 2 découpés tous les x mètres +DROP TABLE IF EXISTS phase_3_troncons_pgr CASCADE ; +CREATE TABLE phase_3_troncons_pgr +( + secteur_id integer, + -- info de routage + id serial, + source bigint, + target bigint, + cost double precision, + reverse_cost double precision, + -- info OSM + osm_id bigint, + highway text, + type text, + oneway text, + ref text, + name_fr text, + name_br text, + the_geom geometry, + --CONSTRAINT enforce_geotype_the_geom CHECK (geometrytype(the_geom) = 'LINESTRING'::text), + CONSTRAINT enforce_srid_the_geom CHECK (st_srid(the_geom) = 2154) +); +CREATE INDEX phase_3_troncons_pgr_geom_idx ON phase_3_troncons_pgr USING gist(the_geom); +ALTER TABLE phase_3_troncons_pgr OWNER to redadeg; + + DROP TABLE IF EXISTS phase_3_trace_troncons CASCADE ; CREATE TABLE phase_3_trace_troncons ( diff --git a/scripts_v2/sql/phase_3_brouillon.sql b/scripts_v2/sql/phase_3_brouillon.sql new file mode 100644 index 0000000..827e6a0 --- /dev/null +++ b/scripts_v2/sql/phase_3_brouillon.sql @@ -0,0 +1,135 @@ + + +-- permet de créer 1 seule ligne pour 1 secteur + +DELETE FROM phase_3_trace_secteurs WHERE secteur_id = 100 ; +WITH t AS ( + SELECT * + FROM phase_2_trace_troncons + WHERE secteur_id = 100 +) +INSERT INTO phase_3_trace_secteurs +SELECT + secteur_id, s.nom_fr, s.nom_br, + 0 as km_reels, + --ST_AsText(ST_LineMerge(ST_Collect(t.the_geom))) as txt_geom, + ST_LineMerge(ST_Collect(t.the_geom)) as the_geom +FROM t JOIN secteur s ON t.secteur_id = s.id +GROUP BY t.secteur_id, s.nom_fr, s.nom_br ; + +UPDATE phase_3_trace_secteurs +SET km_reels = TRUNC( ST_Length(the_geom)::numeric /1000 , 1) +WHERE secteur_id = 100 ; + + + +--The below example simulates a while loop in +--SQL using PostgreSQL generate_series() to cut all +--linestrings in a table to 100 unit segments +-- of which no segment is longer than 100 units +-- units are measured in the SRID units of measurement +-- It also assumes all geometries are LINESTRING or contiguous MULTILINESTRING +--and no geometry is longer than 100 units*10000 +--for better performance you can reduce the 10000 +--to match max number of segments you expect + +TRUNCATE phase_3_trace_troncons ; + +INSERT INTO phase_3_trace_troncons +SELECT + row_number() over(), + secteur_id, + ST_LineSubstring(the_geom, 1000.00*n/length, + CASE + WHEN 1000.00*(n+1) < length THEN 1000.00*(n+1)/length + ELSE 1 + END) As the_geom +FROM + ( + SELECT + secteur_id, + ST_Length(the_geom) AS length, + ST_LineMerge(the_geom) AS the_geom + FROM phase_3_trace_secteurs + ) AS t +CROSS JOIN generate_series(0,10000) AS n +WHERE n*1000.00/length < 1; + + + + + +-- la table qui va accueillir une couche support de calcul itinéraire phase 3 +-- à savoir les tronçons phase 2 découpés tous les x mètres +DROP TABLE IF EXISTS phase_3_troncons_pgr CASCADE ; +CREATE TABLE phase_3_troncons_pgr +( + secteur_id integer, + -- info de routage + id serial, + source bigint, + target bigint, + cost double precision, + reverse_cost double precision, + -- info OSM + osm_id bigint, + highway text, + type text, + oneway text, + ref text, + name_fr text, + name_br text, + the_geom geometry, + --CONSTRAINT enforce_geotype_the_geom CHECK (geometrytype(the_geom) = 'LINESTRING'::text), + CONSTRAINT enforce_srid_the_geom CHECK (st_srid(the_geom) = 2154) +); +CREATE INDEX phase_3_troncons_pgr_geom_idx ON phase_3_troncons_pgr USING gist(the_geom); +ALTER TABLE phase_3_troncons_pgr OWNER to redadeg; + +-- on supprime les données pour le secteur +DELETE FROM phase_3_troncons_pgr WHERE secteur_id = $secteur_id ; + +-- on charge, pour le secteur concerné des tronçons courts découpés tous les x mètres +-- (densification avec ST_LineSubstring ) +INSERT INTO phase_3_troncons_pgr (secteur_id, osm_id, highway, type, oneway, ref, name_fr, name_br, the_geom) + SELECT + secteur_id, osm_id, highway, type, oneway, ref, name_fr, name_br, + ST_LineSubstring(the_geom, $long_km_redadeg*n/length, + CASE + WHEN $long_km_redadeg*(n+1) < length THEN $long_km_redadeg*(n+1)/length + ELSE 1 + END) As the_geom +FROM + ( + SELECT + secteur_id, osm_id, highway, type, oneway, ref, name_fr, name_br, + ST_Length(the_geom) AS length, + the_geom + FROM phase_2_trace_troncons + WHERE secteur_id = $secteur_id + ) AS t +CROSS JOIN generate_series(0,10000) AS n +WHERE n*$long_km_redadeg/length < 1; + +-- calcul des attributs de support du calcul pour PGR +UPDATE phase_3_troncons_pgr +SET cost = st_length(the_geom), reverse_cost = st_length(the_geom) +WHERE secteur_id = $secteur_id ; + +-- optimisation +VACUUM FULL phase_3_troncons_pgr ; + +-- création / maj de la topologie pour les tronçons nouvellement créés +SELECT pgr_createTopology('phase_3_troncons_pgr', 0.001, rows_where:='true', clean:=true); + + + + + + + +SELECT * +FROM pgr_drivingDistance( +'SELECT id, source, target, cost, reverse_cost FROM phase_3_troncons_pgr WHERE SOURCE IS NOT NULL', +107, 300); +