1
- # TODO
1
+ from copy import deepcopy
2
+
3
+ from pystac .item import (Item , Asset )
4
+ from pystac import STACError
5
+
6
+
7
+ class EOItem (Item ):
8
+ EO_FIELDS = ['gsd' , 'platform' , 'instrument' , 'bands' , 'constellation' , 'epsg' ,
9
+ 'cloud_cover' , 'off_nadir' , 'azimuth' , 'sun_azimuth' , 'sun_elevation' ]
10
+
11
+ def __init__ (self ,
12
+ id ,
13
+ geometry ,
14
+ bbox ,
15
+ datetime ,
16
+ properties ,
17
+ gsd ,
18
+ platform ,
19
+ instrument ,
20
+ bands ,
21
+ constellation = None ,
22
+ epsg = None ,
23
+ cloud_cover = None ,
24
+ off_nadir = None ,
25
+ azimuth = None ,
26
+ sun_azimuth = None ,
27
+ sun_elevation = None ,
28
+ stac_extensions = None ,
29
+ href = None ,
30
+ collection = None ,
31
+ assets = {}):
32
+ if stac_extensions is None :
33
+ stac_extensions = []
34
+ if 'eo' not in stac_extensions :
35
+ stac_extensions .append ('eo' )
36
+ super ().__init__ (id , geometry , bbox , datetime ,
37
+ properties , stac_extensions , href ,
38
+ collection )
39
+ self .gsd = gsd
40
+ self .platform = platform
41
+ self .instrument = instrument
42
+ self .bands = [Band .from_dict (b ) for b in bands ]
43
+ self .constellation = constellation
44
+ self .epsg = epsg
45
+ self .cloud_cover = cloud_cover
46
+ self .off_nadir = off_nadir
47
+ self .azimuth = azimuth
48
+ self .sun_azimuth = sun_azimuth
49
+ self .sun_elevation = sun_elevation
50
+
51
+
52
+ def __repr__ (self ):
53
+ return '<EOItem id={}>' .format (self .id )
54
+
55
+ @staticmethod
56
+ def from_dict (d ):
57
+ item = Item .from_dict (d )
58
+ return EOItem .from_item (item )
59
+
60
+ @classmethod
61
+ def from_item (cls , item ):
62
+ eo_params = {}
63
+ for eof in EOItem .EO_FIELDS :
64
+ if eo_key (eof ) in item .properties .keys ():
65
+ eo_params [eof ] = item .properties .pop (eo_key (eof ))
66
+ elif eof in ('gsd' , 'platform' , 'instrument' , 'bands' ):
67
+ raise STACError (
68
+ "Missing required field '{}' in properties" .format (eo_key (eof )))
69
+
70
+ if not any (item .properties ):
71
+ item .properties = None
72
+
73
+ e = cls (item .id , item .geometry , item .bbox , item .datetime ,
74
+ item .properties , stac_extensions = item .stac_extensions ,
75
+ collection = item .collection , ** eo_params )
76
+
77
+ e .links = item .links
78
+ e .assets = item .assets
79
+
80
+ for k , v in item .assets .items ():
81
+ if is_eo (v ):
82
+ e .assets [k ] = EOAsset .from_asset (v )
83
+ e .assets [k ].set_owner (e )
84
+
85
+ return e
86
+
87
+ def get_eo_assets (self ):
88
+ return {k : v for k , v in self .assets .items () if isinstance (v , EOAsset )}
89
+
90
+ def add_asset (self , key , asset ):
91
+ if is_eo (asset ) and not isinstance (asset , EOAsset ):
92
+ asset = EOAsset .from_asset (asset )
93
+ asset .set_owner (self )
94
+ self .assets [key ] = asset
95
+ return self
96
+
97
+ @staticmethod
98
+ def from_file (uri ):
99
+ return EOItem .from_item (Item .from_file (uri ))
100
+
101
+ def clone (self ):
102
+ c = super (EOItem , self ).clone ()
103
+ self .add_eo_fields_to_dict (c .properties )
104
+ return EOItem .from_item (c )
105
+
106
+ def to_dict (self , include_self_link = True ):
107
+ d = super ().to_dict (include_self_link = include_self_link )
108
+ if 'properties' not in d .keys ():
109
+ d ['properties' ] = {}
110
+ self .add_eo_fields_to_dict (d ['properties' ])
111
+ return deepcopy (d )
112
+
113
+ def add_eo_fields_to_dict (self , d ):
114
+ for eof in EOItem .EO_FIELDS :
115
+ try :
116
+ a = getattr (self , eof )
117
+ if a is not None :
118
+ d [eo_key (eof )] = a
119
+ if eof == 'bands' :
120
+ d ['eo:bands' ] = [b .to_dict () for b in d ['eo:bands' ]]
121
+ except AttributeError :
122
+ pass
123
+
124
+
125
+ class EOAsset (Asset ):
126
+ def __init__ (self , href , bands , title = None , media_type = None , properties = None ):
127
+ super ().__init__ (href , title , media_type , properties )
128
+ self .bands = bands
129
+
130
+ @staticmethod
131
+ def from_dict (d ):
132
+ asset = Asset .from_dict (d )
133
+ return EOAsset .from_asset (asset )
134
+
135
+ @classmethod
136
+ def from_asset (cls , asset ):
137
+ a = asset .clone ()
138
+ if not a .properties or 'eo:bands' not in a .properties .keys ():
139
+ raise STACError ('Missing eo:bands property in asset' )
140
+ bands = a .properties .pop ('eo:bands' )
141
+ properties = None
142
+ if any (a .properties ):
143
+ properties = a .properties
144
+ return cls (a .href , bands , a .title , a .media_type , properties )
145
+
146
+ def to_dict (self ):
147
+ d = super ().to_dict ()
148
+ d ['eo:bands' ] = self .bands
149
+
150
+ return d
151
+
152
+ def clone (self ):
153
+ return EOAsset (href = self .href ,
154
+ title = self .title ,
155
+ media_type = self .media_type ,
156
+ bands = self .bands ,
157
+ properties = self .properties )
158
+
159
+ def __repr__ (self ):
160
+ return '<EOAsset href={}>' .format (self .href )
161
+
162
+ def get_band_objs (self ):
163
+ # Not sure exactly how this method fits in but
164
+ # it seems like there should be a way to get the
165
+ # Band objects associated with the indices
166
+ if not self .item :
167
+ raise STACError ('Asset is currently not associated with an item' )
168
+ return [self .item .bands [i ] for i in self .bands ]
169
+
170
+
171
+ class Band :
172
+ def __init__ (self ,
173
+ name = None ,
174
+ common_name = None ,
175
+ gsd = None ,
176
+ center_wavelength = None ,
177
+ full_width_half_max = None ,
178
+ description = None ,
179
+ accuracy = None ):
180
+ self .name = name
181
+ self .common_name = common_name
182
+ self .gsd = gsd
183
+ self .center_wavelength = center_wavelength
184
+ self .full_width_half_max = full_width_half_max
185
+ self .description = description
186
+ self .accuracy = accuracy
187
+
188
+ def __repr__ (self ):
189
+ return '<Band name={}>' .format (self .name )
190
+
191
+ @staticmethod
192
+ def from_dict (d ):
193
+ name = d .get ('name' , None )
194
+ common_name = d .get ('common_name' , None )
195
+ gsd = d .get ('gsd' , None )
196
+ center_wavelength = d .get ('center_wavelength' , None )
197
+ full_width_half_max = d .get ('full_width_half_max' , None )
198
+ description = d .get ('description' , None )
199
+ accuracy = d .get ('accuracy' , None )
200
+
201
+ return Band (name , common_name , gsd , center_wavelength ,
202
+ full_width_half_max , description , accuracy )
203
+
204
+ def to_dict (self ):
205
+ d = {}
206
+ if self .name :
207
+ d ['name' ] = self .name
208
+ if self .common_name :
209
+ d ['common_name' ] = self .common_name
210
+ if self .gsd :
211
+ d ['gsd' ] = self .gsd
212
+ if self .center_wavelength :
213
+ d ['center_wavelength' ] = self .center_wavelength
214
+ if self .full_width_half_max :
215
+ d ['full_width_half_max' ] = self .full_width_half_max
216
+ if self .description :
217
+ d ['description' ] = self .description
218
+ if self .accuracy :
219
+ d ['accuracy' ] = self .accuracy
220
+ return deepcopy (d )
221
+
222
+
223
+ def eo_key (key ):
224
+ return 'eo:{}' .format (key )
225
+
226
+
227
+ def band_range (common_name ):
228
+ name_to_range = {
229
+ 'coastal' : (0.40 , 0.45 ),
230
+ 'blue' : (0.45 , 0.50 ),
231
+ 'green' : (0.50 , 0.60 ),
232
+ 'red' : (0.60 , 0.70 ),
233
+ 'yellow' : (0.58 , 0.62 ),
234
+ 'pan' : (0.50 , 0.70 ),
235
+ 'rededge' : (0.70 , 0.75 ),
236
+ 'nir' : (0.75 , 1.00 ),
237
+ 'nir08' : (0.75 , 0.90 ),
238
+ 'nir09' : (0.85 , 1.05 ),
239
+ 'cirrus' : (1.35 , 1.40 ),
240
+ 'swir16' : (1.55 , 1.75 ),
241
+ 'swir22' : (2.10 , 2.30 ),
242
+ 'lwir' : (10.5 , 12.5 ),
243
+ 'lwir11' : (10.5 , 11.5 ),
244
+ 'lwir12' : (11.5 , 12.5 )
245
+ }
246
+ return name_to_range .get (common_name , common_name )
247
+
248
+
249
+ def band_desc (common_name ):
250
+ r = band_range (common_name )
251
+ if isinstance (r , str ):
252
+ return "Common name: {}" .format (common_name )
253
+ return "Common name: {}, Range: {} to {}" .format (common_name , r [0 ], r [1 ])
254
+
255
+
256
+ def is_eo (obj ):
257
+ if obj .properties :
258
+ if obj .properties .get ('eo:bands' , None ):
259
+ return True
260
+ return False
0 commit comments