python.observation.esobservation

Created on Tue Aug 3 23:40:06 2021

@author: philippe@loco-labs.io

An Observation is an object representing a set of information having spatial and temporal characteristics associated with measurable or observable properties.

The Observation Object is built around three main bricks :

  • Dataset Object which deal with indexing,
  • ESValue Object which integrate the specificities of environmental data,
  • Tools dedicated to particular domains (Shapely for location, TimeSlot for Datation)

The python.observation.esobservation module contains the Observation class.

Documentation is available in other pages :

  1# -*- coding: utf-8 -*-
  2"""
  3Created on Tue Aug  3 23:40:06 2021
  4
  5@author: philippe@loco-labs.io
  6
  7An `Observation` is an object representing a set of information having
  8spatial and temporal characteristics associated with measurable or observable
  9 properties.
 10
 11The `Observation` Object is built around three main bricks :
 12
 13- Dataset Object which deal with indexing,
 14- ESValue Object which integrate the specificities of environmental data,
 15- Tools dedicated to particular domains
 16([Shapely](https://shapely.readthedocs.io/en/stable/manual.html)
 17for location, TimeSlot for Datation)
 18
 19The `python.observation.esobservation` module contains the `Observation` class.
 20
 21Documentation is available in other pages :
 22
 23- The concept of 'observation' is describe in
 24[this page](https://github.com/loco-philippe/Environmental-Sensing/wiki/Observation).
 25- The concept of 'indexed list' is describe in
 26[this page](https://github.com/loco-philippe/Environmental-Sensing/wiki/Indexed-list).
 27- The non-regression test are at [this page]
 28(https://github.com/loco-philippe/Environmental-Sensing/blob/main/python/Tests/test_obs.py)
 29- The [examples]
 30(https://github.com/loco-philippe/Environmental-Sensing/tree/main/python/Examples/Observation)
 31"""
 32import datetime
 33import json
 34from copy import copy
 35import folium
 36import cbor2
 37
 38from observation.dataset import Dataset
 39from observation.util import util
 40from observation.field_interface import FieldEncoder, CborDecoder
 41from observation.esconstante import ES
 42from observation.esvalue import LocationValue, DatationValue, PropertyValue, ExternValue
 43from observation.esvalue_base import ESValue, ESValueEncoder
 44from observation.dataset_analysis import Analysis
 45
 46
 47class Observation(Dataset):
 48    """
 49    An `Observation` is derived from `observation.Dataset` object.
 50
 51    *Additional attributes (for @property see methods)* :
 52
 53    - **name** : textual description
 54    - **param** : namedValue dictionnary (external data)
 55
 56    The methods defined in this class (included inherited) are :
 57
 58    *constructor (@classmethod))*
 59
 60    - `Observation.dic`
 61    - `Observation.std`
 62    - `python.observation.dataset.Dataset.obj`
 63    - `Observation.from_obj`
 64    - `python.observation.dataset.Dataset.from_file`
 65
 66    *dynamic value (getters @property)*
 67
 68    - `Observation.bounds`
 69    - `Observation.id`
 70    - `Observation.jsonFeature`
 71    - `Observation.setLocation`
 72    - `Observation.setDatation`
 73    - `Observation.setProperty`
 74    - `Observation.setResult`
 75
 76    *dynamic value inherited (getters @property)*
 77
 78    - `python.observation.dataset.Dataset.extidx`
 79    - `python.observation.dataset.Dataset.extidxext`
 80    - `python.observation.dataset.Dataset.idxname`
 81    - `python.observation.dataset.Dataset.idxlen`
 82    - `python.observation.dataset.Dataset.iidx`
 83    - `python.observation.dataset.Dataset.keys`
 84    - `python.observation.dataset.Dataset.lenindex`
 85    - `python.observation.dataset.Dataset.lenidx`
 86    - `python.observation.dataset.Dataset.lidx`
 87    - `python.observation.dataset.Dataset.lidxrow`
 88    - `python.observation.dataset.Dataset.lvar`
 89    - `python.observation.dataset.Dataset.lvarrow`
 90    - `python.observation.dataset.Dataset.lname`
 91    - `python.observation.dataset.Dataset.lunicname`
 92    - `python.observation.dataset.Dataset.lunicrow`
 93    - `python.observation.dataset.Dataset.setidx`
 94
 95    *global value (getters @property)*
 96
 97    - `python.observation.dataset.Dataset.complete`
 98    - `python.observation.dataset.Dataset.consistent`
 99    - `python.observation.dataset.Dataset.dimension`
100    - `python.observation.dataset.Dataset.lencomplete`
101    - `python.observation.dataset.Dataset.primary`
102    - `python.observation.dataset.Dataset.zip`
103
104    *selecting - infos methods*
105
106    - `python.observation.dataset.Dataset.couplingmatrix`
107    - `python.observation.dataset.Dataset.idxrecord`
108    - `python.observation.dataset.Dataset.indexinfos`
109    - `python.observation.dataset.Dataset.indicator`
110    - `python.observation.dataset.Dataset.iscanonorder`
111    - `python.observation.dataset.Dataset.isinrecord`
112    - `python.observation.dataset.Dataset.keytoval`
113    - `python.observation.dataset.Dataset.loc`
114    - `python.observation.dataset.Dataset.nindex`
115    - `python.observation.dataset.Dataset.record`
116    - `python.observation.dataset.Dataset.recidx`
117    - `python.observation.dataset.Dataset.recvar`
118    - `python.observation.dataset.Dataset.valtokey`
119
120    *add - update methods*
121
122    - `python.observation.dataset.Dataset.add`
123    - `python.observation.dataset.Dataset.addindex`
124    - `python.observation.dataset.Dataset.append`
125    - `Observation.appendObs`
126    - `python.observation.dataset.Dataset.delindex`
127    - `python.observation.dataset.Dataset.delrecord`
128    - `python.observation.dataset.Dataset.renameindex`
129    - `python.observation.dataset.Dataset.setname`
130    - `python.observation.dataset.Dataset.updateindex`
131
132    *structure management - methods*
133
134    - `python.observation.dataset.Dataset.applyfilter`
135    - `python.observation.dataset.Dataset.coupling`
136    - `python.observation.dataset.Dataset.full`
137    - `python.observation.dataset.Dataset.getduplicates`
138    - `python.observation.dataset.Dataset.merge`
139    - `python.observation.dataset.Dataset.reindex`
140    - `python.observation.dataset.Dataset.reorder`
141    - `python.observation.dataset.Dataset.setfilter`
142    - `python.observation.dataset.Dataset.sort`
143    - `python.observation.dataset.Dataset.swapindex`
144    - `python.observation.dataset.Dataset.setcanonorder`
145    - `python.observation.dataset.Dataset.tostdcodec`
146
147    *exports methods*
148
149    - `Observation.choropleth`
150    - `python.observation.dataset.Dataset.json`
151    - `python.observation.dataset.Dataset.plot`
152    - `python.observation.dataset.Dataset.to_csv`
153    - `python.observation.dataset.Dataset.to_file`
154    - `Observation.to_obj`
155    - `Observation.to_xarray`
156    - `python.observation.dataset.Dataset.to_dataframe`
157    - `python.observation.dataset.Dataset.view`
158    - `python.observation.dataset.Dataset.vlist`
159    - `python.observation.dataset.Dataset.voxel`
160    """
161
162# %% constructor
163    def __init__(self, listidx=None, name=None, param=None, reindex=True):
164        '''Observation constructor
165
166        *Parameters*
167
168        - **listidx**  : object (default None) - list of Field data or Dataset or Observation
169        - **name**     : string (default None) - Obs name
170        - **param**    : dict (default None) - Dict with parameter data or user's data'''
171
172        if isinstance(listidx, Observation):
173            self.lindex = [copy(idx) for idx in listidx.lindex]
174            if not listidx.param is None:
175                self.param = dict(listidx.param.items())
176            else:
177                self.param = param
178            self.name = listidx.name
179            self.analysis = Analysis(self)
180            return
181
182        if isinstance(listidx, Dataset):
183            self.lindex = [copy(idx) for idx in listidx.lindex]
184            self.param = param
185            self.name = name
186            self.analysis = Analysis(self)
187            return
188
189        if not listidx:
190            Dataset.__init__(self)
191        else:
192            Dataset.__init__(self, listidx=listidx, reindex=reindex)
193        self.name = name
194        self.param = param
195        return
196
197    @classmethod
198    def dic(cls, idxdic=None, typevalue=ES.def_clsName, name=None, param=None):
199        '''
200        Observation constructor (external dictionnary).
201
202        *Parameters*
203
204        - **idxdic** : dict (default None) - dict of Field element (Field name :
205        list of Field values)
206        - **typevalue** : str (default ES.def_clsName) - default value class (None or NamedValue)
207        - **var** :  int (default None) - row of the variable
208        - **name**     : string (default None) - Observation name
209        - **param**    : dict (default None) - Dict with parameter data or user's data'''
210        listidx = Dataset.dic(idxdic, typevalue=typevalue)
211        return cls(listidx=listidx, name=name, param=param)
212
213    @classmethod
214    def std(cls, result=None, datation=None, location=None, property=None,
215            name=None, param=None, typevalue=ES.def_clsName):
216        '''
217        Generate an Observation Object with standard indexes
218
219        *Parameters*
220
221        - **datation** : compatible Field (default None) - index for DatationValue
222        - **location** : compatible Field (default None) - index for LocationValue
223        - **property** : compatible Field (default None) - index for PropertyValue
224        - **result  ** : compatible Field (default None) - index for Variable(NamedValue)
225        - **name**     : string (default None) - Observation name
226        - **param**    : dict (default None) - Dict with parameter data or user's data'''
227        idxdic = {}
228        length = 0
229        std_val = (result, datation, location, property)
230        es_val = (ES.res_classES, ES.dat_classES,
231                  ES.loc_classES, ES.prp_classES)
232        for std, esv in zip(std_val, es_val):
233            value = []
234            if not std is None and isinstance(std, list):
235                value = std
236            elif not std is None and not isinstance(std, list):
237                value = [std]
238            length = max(length, len(value))
239            idxdic[esv] = value
240        for item in idxdic.items():
241            if len(item[1]) == 1:
242                idxdic[item[0]] = item[1] * length
243        return cls.dic(idxdic=idxdic, typevalue=typevalue, name=name, param=param)
244
245    @classmethod
246    def from_obj(cls, bs=None, reindex=True, context=True):
247        '''
248        Generate an Observation Object from a bytes, string or dic value
249
250        *Parameters*
251
252        - **bs** : bytes, string or dict data to convert
253        - **reindex** : boolean (default True) - if True, default codec for each Field
254        - **context** : boolean (default True) - if False, only codec and keys are included'''
255        if not bs:
256            bs = {}
257        if isinstance(bs, bytes):
258            dic = cbor2.loads(bs)
259        elif isinstance(bs, str):
260            dic = json.loads(bs, object_hook=CborDecoder().codecbor)
261        elif isinstance(bs, dict):
262            dic = bs
263        else:
264            raise ObsError("the type of parameter is not available")
265
266        param = None
267        if ES.param in dic:
268            param = dic[ES.param]
269        if param and not isinstance(param, dict):
270            raise ObsError('param is not a dict')
271
272        name = None
273        if ES.name in dic:
274            name = dic[ES.name]
275        if name and not isinstance(name, str):
276            raise ObsError('name is not a str')
277
278        data = None
279        if ES.data in dic:
280            data = dic[ES.data]
281        if data and not isinstance(data, (list, dict)):
282            raise ObsError('data is not a list and not a dict')
283
284        return cls(listidx=Dataset.obj(data, reindex=reindex, context=context),
285                   name=name, param=param)
286
287# %% special
288    def __copy__(self):
289        ''' Copy all the data '''
290        return Observation(self)
291
292    def __str__(self):
293        '''return string format'''
294        stro = ''
295        if self.name:
296            stro = ES.name + ': ' + self.name + '\n'
297        stri = Dataset.__str__(self)
298        if not stri == '':
299            stro += ES.data + ':\n' + stri
300        if self.param:
301            stro += ES.param + ':\n    ' + json.dumps(self.param) + '\n'
302        return stro
303
304    def __hash__(self):
305        '''return sum of all hash(Field)'''
306        return hash(json.dumps(self.param)) + hash(self.name) + Dataset.__hash__(self)
307
308# %% properties
309    @property
310    def bounds(self):
311        '''
312        **list of `observation.esvalue` (@property)** : `observation.esvalue`
313        bounding box for each axis.'''
314        bound = [None, None, None]
315        if self.setDatation:
316            bound[0] = ESValue.boundingBox(self.setDatation).bounds
317        if self.setLocation:
318            bound[1] = ESValue.boundingBox(self.setLocation).bounds
319        if self.setProperty:
320            bound[2] = ESValue.boundingBox(self.setProperty).bounds
321        return bound
322
323    @property
324    def __geo_interface__(self):
325        '''**dict (@property)** : return the union of Location geometry (see shapely)'''
326        codecgeo = self.nindex('location').codec
327        if len(codecgeo) == 0:
328            return ""
329        if len(codecgeo) == 1:
330            return codecgeo[0].value.__geo_interface__
331        collec = codecgeo[0].value
332        for loc in codecgeo[1:]:
333            collec = collec.union(loc.value)
334        return collec.__geo_interface__
335
336    @property
337    def id(self):
338        '''**integer (@property)** : hash value (unique)'''
339        return hash(self)
340
341    @property
342    def jsonFeature(self):
343        '''**string (@property)** : "FeatureCollection" with Location geometry'''
344        if self.setLocation:
345            geo = self.__geo_interface__
346            if geo['type'][:5] == 'Multi':
347                typ = geo['type'][5:]
348                lis = [{"type":  typ, "coordinates": geo['coordinates'][i]}
349                       for i in range(len(geo['coordinates']))]
350            elif geo['type'] in ['Point', 'Polygon']:
351                lis = [geo]
352            elif geo['type'] == 'GeometryCollection':
353                lis = geo['geometries']
354            fea = [{"type": "Feature", "id": i, "geometry": lis[i]}
355                   for i in range(len(lis))]
356            return json.dumps({"type": "FeatureCollection", "features": fea},
357                              cls=ESValueEncoder)
358        return ''
359
360    @property
361    def setDatation(self):
362        '''**list (@property)** : list of codec values in the datation index'''
363        if self.nindex(ES.dat_classES):
364            return self.nindex(ES.dat_classES).codec
365        return None
366
367    @property
368    def setLocation(self):
369        '''**list (@property)** : list of codec values in the location index'''
370        if self.nindex(ES.loc_classES):
371            return self.nindex(ES.loc_classES).codec
372        return None
373
374    @property
375    def setProperty(self):
376        '''**list (@property)** : list of codec values in the property index'''
377        if self.nindex(ES.prp_classES):
378            return self.nindex(ES.prp_classES).codec
379        return None
380
381    @property
382    def setResult(self):
383        '''
384        **list (@property)** : list of codec values in the result index'''
385        if self.nindex(ES.res_classES):
386            return self.nindex(ES.res_classES).codec
387        return None
388
389# %% methods
390    def appendObs(self, obs, unique=False, fillvalue='-'):
391        '''
392        Add an `Observation` as a new Result `observation.esvalue` with bounding
393        box for the Index `observation.esvalue`
394
395        *Parameters*
396
397        - **obs** : Observation object
398        - **fillvalue** : object value used for default value
399
400        *Returns*
401
402        - **int** : last index in the `Observation`'''
403        lname = self.lname
404        record = [fillvalue] * len(lname)
405        if ES.dat_classES in lname:
406            record[lname.index(ES.dat_classES)] = DatationValue.Box(
407                obs.bounds[0])
408        if ES.loc_classES in lname:
409            record[lname.index(ES.loc_classES)] = LocationValue.Box(
410                obs.bounds[1])
411        if ES.prp_classES in lname:
412            record[lname.index(ES.prp_classES)] = PropertyValue.Box(
413                obs.bounds[2])
414        if ES.res_classES in lname:
415            record[lname.index(ES.res_classES)] = ExternValue(obs)
416        return self.append(record, unique=unique)
417
418    def choropleth(self, name="choropleth", line=True):
419        '''
420        Display `Observation` on a folium.Map (only with dimension=1)
421
422        - **name** : String, optionnal (default 'choropleth') - Name of the choropleth
423        - **line** : Boolean, optionnal (default True) - Line between recods if True
424
425        *Returns* : None'''
426        primary = self.primary
427        if self.dimension == 1:
428            mapf = folium.Map(
429                location=self.setLocation[0].coorInv, zoom_start=6)
430            folium.Choropleth(
431                geo_data=self.jsonFeature,
432                name=self.name,
433                data=self.to_xarray(
434                    numeric=True, coord=True).to_dataframe(name='obs'),
435                key_on="feature.id",
436                columns=[self.idxname[primary[0]] + '_row', 'obs'],
437                fill_color="OrRd",
438                fill_opacity=0.7,
439                line_opacity=0.4,
440                line_weight=2,
441                legend_name=name
442            ).add_to(mapf)
443            if line:
444                folium.PolyLine(
445                    util.funclist(self.nindex('location'),
446                                  LocationValue.vPointInv)
447                ).add_to(mapf)
448            folium.LayerControl().add_to(mapf)
449            return mapf
450        return None
451
452    def to_obj(self, **kwargs):
453        '''Return a formatted object (json string, cbor bytes or json dict).
454
455        *Parameters (kwargs)*
456
457        - **encoded** : boolean (default False) - choice for return format
458        (string/bytes if True, dict else)
459        - **encode_format**  : string (default 'json')- choice for return format (json, cbor)
460        - **codif** : dict (default ES.codeb). Numerical value for string in CBOR encoder
461        - **modecodec** : string (default 'optimize') - if 'full', each index is with a full codec
462        if 'default' each index has keys, if 'optimize' keys are optimized,
463        if 'dict' dict format is used, if 'nokeys' keys are absent, if 'ndjson' 
464        a list of ndjson elements is created
465        - **name** : boolean (default False) - if False, default index name are not included
466        - **fullvar** : boolean (default True) - if True and modecodec='optimize,
467        variable index is with a full codec
468        - **geojson** : boolean (default False) - geojson for LocationValue if True
469
470        - **json_param**     : Boolean - include Obs Param
471        - **json_info**      : Boolean - include all infos
472        - **json_info_detail**: Boolean - include the other infos
473
474        *Returns* : string, bytes or dict'''
475        option = {'modecodec': 'optimize', 'encoded': False,
476                  'encode_format': 'json', 'codif': ES.codeb, 'name': False,
477                  'json_param': False, 'json_info': False, 'json_info_detail': False,
478                  'geojson': False, 'fullvar': True} | kwargs
479        if option['modecodec'] == 'ndjson':
480            lisobs = {ES.id: self.id}
481            if self.param:
482                lisobs[ES.param] = self.param
483            if self.name:
484                lisobs[ES.name] = self.name
485            return [lisobs] + Dataset.to_obj(self, modecodec='ndjson', id=self.id)
486        option2 = option | {'encoded': False, 'encode_format': 'json'}
487        dic = {ES.type: ES.obs_classES}
488
489        if self.name:
490            dic[ES.obs_name] = self.name
491        if self.param:
492            dic[ES.obs_param] = self.param
493        dic[ES.obs_data] = Dataset.to_obj(self, **option2)
494        if option["json_param"] and self.param:
495            dic[ES.obs_param] = self.param
496        dic |= self._info(**option)
497        if option['codif'] and option['encode_format'] != 'cbor':
498            js2 = {}
499            for key, val in dic.items():
500                if key in option['codif']:
501                    js2[option['codif'][key]] = val
502                else:
503                    js2[key] = val
504        else:
505            js2 = dic
506
507        if option['encoded'] and option['encode_format'] == 'json':
508            return json.dumps(js2, cls=FieldEncoder)
509        if option['encoded'] and option['encode_format'] == 'cbor':
510            return cbor2.dumps(js2, datetime_as_timestamp=True,
511                               timezone=datetime.timezone.utc, canonical=True)
512        return dic
513
514    def to_xarray(self, info=False, idxname=None, varname=None, fillvalue='?',
515                  fillextern=True, lisfunc=None, numeric=False, npdtype=None,
516                  **kwargs):
517        '''
518        Complete the Observation and generate a Xarray DataArray with the dimension define by idx.
519
520        *Parameters*
521
522        - **info** : boolean (default False) - if True, add _dict attributes to attrs Xarray
523        - **idxname** : list (default none) - list of idx to be completed. If None,
524        self.primary is used.
525        - **varname** : string (default none) - Name of the variable to use. If None,
526        first lvarname is used.
527        - **fillvalue** : object (default '?') - value used for the new extval
528        - **fillextern** : boolean(default True) - if True, fillvalue is converted to typevalue
529        - **lisfunc** : function (default none) - list of function to apply to indexes before export
530        - **numeric** : Boolean (default False) - Generate a numeric DataArray.Values.
531        - **npdtype** : string (default None) - numpy dtype for the DataArray ('object' if None)
532        - **kwargs** : parameter for lisfunc
533
534        *Returns* : DataArray '''
535        return Dataset.to_xarray(self, info=info, idxname=idxname, varname=varname,
536                               fillvalue=fillvalue, fillextern=fillextern,
537                               lisfunc=lisfunc, name=self.name, numeric=numeric,
538                               npdtype=npdtype, attrs=self.param, **kwargs)
539# %% internal
540
541    def _info(self, **kwargs):
542        ''' Create json dict with info datas
543
544        *Parameters*
545
546        - **json_info** : boolean (default False) - if True, add main information
547        about Observation and Field
548        - **json_info_detail** : boolean (default False) - if True, add complemantary
549        information about Field
550        '''
551        option = {"json_info": False, "json_info_detail": False} | kwargs
552        dcobs = {}
553        dcindex = {}
554        if not option['json_info']:
555            return dcobs
556        dcobs[ES.name] = self.name
557        dcobs[ES.id] = self.id
558        dcobs[ES.length] = len(self)
559        dcobs[ES.lenindex] = self.lenindex
560        dcobs[ES.complete] = self.complete
561        dcobs[ES.dimension] = self.dimension
562        if option['json_info_detail']:
563            infos = self.indexinfos()
564        for ind, idx in enumerate(self.lidx):
565            dcidx = {}
566            dcidx[ES.num] = ind
567            dcidx[ES.typevalue] = idx.typevalue
568            dcidx[ES.lencodec] = len(idx.codec)
569            dcidx[ES.box] = Observation._info_box(idx, **option)
570            if option['json_info_detail']:
571                dcidx |= infos[ind]
572            dcindex[idx.name] = dcidx
573        return {ES.information: {ES.observation: dcobs, ES.index: dcindex}}
574
575    @staticmethod
576    def _info_box(idx, **option):
577        ''' return box informations's'''
578        if idx.typevalue == ES.dat_clsName:
579            return DatationValue.boundingBox(idx.codec)
580        if idx.typevalue == ES.loc_clsName and not option["geojson"]:
581            return LocationValue.boundingBox(idx.codec)
582        if idx.typevalue == ES.loc_clsName and option["geojson"]:
583            return LocationValue.Box(LocationValue.boundingBox(idx.codec)).__geo_interface__
584        if idx.typevalue == ES.prp_clsName:
585            return PropertyValue.boundingBox(idx.codec)
586        return None
587
588
589class ObsError(Exception):
590    '''Observation exception'''
class Observation(observation.dataset.Dataset):
 48class Observation(Dataset):
 49    """
 50    An `Observation` is derived from `observation.Dataset` object.
 51
 52    *Additional attributes (for @property see methods)* :
 53
 54    - **name** : textual description
 55    - **param** : namedValue dictionnary (external data)
 56
 57    The methods defined in this class (included inherited) are :
 58
 59    *constructor (@classmethod))*
 60
 61    - `Observation.dic`
 62    - `Observation.std`
 63    - `python.observation.dataset.Dataset.obj`
 64    - `Observation.from_obj`
 65    - `python.observation.dataset.Dataset.from_file`
 66
 67    *dynamic value (getters @property)*
 68
 69    - `Observation.bounds`
 70    - `Observation.id`
 71    - `Observation.jsonFeature`
 72    - `Observation.setLocation`
 73    - `Observation.setDatation`
 74    - `Observation.setProperty`
 75    - `Observation.setResult`
 76
 77    *dynamic value inherited (getters @property)*
 78
 79    - `python.observation.dataset.Dataset.extidx`
 80    - `python.observation.dataset.Dataset.extidxext`
 81    - `python.observation.dataset.Dataset.idxname`
 82    - `python.observation.dataset.Dataset.idxlen`
 83    - `python.observation.dataset.Dataset.iidx`
 84    - `python.observation.dataset.Dataset.keys`
 85    - `python.observation.dataset.Dataset.lenindex`
 86    - `python.observation.dataset.Dataset.lenidx`
 87    - `python.observation.dataset.Dataset.lidx`
 88    - `python.observation.dataset.Dataset.lidxrow`
 89    - `python.observation.dataset.Dataset.lvar`
 90    - `python.observation.dataset.Dataset.lvarrow`
 91    - `python.observation.dataset.Dataset.lname`
 92    - `python.observation.dataset.Dataset.lunicname`
 93    - `python.observation.dataset.Dataset.lunicrow`
 94    - `python.observation.dataset.Dataset.setidx`
 95
 96    *global value (getters @property)*
 97
 98    - `python.observation.dataset.Dataset.complete`
 99    - `python.observation.dataset.Dataset.consistent`
100    - `python.observation.dataset.Dataset.dimension`
101    - `python.observation.dataset.Dataset.lencomplete`
102    - `python.observation.dataset.Dataset.primary`
103    - `python.observation.dataset.Dataset.zip`
104
105    *selecting - infos methods*
106
107    - `python.observation.dataset.Dataset.couplingmatrix`
108    - `python.observation.dataset.Dataset.idxrecord`
109    - `python.observation.dataset.Dataset.indexinfos`
110    - `python.observation.dataset.Dataset.indicator`
111    - `python.observation.dataset.Dataset.iscanonorder`
112    - `python.observation.dataset.Dataset.isinrecord`
113    - `python.observation.dataset.Dataset.keytoval`
114    - `python.observation.dataset.Dataset.loc`
115    - `python.observation.dataset.Dataset.nindex`
116    - `python.observation.dataset.Dataset.record`
117    - `python.observation.dataset.Dataset.recidx`
118    - `python.observation.dataset.Dataset.recvar`
119    - `python.observation.dataset.Dataset.valtokey`
120
121    *add - update methods*
122
123    - `python.observation.dataset.Dataset.add`
124    - `python.observation.dataset.Dataset.addindex`
125    - `python.observation.dataset.Dataset.append`
126    - `Observation.appendObs`
127    - `python.observation.dataset.Dataset.delindex`
128    - `python.observation.dataset.Dataset.delrecord`
129    - `python.observation.dataset.Dataset.renameindex`
130    - `python.observation.dataset.Dataset.setname`
131    - `python.observation.dataset.Dataset.updateindex`
132
133    *structure management - methods*
134
135    - `python.observation.dataset.Dataset.applyfilter`
136    - `python.observation.dataset.Dataset.coupling`
137    - `python.observation.dataset.Dataset.full`
138    - `python.observation.dataset.Dataset.getduplicates`
139    - `python.observation.dataset.Dataset.merge`
140    - `python.observation.dataset.Dataset.reindex`
141    - `python.observation.dataset.Dataset.reorder`
142    - `python.observation.dataset.Dataset.setfilter`
143    - `python.observation.dataset.Dataset.sort`
144    - `python.observation.dataset.Dataset.swapindex`
145    - `python.observation.dataset.Dataset.setcanonorder`
146    - `python.observation.dataset.Dataset.tostdcodec`
147
148    *exports methods*
149
150    - `Observation.choropleth`
151    - `python.observation.dataset.Dataset.json`
152    - `python.observation.dataset.Dataset.plot`
153    - `python.observation.dataset.Dataset.to_csv`
154    - `python.observation.dataset.Dataset.to_file`
155    - `Observation.to_obj`
156    - `Observation.to_xarray`
157    - `python.observation.dataset.Dataset.to_dataframe`
158    - `python.observation.dataset.Dataset.view`
159    - `python.observation.dataset.Dataset.vlist`
160    - `python.observation.dataset.Dataset.voxel`
161    """
162
163# %% constructor
164    def __init__(self, listidx=None, name=None, param=None, reindex=True):
165        '''Observation constructor
166
167        *Parameters*
168
169        - **listidx**  : object (default None) - list of Field data or Dataset or Observation
170        - **name**     : string (default None) - Obs name
171        - **param**    : dict (default None) - Dict with parameter data or user's data'''
172
173        if isinstance(listidx, Observation):
174            self.lindex = [copy(idx) for idx in listidx.lindex]
175            if not listidx.param is None:
176                self.param = dict(listidx.param.items())
177            else:
178                self.param = param
179            self.name = listidx.name
180            self.analysis = Analysis(self)
181            return
182
183        if isinstance(listidx, Dataset):
184            self.lindex = [copy(idx) for idx in listidx.lindex]
185            self.param = param
186            self.name = name
187            self.analysis = Analysis(self)
188            return
189
190        if not listidx:
191            Dataset.__init__(self)
192        else:
193            Dataset.__init__(self, listidx=listidx, reindex=reindex)
194        self.name = name
195        self.param = param
196        return
197
198    @classmethod
199    def dic(cls, idxdic=None, typevalue=ES.def_clsName, name=None, param=None):
200        '''
201        Observation constructor (external dictionnary).
202
203        *Parameters*
204
205        - **idxdic** : dict (default None) - dict of Field element (Field name :
206        list of Field values)
207        - **typevalue** : str (default ES.def_clsName) - default value class (None or NamedValue)
208        - **var** :  int (default None) - row of the variable
209        - **name**     : string (default None) - Observation name
210        - **param**    : dict (default None) - Dict with parameter data or user's data'''
211        listidx = Dataset.dic(idxdic, typevalue=typevalue)
212        return cls(listidx=listidx, name=name, param=param)
213
214    @classmethod
215    def std(cls, result=None, datation=None, location=None, property=None,
216            name=None, param=None, typevalue=ES.def_clsName):
217        '''
218        Generate an Observation Object with standard indexes
219
220        *Parameters*
221
222        - **datation** : compatible Field (default None) - index for DatationValue
223        - **location** : compatible Field (default None) - index for LocationValue
224        - **property** : compatible Field (default None) - index for PropertyValue
225        - **result  ** : compatible Field (default None) - index for Variable(NamedValue)
226        - **name**     : string (default None) - Observation name
227        - **param**    : dict (default None) - Dict with parameter data or user's data'''
228        idxdic = {}
229        length = 0
230        std_val = (result, datation, location, property)
231        es_val = (ES.res_classES, ES.dat_classES,
232                  ES.loc_classES, ES.prp_classES)
233        for std, esv in zip(std_val, es_val):
234            value = []
235            if not std is None and isinstance(std, list):
236                value = std
237            elif not std is None and not isinstance(std, list):
238                value = [std]
239            length = max(length, len(value))
240            idxdic[esv] = value
241        for item in idxdic.items():
242            if len(item[1]) == 1:
243                idxdic[item[0]] = item[1] * length
244        return cls.dic(idxdic=idxdic, typevalue=typevalue, name=name, param=param)
245
246    @classmethod
247    def from_obj(cls, bs=None, reindex=True, context=True):
248        '''
249        Generate an Observation Object from a bytes, string or dic value
250
251        *Parameters*
252
253        - **bs** : bytes, string or dict data to convert
254        - **reindex** : boolean (default True) - if True, default codec for each Field
255        - **context** : boolean (default True) - if False, only codec and keys are included'''
256        if not bs:
257            bs = {}
258        if isinstance(bs, bytes):
259            dic = cbor2.loads(bs)
260        elif isinstance(bs, str):
261            dic = json.loads(bs, object_hook=CborDecoder().codecbor)
262        elif isinstance(bs, dict):
263            dic = bs
264        else:
265            raise ObsError("the type of parameter is not available")
266
267        param = None
268        if ES.param in dic:
269            param = dic[ES.param]
270        if param and not isinstance(param, dict):
271            raise ObsError('param is not a dict')
272
273        name = None
274        if ES.name in dic:
275            name = dic[ES.name]
276        if name and not isinstance(name, str):
277            raise ObsError('name is not a str')
278
279        data = None
280        if ES.data in dic:
281            data = dic[ES.data]
282        if data and not isinstance(data, (list, dict)):
283            raise ObsError('data is not a list and not a dict')
284
285        return cls(listidx=Dataset.obj(data, reindex=reindex, context=context),
286                   name=name, param=param)
287
288# %% special
289    def __copy__(self):
290        ''' Copy all the data '''
291        return Observation(self)
292
293    def __str__(self):
294        '''return string format'''
295        stro = ''
296        if self.name:
297            stro = ES.name + ': ' + self.name + '\n'
298        stri = Dataset.__str__(self)
299        if not stri == '':
300            stro += ES.data + ':\n' + stri
301        if self.param:
302            stro += ES.param + ':\n    ' + json.dumps(self.param) + '\n'
303        return stro
304
305    def __hash__(self):
306        '''return sum of all hash(Field)'''
307        return hash(json.dumps(self.param)) + hash(self.name) + Dataset.__hash__(self)
308
309# %% properties
310    @property
311    def bounds(self):
312        '''
313        **list of `observation.esvalue` (@property)** : `observation.esvalue`
314        bounding box for each axis.'''
315        bound = [None, None, None]
316        if self.setDatation:
317            bound[0] = ESValue.boundingBox(self.setDatation).bounds
318        if self.setLocation:
319            bound[1] = ESValue.boundingBox(self.setLocation).bounds
320        if self.setProperty:
321            bound[2] = ESValue.boundingBox(self.setProperty).bounds
322        return bound
323
324    @property
325    def __geo_interface__(self):
326        '''**dict (@property)** : return the union of Location geometry (see shapely)'''
327        codecgeo = self.nindex('location').codec
328        if len(codecgeo) == 0:
329            return ""
330        if len(codecgeo) == 1:
331            return codecgeo[0].value.__geo_interface__
332        collec = codecgeo[0].value
333        for loc in codecgeo[1:]:
334            collec = collec.union(loc.value)
335        return collec.__geo_interface__
336
337    @property
338    def id(self):
339        '''**integer (@property)** : hash value (unique)'''
340        return hash(self)
341
342    @property
343    def jsonFeature(self):
344        '''**string (@property)** : "FeatureCollection" with Location geometry'''
345        if self.setLocation:
346            geo = self.__geo_interface__
347            if geo['type'][:5] == 'Multi':
348                typ = geo['type'][5:]
349                lis = [{"type":  typ, "coordinates": geo['coordinates'][i]}
350                       for i in range(len(geo['coordinates']))]
351            elif geo['type'] in ['Point', 'Polygon']:
352                lis = [geo]
353            elif geo['type'] == 'GeometryCollection':
354                lis = geo['geometries']
355            fea = [{"type": "Feature", "id": i, "geometry": lis[i]}
356                   for i in range(len(lis))]
357            return json.dumps({"type": "FeatureCollection", "features": fea},
358                              cls=ESValueEncoder)
359        return ''
360
361    @property
362    def setDatation(self):
363        '''**list (@property)** : list of codec values in the datation index'''
364        if self.nindex(ES.dat_classES):
365            return self.nindex(ES.dat_classES).codec
366        return None
367
368    @property
369    def setLocation(self):
370        '''**list (@property)** : list of codec values in the location index'''
371        if self.nindex(ES.loc_classES):
372            return self.nindex(ES.loc_classES).codec
373        return None
374
375    @property
376    def setProperty(self):
377        '''**list (@property)** : list of codec values in the property index'''
378        if self.nindex(ES.prp_classES):
379            return self.nindex(ES.prp_classES).codec
380        return None
381
382    @property
383    def setResult(self):
384        '''
385        **list (@property)** : list of codec values in the result index'''
386        if self.nindex(ES.res_classES):
387            return self.nindex(ES.res_classES).codec
388        return None
389
390# %% methods
391    def appendObs(self, obs, unique=False, fillvalue='-'):
392        '''
393        Add an `Observation` as a new Result `observation.esvalue` with bounding
394        box for the Index `observation.esvalue`
395
396        *Parameters*
397
398        - **obs** : Observation object
399        - **fillvalue** : object value used for default value
400
401        *Returns*
402
403        - **int** : last index in the `Observation`'''
404        lname = self.lname
405        record = [fillvalue] * len(lname)
406        if ES.dat_classES in lname:
407            record[lname.index(ES.dat_classES)] = DatationValue.Box(
408                obs.bounds[0])
409        if ES.loc_classES in lname:
410            record[lname.index(ES.loc_classES)] = LocationValue.Box(
411                obs.bounds[1])
412        if ES.prp_classES in lname:
413            record[lname.index(ES.prp_classES)] = PropertyValue.Box(
414                obs.bounds[2])
415        if ES.res_classES in lname:
416            record[lname.index(ES.res_classES)] = ExternValue(obs)
417        return self.append(record, unique=unique)
418
419    def choropleth(self, name="choropleth", line=True):
420        '''
421        Display `Observation` on a folium.Map (only with dimension=1)
422
423        - **name** : String, optionnal (default 'choropleth') - Name of the choropleth
424        - **line** : Boolean, optionnal (default True) - Line between recods if True
425
426        *Returns* : None'''
427        primary = self.primary
428        if self.dimension == 1:
429            mapf = folium.Map(
430                location=self.setLocation[0].coorInv, zoom_start=6)
431            folium.Choropleth(
432                geo_data=self.jsonFeature,
433                name=self.name,
434                data=self.to_xarray(
435                    numeric=True, coord=True).to_dataframe(name='obs'),
436                key_on="feature.id",
437                columns=[self.idxname[primary[0]] + '_row', 'obs'],
438                fill_color="OrRd",
439                fill_opacity=0.7,
440                line_opacity=0.4,
441                line_weight=2,
442                legend_name=name
443            ).add_to(mapf)
444            if line:
445                folium.PolyLine(
446                    util.funclist(self.nindex('location'),
447                                  LocationValue.vPointInv)
448                ).add_to(mapf)
449            folium.LayerControl().add_to(mapf)
450            return mapf
451        return None
452
453    def to_obj(self, **kwargs):
454        '''Return a formatted object (json string, cbor bytes or json dict).
455
456        *Parameters (kwargs)*
457
458        - **encoded** : boolean (default False) - choice for return format
459        (string/bytes if True, dict else)
460        - **encode_format**  : string (default 'json')- choice for return format (json, cbor)
461        - **codif** : dict (default ES.codeb). Numerical value for string in CBOR encoder
462        - **modecodec** : string (default 'optimize') - if 'full', each index is with a full codec
463        if 'default' each index has keys, if 'optimize' keys are optimized,
464        if 'dict' dict format is used, if 'nokeys' keys are absent, if 'ndjson' 
465        a list of ndjson elements is created
466        - **name** : boolean (default False) - if False, default index name are not included
467        - **fullvar** : boolean (default True) - if True and modecodec='optimize,
468        variable index is with a full codec
469        - **geojson** : boolean (default False) - geojson for LocationValue if True
470
471        - **json_param**     : Boolean - include Obs Param
472        - **json_info**      : Boolean - include all infos
473        - **json_info_detail**: Boolean - include the other infos
474
475        *Returns* : string, bytes or dict'''
476        option = {'modecodec': 'optimize', 'encoded': False,
477                  'encode_format': 'json', 'codif': ES.codeb, 'name': False,
478                  'json_param': False, 'json_info': False, 'json_info_detail': False,
479                  'geojson': False, 'fullvar': True} | kwargs
480        if option['modecodec'] == 'ndjson':
481            lisobs = {ES.id: self.id}
482            if self.param:
483                lisobs[ES.param] = self.param
484            if self.name:
485                lisobs[ES.name] = self.name
486            return [lisobs] + Dataset.to_obj(self, modecodec='ndjson', id=self.id)
487        option2 = option | {'encoded': False, 'encode_format': 'json'}
488        dic = {ES.type: ES.obs_classES}
489
490        if self.name:
491            dic[ES.obs_name] = self.name
492        if self.param:
493            dic[ES.obs_param] = self.param
494        dic[ES.obs_data] = Dataset.to_obj(self, **option2)
495        if option["json_param"] and self.param:
496            dic[ES.obs_param] = self.param
497        dic |= self._info(**option)
498        if option['codif'] and option['encode_format'] != 'cbor':
499            js2 = {}
500            for key, val in dic.items():
501                if key in option['codif']:
502                    js2[option['codif'][key]] = val
503                else:
504                    js2[key] = val
505        else:
506            js2 = dic
507
508        if option['encoded'] and option['encode_format'] == 'json':
509            return json.dumps(js2, cls=FieldEncoder)
510        if option['encoded'] and option['encode_format'] == 'cbor':
511            return cbor2.dumps(js2, datetime_as_timestamp=True,
512                               timezone=datetime.timezone.utc, canonical=True)
513        return dic
514
515    def to_xarray(self, info=False, idxname=None, varname=None, fillvalue='?',
516                  fillextern=True, lisfunc=None, numeric=False, npdtype=None,
517                  **kwargs):
518        '''
519        Complete the Observation and generate a Xarray DataArray with the dimension define by idx.
520
521        *Parameters*
522
523        - **info** : boolean (default False) - if True, add _dict attributes to attrs Xarray
524        - **idxname** : list (default none) - list of idx to be completed. If None,
525        self.primary is used.
526        - **varname** : string (default none) - Name of the variable to use. If None,
527        first lvarname is used.
528        - **fillvalue** : object (default '?') - value used for the new extval
529        - **fillextern** : boolean(default True) - if True, fillvalue is converted to typevalue
530        - **lisfunc** : function (default none) - list of function to apply to indexes before export
531        - **numeric** : Boolean (default False) - Generate a numeric DataArray.Values.
532        - **npdtype** : string (default None) - numpy dtype for the DataArray ('object' if None)
533        - **kwargs** : parameter for lisfunc
534
535        *Returns* : DataArray '''
536        return Dataset.to_xarray(self, info=info, idxname=idxname, varname=varname,
537                               fillvalue=fillvalue, fillextern=fillextern,
538                               lisfunc=lisfunc, name=self.name, numeric=numeric,
539                               npdtype=npdtype, attrs=self.param, **kwargs)
540# %% internal
541
542    def _info(self, **kwargs):
543        ''' Create json dict with info datas
544
545        *Parameters*
546
547        - **json_info** : boolean (default False) - if True, add main information
548        about Observation and Field
549        - **json_info_detail** : boolean (default False) - if True, add complemantary
550        information about Field
551        '''
552        option = {"json_info": False, "json_info_detail": False} | kwargs
553        dcobs = {}
554        dcindex = {}
555        if not option['json_info']:
556            return dcobs
557        dcobs[ES.name] = self.name
558        dcobs[ES.id] = self.id
559        dcobs[ES.length] = len(self)
560        dcobs[ES.lenindex] = self.lenindex
561        dcobs[ES.complete] = self.complete
562        dcobs[ES.dimension] = self.dimension
563        if option['json_info_detail']:
564            infos = self.indexinfos()
565        for ind, idx in enumerate(self.lidx):
566            dcidx = {}
567            dcidx[ES.num] = ind
568            dcidx[ES.typevalue] = idx.typevalue
569            dcidx[ES.lencodec] = len(idx.codec)
570            dcidx[ES.box] = Observation._info_box(idx, **option)
571            if option['json_info_detail']:
572                dcidx |= infos[ind]
573            dcindex[idx.name] = dcidx
574        return {ES.information: {ES.observation: dcobs, ES.index: dcindex}}
575
576    @staticmethod
577    def _info_box(idx, **option):
578        ''' return box informations's'''
579        if idx.typevalue == ES.dat_clsName:
580            return DatationValue.boundingBox(idx.codec)
581        if idx.typevalue == ES.loc_clsName and not option["geojson"]:
582            return LocationValue.boundingBox(idx.codec)
583        if idx.typevalue == ES.loc_clsName and option["geojson"]:
584            return LocationValue.Box(LocationValue.boundingBox(idx.codec)).__geo_interface__
585        if idx.typevalue == ES.prp_clsName:
586            return PropertyValue.boundingBox(idx.codec)
587        return None

