55__classification__ = "UNCLASSIFIED"
66__author__ = "Thomas McCullough"
77
8+ from collections import OrderedDict
9+ import json
810import logging
911import os
10- from uuid import uuid4
1112from typing import Optional , Dict , List , Any , Union
12- import json
13-
14- from collections import OrderedDict
13+ from uuid import uuid4
1514
1615from sarpy .geometry .geometry_elements import Jsonable , FeatureCollection , Feature , \
1716 GeometryCollection , GeometryObject , Geometry , basic_assemble_from_collection
@@ -33,7 +32,7 @@ def __init__(self, uid=None, name=None, color=None):
3332 if uid is None :
3433 uid = str (uuid4 ())
3534 if not isinstance (uid , str ):
36- raise TypeError ('uid must be a string' )
35+ raise TypeError ('uid must be a string, got {}' . format ( type ( uid )) )
3736 self ._uid = uid
3837
3938 self .name = name
@@ -60,7 +59,7 @@ def name(self, value):
6059 if value is None or isinstance (value , str ):
6160 self ._name = value
6261 else :
63- raise TypeError ('Got unexpected type for name' )
62+ raise TypeError ('Got unexpected type of {} for name' . format ( type ( value )) )
6463
6564 @property
6665 def color (self ):
@@ -75,7 +74,7 @@ def color(self, value):
7574 if value is None or isinstance (value , str ):
7675 self ._color = value
7776 else :
78- raise TypeError ('Got unexpected type for color' )
77+ raise TypeError ('Got unexpected type of {} for color' . format ( type ( value )) )
7978
8079 @classmethod
8180 def from_dict (cls , the_json ):
@@ -90,10 +89,18 @@ def from_dict(cls, the_json):
9089 -------
9190 GeometryProperties
9291 """
93-
94- typ = the_json ['type' ]
92+
93+ if not isinstance (the_json , dict ):
94+ raise TypeError ('This requires a dict. Got type {}' .format (type (the_json )))
95+
96+ typ = the_json .get ('type' , None ) # prevents key error from being thrown if 'type' isn't in the_json
97+
98+ if typ is None :
99+ raise KeyError ("the json requires the field 'type'" )
100+
95101 if typ != cls ._type :
96- raise ValueError ('GeometryProperties cannot be constructed from {}' .format (the_json ))
102+ raise ValueError ('GeometryProperties cannot be constructed from {}, expecting {}' .format (typ , cls ._type ))
103+
97104 return cls (
98105 uid = the_json .get ('uid' , None ),
99106 name = the_json .get ('name' , None ),
@@ -168,7 +175,7 @@ def name(self, value):
168175 if value is None or isinstance (value , str ):
169176 self ._name = value
170177 else :
171- raise TypeError ('Got unexpected type for name' )
178+ raise TypeError (f 'Got unexpected value of type { type ( value ) } for name' )
172179
173180 @property
174181 def description (self ):
@@ -182,7 +189,7 @@ def description(self, value):
182189 if value is None or isinstance (value , str ):
183190 self ._description = value
184191 else :
185- raise TypeError ('Got unexpected type for description' )
192+ raise TypeError (f 'Got unexpected value of type { type ( value ) } for description' )
186193
187194 @property
188195 def directory (self ):
@@ -198,7 +205,7 @@ def directory(self, value):
198205 return
199206
200207 if not isinstance (value , str ):
201- raise TypeError ('Got unexpected type for directory' )
208+ raise TypeError (f 'Got unexpected value of type { type ( value ) } for directory' )
202209
203210 parts = [entry .strip () for entry in value .split ('/' )]
204211 self ._directory = '/' .join ([entry for entry in parts if entry != '' ])
@@ -218,7 +225,7 @@ def geometry_properties(self, value):
218225 self ._geometry_properties = []
219226 return
220227 if not isinstance (value , list ):
221- raise TypeError ('Got unexpected value for geometry properties' )
228+ raise TypeError (f 'Got unexpected value of type { type ( value ) } for geometry properties' )
222229
223230 self ._geometry_properties = []
224231 for entry in value :
@@ -242,8 +249,9 @@ def add_geometry_property(self, entry):
242249 entry = GeometryProperties .from_dict (entry )
243250
244251 if not isinstance (entry , GeometryProperties ):
245- raise TypeError ('Got entry of unexpected type for geometry properties list' )
246- self ._geometry_properties .append (entry )
252+ raise TypeError (f'Got unexpected value of type { type (entry )} for geometry properties' )
253+
254+ self .geometry_properties .append (entry )
247255
248256 def get_geometry_property (self , item ):
249257 """
@@ -303,7 +311,7 @@ def parameters(self, value):
303311 if value is None or isinstance (value , Jsonable ):
304312 self ._parameters = value
305313 else :
306- raise TypeError ('Got unexpected type for parameters' )
314+ raise TypeError (f 'Got unexpected value of type { type ( value ) } for parameters' )
307315
308316 @classmethod
309317 def from_dict (cls , the_json ):
@@ -319,9 +327,17 @@ def from_dict(cls, the_json):
319327 AnnotationProperties
320328 """
321329
322- typ = the_json ['type' ]
330+ if not isinstance (the_json , dict ):
331+ raise TypeError ('This requires a dict. Got type {}' .format (type (the_json )))
332+
333+ typ = the_json .get ('type' , None ) # prevents key error from being thrown if 'type' isn't in the_json
334+
335+ if typ is None :
336+ raise KeyError ("the json requires the field 'type'" )
337+
323338 if typ != cls ._type :
324- raise ValueError ('AnnotationProperties cannot be constructed from {}' .format (the_json ))
339+ raise ValueError ('AnnotationProperties cannot be constructed from {}, expecting {}' .format (typ , cls ._type ))
340+
325341 return cls (
326342 name = the_json .get ('name' , None ),
327343 description = the_json .get ('description' , None ),
@@ -334,6 +350,7 @@ def to_dict(self, parent_dict=None):
334350 Serialize to json.
335351
336352 Parameters
353+
337354 ----------
338355 parent_dict : None|Dict
339356
@@ -373,6 +390,7 @@ class AnnotationFeature(Feature):
373390 populated with AnnotationProperties instance.
374391 """
375392 _allowed_geometries = None
393+ _type = "AnnotationFeature"
376394
377395 @property
378396 def properties (self ):
@@ -395,7 +413,7 @@ def properties(self, properties):
395413 elif isinstance (properties , dict ):
396414 self ._properties = AnnotationProperties .from_dict (properties )
397415 else :
398- raise TypeError ('Got an unexpected type for properties attribute of class {}' .format (self .__class__ ))
416+ raise TypeError ('Got an unexpected type for properties attribute of type {}' .format (properties .__class__ ))
399417
400418 def get_name (self ):
401419 """
@@ -560,7 +578,7 @@ def _validate_geometry_element(self, geometry):
560578 if geometry is None :
561579 return geometry
562580 if not isinstance (geometry , Geometry ):
563- raise TypeError ('geometry must be an instance of Geometry base class' )
581+ raise TypeError ('geometry must be an instance of Geometry base class. Got {}' . format ( type ( geometry )) )
564582
565583 if self ._allowed_geometries is not None and geometry .__class__ not in self ._allowed_geometries :
566584 raise TypeError ('geometry ({}) is not of one of the allowed types ({})' .format (geometry , self ._allowed_geometries ))
@@ -577,30 +595,35 @@ def add_geometry_element(self, geometry, properties=None):
577595 """
578596
579597 if not isinstance (geometry , GeometryObject ):
580- raise TypeError ('geometry must be a GeometryObject instance' )
598+ raise TypeError ('geometry must be a GeometryObject instance. Got {}' . format ( type ( geometry )) )
581599
582600 if properties is None :
583601 properties = GeometryProperties ()
602+
584603 if not isinstance (properties , GeometryProperties ):
585- raise TypeError ('properties must be a GeometryProperties instance' )
604+ raise TypeError ('properties must be a GeometryProperties instance. Got {}' .format (type (properties )))
605+
586606 if self .properties is None :
587607 self .properties = AnnotationProperties ()
608+
609+ if self .geometry is None :
610+ self .geometry = GeometryCollection ()
588611
589612 # handle the geometry
590- self ._geometry = self ._validate_geometry_element (
613+ self .geometry = self ._validate_geometry_element (
591614 basic_assemble_from_collection (self .geometry , geometry ))
592615
593- # add the geometry property
594- self .properties .add_geometry_property (properties )
595-
596- # check that they are in sync
616+ # check that they are in sync and warns before adding the geometry element
597617 if len (self .properties .geometry_properties ) != self .geometry_count :
598618 logger .warning (
599619 'There are {} geometry elements defined\n \t '
600620 'and {} geometry properties populated. '
601621 'This is likely to cause problems.' .format (
602622 self .geometry_count , len (self .properties .geometry_properties )))
603623
624+ # add the geometry property
625+ self .properties .add_geometry_property (properties )
626+
604627 def remove_geometry_element (self , item ):
605628 """
606629 Remove the geometry element at the given index
@@ -623,12 +646,33 @@ def remove_geometry_element(self, item):
623646 del self .geometry .collection [index ]
624647 del self .properties .geometry_properties [index ]
625648
649+ @classmethod
650+ def from_dict (cls , the_json ):
651+ if not isinstance (the_json , dict ):
652+ raise TypeError ('This requires a dict. Got type {}' .format (type (the_json )))
653+
654+ typ = the_json .get ('type' , None ) # prevents key error from being thrown if 'type' isn't in the_json
655+
656+ if typ is None :
657+ raise KeyError ("the json requires the field 'type'" )
658+
659+ if typ != cls ._type :
660+ raise ValueError ('AnnotationFeature cannot be constructed from {}, expecting {}' .format (typ , cls ._type ))
661+
662+ the_id = the_json .get ('id' , None )
663+ if the_id is None :
664+ the_id = the_json .get ('uid' , None )
665+
666+ return cls (uid = the_id ,
667+ geometry = the_json .get ('geometry' , None ),
668+ properties = the_json .get ('properties' , None ))
626669
627670class AnnotationCollection (FeatureCollection ):
628671 """
629672 An extension of the FeatureCollection class which has the features are
630673 AnnotationFeature instances.
631674 """
675+ _type = "AnnotationCollection"
632676
633677 @property
634678 def features (self ):
@@ -685,6 +729,27 @@ def __getitem__(self, item):
685729 index = self ._feature_dict [item ]
686730 return self ._features [index ]
687731 return self ._features [item ]
732+
733+ @classmethod
734+ def from_dict (cls , the_json ):
735+ if not isinstance (the_json , dict ):
736+ raise TypeError ('This requires a dict. Got type {}' .format (type (the_json )))
737+
738+ typ = the_json .get ('type' , None ) # prevents key error from being thrown if 'type' isn't in the_json
739+
740+ if typ is None :
741+ raise KeyError ("the json requires the field 'type'" )
742+
743+ if typ != cls ._type :
744+ raise ValueError ('AnnotationCollection cannot be constructed from {}, expecting {}' .format (typ , cls ._type ))
745+
746+ features = the_json .get ('features' , None )
747+ if features is None :
748+ feature_list = None
749+ else :
750+ feature_list = [AnnotationFeature .from_dict (entry ) for entry in features ]
751+
752+ return cls (features = feature_list )
688753
689754
690755class FileAnnotationCollection (Jsonable ):
@@ -856,7 +921,8 @@ def from_dict(cls, the_dict):
856921
857922 typ = the_dict .get ('type' , 'NONE' )
858923 if typ != cls ._type :
859- raise ValueError ('FileAnnotationCollection cannot be constructed from the input dictionary' )
924+ raise ValueError ('FileAnnotationCollection cannot be constructed from {}, expecting {}' .format (typ , cls ._type ))
925+
860926
861927 return cls (
862928 version = the_dict .get ('version' , 'UNKNOWN' ),
0 commit comments