In [ ]:
import requests
import pandas as pd
pd.options.mode.chained_assignment = None  # default='warn'
import geopandas as gpd
from tqdm import tqdm
import shapely.geometry
In [ ]:
class telechargement_geo_spatial():
    """Méthode de télécharment des observations d'une observedProperties par intersection spatiale et filtre temporel

    Attributes:
        url_service (string): url du service sensorthing
        geom (geometry): geometry pour réaliser l'intersection
        obsP (string): observedProperty ciblée
        date_start (string): date de début d'observation
        date_end (string): date de fin d'observation
    """
    def __init__(self, url_service, geom,  obsP, date_start, date_end):
        self.url_service = url_service
        self.geom = geom
        self.obsP = obsP
        self.date_start = date_start
        self.date_end = date_end

        if self.geom.geom_type == 'MultiPolygon':
            print("MultipolygonE, conversion en polygone")
            self.geom =self.geom.geoms[0]

    def get_data(self,url_requete):
        """Télécharge les données d'une requête sensorthing avec itération dans les pages

        Args:
            url_requete (str): url à télécharger
        Returns:
            dataframe: dataframe (pandas) des données téléchargées
        """
        r=requests.get(url=url_requete)
        if r.status_code not in [200, 201, 202]:
            print(r.status_code)
            if r.status_code == 414:
                print("url trop grande = la géometrie en entrée est trop complexe \n \
                tentative d'intersection par la bounding box")

                self.geom=shapely.geometry.box(*self.geom.bounds, ccw=True)
                url_requete=self.inter_geo_temporelle()
                print(url_requete)
                r=requests.get(url=url_requete)

        data=r.json()
        df_all=pd.DataFrame(data['value'])
        while '@iot.nextLink' in data:
            r=requests.get(url=data['@iot.nextLink'])
            data=r.json()
            df_all=pd.concat([df_all,pd.DataFrame(data['value'])])
        return df_all

    def inter_geo_temporelle(self):
        """Façonne l'url de requête selon les différents arguments de la classe
        Returns:
            string: url avec les filtres
        """
        objet='Datastreams'
        rqt=f"$filter=geo.intersects(Thing/Locations/location, geography'{self.geom}') \
    and ObservedProperty/name eq'{self.obsP}'\
&$select=name,description,unitOfMeasurement,Observations,phenomenonTime\
&$expand=Thing/Locations($select=name,@iot.id,location)"
        url_rqt=f"{self.url_service}{objet}?{rqt}"
        print("Requête générée:\n")
        #print(url_rqt)
        return url_rqt

    def get_all(self,df):
        """Télécharge les observations des datastreams
        Args:
            df (dataframe): dataframe des datastreams avec une colonne contenant les lien vers les observations
        Returns:
            dataframe: dataframe des données téléchargées pour tous les datastreams avec les coordonnées géographiques de chaque observations
        """
        df_data=pd.DataFrame()
        print("\nTéléchargement en cours : \n")
        for idx,row in tqdm(df.iterrows(), total=df.shape[0]):
            #print(row['Observations@iot.navigationLink'])
            rqt=row['Observations@iot.navigationLink']
            rqt+=f"?$select=result,phenomenonTime&$filter=phenomenonTime ge {self.date_start} and phenomenonTime le {self.date_end}"
            df_inter=pd.DataFrame(self.get_data(rqt))
            df_inter['name']=row['name']
            df_inter['x']=row['Thing']['Locations'][0]['location']['geometry']['coordinates'][0]
            df_inter['y']=row['Thing']['Locations'][0]['location']['geometry']['coordinates'][1]
            df_data=pd.concat([df_data,df_inter])
            df_data=df_data.reset_index(drop=True)
        self.unite = row['unitOfMeasurement']['name']
        self.symbol = row['unitOfMeasurement']['symbol']
        return df_data

    def telechargement(self):
        """Fonction qui automatise la classe
        Returns:
            dataframe: dataframe des données téléchargées pour tous les datastreams avec les coordonnées géographiques de chaque observations
        """
        print(f"Recherche des Datastreams par l'ObservedProperty : {self.obsP} par intersection spatial sur la période : {self.date_start} {self.date_end}")
        url_rqt = self.inter_geo_temporelle()
        df_rqt = self.get_data(url_rqt)
        if len(df_rqt) == 0:
            print("\nIl n'y a pas d'objet dans la zone d'intersection, veuillez changer de zone")
            return 0
        else:
            print(f"\nNombre d'objet à téléchargerger :{len(df_rqt)}")
        #faire un retour si pas d'objet
        #refaire la requete si erreur 414 par bounding box
        df = self.get_all(df_rqt)
        df=df.rename(columns={'result':self.obsP})
        return df