An Observation is derived from observation.Dataset object.

Additional attributes (for @property see methods) :

  • name : textual description
  • param : namedValue dictionnary (external data)

The methods defined in this class (included inherited) are :

constructor (@classmethod))

dynamic value (getters @property)

dynamic value inherited (getters @property)

global value (getters @property)

selecting - infos methods

add - update methods

structure management - methods

exports methods

Observation(listidx=None, name=None, param=None, reindex=True)
164    def __init__(self, listidx=None, name=None, param=None, reindex=True):
165        '''Observation constructor
166
167        *Parameters*
168
169        - **listidx**  : object (default None) - list of Field data or Dataset or Observation
170        - **name**     : string (default None) - Obs name
171        - **param**    : dict (default None) - Dict with parameter data or user's data'''
172
173        if isinstance(listidx, Observation):
174            self.lindex = [copy(idx) for idx in listidx.lindex]
175            if not listidx.param is None:
176                self.param = dict(listidx.param.items())
177            else:
178                self.param = param
179            self.name = listidx.name
180            self.analysis = Analysis(self)
181            return
182
183        if isinstance(listidx, Dataset):
184            self.lindex = [copy(idx) for idx in listidx.lindex]
185            self.param = param
186            self.name = name
187            self.analysis = Analysis(self)
188            return
189
190        if not listidx:
191            Dataset.__init__(self)
192        else:
193            Dataset.__init__(self, listidx=listidx, reindex=reindex)
194        self.name = name
195        self.param = param
196        return

