Python_scripts/1_SICEN/import_serena_to_sicen.py

500 lines
17 KiB
Python

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-.
from sqlalchemy import create_engine #, text
from sqlalchemy.engine import URL
from shapely.geometry import Point
from shapely.ops import nearest_points
import pandas as pd
import geopandas as gpd
numerisateur = ['GEIER Colas']
lib_etude = 'Echange de données (partenaires)'
# précision possible :
# GPS / 0 à 10m / 10 à 100m / 100 à 500m / commune / lieu-dit
precision_gps = 'GPS'
# Parametres bdd
user = 'cgeier'
pwd = 'adm1n*bdCen'
adr = '91.134.194.221'
port = '5432'
base = 'sicen2'
url = URL.create('postgresql+psycopg2',
username=user,
password=pwd,
host=adr,
database=base,
)
con = create_engine(url)
dict_sp = {
'Dilatata dilatata (Gould, 1841)':'Menetus dilatatus (A.A. Gould, 1841)',
'Gomphocerippus biguttulus biguttulus (Linnaeus, 1758)':'Chorthippus biguttulus biguttulus (Linnaeus, 1758)',
'Gomphocerippus mollis mollis (Charpentier, 1825)':'Chorthippus mollis mollis (Charpentier, 1825)',
}
dict_columns = {
'TAXO_LATIN_C':'nom_latin',
'RELV_NOM':'lot_donnees',
'OBSE_DATE_J':'date_obs',
'OBSE_LON':'longitude',
'OBSE_LAT':'latitude',
'SITE_NOM':'localisation',
'OBSE_PLACE':'rmq_loc',
'OBSV_LIBEL':'observateur',
'DETM_LIBEL':'validateur',
'OBSE_COMMENT':'remarque_obs',
'OBSE_PCOLE_CHOI':'protocole',
'OBSE_PRECIS_CHOI':'effectif_textuel',
'OBSE_NOMBRE':'effectif',
'OBSE_TIME':'heure_obs',
'OBSE_COMP_CHOI':'reprostatut',
'OBSE_CARACT_CHOI':'reprocaract',
'OBSE_SIG_OBJ_ID':'id_origine',
'OBSE_NEBUL_CHOI':'nebulosite',
'OBSE_TPTAIR':'temperature',
'OBSE_VENT_CHOI':'vent'
}
# dict_columns = {
# 'Nom scientifique':'nom_latin',
# 'CD_nom':'cd_nom',
# 'Observateur':'observateur',
# 'Date_Relevé':'date_obs',
# 'Code_Relevé':'id_waypoint',
# 'X lambert 93':'longitude',
# 'Y Lambert 93':'latitude',
# 'Altinf_Relevé':'elevation',
# 'Nom_LieuDit':'localisation',
# 'Description_Relevé':'remarque_obs',
# 'Statuts':'remarque_obsStatut',
# 'Lot_données':'lot_donnees',
# 'Localisation_Relevé':'rmq_loc'
# }
# sql = 'SELECT * FROM %s.%s LIMIT 10;' % ('saisie','saisie_observation')
# saisie = gpd.read_postgis(
# sql = sql,
# con = con,
# geom_col = 'geometrie'
# )
def near(point,df2,geom_union,src_column):
# find the nearest point and return the corresponding Place value
geom2_col = df2.geometry.name
nearest = df2[geom2_col] == nearest_points(point, geom_union)[1]
# print(nearest)
# print(df2[nearest][src_column])
return df2[nearest][src_column].values[0]
def get_refGeomTable(table, schema,geom_col='geometrie',colnames=None,mask=None,buffer=None):
'''
Récupération de données géométriques dasn une table de référence
'''
pkey = con.dialect.get_pk_constraint(con,table,schema)['constrained_columns'][0]
if colnames:
colnames = [pkey,*colnames]
else:
colnames = [pkey]
cols = ','.join(colnames)
sql = 'SELECT %s,%s FROM %s.%s' % (cols,geom_col,schema,table)
if mask:
if buffer :
mask = mask.buffer(buffer)
sql = sql + """ WHERE ST_Intersects({geom_col}, 'SRID=2154;{mask}')""".format(
geom_col=geom_col,
mask=mask)
gdf = gpd.read_postgis(
sql,con,
geom_col=geom_col)
return gdf
def get_refTable(table, schema, colnames=None):
pkey = con.dialect.get_pk_constraint(con,table,schema)['constrained_columns'][0]
if colnames:
colnames = [pkey,*colnames]
else:
colnames = [pkey]
sql = 'SELECT %s' % (','.join(colnames))
sql = sql + ' FROM %s.%s' % (schema,table)
df = pd.read_sql(sql,con)
return df
def create_geometry(x,y):
geom = [Point(xy) for xy in zip(x, y)]
return geom
def normalize_obs(obs,name='first',sep=';'):
'''
:obs: pd.Series. Colonne des observateurs.
:name: str. 'first' si le nom est cité en premier.
'first',None. Défault 'first'.
:sep: str. séparateur de personne dans le cas de
multi-observateur. Défault ';'.
'''
obs = obs.replace(sep,',',regex=True)
obs = obs.replace(' ,',',',regex=True)
obs = obs.replace(', ',',',regex=True)
obs = obs.str.split(',',expand=True).stack()
obs = obs.str.split(' ',expand=True)
if name == 'first':
obs[0] = obs[0].str.upper()
obs[1] = obs[1].str[0].str.upper() + obs[1].str[1:]
obs['pers'] = obs[0] + ' ' + obs[1]
else:
obs[1] = obs[1].str.upper()
obs[0] = obs[0].str[0].str.upper() + obs[0].str[1:]
obs['pers'] = obs[1] + ' ' + obs[0]
obs = obs['pers'].unstack()
obs['pers'] = obs[0]
if 1 in obs.columns:
obs.loc[~obs[1].isna(),'pers'] = obs.loc[~obs[1].isna(),'pers'] + '&' +\
obs.loc[~obs[1].isna(),1]
return obs['pers']
def get_pers(obs):
'''
:obs: pd.Series. Colonne des observateurs.
'''
obs = pd.Series(obs.unique())
obs = obs.str.split(' ',expand=True)
obs_name = "','".join(obs[0].str.upper().tolist())
obs_surn = "','".join(obs[1].tolist())
sql = '''
SELECT
concat(nom,' ',prenom) pers,
id_personne,
id_structure
FROM md.personne
WHERE nom in ('%s')
AND prenom in ('%s')
AND id_structure != 49
;''' % (obs_name,obs_surn)
obs = pd.read_sql(sql,con)
return obs
# import TAXREF
taxref = pd.read_sql_table(
table_name='taxref',
con=con,
schema='inpn'
)
taxref = taxref[[
'regne','phylum','classe','ordre',
'famille','nom_valide','nom_vern',
'nom_complet','cd_nom']]
taxref['nom_latin'] = taxref.nom_complet.str.replace(' \(\#ref\)','',regex=True).copy()
# taxref['nom_latin2'] = taxref.nom_latin.str.split(r' \(|, [0-9]',expand=True)[0]
tmp = taxref[
(taxref.nom_latin=='Euconulus Reinhardt, 1883')&
(taxref.cd_nom!='192372')].index
taxref.drop(tmp,inplace=True)
# import DATA
path = '/home/colas/Documents/9_PROJETS/4_SICEN/IMPORT/'
file = 'Données Le Cheylas GD_2020.xlsx'
# file = 'DonneesMeF.ods'
df = pd.read_excel(path+file)
# df = pd.read_csv(path+file)
# mise en forme DATA
df.index.name = 'id'
df.rename(columns=dict_columns, inplace=True)
keep_col = df.columns[df.columns.isin(dict_columns.values())]
df = df[keep_col]
df.nom_latin.replace(dict_sp,inplace=True)
df['statut_validation'] = 'validée'
# df['lieu_dit'] = df['localisation'].copy()
if 'EFFC_OIS_Nb_Adult_Mâl' in df.columns:
df.loc[~df.EFFC_OIS_Nb_Adult_Mâl.isna(),'type_effectif'] = 'Adulte'
del df['EFFC_OIS_Nb_Adult_Mâl']
if 'id_origine' in df.columns:
df.loc[~df.id_origine.isna(),'id_origine'] = df.loc[~df.id_origine.isna(),'id_origine'].astype(int).astype(str)
# Concaténation des remarques de localisation
if 'rmq_loc' in df.columns:
df.loc[~df.rmq_loc.isna(),'localisation'] = df.loc[~df.rmq_loc.isna(),'localisation'] + \
', ' + df.loc[~df.rmq_loc.isna(),'rmq_loc']
del df['rmq_loc']
# Concaténation des remarques observateurs
if 'remarque_obs' in df.columns:
df.loc[~df.remarque_obs.isna(),'remarque_obs'] = 'Commentaires : ' + \
df.loc[~df.remarque_obs.isna(),'remarque_obs']
if 'remarque_obsStatut' in df.columns:
df.loc[~df.remarque_obsStatut.isna(),'remarque_obs'] = df.loc[~df.remarque_obsStatut.isna(),'remarque_obs'] + \
'; rmq_statut : ' + df.loc[~df.remarque_obsStatut.isna(),'remarque_obsStatut']
del df['remarque_obsStatut']
if 'nebulosite' in df.columns:
df.loc[~df.nebulosite.isna(),'remarque_obs'] = df.loc[~df.nebulosite.isna(),'remarque_obs'] + \
'; nebul : ' + df.loc[~df.nebulosite.isna(),'nebulosite']
del df['nebulosite']
if 'temperature' in df.columns:
df.loc[~df.temperature.isna(),'remarque_obs'] = df.loc[~df.temperature.isna(),'remarque_obs'] + \
'; temp : ' + df.loc[~df.temperature.isna(),'temperature'].astype(str)
del df['temperature']
if 'vent' in df.columns:
df.loc[~df.vent.isna(),'remarque_obs'] = df.loc[~df.vent.isna(),'remarque_obs'] + \
'; ' + df.loc[~df.vent.isna(),'vent']
del df['vent']
if 'id_waypoint' in df.columns:
df.loc[~df.id_waypoint.isna(),'id_waypoint'] = df.loc[~df.id_waypoint.isna(),'id_waypoint']\
.astype(int).astype(str)
if 'precision' not in df.columns:
df['precision'] = 'GPS'
# format date / time
# df['date_obs'] = df['date_obs'].astype(str).astype(int)
# df['date_obs'] = pd.to_datetime(df['date_obs'])
# df['date_obs'].astype('datetime64')
# pd.to_datetime(df['heure_obs'],format='%H:%M:%S')
# df['heure_obs']
# create geometrie
df['geometrie'] = create_geometry(df.longitude, df.latitude)
if all(df.longitude < 10):
epsg = 4326
elif all(df.longitude > 900000):
epsg = 2154
df = df.set_geometry('geometrie', crs=epsg)
if epsg == 4326:
df.to_crs(2154, inplace=True)
elif epsg == 2154:
df['longitude'] = df.to_crs(4326).geometrie.x
df['latitude'] = df.to_crs(4326).geometrie.y
df_union = df.unary_union
# join code_INSEE & Lieu-dit
# commune
com = get_refGeomTable(
'commune', 'ign_bd_topo',
geom_col='geometrie',mask=df_union)
com_union = com.unary_union
# lieu_dit
ldt = get_refGeomTable(
'lieu_dit', 'ign_bd_topo', colnames=['nom'],
geom_col='geometrie',mask=com_union, buffer=1000)
ldt.rename(columns={'id': 'id_lieu_dit'}, inplace=True)
ldt_union = ldt.unary_union
# jointure lieu_dit / communes
if 'lieu_dit' in df.columns:
df['lieu_dit'] = df.lieu_dit.str.lower()
df = pd.merge(df,ldt[['id_lieu_dit','nom']],left_on='lieu_dit',right_on='nom')
del df['nom']
del df['lieu_dit']
else:
df['id_lieu_dit'] = [
near(geom,ldt,ldt_union,'id_lieu_dit')
for geom in df.geometrie]
df = gpd.sjoin(df,com)
del df['index_right']
# get observateur/validateur
df['observateur'] = normalize_obs(df['observateur'], sep='-')
obs = get_pers(df.observateur)
if not obs.pers.is_unique:
raise Exception('duplicate observateur')
ind = obs[(obs.pers == 'GRANGE Benjamin')&(obs.id_structure == 1)].index
obs.drop(ind, inplace=True)
obs['id_personne'] = obs['id_personne'].astype(str)
obs['id_structure'] = obs['id_structure'].astype(str)
df['observateur'] = df['observateur'].replace(
to_replace=[*obs.pers],
value=[*obs.id_personne],
regex=True)
if 'validateur' in df.columns:
val = get_pers(df.validateur)
val['id_personne'] = val['id_personne'].astype(str)
df['validateur'].replace(*obs.pers,*obs.id_personne, inplace=True)
else:
df['validateur'] = df['observateur'].str.split('&',expand=True)[0]
for p in obs.iterrows():
o = p[1]['id_personne']
s = p[1]['id_structure']
df.loc[df.observateur.str.contains(o),'structure'] = s
# get numerisateur
num = get_pers(pd.Series(numerisateur))
df['numerisateur'] = num.id_personne.astype(str)[0]
# get id_lot / id_etude
lot = get_refTable('lot_donnee','md',colnames=['libelle'])
etude = get_refTable('etude','md',colnames=['nom_etude'])
df = pd.merge(df,lot,left_on='lot_donnees',right_on='libelle')
del df['libelle']
del df['lot_donnees']
df['id_etude'] = etude.loc[etude.nom_etude == lib_etude,'id_etude'].values[0]
# get protocole
if 'protocole' in df.columns:
proto = get_refTable('protocole','md',colnames=['libelle'])
df.protocole = df.protocole \
.replace(
to_replace=[
'Capture à vue (à la main, au filet à papillon...)',
"Recherche ciblée (bouses, cadavres d'animaux...)"],
value=['Capture à vue',"Recherche d'indices de présence"])
df = pd.merge(df,proto, left_on='protocole',right_on='libelle')
del df['libelle']
del df['protocole']
# join taxref
if 'cd_nom' in df.columns:
if df['cd_nom'].dtype == int: df['cd_nom'] = df['cd_nom'].astype(str)
if 'nom_latin' in taxref.columns: del taxref['nom_latin']
if 'nom_latin' in df.columns: del df['nom_latin']
df = df.merge(taxref,on='cd_nom')
else:
df.nom_latin = df.nom_latin.str.strip()
tt = taxref[taxref.nom_latin.isin(df.nom_latin.unique())]
len_taxo = df.nom_latin.unique().shape[0]
len_taxref = tt.shape[0]
if len_taxo == len_taxref:
df = df.merge(tt,on='nom_latin')
del df['nom_latin']
else:
contain = '|'.join(df.nom_latin.unique())
tt = taxref[taxref.nom_latin.str.contains(contain)]
len_taxref = tt.shape[0]
if len_taxo == len_taxref:
df = df.merge(tt,on='nom_latin')
del df['nom_latin']
else:
raise Exception('error corespondance cd_nom')
contain = '|'.join(df.nom_latin.str.split(r' \(|, [0-9]',expand=True)[0].unique())
tt = taxref[taxref.nom_latin2.str.contains(contain)]
# get statuts de reproduction
if 'reprostatut' in df.columns:
if 'reprocaract' in df.columns:
df.loc[(~df.reprostatut.isna())&(~df.reprocaract.isna()),'remarque_obs'] = df['remarque_obs'] + \
'; rmq_repro : ' + df.loc[(~df.reprostatut.isna())&(~df.reprocaract.isna()),'reprocaract']
df.loc[df.reprostatut.isna(),'reprostatut'] = df.loc[df.reprostatut.isna(),'reprocaract']
del df['reprocaract']
sql = 'SELECT unnest(enum_range(null::{0}.{1})) {1}'.format('saisie','enum_reprostatut')
reprostat = pd.read_sql(sql,con)
reprostat = pd.concat(
[reprostat,reprostat.enum_reprostatut.str.split('_',expand=True)],
axis=1 )
lst_phyl = df.phylum.unique()
lst_clas = set([*df.classe.unique()])
lst_ordr = set([*df.ordre.unique()])
AMP = set(['Amphibia'])
CHIR = set([])
INV = set(['Bivalvia','Gastropoda'])
MAM = set([])
ODO = set(['Hexapoda'])
ordr_ODO = set(['Odonata'])
ordr_INV = set(['Orthoptera','Lepidoptera'])
OIS = set(['Aves'])
REP = set([])
if any(lst_clas.intersection(AMP)):
# AMP = list(AMP)
repro = reprostat[reprostat[0]=='AMP']
df.loc[df.classe.isin(AMP),'reprostatut'] = df[df.classe.isin(AMP)].reprostatut \
.replace(
to_replace='Reproduction confirmée',
value='Accouplement') \
.replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
# df.loc[df.classe.isin(AMP),'reprostatut'] = df[df.classe.isin(AMP)].reprostatut \
# .replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
if any(lst_clas.intersection(CHIR)):
# CHIR = list(CHIR)
repro = reprostat[reprostat[0]=='CHIR']
if any(lst_clas.intersection(MAM)):
# MAM = list(MAM)
repro = reprostat[reprostat[0]=='MAM']
if any(lst_clas.intersection(INV)):
# INV = list(INV)
repro = reprostat[reprostat[0]=='INV']
df.loc[df.classe.isin(INV),'reprostatut'] = df[df.classe.isin(INV)].reprostatut \
.replace(
to_replace='Reproduction confirmée',
value='Accouplement') \
.replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
# df.loc[df.classe.isin(INV),'reprostatut'] = df[df.classe.isin(INV)].reprostatut \
# .replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
if any(lst_clas.intersection(ODO)) and any(lst_ordr.intersection(ordr_INV)):
# ODO = list(ODO)
# ordr_INV = list(ordr_INV)
repro = reprostat[reprostat[0]=='INV']
df.loc[(df.classe.isin(ODO))&(df.ordre.isin(ordr_INV)),'reprostatut'] = \
df[(df.classe.isin(ODO))&(df.ordre.isin(ordr_INV))].reprostatut \
.replace(
to_replace='Reproduction confirmée',
value='Accouplement') \
.replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
# df.loc[(df.classe.isin(ODO))&(df.ordre.isin(ordr_INV)),'reprostatut'] = \
# df[(df.classe.isin(ODO))&(df.ordre.isin(ordr_INV))].reprostatut \
# .replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
# ODO = set(ODO)
if any(lst_clas.intersection(ODO)) and any(lst_ordr.intersection(ordr_ODO)):
# ODO = list(ODO)
# ordr_ODO = list(ordr_ODO)
repro = reprostat[reprostat[0]=='ODO']
df.loc[(df.classe.isin(ODO))&(df.ordre.isin(ordr_ODO)),'reprostatut'] = \
df[(df.classe.isin(ODO))&(df.ordre.isin(ordr_ODO))].reprostatut \
.replace(
to_replace=['Accouplement','Reproduction confirmée','Reproduction possible','Reproduction probable'],
value=['Tandem','Exuvie/émergence','Mâles+Femelles','Immature']) \
.replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
# df.loc[(df.classe.isin(ODO))&(df.ordre.isin(ordr_ODO)),'reprostatut'] = \
# df[(df.classe.isin(ODO))&(df.ordre.isin(ordr_ODO))].reprostatut \
# .replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
if any(lst_clas.intersection(OIS)):
# OIS = list(OIS)
repro = reprostat[reprostat[0]=='OIS']
df.loc[df.classe.isin(OIS),'reprostatut'] = df[df.classe.isin(OIS)].reprostatut \
.replace(
to_replace='Déplacement (marche, vol, nage)',
value='Fuite - envol') \
.replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
# df.loc[df.classe.isin(OIS),'reprostatut'] = df[df.classe.isin(OIS)].reprostatut \
# .replace(to_replace=[*repro[1]],value=[*repro.enum_reprostatut])
if any(lst_clas.intersection(REP)):
# REP = list(REP)
repro = reprostat[reprostat[0]=='REP']
df.to_postgis(
name='saisie_observation',
con=con,
schema='saisie',
if_exists='append',
index=False,
geom_col='geometrie',
)