In [ ]:
zone=gpd.read_file('/home/tloree/bosco/bv_cible.geojson')
print(zone.crs)
zone=zone.to_crs('EPSG:4326')
print(zone.crs)
zone.plot()
epsg:4326
epsg:4326
Out[ ]:
<AxesSubplot: >
In [ ]:
url_service='https://frost.geosas.fr/bosco/v1.1/'

geom=zone.geometry[0]
obsP="Soil moisture"
date_start, date_end = '2017-01-01T00:00:00Z','2021-01-01T00:00:00Z'

instance = telechargement_geo_spatial(url_service, geom,  obsP, date_start, date_end)
df = instance.telechargement()
unite = instance.unite
symbol = instance.symbol

display(df)
Recherche des Datastreams par l'ObservedProperty : Soil moisture par intersection spatial sur la période : 2017-01-01T00:00:00Z 2021-01-01T00:00:00Z
Requête générée:

414
url trop grande = la géometrie en entrée est trop complexe 
                 tentative d'intersection par la bounding box
Requête générée:

https://frost.geosas.fr/bosco/v1.1/Datastreams?$filter=geo.intersects(Thing/Locations/location, geography'POLYGON ((-4.191855010997296 48.03311297532656, -4.191855010997296 48.09707888083986, -4.302083796169617 48.09707888083986, -4.302083796169617 48.03311297532656, -4.191855010997296 48.03311297532656))')     and ObservedProperty/name eq'Soil moisture'&$select=name,description,unitOfMeasurement,Observations,phenomenonTime&$expand=Thing/Locations($select=name,@iot.id,location)

Nombre d'objet à téléchargerger :993

Téléchargement en cours : 

100%|██████████| 993/993 [01:56<00:00,  8.53it/s]
Soil moisture phenomenonTime name x y
0 16.8 2017-01-03T12:00:00Z Soil moisture, parcel n° 28934 -4.290617 48.065737
1 20.2 2017-01-09T12:00:00Z Soil moisture, parcel n° 28934 -4.290617 48.065737
2 32.6 2017-01-11T12:00:00Z Soil moisture, parcel n° 28934 -4.290617 48.065737
3 31.0 2017-01-15T12:00:00Z Soil moisture, parcel n° 28934 -4.290617 48.065737
4 8.0 2017-01-21T12:00:00Z Soil moisture, parcel n° 28934 -4.290617 48.065737
... ... ... ... ... ...
344006 30.6 2020-12-19T12:00:00Z Soil moisture, parcel n° 563475 -4.294535 48.096511
344007 32.2 2020-12-21T12:00:00Z Soil moisture, parcel n° 563475 -4.294535 48.096511
344008 28.0 2020-12-25T12:00:00Z Soil moisture, parcel n° 563475 -4.294535 48.096511
344009 30.2 2020-12-27T12:00:00Z Soil moisture, parcel n° 563475 -4.294535 48.096511
344010 28.4 2020-12-31T12:00:00Z Soil moisture, parcel n° 563475 -4.294535 48.096511

344011 rows × 5 columns