Observation constructor

Parameters

  • listidx : object (default None) - list of Field data or Dataset or Observation
  • name : string (default None) - Obs name
  • param : dict (default None) - Dict with parameter data or user's data
name
param
@classmethod
def dic(cls, idxdic=None, typevalue=None, name=None, param=None):
198    @classmethod
199    def dic(cls, idxdic=None, typevalue=ES.def_clsName, name=None, param=None):
200        '''
201        Observation constructor (external dictionnary).
202
203        *Parameters*
204
205        - **idxdic** : dict (default None) - dict of Field element (Field name :
206        list of Field values)
207        - **typevalue** : str (default ES.def_clsName) - default value class (None or NamedValue)
208        - **var** :  int (default None) - row of the variable
209        - **name**     : string (default None) - Observation name
210        - **param**    : dict (default None) - Dict with parameter data or user's data'''
211        listidx = Dataset.dic(idxdic, typevalue=typevalue)
212        return cls(listidx=listidx, name=name, param=param)

Observation constructor (external dictionnary).

Parameters

  • idxdic : dict (default None) - dict of Field element (Field name : list of Field values)
  • typevalue : str (default ES.def_clsName) - default value class (None or NamedValue)
  • var : int (default None) - row of the variable
  • name : string (default None) - Observation name
  • param : dict (default None) - Dict with parameter data or user's data
