#!/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', )