5353# create logger
5454logger = logging .getLogger (__name__ )
5555
56+ # profiling times for each major function
57+ profiles = {}
58+
5659# default asset
5760DEFAULT_ASSET = "nsidc-s3"
5861
9497SC_BACKWARD = 0
9598SC_FORWARD = 1
9699
97- # gps-based epoch for delta times #
100+ # gps-based epoch for delta times
98101ATLAS_SDP_EPOCH = datetime .datetime (2018 , 1 , 1 )
99102
103+ # ancillary list types
104+ ATL03_GEOLOCATION = 0
105+ ATL03_GEOCORRECTION = 1
106+ ATL03_HEIGHT = 2
107+ ATL08_SIGNAL_PHOTON = 3
100108
109+ # ancillary list type matching
110+ ancillary_lists = {
111+ ATL03_GEOLOCATION : "atl03_geolocation_fields" ,
112+ ATL03_GEOCORRECTION : "atl03_geocorrection_fields" ,
113+ ATL03_HEIGHT : "atl03_height_fields" ,
114+ ATL08_SIGNAL_PHOTON : "atl08_signal_photon_fields"
115+ }
101116
102117###############################################################################
103118# NSIDC UTILITIES
115130# The above copyright notice and this permission notice shall be included
116131# in all copies or substantial portions of the Software.
117132
118-
119133# WGS84 / Mercator, Earth as Geoid, Coordinate system on the surface of a sphere or ellipsoid of reference.
120134EPSG_MERCATOR = "EPSG:4326"
121135
@@ -316,29 +330,32 @@ def __get_values(data, dtype, size):
316330#
317331def __query_resources (parm , version , return_polygons = False ):
318332
333+ # Latch Start Time
334+ tstart = time .perf_counter ()
335+
319336 # Check Parameters are Valid
320337 if ("poly" not in parm ) and ("t0" not in parm ) and ("t1" not in parm ):
321338 logger .error ("Must supply some bounding parameters with request (poly, t0, t1)" )
322339 return []
323340
324- # Submission Arguments for CRM #
341+ # Submission Arguments for CMR
325342 kwargs = {}
326343 kwargs ['version' ] = version
327344 kwargs ['return_polygons' ] = return_polygons
328345
329- # Pull Out Polygon #
346+ # Pull Out Polygon
330347 if "clusters" in parm and parm ["clusters" ] and len (parm ["clusters" ]) > 0 :
331348 kwargs ['polygon' ] = parm ["clusters" ]
332349 elif "poly" in parm and parm ["poly" ] and len (parm ["poly" ]) > 0 :
333350 kwargs ['polygon' ] = parm ["poly" ]
334351
335- # Pull Out Time Period #
352+ # Pull Out Time Period
336353 if "t0" in parm :
337354 kwargs ['time_start' ] = parm ["t0" ]
338355 if "t1" in parm :
339356 kwargs ['time_end' ] = parm ["t1" ]
340357
341- # Build Filters #
358+ # Build Filters
342359 name_filter_enabled = False
343360 rgt_filter = '????'
344361 if "rgt" in parm :
@@ -355,19 +372,22 @@ def __query_resources(parm, version, return_polygons=False):
355372 if name_filter_enabled :
356373 kwargs ['name_filter' ] = '*_' + rgt_filter + cycle_filter + region_filter + '_*'
357374
358- # Make CMR Request #
375+ # Make CMR Request
359376 if return_polygons :
360377 resources ,polygons = cmr (** kwargs )
361378 else :
362379 resources = cmr (** kwargs )
363380
364- # Check Resources are Under Limit #
381+ # Check Resources are Under Limit
365382 if (len (resources ) > max_requested_resources ):
366383 raise RuntimeError ('Exceeded maximum requested granules: {} (current max is {})\n Consider using icesat2.set_max_resources to set a higher limit.' .format (len (resources ), max_requested_resources ))
367384 else :
368385 logger .info ("Identified %d resources to process" , len (resources ))
369386
370- # Return Resources #
387+ # Update Profile
388+ profiles [__query_resources .__name__ ] = time .perf_counter () - tstart
389+
390+ # Return Resources
371391 if return_polygons :
372392 return (resources ,polygons )
373393 else :
@@ -385,7 +405,11 @@ def __emptyframe(**kwargs):
385405# Dictionary to GeoDataFrame
386406#
387407def __todataframe (columns , delta_time_key = "delta_time" , lon_key = "lon" , lat_key = "lat" , ** kwargs ):
388- # set default keyword arguments
408+
409+ # Latch Start Time
410+ tstart = time .perf_counter ()
411+
412+ # Set Default Keyword Arguments
389413 kwargs ['index_key' ] = "time"
390414 kwargs ['crs' ] = EPSG_MERCATOR
391415
@@ -416,13 +440,20 @@ def __todataframe(columns, delta_time_key="delta_time", lon_key="lon", lat_key="
416440 # Sort values for reproducible output despite async processing
417441 gdf .sort_index (inplace = True )
418442
443+ # Update Profile
444+ profiles [__todataframe .__name__ ] = time .perf_counter () - tstart
445+
419446 # Return GeoDataFrame
420447 return gdf
421448
422449#
423450# GeoDataFrame to Polygon
424451#
425452def __gdf2poly (gdf ):
453+
454+ # latch start time
455+ tstart = time .perf_counter ()
456+
426457 # pull out coordinates
427458 hull = gdf .unary_union .convex_hull
428459 polygon = [{"lon" : coord [0 ], "lat" : coord [1 ]} for coord in list (hull .exterior .coords )]
@@ -438,6 +469,9 @@ def __gdf2poly(gdf):
438469 # replace region with counter-clockwise version #
439470 polygon = ccw_poly
440471
472+ # Update Profile
473+ profiles [__gdf2poly .__name__ ] = time .perf_counter () - tstart
474+
441475 # return polygon
442476 return polygon
443477
@@ -705,6 +739,8 @@ def atl06p(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, callb
705739 [622412 rows x 16 columns]
706740 '''
707741 try :
742+ tstart = time .perf_counter ()
743+
708744 # Get List of Resources from CMR (if not supplied)
709745 if resources == None :
710746 resources = __query_resources (parm , version )
@@ -719,46 +755,62 @@ def atl06p(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, callb
719755 # Make API Processing Request
720756 rsps = sliderule .source ("atl06p" , rqst , stream = True , callbacks = callbacks )
721757
722- start_time = time .perf_counter ()
723758 # Flatten Responses
759+ tstart_flatten = time .perf_counter ()
724760 columns = {}
725- rectype = None
726- num_rows = 0
727- sample_rec = None
728- if len (rsps ) <= 0 :
729- logger .debug ("No response returned" )
730- else :
731- for rsp in rsps :
732- # Determine Record Type
733- if rectype == None :
734- if rsp ['__rectype' ] == 'atl06rec' :
735- rectype = 'atl06rec.elevation'
736- sample_rec = rsp
737- elif rsp ['__rectype' ] == 'atl06rec-compact' :
738- rectype = 'atl06rec-compact.elevation'
739- sample_rec = rsp
740- # Count Rows
741- num_rows += len (rsp ["elevation" ])
742- # Check Valid ATL06 Record Returned
743- if rectype == None :
744- raise RuntimeError ("Invalid record types returned for this api" )
745- # Build Columns
746- for field in sample_rec ["elevation" ][0 ].keys ():
747- fielddef = sliderule .get_definition (rectype , field )
748- if len (fielddef ) > 0 :
749- columns [field ] = numpy .empty (num_rows , fielddef ["nptype" ])
750- # Populate Columns
751- elev_cnt = 0
761+ elevation_records = []
762+ num_elevations = 0
763+ field_dictionary = {}
764+ if len (rsps ) > 0 :
765+ # Sort Records
752766 for rsp in rsps :
753- for elevation in rsp ["elevation" ]:
754- for field in columns :
755- columns [field ][elev_cnt ] = elevation [field ]
756- elev_cnt += 1
767+ if 'atl06rec' in rsp ['__rectype' ]:
768+ elevation_records += rsp ,
769+ num_elevations += len (rsp ['elevation' ])
770+ elif 'atlxxrec' in rsp ['__rectype' ]:
771+ if rsp ['list_type' ] == ATL03_GEOLOCATION or rsp ['list_type' ] == ATL03_GEOCORRECTION :
772+ field_name = parm [ancillary_lists [rsp ['list_type' ]]][rsp ['field_index' ]]
773+ if field_name in field_dictionary :
774+ data = __get_values (rsp ['data' ], rsp ['data_type' ], len (rsp ['data' ]))
775+ # Add Left Pair Track Entry
776+ field_dictionary [field_name ]['extent_id' ] += rsp ['extent_id' ] | 0x2 ,
777+ field_dictionary [field_name ][field_name ] += data [0 ],
778+ # Add Right Pair Track Entry
779+ field_dictionary [field_name ]['extent_id' ] += rsp ['extent_id' ] | 0x3 ,
780+ field_dictionary [field_name ][field_name ] += data [1 ],
781+ else :
782+ field_dictionary [field_name ] = {"extent_id" : [], field_name : []}
783+ # Build Elevation Columns
784+ if num_elevations > 0 :
785+ # Initialize Columns
786+ sample_elevation_record = elevation_records [0 ]["elevation" ][0 ]
787+ for field in sample_elevation_record .keys ():
788+ fielddef = sliderule .get_definition (sample_elevation_record ['__rectype' ], field )
789+ if len (fielddef ) > 0 :
790+ columns [field ] = numpy .empty (num_elevations , fielddef ["nptype" ])
791+ # Populate Columns
792+ elev_cnt = 0
793+ for record in elevation_records :
794+ for elevation in record ["elevation" ]:
795+ for field in columns :
796+ columns [field ][elev_cnt ] = elevation [field ]
797+ elev_cnt += 1
798+ else :
799+ logger .debug ("No response returned" )
800+ profiles ["flatten" ] = time .perf_counter () - tstart_flatten
757801
758- # Return Response
802+ # Build GeoDataFrame
759803 gdf = __todataframe (columns , "delta_time" , "lon" , "lat" )
760- end_time = time .perf_counter ()
761- print ("Execution Time: {} seconds" .format (end_time - start_time ))
804+
805+ # Merge Ancillary Fields
806+ tstart_merge = time .perf_counter ()
807+ for field in field_dictionary :
808+ df = geopandas .pd .DataFrame (field_dictionary [field ])
809+ gdf = gdf .merge (df , on = 'extent_id' , how = 'inner' )
810+ profiles ["merge" ] = time .perf_counter () - tstart_merge
811+
812+ # Return Response
813+ profiles [atl06p .__name__ ] = time .perf_counter () - tstart
762814 return gdf
763815
764816 # Handle Runtime Errors
@@ -823,6 +875,8 @@ def atl03sp(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, call
823875 ATL03 segments (see `Photon Segments <../user_guide/ICESat-2.html#photon-segments>`_)
824876 '''
825877 try :
878+ tstart = time .perf_counter ()
879+
826880 # Get List of Resources from CMR (if not specified)
827881 if resources == None :
828882 resources = __query_resources (parm , version )
@@ -838,6 +892,7 @@ def atl03sp(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, call
838892 rsps = sliderule .source ("atl03sp" , rqst , stream = True , callbacks = callbacks )
839893
840894 # Flatten Responses
895+ tstart_flatten = time .perf_counter ()
841896 columns = {}
842897 if len (rsps ) <= 0 :
843898 logger .debug ("no response returned" )
@@ -885,6 +940,7 @@ def atl03sp(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, call
885940 ph_index += 1
886941 # Rename Count Column to Pair Column
887942 columns ["pair" ] = columns .pop ("count" )
943+ profiles ["flatten" ] = time .perf_counter () - tstart_flatten
888944
889945 # Create DataFrame
890946 df = __todataframe (columns , "delta_time" , "longitude" , "latitude" )
@@ -893,6 +949,7 @@ def atl03sp(parm, asset=DEFAULT_ASSET, version=DEFAULT_ICESAT2_SDP_VERSION, call
893949 df ['spot' ] = df .apply (lambda row : __calcspot (row ["sc_orient" ], row ["track" ], row ["pair" ]), axis = 1 )
894950
895951 # Return Response
952+ profiles [atl03sp .__name__ ] = time .perf_counter () - tstart
896953 return df
897954
898955 # Error Case
@@ -955,9 +1012,11 @@ def h5 (dataset, resource, asset=DEFAULT_ASSET, datatype=sliderule.datatypes["DY
9551012 >>> longitudes = icesat2.h5("/gt1r/land_ice_segments/longitude", resource, asset)
9561013 >>> df = pd.DataFrame(data=list(zip(heights, latitudes, longitudes)), index=segments, columns=["h_mean", "latitude", "longitude"])
9571014 '''
1015+ tstart = time .perf_counter ()
9581016 datasets = [ { "dataset" : dataset , "datatype" : datatype , "col" : col , "startrow" : startrow , "numrows" : numrows } ]
9591017 values = h5p (datasets , resource , asset = asset )
9601018 if len (values ) > 0 :
1019+ profiles [h5 .__name__ ] = time .perf_counter () - tstart
9611020 return values [dataset ]
9621021 else :
9631022 return numpy .empty (0 )
@@ -1017,6 +1076,9 @@ def h5p (datasets, resource, asset=DEFAULT_ASSET):
10171076 '/gt1r/land_ice_segments/h_li': array([45.72632446, 45.76512574, 45.76337375, 45.77102473, 45.81307948]),
10181077 '/gt3r/land_ice_segments/h_li': array([45.14954134, 45.18970635, 45.16637644, 45.15235916, 45.17135806])}
10191078 '''
1079+ # Latch Start Time
1080+ tstart = time .perf_counter ()
1081+
10201082 # Baseline Request
10211083 rqst = {
10221084 "asset" : asset ,
@@ -1036,6 +1098,9 @@ def h5p (datasets, resource, asset=DEFAULT_ASSET):
10361098 for result in rsps :
10371099 results [result ["dataset" ]] = __get_values (result ["data" ], result ["datatype" ], result ["size" ])
10381100
1101+ # Update Profiles
1102+ profiles [h5p .__name__ ] = time .perf_counter () - tstart
1103+
10391104 # Return Results
10401105 return results
10411106
@@ -1088,6 +1153,7 @@ def toregion(source, tolerance=0.0, cellsize=0.01, n_clusters=1):
10881153 >>> atl06 = icesat2.atl06p(parms)
10891154 '''
10901155
1156+ tstart = time .perf_counter ()
10911157 tempfile = "temp.geojson"
10921158
10931159 if isinstance (source , geopandas .GeoDataFrame ):
@@ -1170,8 +1236,10 @@ def toregion(source, tolerance=0.0, cellsize=0.01, n_clusters=1):
11701236 c_poly = __gdf2poly (c_gdf )
11711237 clusters .append (c_poly )
11721238
1239+ # update timing profiles
1240+ profiles [toregion .__name__ ] = time .perf_counter () - tstart
11731241
1174- # return region #
1242+ # return region
11751243 return {
11761244 "gdf" : gdf ,
11771245 "poly" : polygon , # convex hull of polygons
0 commit comments