@classmethod
def std( cls, result=None, datation=None, location=None, property=None, name=None, param=None, typevalue=None):
214    @classmethod
215    def std(cls, result=None, datation=None, location=None, property=None,
216            name=None, param=None, typevalue=ES.def_clsName):
217        '''
218        Generate an Observation Object with standard indexes
219
220        *Parameters*
221
222        - **datation** : compatible Field (default None) - index for DatationValue
223        - **location** : compatible Field (default None) - index for LocationValue
224        - **property** : compatible Field (default None) - index for PropertyValue
225        - **result  ** : compatible Field (default None) - index for Variable(NamedValue)
226        - **name**     : string (default None) - Observation name
227        - **param**    : dict (default None) - Dict with parameter data or user's data'''
228        idxdic = {}
229        length = 0
230        std_val = (result, datation, location, property)
231        es_val = (ES.res_classES, ES.dat_classES,
232                  ES.loc_classES, ES.prp_classES)
233        for std, esv in zip(std_val, es_val):
234            value = []
235            if not std is None and isinstance(std, list):
236                value = std
237            elif not std is None and not isinstance(std, list):
238                value = [std]
239            length = max(length, len(value))
240            idxdic[esv] = value
241        for item in idxdic.items():
242            if len(item[1]) == 1:
243                idxdic[item[0]] = item[1] * length
244        return cls.dic(idxdic=idxdic, typevalue=typevalue, name=name, param=param)

