1212# License for the specific language governing permissions and limitations under
1313# the License.
1414"""Functionality for interacting with GeoJSON."""
15- import json
1615import logging
1716
1817import shapely .geometry as sgeom
@@ -31,114 +30,94 @@ class WrongTypeException(GeoJSONException):
3130 pass
3231
3332
34- class Geometry (dict ):
35- '''Manage and validate GeoJSON geometry description.'''
36- def __init__ (
37- self ,
38- data : dict
39- ):
40- """
41- Initialize GeoJSON.
42-
43- If data represents a GeoJSON FeatureClass, the geometry of the first
44- Feature is used.
45-
46- Parameters:
47- data: GeoJSON geometry, Feature, or FeatureClass.
48- """
49- geom = self ._geom_from_dict (data )
50- super ().__init__ (geom )
51- self ._validate ()
52-
53- @classmethod
54- def _geom_from_dict (
55- cls ,
56- data : dict
57- ):
58- '''Extract the geometry description from GeoJSON.
59-
60- If data represents a FeatureClass, the geometry from the first feature
61- is used.
62-
63- Parameters:
64- data: GeoJSON geometry, Feature, or FeatureClass.
65-
66- Raises:
67- GeoJSONException: If data is not valid GeoJSON geometry, Feature,
33+ class DataLossWarning (UserWarning ):
34+ """Warn that data will be lost."""
35+
36+
37+ def as_geom (data : dict ) -> dict :
38+ """Extract the geometry from GeoJSON and validate.
39+
40+ Parameters:
41+ data: GeoJSON geometry, Feature, or FeatureClass.
42+
43+ Returns:
44+ GeoJSON geometry.
45+
46+ Raises:
47+ GeoJSONException: If data is not valid GeoJSON geometry, Feature,
6848 or FeatureClass.
69- '''
70- if set (('coordinates' , 'type' )).issubset (set (data .keys ())):
71- # already a geom
72- ret = data
73- else :
49+ DataLossWarning: If more than one Feature is in a FeatureClass.
50+ """
51+ geom = geom_from_geojson (data )
52+ validate_geom (geom )
53+ return geom
54+
55+
56+ def as_polygon (data : dict ) -> dict :
57+ geom = as_geom (data )
58+ geom_type = geom ['type' ]
59+ if geom_type .lower () != 'polygon' :
60+ raise WrongTypeException (
61+ f'Invalid geometry type: { geom_type } is not Polygon.' )
62+ return geom
63+
64+
65+ def geom_from_geojson (data : dict ) -> dict :
66+ """Get GeoJSON geometry from GeoJSON.
67+
68+ Parameters:
69+ data: GeoJSON geometry, Feature, or FeatureClass.
70+
71+ Returns:
72+ GeoJSON geometry.
73+
74+ Raises:
75+ GeoJSONException: If data is not valid GeoJSON geometry
76+ """
77+ if set (('coordinates' , 'type' )).issubset (set (data .keys ())):
78+ # already a geom
79+ ret = data
80+ else :
81+ try :
82+ # feature
83+ ret = as_geom (data ["geometry" ])
84+ except KeyError :
7485 try :
75- # feature
76- ret = cls . _geom_from_dict ( data ["geometry" ])
86+ # featureclass
87+ features = data ['features' ]
7788 except KeyError :
78- try :
79- # featureclass
80- features = data ['features' ]
81- except KeyError :
82- raise GeoJSONException ('Invalid GeoJSON' )
83-
84- ret = cls ._geom_from_dict (features [0 ])
85- return ret
86-
87- def __eq__ (self , other ):
88- def _tuple_to_list (obj ):
89- return json .loads (
90- json .dumps (obj ).replace (")" , "]" ).replace ("(" , "[" ))
91-
92- return _tuple_to_list (self ) == _tuple_to_list (other )
93-
94- def _validate (self ):
95- '''
96- Raises:
97- GeoJSONException: If data is not valid GeoJSON geometry, Feature,
98- or FeatureClass.
99- WrongTypeException: If geometry coordinates do not fit type.
100- '''
101- data = self
102- if 'type' not in data :
103- raise GeoJSONException (
104- 'Missing \' type\' key.' )
105- elif 'coordinates' not in data :
106- raise GeoJSONException (
107- 'Missing \' coordinates\' key.' )
108-
109- try :
110- sgeom .shape (data )
111- except ValueError as e :
112- # invalid type or coordinates
113- raise GeoJSONException (e )
114- except TypeError :
115- # wrong type
116- raise WrongTypeException ('Geometry coordinates do not fit type' )
117-
118- @property
119- def type (self ):
120- return self ['type' ]
121-
122-
123- class Polygon (Geometry ):
124- def __init__ (
125- self ,
126- data : dict
127- ):
128- """
129- Initialize GeoJSON.
130-
131- If data represents a GeoJSON FeatureClass, the geometry of the first
132- Feature is used.
133-
134- Parameters:
135- data: Feature, FeatureClass, or geometry GeoJSON description.
136-
137-
138- Raises:
139- WrongTypeException: If data geometry type is not Polygon.
140- """
141- super ().__init__ (data )
142- if self .type .lower () != 'polygon' :
143- raise WrongTypeException (
144- f'Invalid geometry type: { self .type } is not Polygon.' )
89+ raise GeoJSONException ('Invalid GeoJSON: {data}' )
90+
91+ if len (features ) > 1 :
92+ raise DataLossWarning (
93+ 'FeatureClass has more than one Feature, using only first'
94+ ' feature.' )
95+
96+ ret = as_geom (features [0 ])
97+ return ret
98+
99+
100+ def validate_geom (data : dict ):
101+ """Validate GeoJSON geometry.
102+
103+ Parameters:
104+ data: GeoJSON geometry.
105+
106+ Raises:
107+ GeoJSONException: If data is not a valid GeoJSON geometry.
108+ """
109+ if 'type' not in data :
110+ raise GeoJSONException (
111+ 'Missing \' type\' key.' )
112+ elif 'coordinates' not in data :
113+ raise GeoJSONException (
114+ 'Missing \' coordinates\' key.' )
115+
116+ try :
117+ sgeom .shape (data )
118+ except ValueError as e :
119+ # invalid type or coordinates
120+ raise GeoJSONException (e )
121+ except TypeError :
122+ # wrong type
123+ raise GeoJSONException ('Geometry coordinates do not fit type' )
0 commit comments