Generate an Observation Object with standard indexes

Parameters

  • datation : compatible Field (default None) - index for DatationValue
  • location : compatible Field (default None) - index for LocationValue
  • property : compatible Field (default None) - index for PropertyValue
  • *result * : compatible Field (default None) - index for Variable(NamedValue)
  • name : string (default None) - Observation name
  • param : dict (default None) - Dict with parameter data or user's data
@classmethod
def from_obj(cls, bs=None, reindex=True, context=True):
246    @classmethod
247    def from_obj(cls, bs=None, reindex=True, context=True):
248        '''
249        Generate an Observation Object from a bytes, string or dic value
250
251        *Parameters*
252
253        - **bs** : bytes, string or dict data to convert
254        - **reindex** : boolean (default True) - if True, default codec for each Field
255        - **context** : boolean (default True) - if False, only codec and keys are included'''
256        if not bs:
257            bs = {}
258        if isinstance(bs, bytes):
259            dic = cbor2.loads(bs)
260        elif isinstance(bs, str):
261            dic = json.loads(bs, object_hook=CborDecoder().codecbor)
262        elif isinstance(bs, dict):
263            dic = bs
264        else:
265            raise ObsError("the type of parameter is not available")
266
267        param = None
268        if ES.param in dic:
269            param = dic[ES.param]
270        if param and not isinstance(param, dict):
271            raise ObsError('param is not a dict')
272
273        name = None
274        if ES.name in dic:
275            name = dic[ES.name]
276        if name and not isinstance(name, str):
277            raise ObsError('name is not a str')
278
279        data = None
280        if ES.data in dic:
281            data = dic[ES.data]
282        if data and not isinstance(data, (list, dict)):
283            raise ObsError('data is not a list and not a dict')
284
285        return cls(listidx=Dataset.obj(data, reindex=reindex, context=context),
286                   name=name, param=param)

Generate an Observation Object from a bytes, string or dic value

Parameters

  • bs : bytes, string or dict data to convert
  • reindex : boolean (default True) - if True, default codec for each Field
  • context : boolean (default True) - if False, only codec and keys are included
bounds

list of observation.esvalue (@property) : observation.esvalue bounding box for each axis.

id

integer (@property) : hash value (unique)

jsonFeature

string (@property) : "FeatureCollection" with Location geometry

setDatation

list (@property) : list of codec values in the datation index

setLocation

list (@property) : list of codec values in the location index

setProperty

list (@property) : list of codec values in the property index

setResult

list (@property) : list of codec values in the result index

def appendObs(self, obs, unique=False, fillvalue='-'):
391    def appendObs(self, obs, unique=False, fillvalue='-'):
392        '''
393        Add an `Observation` as a new Result `observation.esvalue` with bounding
394        box for the Index `observation.esvalue`
395
396        *Parameters*
397
398        - **obs** : Observation object
399        - **fillvalue** : object value used for default value
400
401        *Returns*
402
403        - **int** : last index in the `Observation`'''
404        lname = self.lname
405        record = [fillvalue] * len(lname)
406        if ES.dat_classES in lname:
407            record[lname.index(ES.dat_classES)] = DatationValue.Box(
408                obs.bounds[0])
409        if ES.loc_classES in lname:
410            record[lname.index(ES.loc_classES)] = LocationValue.Box(
411                obs.bounds[1])
412        if ES.prp_classES in lname:
413            record[lname.index(ES.prp_classES)] = PropertyValue.Box(
414                obs.bounds[2])
415        if ES.res_classES in lname:
416            record[lname.index(ES.res_classES)] = ExternValue(obs)
417        return self.append(record, unique=unique)

Add an Observation as a new Result observation.esvalue with bounding box for the Index observation.esvalue

Parameters

  • obs : Observation object
  • fillvalue : object value used for default value

Returns

def choropleth(self, name='choropleth', line=True):
419    def choropleth(self, name="choropleth", line=True):
420        '''
421        Display `Observation` on a folium.Map (only with dimension=1)
422
423        - **name** : String, optionnal (default 'choropleth') - Name of the choropleth
424        - **line** : Boolean, optionnal (default True) - Line between recods if True
425
426        *Returns* : None'''
427        primary = self.primary
428        if self.dimension == 1:
429            mapf = folium.Map(
430                location=self.setLocation[0].coorInv, zoom_start=6)
431            folium.Choropleth(
432                geo_data=self.jsonFeature,
433                name=self.name,
434                data=self.to_xarray(
435                    numeric=True, coord=True).to_dataframe(name='obs'),
436                key_on="feature.id",
437                columns=[self.idxname[primary[0]] + '_row', 'obs'],
438                fill_color="OrRd",
439                fill_opacity=0.7,
440                line_opacity=0.4,
441                line_weight=2,
442                legend_name=name
443            ).add_to(mapf)
444            if line:
445                folium.PolyLine(
446                    util.funclist(self.nindex('location'),
447                                  LocationValue.vPointInv)
448                ).add_to(mapf)
449            folium.LayerControl().add_to(mapf)
450            return mapf
451        return None

Display Observation on a folium.Map (only with dimension=1)

  • name : String, optionnal (default 'choropleth') - Name of the choropleth
  • line : Boolean, optionnal (default True) - Line between recods if True

Returns : None

def to_obj(self, **kwargs):
453    def to_obj(self, **kwargs):
454        '''Return a formatted object (json string, cbor bytes or json dict).
455
456        *Parameters (kwargs)*
457
458        - **encoded** : boolean (default False) - choice for return format
459        (string/bytes if True, dict else)
460        - **encode_format**  : string (default 'json')- choice for return format (json, cbor)
461        - **codif** : dict (default ES.codeb). Numerical value for string in CBOR encoder
462        - **modecodec** : string (default 'optimize') - if 'full', each index is with a full codec
463        if 'default' each index has keys, if 'optimize' keys are optimized,
464        if 'dict' dict format is used, if 'nokeys' keys are absent, if 'ndjson' 
465        a list of ndjson elements is created
466        - **name** : boolean (default False) - if False, default index name are not included
467        - **fullvar** : boolean (default True) - if True and modecodec='optimize,
468        variable index is with a full codec
469        - **geojson** : boolean (default False) - geojson for LocationValue if True
470
471        - **json_param**     : Boolean - include Obs Param
472        - **json_info**      : Boolean - include all infos
473        - **json_info_detail**: Boolean - include the other infos
474
475        *Returns* : string, bytes or dict'''
476        option = {'modecodec': 'optimize', 'encoded': False,
477                  'encode_format': 'json', 'codif': ES.codeb, 'name': False,
478                  'json_param': False, 'json_info': False, 'json_info_detail': False,
479                  'geojson': False, 'fullvar': True} | kwargs
480        if option['modecodec'] == 'ndjson':
481            lisobs = {ES.id: self.id}
482            if self.param:
483                lisobs[ES.param] = self.param
484            if self.name:
485                lisobs[ES.name] = self.name
486            return [lisobs] + Dataset.to_obj(self, modecodec='ndjson', id=self.id)
487        option2 = option | {'encoded': False, 'encode_format': 'json'}
488        dic = {ES.type: ES.obs_classES}
489
490        if self.name:
491            dic[ES.obs_name] = self.name
492        if self.param:
493            dic[ES.obs_param] = self.param
494        dic[ES.obs_data] = Dataset.to_obj(self, **option2)
495        if option["json_param"] and self.param:
496            dic[ES.obs_param] = self.param
497        dic |= self._info(**option)
498        if option['codif'] and option['encode_format'] != 'cbor':
499            js2 = {}
500            for key, val in dic.items():
501                if key in option['codif']:
502                    js2[option['codif'][key]] = val
503                else:
504                    js2[key] = val
505        else:
506            js2 = dic
507
508        if option['encoded'] and option['encode_format'] == 'json':
509            return json.dumps(js2, cls=FieldEncoder)
510        if option['encoded'] and option['encode_format'] == 'cbor':
511            return cbor2.dumps(js2, datetime_as_timestamp=True,
512                               timezone=datetime.timezone.utc, canonical=True)
513        return dic

Return a formatted object (json string, cbor bytes or json dict).

Parameters (kwargs)

  • encoded : boolean (default False) - choice for return format (string/bytes if True, dict else)
  • encode_format : string (default 'json')- choice for return format (json, cbor)
  • codif : dict (default ES.codeb). Numerical value for string in CBOR encoder
  • modecodec : string (default 'optimize') - if 'full', each index is with a full codec if 'default' each index has keys, if 'optimize' keys are optimized, if 'dict' dict format is used, if 'nokeys' keys are absent, if 'ndjson' a list of ndjson elements is created
  • name : boolean (default False) - if False, default index name are not included
  • fullvar : boolean (default True) - if True and modecodec='optimize, variable index is with a full codec
  • geojson : boolean (default False) - geojson for LocationValue if True

  • json_param : Boolean - include Obs Param

  • json_info : Boolean - include all infos
  • json_info_detail: Boolean - include the other infos

Returns : string, bytes or dict

def to_xarray( self, info=False, idxname=None, varname=None, fillvalue='?', fillextern=True, lisfunc=None, numeric=False, npdtype=None, **kwargs):
515    def to_xarray(self, info=False, idxname=None, varname=None, fillvalue='?',
516                  fillextern=True, lisfunc=None, numeric=False, npdtype=None,
517                  **kwargs):
518        '''
519        Complete the Observation and generate a Xarray DataArray with the dimension define by idx.
520
521        *Parameters*
522
523        - **info** : boolean (default False) - if True, add _dict attributes to attrs Xarray
524        - **idxname** : list (default none) - list of idx to be completed. If None,
525        self.primary is used.
526        - **varname** : string (default none) - Name of the variable to use. If None,
527        first lvarname is used.
528        - **fillvalue** : object (default '?') - value used for the new extval
529        - **fillextern** : boolean(default True) - if True, fillvalue is converted to typevalue
530        - **lisfunc** : function (default none) - list of function to apply to indexes before export
531        - **numeric** : Boolean (default False) - Generate a numeric DataArray.Values.
532        - **npdtype** : string (default None) - numpy dtype for the DataArray ('object' if None)
533        - **kwargs** : parameter for lisfunc
534
535        *Returns* : DataArray '''
536        return Dataset.to_xarray(self, info=info, idxname=idxname, varname=varname,
537                               fillvalue=fillvalue, fillextern=fillextern,
538                               lisfunc=lisfunc, name=self.name, numeric=numeric,
539                               npdtype=npdtype, attrs=self.param, **kwargs)

Complete the Observation and generate a Xarray DataArray with the dimension define by idx.

Parameters

  • info : boolean (default False) - if True, add _dict attributes to attrs Xarray
  • idxname : list (default none) - list of idx to be completed. If None, self.primary is used.
  • varname : string (default none) - Name of the variable to use. If None, first lvarname is used.
  • fillvalue : object (default '?') - value used for the new extval
  • fillextern : boolean(default True) - if True, fillvalue is converted to typevalue
  • lisfunc : function (default none) - list of function to apply to indexes before export
  • numeric : Boolean (default False) - Generate a numeric DataArray.Values.
  • npdtype : string (default None) - numpy dtype for the DataArray ('object' if None)
  • kwargs : parameter for lisfunc

Returns : DataArray

Inherited Members
observation.dataset.Dataset
field_class
field
analysis
lindex
from_csv
from_file
ntv
from_ntv
merge
ext
complete
consistent
category
dimension
extidx
extidxext
groups
idxname
idxlen
indexlen
iidx
iindex
keys
lencomplete
lenindex
lenidx
lidx
lisvar
lvar
lvarname
lunicrow
lvarrow
lidxrow
lunicname
lname
primary
primaryname
secondary
secondaryname
setidx
tiindex
zip
observation.dataset_structure.DatasetStructure
add
addindex
append
applyfilter
couplingmatrix
coupling
delrecord
delindex
full
getduplicates
iscanonorder
isinrecord
idxrecord
indexinfos
indicator
keytoval
loc
mix
merging
nindex
orindex
record
recidx
recvar
reindex
renameindex
reorder
setcanonorder
setfilter
setname
sort
swapindex
tostdcodec
tree
updateindex
valtokey
observation.dataset_interface.DatasetInterface
json
plot
to_csv
to_dataframe
to_file
to_ntv
voxel
view
vlist
class ObsError(builtins.Exception):
590class ObsError(Exception):
591    '''Observation exception'''

Observation exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args