def write_atl03_las(atlstruct, outpath): xx = np.array(atlstruct.df.easting) yy = np.array(atlstruct.df.northing) zz = np.array(atlstruct.df.h_ph) cc = np.array(atlstruct.df.classification) ii = np.array(atlstruct.df.signal_conf_ph) sigconf = np.array(atlstruct.df.signal_conf_ph) hemi = atlstruct.hemi zone = atlstruct.zone print(' Writing ATL03 .las file...', end=" ") try: outname = atlstruct.atl03FileName + '_' + atlstruct.gtNum + '.las' except AttributeError: # occasionally atl03FileName is not in the atlstruct outname = atlstruct.atlFileName + '_' + atlstruct.gtNum + '.las' outfile = os.path.normpath(outpath + '/' + outname) if (not os.path.exists(os.path.normpath(outpath))): os.mkdir(os.path.normpath(outpath)) # Get projection if (atlstruct.zone == '3413' or atlstruct.zone == '3976'): # 3413 = Arctic, 3976 = Antarctic lasProjection = atlstruct.hemi # Write .las file writeLas(xx, yy, zz, lasProjection, outfile, cc, ii, sigconf) else: # Write .las file for UTM projection case writeLas(xx, yy, zz, 'utm', outfile, cc, ii, sigconf, hemi, zone) print('Complete')
def getAtlTruthSwath(atlMeasuredData, rotationData, truthHeaderDF, truthFilePaths, buffer, outFilePath, createTruthFile, truthFileType, useExistingTruth, logFileID=False): # Start timer timeStart = runTime.time() # Initialize data atlTruthData = [] # Print message gtNum = atlMeasuredData.gtNum beamNum = atlMeasuredData.beamNum beamStrength = atlMeasuredData.beamStrength writeLog( ' Ground Track Number: %s (Beam #%s, Beam Strength: %s)\n' % (gtNum, beamNum, beamStrength), logFileID) if (useExistingTruth): # Get truth swath .las file truthFilePath = truthFilePaths[0] # Get truth file header info writeLog(' Reading in reference buffer file: %s\n' % truthFilePath, logFileID) atlTruthData = loadTruthFile(truthFilePath, atlMeasuredData, rotationData, truthFileType, outFilePath, logFileID) else: # Reproject unmatching ESPG files to match ICESat-2 EPSG code truthHeaderNewDF = reprojectHeaderData(truthHeaderDF, atlMeasuredData, logFileID) # Find truth files that intersect ICESat-2 track writeLog( ' Determining which reference files intersect ground track...', logFileID) matchingTruthFiles = findMatchingTruthFiles(truthHeaderNewDF, atlMeasuredData, rotationData, buffer) # Read truth files that intersect ICESat-2 track if (len(matchingTruthFiles) > 0): # Print messages writeLog( ' Ground track crosses over %d (out of %d) reference files' % (len(matchingTruthFiles), len(truthHeaderDF)), logFileID) writeLog( ' Reading from directory: %s' % ntpath.dirname(matchingTruthFiles[0]), logFileID) writeLog('', logFileID) writeLog(' Reading File:', logFileID) writeLog(' -------------', logFileID) # Initialize parameters fileNum = 1 atlTruthData = atlTruthStruct([], [], [], [], [], [], [], [], [], [], []) # Loop over matching files for i in range(0, len(matchingTruthFiles)): # Get truth file to read truthFilePath = matchingTruthFiles[i] # Get truth base file name baseName = ntpath.basename(truthFilePath) # Read truth file writeLog(' %d) %s' % (fileNum, baseName), logFileID) atlTruthDataSingle = loadTruthFile(truthFilePath, atlMeasuredData, rotationData, truthFileType, outFilePath, logFileID) # Get truth file buffer if (bool(atlTruthDataSingle)): writeLog(' Buffering data...', logFileID) atlTruthDataBuffer = makeBuffer(atlTruthDataSingle, atlMeasuredData, rotationData, buffer) # Append truth files atlTruthData.append(atlTruthDataBuffer) # Increment counter fileNum += 1 # endIf # endFor else: # No data to process atlTruthData = [] writeLog('', logFileID) writeLog( ' WARNING: No matching reference files intersect ground track.', logFileID) # endIf # endIf # Test if atlTruthData is empty atlTruthEmpty = (len(atlTruthData.easting) == 0) and (len( atlTruthData.northing) == 0) # Inform user if data is empty if (atlTruthEmpty): writeLog('ERROR: Reference data is empty.', logFileID) # endIf # Create output file if (createTruthFile and not (atlTruthEmpty)): # Set output file name if (useExistingTruth): outName = os.path.basename(truthFilePath) else: outName = atlMeasuredData.atl03FileName + '_' + \ atlMeasuredData.gtNum + '_REFERENCE_' + str(buffer) + \ 'L' + str(buffer) + 'Rm_buffer.las' # endIf # Set full output file name and path outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Write to file writeLog('', logFileID) writeLog(' Writing reference .las file...', logFileID) writeLas(np.ravel(atlTruthData.easting), np.ravel(atlTruthData.northing), np.ravel(atlTruthData.z), 'utm', outPath, np.ravel(atlTruthData.classification), np.ravel(atlTruthData.intensity), None, atlTruthData.hemi, atlTruthData.zone) # endIf # End timer timeEnd = runTime.time() timeElapsedTotal = timeEnd - timeStart timeElapsedMin = np.floor(timeElapsedTotal / 60) timeElapsedSec = timeElapsedTotal % 60 # Print completion message writeLog('', logFileID) writeLog( ' Module Completed in %d min %d sec.' % (timeElapsedMin, timeElapsedSec), logFileID) writeLog('\n', logFileID) # Return object return atlTruthData
def getAtlMeasuredSwath(atl03FilePath=False, atl08FilePath=False, outFilePath=False, gtNum='gt1r', trimInfo='auto', createAtl03LasFile=False, createAtl03KmlFile=False, createAtl08KmlFile=False, createAtl03CsvFile=False, createAtl08CsvFile=False, logFileID=False): # pklHeaderFile = None, LAS_DIR = None): # Initialize outputs atl03Data = [] headerData = False rotationData = False # Initialize ATL08 data atl08Data = [] atl08_lat = [] atl08_lon = [] atl08_maxCanopy = [] atl08_teBestFit = [] atl08_teMedian = [] atl08_time = [] atl08_easting = [] atl08_northing = [] atl08_crossTrack = [] atl08_alongTrack = [] atl08_classification = [] atl08_intensity = [] # pklHeader = False # if type(pklHeaderFile) != type(None): # pklHeader = True # if type(LAS_DIR) == type(None): # raise ValueError('LAS_DIR == None, please input LAS_DIR argument') # Only execute code if input ATL03 .h5 file and output path declared if (atl03FilePath and outFilePath): # Start timer timeStart = runTime.time() # Get beam # and S/W beamNum = GtToBeamNum(atl03FilePath, gtNum) beamStrength = GtToBeamSW(atl03FilePath, gtNum) # Print message writeLog( ' Ground Track Number: %s (Beam #%s, Beam Strength: %s)\n' % (gtNum, beamNum, beamStrength), logFileID) # Get ATL03 file path/name atl03FilePath = os.path.normpath(os.path.abspath(atl03FilePath)) atl03FileName = os.path.splitext(os.path.basename(atl03FilePath))[0] # Read ATL03 data from h5 file writeLog(' Reading ATL03 .h5 file: %s' % atl03FilePath, logFileID) lat_all = readAtl03H5(atl03FilePath, '/heights/lat_ph', gtNum) lon_all = readAtl03H5(atl03FilePath, '/heights/lon_ph', gtNum) z_all = readAtl03H5(atl03FilePath, '/heights/h_ph', gtNum) deltaTime_all = readAtl03H5(atl03FilePath, '/heights/delta_time', gtNum) signalConf_all = readAtl03H5(atl03FilePath, '/heights/signal_conf_ph', gtNum) zGeoidal = readAtl03H5(atl03FilePath, '/geophys_corr/geoid', gtNum) zGeoidal_deltaTime = readAtl03H5(atl03FilePath, '/geophys_corr/delta_time', gtNum) zGeoidal_all = interp_vals(zGeoidal_deltaTime, zGeoidal, deltaTime_all, removeThresh=True) zMsl_all = z_all - zGeoidal_all atl03_ph_index_beg, atl03_segment_id = readAtl03DataMapping( atl03FilePath, gtNum) badVars = [] if (len(lat_all) == 0): badVar = 'Latitude (lat_ph)' badVars.append(badVar) # endIf if (len(lon_all) == 0): badVar = 'Longitude (lon_ph)' badVars.append(badVar) # endIf if (len(z_all) == 0): badVar = 'Height (h_ph)' badVars.append(badVar) # endIf if (len(deltaTime_all) == 0): badVar = 'Delta Time (delta_time)' badVars.append(badVar) # endIf if (len(signalConf_all) == 0): badVar = 'Signal Confidence (signal_conf_ph)' badVars.append(badVar) # endIf if (len(badVars) == 0): # Get time from delta time min_delta_time = np.min(deltaTime_all) time_all = deltaTime_all - min_delta_time # Get track direction if (np.abs(lat_all[-1]) >= np.abs(lat_all[0])): trackDirection = 'Ascending' else: trackDirection = 'Descending' # endIf writeLog(' Track Direction: %s' % trackDirection, logFileID) # Extract metadata from ATL03 file name atl03h5Info = getNameParts(atl03FileName) # Map ATL08 to ATL03 ground photons if (atl08FilePath): # Get ATL08 file path/name atl08FilePath = os.path.normpath( os.path.abspath(atl08FilePath)) atl08FileName = os.path.splitext( os.path.basename(atl08FilePath))[0] atl08h5Info = getNameParts(atl08FileName) # Read ATL08 data from .h5 file writeLog(' Reading ATL08 .h5 file: %s' % atl08FilePath, logFileID) atl08_lat = readAtl08H5(atl08FilePath, '/land_segments/latitude', gtNum) atl08_lon = readAtl08H5(atl08FilePath, '/land_segments/longitude', gtNum) atl08_maxCanopy = readAtl08H5( atl08FilePath, '/land_segments/canopy/h_max_canopy_abs', gtNum) atl08_teBestFit = readAtl08H5( atl08FilePath, '/land_segments/terrain/h_te_best_fit', gtNum) atl08_teMedian = readAtl08H5( atl08FilePath, '/land_segments/terrain/h_te_median', gtNum) atl08_deltaTime = readAtl08H5(atl08FilePath, '/land_segments/delta_time', gtNum) atl08_zGeoidal = interp_vals(zGeoidal_deltaTime, zGeoidal, atl08_deltaTime, removeThresh=True) atl08_maxCanopyMsl = atl08_maxCanopy - atl08_zGeoidal atl08_teBestFitMsl = atl08_teBestFit - atl08_zGeoidal atl08_teMedianMsl = atl08_teMedian - atl08_zGeoidal atl08_classed_pc_indx, atl08_classed_pc_flag, atl08_segment_id = readAtl08DataMapping( atl08FilePath, gtNum) atl08_signalConf = np.zeros(np.size(atl08_lat)) atl08_classification = np.zeros(np.size(atl08_lat)) atl08_intensity = np.zeros(np.size(atl08_lat)) if ((len(atl08_lat) > 0) and (len(atl08_lon) > 0)): # Get time from delta time atl08_time = atl08_deltaTime - min_delta_time # Do ATL08 to ATL03 mapping writeLog(' Mapping ATL08 to ATL03 Ground Photons...', logFileID) try: classification_all = getAtl08Mapping( atl03_ph_index_beg, atl03_segment_id, atl08_classed_pc_indx, atl08_classed_pc_flag, atl08_segment_id) except: writeLog( ' WARNING: Could not map ATL08 to ATL03 Ground Photons.', logFileID) classification_all = atl08_classification # endTry # Get length to trim data by class_length = len(classification_all) lat_length = len(lat_all) data_length = np.min([class_length, lat_length]) # Trim ATL03 data down to size of classification array atl03_lat = lat_all[0:data_length] atl03_lon = lon_all[0:data_length] atl03_z = z_all[0:data_length] atl03_zMsl = zMsl_all[0:data_length] atl03_time = time_all[0:data_length] atl03_deltaTime = deltaTime_all[0:data_length] atl03_signalConf = signalConf_all[0:data_length] atl03_classification = classification_all[0:data_length] atl03_intensity = np.zeros(np.size(atl03_lat)) dataIsMapped = True else: writeLog( ' WARNING: ATL08 file does not contain usable data.', logFileID) atl08FilePath = False # Store data atl03_lat = lat_all atl03_lon = lon_all atl03_z = z_all atl03_zMsl = zMsl_all atl03_time = time_all atl03_deltaTime = deltaTime_all atl03_signalConf = signalConf_all atl03_classification = np.zeros(np.size(atl03_lat)) atl03_intensity = np.zeros(np.size(atl03_lat)) dataIsMapped = False # endIf else: # Message to screen writeLog(' Not Mapping ATL08 to ATL03 Ground Photons', logFileID) # Store data atl03_lat = lat_all atl03_lon = lon_all atl03_z = z_all atl03_zMsl = zMsl_all atl03_time = time_all atl03_deltaTime = deltaTime_all atl03_signalConf = signalConf_all atl03_classification = np.zeros(np.size(atl03_lat)) atl03_intensity = np.zeros(np.size(atl03_lat)) dataIsMapped = False # endIf # Get trim options trimParts = trimInfo.split(',') trimMode = trimParts[0] trimType = 'None' if (('manual' in trimMode.lower()) and (len(trimParts) > 1)): trimType = trimParts[1] trimMin = float(trimParts[2]) trimMax = float(trimParts[3]) # endIf # If selected to manually trim data, do this first if ('manual' in trimMode.lower()): # Trim by lat or time if ('lonlat' in trimType.lower()): lonMin, lonMax = float(trimParts[2]), float(trimParts[3]) latMin, latMax = float(trimParts[4]), float(trimParts[5]) writeLog( ' Manual Trim Mode (Min Lon: %s, Max Lon: %s)' % (lonMin, lonMax), logFileID) writeLog( ' Manual Trim Mode (Min Lat: %s, Max Lat: %s)' % (latMin, latMax), logFileID) atl03IndsToKeepLon = (atl03_lon >= lonMin) & (atl03_lon <= lonMax) atl03IndsToKeepLat = (atl03_lat >= latMin) & (atl03_lat <= latMax) atl03IndsToKeep = atl03IndsToKeepLon & atl03IndsToKeepLat if (atl08FilePath): atl08IndsToKeepLon = (atl08_lon >= lonMin) & (atl08_lon <= lonMax) atl08IndsToKeepLat = (atl08_lat >= latMin) & (atl08_lat <= latMax) atl08IndsToKeep = atl08IndsToKeepLon & atl08IndsToKeepLat elif ('lat' in trimType.lower()): writeLog( ' Manual Trim Mode (Min Lat: %s, Max Lat: %s)' % (trimMin, trimMax), logFileID) atl03IndsToKeep = (atl03_lat >= trimMin) & (atl03_lat <= trimMax) if (atl08FilePath): atl08IndsToKeep = (atl08_lat >= trimMin) & (atl08_lat <= trimMax) # endIf elif ('lon' in trimType.lower()): writeLog( ' Manual Trim Mode (Min Lon: %s, Max Lon: %s)' % (trimMin, trimMax), logFileID) atl03IndsToKeep = (atl03_lon >= trimMin) & (atl03_lon <= trimMax) if (atl08FilePath): atl08IndsToKeep = (atl03_lon >= trimMin) & (atl03_lon <= trimMax) # endIf elif ('time' in trimType.lower()): writeLog( ' Manual Trim Mode (Min Time: %s, Max Time: %s)' % (trimMin, trimMax), logFileID) atl03IndsToKeep = (atl03_time >= trimMin) & (atl03_time <= trimMax) if (atl08FilePath): atl08IndsToKeep = (atl08_time >= trimMin) & (atl08_time <= trimMax) # endIf else: writeLog( ' Manual Trim Mode is Missing Args, Not Trimming Data', logFileID) atl03IndsToKeep = np.ones(np.shape(atl03_lat), dtype=bool) if (atl08FilePath): atl08IndsToKeep = np.ones(np.shape(atl08_lat), dtype=bool) # endIf # endif # Trim ATL03 data if atl03IndsToKeep.sum() == 0: # no data left given manual constraints return atl03Data, atl08Data, headerData, rotationData # endIf atl03_lat = atl03_lat[atl03IndsToKeep] atl03_lon = atl03_lon[atl03IndsToKeep] atl03_z = atl03_z[atl03IndsToKeep] atl03_zMsl = atl03_zMsl[atl03IndsToKeep] atl03_time = atl03_time[atl03IndsToKeep] atl03_deltaTime = atl03_deltaTime[atl03IndsToKeep] atl03_signalConf = atl03_signalConf[atl03IndsToKeep] atl03_classification = atl03_classification[atl03IndsToKeep] atl03_intensity = atl03_intensity[atl03IndsToKeep] # Trim ATL08 data if (atl08FilePath): atl08_lat = atl08_lat[atl08IndsToKeep] atl08_lon = atl08_lon[atl08IndsToKeep] atl08_maxCanopy = atl08_maxCanopy[atl08IndsToKeep] atl08_teBestFit = atl08_teBestFit[atl08IndsToKeep] atl08_teMedian = atl08_teMedian[atl08IndsToKeep] atl08_maxCanopyMsl = atl08_maxCanopyMsl[atl08IndsToKeep] atl08_teBestFitMsl = atl08_teBestFitMsl[atl08IndsToKeep] atl08_teMedianMsl = atl08_teMedianMsl[atl08IndsToKeep] atl08_time = atl08_time[atl08IndsToKeep] atl08_deltaTime = atl08_deltaTime[atl08IndsToKeep] atl08_signalConf = atl08_signalConf[atl08IndsToKeep] atl08_classification = atl08_classification[ atl08IndsToKeep] atl08_intensity = atl08_intensity[atl08IndsToKeep] # endIf elif ('none' in trimMode.lower()): writeLog(' Trim Mode: None', logFileID) # endIf # Remove ATL08 points above 1e30 if (atl08FilePath): indsBelowThresh = (atl08_maxCanopy <= 1e30) | ( atl08_teBestFit <= 1e30) | (atl08_teMedian <= 1e30) atl08_lat = atl08_lat[indsBelowThresh] atl08_lon = atl08_lon[indsBelowThresh] atl08_maxCanopy = atl08_maxCanopy[indsBelowThresh] atl08_teBestFit = atl08_teBestFit[indsBelowThresh] atl08_teMedian = atl08_teMedian[indsBelowThresh] atl08_maxCanopyMsl = atl08_maxCanopyMsl[indsBelowThresh] atl08_teBestFitMsl = atl08_teBestFitMsl[indsBelowThresh] atl08_teMedianMsl = atl08_teMedianMsl[indsBelowThresh] atl08_time = atl08_time[indsBelowThresh] atl08_deltaTime = atl08_deltaTime[indsBelowThresh] atl08_signalConf = atl08_signalConf[indsBelowThresh] atl08_classification = atl08_classification[indsBelowThresh] atl08_intensity = atl08_intensity[indsBelowThresh] # endIf # If selected to auto trim data, then trim if truth region exists if ('auto' in trimMode.lower()): # Message to user writeLog(' Trim Mode: Auto', logFileID) # Read kmlBounds.txt file to get min/max extents to auto trim kmlBoundsTextFile = 'kmlBounds.txt' kmlRegionName = False if (os.path.exists(kmlBoundsTextFile)): # and not pklHeader: # Message to user writeLog(' Finding Reference Region...', logFileID) try: # Read kmlBounds.txt file and get contents kmlInfo = readTruthRegionsTxtFile(kmlBoundsTextFile) # Loop through kmlBounds.txt and find matching TRUTH area maxCounter = len(kmlInfo.regionName) counter = 0 while (not kmlRegionName): latInFile = ( atl03_lat >= kmlInfo.latMin[counter]) & ( atl03_lat <= kmlInfo.latMax[counter]) lonInFile = ( atl03_lon >= kmlInfo.lonMin[counter]) & ( atl03_lon <= kmlInfo.lonMax[counter]) trackInRegion = any(latInFile & lonInFile) if (trackInRegion): # Get truth region info kmlRegionName = kmlInfo.regionName[counter] kmlLatMin = kmlInfo.latMin[counter] kmlLatMax = kmlInfo.latMax[counter] kmlLonMin = kmlInfo.lonMin[counter] kmlLonMax = kmlInfo.lonMax[counter] # Print truth region writeLog( ' Reference File Region: %s' % kmlRegionName, logFileID) # endIf if (counter >= maxCounter): # Send message to user writeLog( ' No Reference File Region Found in kmlBounds.txt', logFileID) break # endIf # Increment counter counter += 1 # endWhile except: pass # endTry # endIf if (kmlRegionName): # Trim ATL03 data based on TRUTH region writeLog( ' Auto-Trimming Data Based on Reference Region...', logFileID) atl03IndsInRegion = (atl03_lat >= kmlLatMin) & ( atl03_lat <= kmlLatMax) & (atl03_lon >= kmlLonMin) & ( atl03_lon <= kmlLonMax) atl03_lat = atl03_lat[atl03IndsInRegion] atl03_lon = atl03_lon[atl03IndsInRegion] atl03_z = atl03_z[atl03IndsInRegion] atl03_zMsl = atl03_zMsl[atl03IndsInRegion] atl03_time = atl03_time[atl03IndsInRegion] atl03_deltaTime = atl03_deltaTime[atl03IndsInRegion] atl03_signalConf = atl03_signalConf[atl03IndsInRegion] atl03_classification = atl03_classification[ atl03IndsInRegion] atl03_intensity = atl03_intensity[atl03IndsInRegion] # Trim ATL08 data based on TRUTH region if (atl08FilePath): atl08IndsInRegion = (atl08_lat >= kmlLatMin) & ( atl08_lat <= kmlLatMax ) & (atl08_lon >= kmlLonMin) & (atl08_lon <= kmlLonMax) atl08_lat = atl08_lat[atl08IndsInRegion] atl08_lon = atl08_lon[atl08IndsInRegion] atl08_maxCanopy = atl08_maxCanopy[atl08IndsInRegion] atl08_teBestFit = atl08_teBestFit[atl08IndsInRegion] atl08_teMedian = atl08_teMedian[atl08IndsInRegion] atl08_maxCanopyMsl = atl08_maxCanopyMsl[ atl08IndsInRegion] atl08_teBestFitMsl = atl08_teBestFitMsl[ atl08IndsInRegion] atl08_teMedianMsl = atl08_teMedianMsl[ atl08IndsInRegion] atl08_time = atl08_time[atl08IndsInRegion] atl08_deltaTime = atl08_deltaTime[atl08IndsInRegion] atl08_signalConf = atl08_signalConf[atl08IndsInRegion] atl08_classification = atl08_classification[ atl08IndsInRegion] atl08_intensity = atl08_intensity[atl08IndsInRegion] #endIf # endIf # endIf # Convert lat/lon coordinates to UTM writeLog(' Converting Lat/Lon to UTM...', logFileID) # Allow function to determine UTM zone for ATL03 atl03_easting, atl03_northing, zone, hemi = getLatLon2UTM( atl03_lon, atl03_lat) # Allow function to determine UTM zone for ATL03 if (atl08FilePath): atl08_easting, atl08_northing, atl08_zone, atl08_hemi = getLatLon2UTM( atl08_lon, atl08_lat) # endIf # Print UTM zone writeLog(' UTM Zone: %s %s' % (zone, hemi), logFileID) # Transform MEASURED data to CT/AT plane writeLog(' Computing CT/AT Frame Rotation...', logFileID) desiredAngle = 90 atl03_crossTrack, atl03_alongTrack, R_mat, xRotPt, yRotPt, phi = getCoordRotFwd( atl03_easting, atl03_northing, [], [], [], desiredAngle) if (atl08FilePath): atl08_crossTrack, atl08_alongTrack, _, _, _, _ = getCoordRotFwd( atl08_easting, atl08_northing, R_mat, xRotPt, yRotPt, []) # endIf # Store rotation object rotationData = atlRotationStruct(R_mat, xRotPt, yRotPt, desiredAngle, phi) # Reassign class -1 to 0 (NOTE: this may need to change later) inds_eq_neg1 = atl03_classification == -1 atl03_classification[inds_eq_neg1] = 0 # Store data in class structure atl03Data = atl03Struct(atl03_lat, atl03_lon, \ atl03_easting, atl03_northing, \ atl03_crossTrack, atl03_alongTrack, \ atl03_z, \ atl03_zMsl, atl03_time, \ atl03_deltaTime, \ atl03_signalConf, atl03_classification, atl03_intensity, \ gtNum, beamNum, beamStrength, zone, hemi, \ atl03FilePath, atl03FileName, \ trackDirection, \ atl03h5Info, \ dataIsMapped) # Store data in class structure if (atl08FilePath): atl08Data = atl08Struct(atl08_lat, atl08_lon, \ atl08_easting, atl08_northing, \ atl08_crossTrack, atl08_alongTrack, \ atl08_maxCanopy, atl08_teBestFit, atl08_teMedian, \ atl08_maxCanopyMsl, atl08_teBestFitMsl, atl08_teMedianMsl, \ atl08_time, \ atl08_deltaTime, \ atl08_signalConf, atl08_classification, atl08_intensity, \ gtNum, atl08_zone, atl08_hemi, \ atl08FilePath, atl08FileName, \ trackDirection, \ atl08h5Info, \ dataIsMapped) # endIf # Create output ATL03 .las file if (createAtl03LasFile): writeLog(' Writing ATL03 .las file...', logFileID) outName = atl03Data.atl03FileName + '_' + atl03Data.gtNum + '.las' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Get projection if (atl03Data.zone == '3413' or atl03Data.zone == '3976'): # NSIDC Polar Stereographic North/South cases (3413 = Arctic, 3976 = Antarctic) lasProjection = atl03Data.hemi # Write .las file writeLas(np.ravel(atl03Data.easting), np.ravel(atl03Data.northing), np.ravel(atl03Data.z), lasProjection, outPath, np.ravel(atl03Data.classification), np.ravel(atl03Data.intensity), np.ravel(atl03Data.signalConf)) else: # Write .las file for UTM projection case writeLas(np.ravel(atl03Data.easting), np.ravel(atl03Data.northing), np.ravel(atl03Data.z), 'utm', outPath, np.ravel(atl03Data.classification), np.ravel(atl03Data.intensity), np.ravel(atl03Data.signalConf), atl03Data.hemi, atl03Data.zone) # endIf # endIf # Create output ATL03 .kml file if (createAtl03KmlFile): writeLog(' Writing ATL03 .kml file...', logFileID) outName = atl03Data.atl03FileName + '_' + atl03Data.gtNum + '.kml' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Get array of input time values timeStep = 1 # seconds timeVals = np.arange(np.min(atl03Data.time), np.max(atl03Data.time) + 1, timeStep) # Get closest time values from IceSat MEASURED data timeIn, indsToUse = getClosest(atl03Data.time, timeVals) # Reduce lat/lon values to user-specified time scale lonsIn = atl03Data.lon[indsToUse] latsIn = atl03Data.lat[indsToUse] # Write .kml file writeKml(latsIn, lonsIn, timeIn, outPath) # endIf # Create output ATL08 .kml file if (createAtl08KmlFile): if (atl08FilePath): writeLog(' Writing ATL08 .kml file...', logFileID) outName = atl08Data.atl08FileName + '_' + atl08Data.gtNum + '.kml' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Get array of input time values timeStep = 1 # seconds timeVals = np.arange(np.min(atl08Data.time), np.max(atl08Data.time) + 1, timeStep) # Get closest time values from IceSat MEASURED data timeIn, indsToUse = getClosest(atl08Data.time, timeVals) # Reduce lat/lon values to user-specified time scale lonsIn = atl08Data.lon[indsToUse] latsIn = atl08Data.lat[indsToUse] # Write .kml file writeKml(latsIn, lonsIn, timeIn, outPath) # endIf # endIf # Create output ATL03 .csv file if (createAtl03CsvFile): writeLog(' Writing ATL03 .csv file...', logFileID) outName = atl03Data.atl03FileName + '_' + atl03Data.gtNum + '.csv' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Write .csv file if (atl03Data.zone == '3413' or atl03Data.zone == '3976'): namelist = ['Time (sec)', 'Delta Time (sec)', \ 'Latitude (deg)', 'Longitude (deg)', \ 'Polar Stereo X (m)', 'Polar Stereo Y (m)', \ 'Cross-Track (m)', 'Along-Track (m)', \ 'Height (m HAE)', 'Height (m MSL)', \ 'Classification', 'Signal Confidence'] else: namelist = ['Time (sec)', 'Delta Time (sec)', \ 'Latitude (deg)', 'Longitude (deg)', \ 'Easting (m)', 'Northing (m)', \ 'Cross-Track (m)', 'Along-Track (m)', \ 'Height (m HAE)', 'Height (m MSL)', \ 'Classification', 'Signal Confidence'] # endIf datalist = [atl03Data.time, atl03Data.deltaTime, \ atl03Data.lat, atl03Data.lon, \ atl03Data.easting, atl03Data.northing, \ atl03Data.crossTrack, atl03Data.alongTrack,\ atl03Data.z, atl03Data.zMsl, \ atl03Data.classification, atl03Data.signalConf] writeArrayToCSV(outPath, namelist, datalist) # endIf # Create output ATL08 .csv file if (createAtl08CsvFile): if (atl08FilePath): writeLog(' Writing ATL08 .csv file...', logFileID) outName = atl08Data.atl08FileName + '_' + atl08Data.gtNum + '.csv' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Write .csv file if (atl03Data.zone == '3413' or atl03Data.zone == '3976'): namelist = ['Time (sec)', 'Delta Time (sec)', \ 'Latitude (deg)', 'Longitude (deg)', \ 'Polar Stereo X (m)', 'Polar Stereo Y (m)', \ 'Cross-Track (m)', 'Along-Track (m)', \ 'Max Canopy (m)', \ 'Terrain Best Fit (m)', 'Terrain Median (m)'] else: namelist = ['Time (sec)', 'Delta Time (sec)', \ 'Latitude (deg)', 'Longitude (deg)', \ 'Easting (m)', 'Northing (m)', \ 'Cross-Track (m)', 'Along-Track (m)', \ 'Max Canopy (m)', \ 'Terrain Best Fit (m)', 'Terrain Median (m)'] # endIf datalist = [atl08Data.time, atl08Data.deltaTime, \ atl08Data.lat, atl08Data.lon, \ atl08Data.easting, atl08Data.northing, \ atl08Data.crossTrack, atl08Data.alongTrack,\ atl08Data.maxCanopy, \ atl08Data.teBestFit, atl08Data.teMedian] writeArrayToCSV(outPath, namelist, datalist) # endIf # endIf # End timer timeEnd = runTime.time() timeElapsedTotal = timeEnd - timeStart timeElapsedMin = np.floor(timeElapsedTotal / 60) timeElapsedSec = timeElapsedTotal % 60 # Print completion message writeLog('', logFileID) writeLog( ' Module Completed in %d min %d sec.' % (timeElapsedMin, timeElapsedSec), logFileID) writeLog('\n', logFileID) else: writeLog('\n', logFileID) writeLog(' *** Could not process data.', logFileID) writeLog(' *** ATL03 .h5 file missing these fields:', logFileID) for i in range(0, len(badVars)): writeLog(' %s) %s' % (i + 1, badVars[i]), logFileID) writeLog('\n', logFileID) # endIf else: # Message to user writeLog('Input correct ATL03 input .h5 file and/or output path.', logFileID) # endIf # Return object return atl03Data, atl08Data, rotationData
def getAtlTruthSwath(atlMeasuredData, headerData, rotationData, useExistingTruth, truthSwathDir, buffer, outFilePath, createTruthFile): # Start timer timeStart = runTime.time() # Print message print(' Ground Track Number: %s' % atlMeasuredData.gtNum) # Initialze truthFilePath/truthDir truthFilePath = [] truthDir = [] # atlTruthDataFiltered = [] # Initialize dem/lidar flags demfilecheck = False lidarfilecheck = False # Determine if truthSwathDir is folder or file if os.path.isfile(truthSwathDir) == True: file = ntpath.basename(truthSwathDir) truthFilePath = truthSwathDir extension = file.split('.')[-1] if (extension == 'las') or (extension == 'laz'): print('File is lidar LAS/LAZ File') lidarfilecheck = True else: print('File is DSM/DTM .tif File') demfilecheck = True # endIf else: truthDir = truthSwathDir # endIf # Get rotation data R_mat = rotationData.R_mat xRotPt = rotationData.xRotPt yRotPt = rotationData.yRotPt desiredAngle = rotationData.desiredAngle phi = rotationData.phi # If truth swath exists, read .las file if (useExistingTruth and truthFilePath and demfilecheck == False): # Read in truth file .las file print(' Reading in Lidar Truth File: %s' % truthFilePath) lasTruthData = readLas(truthFilePath) # Rotate TRUTH data to CT/AT plane print(' Rotating Data to CT/AT Frame...') lasTruthData_x_newRot, lasTruthData_y_newRot, _, _, _, _ = \ getCoordRotFwd(lasTruthData.x, lasTruthData.y, R_mat, xRotPt, yRotPt, desiredAngle) # # Store data as object # atlTruthData = atlTruthStruct(lasTruthData.x, lasTruthData.y, # lasTruthData_x_newRot, # lasTruthData_y_newRot, # lasTruthData.z, # lasTruthData.classification, # lasTruthData.intensity, # headerData.zone, headerData.hemi) # Store data as object (CHANGE AFTER DEMO!!!) atlTruthData = atlTruthStruct( lasTruthData.x, lasTruthData.y, lasTruthData_x_newRot, lasTruthData_y_newRot, lasTruthData.z, lasTruthData.classification, lasTruthData.intensity, atlMeasuredData.zone, atlMeasuredData.hemi) elif (demfilecheck == True): ###New # 1. Find EPSG Code from DEM epsg_dem = readDEMepsg(truthFilePath) # 2. Determine if EPSG Code is the same for the ATL03 Measured epsg_atl = identifyEPSG(atlMeasuredData.hemi, atlMeasuredData.zone) epsg_atl = epsg_atl.split(':')[-1] if epsg_atl == epsg_dem: print(' DEM Projection matches target') # 3b. If EPSG code is the same, set DEM as a target truthFilePathDEM = truthFilePath else: print(' Reprojecting DEM') # 3a. If EPSG code does not match, use GDAL Warp to create new DEM file = ntpath.basename(truthSwathDir) truthFilePath = truthSwathDir originalname = file.split('.')[0] newname = originalname + "_projected.tif" truthFilePathDEM = os.path.normpath( os.path.join(outFilePath, newname)) srs = ogr.osr.SpatialReference() srs.ImportFromEPSG(np.int(epsg_atl)) warpoptions = gdal.WarpOptions(dstSRS=srs) gdal.Warp(truthFilePathDEM, truthFilePath, options=warpoptions) # 4. Call the Format DEM Function print(' Reading in DEM Truth File: %s' % truthFilePathDEM) xarr, yarr, zarr, intensity, classification, epsg = \ formatDEM(truthFilePathDEM) # 5. Run a quick filter to remove points not even remotely close print(' Running Quick Filter') xmin = np.min(atlMeasuredData.easting.flatten()) - 100 xmax = np.max(atlMeasuredData.easting.flatten()) + 100 ymin = np.min(atlMeasuredData.northing.flatten()) - 100 ymax = np.max(atlMeasuredData.northing.flatten()) + 100 quickfilter = np.where((xarr > xmin) & (xarr < xmax) & (yarr > ymin) & (yarr < ymax)) xcoord = xarr[quickfilter] ycoord = yarr[quickfilter] zarr = zarr[quickfilter] intensity = intensity[quickfilter] classification = classification[quickfilter] # ###Original # # # # Apply EPSG code to X,Y # # 'epsg:32613' # epsg_in = 'epsg:' + str(epsg) # epsg_out = identifyEPSG(atlMeasuredData.hemi,atlMeasuredData.zone) # # if epsg_in == epsg_out: # print(' DEM and Measured Match EPSG Code') # xcoord = xarr # ycoord = yarr # else: # print(' Transforming Projection for DEM') # xx,yy = zip(*[transform(epsg_in, epsg_out, xarr[i],yarr[i]) \ # for i in range(0,len(xarr))]) # xcoord = np.asarray(xx) # ycoord = np.asarray(yy) # Rotate Data for along-track/cross-track print(' Rotating Data to CT/AT Frame...') x_newRot, y_newRot, _, _, _, _ = getCoordRotFwd( xcoord, ycoord, R_mat, xRotPt, yRotPt, desiredAngle) # Store Data as Object atlTruthData = atlTruthStruct(xcoord, ycoord, x_newRot, y_newRot, zarr, classification, intensity, atlMeasuredData.zone, atlMeasuredData.hemi) print(' DEM being used for ATL Truth Data') else: # Create new truth .las file print(' Creating new Truth File...') # Get MEASURED rotated buffer bounds data xRotL = atlMeasuredData.crossTrack - buffer xRotR = atlMeasuredData.crossTrack + buffer yRot = atlMeasuredData.alongTrack # Get MEASURED buffer bounds data in easting/northing plane xL, yL, _, _, _ = getCoordRotRev(xRotL, yRot, R_mat, xRotPt, yRotPt) xR, yR, _, _, _ = getCoordRotRev(xRotR, yRot, R_mat, xRotPt, yRotPt) # Rotate truth header file min/max x,y points to CT/AT plane xMinyMinHeaderRotX, _, _, _, _, _ = getCoordRotFwd( headerData.xmin, headerData.ymin, R_mat, xRotPt, yRotPt, desiredAngle) xMinyMaxHeaderRotX, _, _, _, _, _ = getCoordRotFwd( headerData.xmin, headerData.ymax, R_mat, xRotPt, yRotPt, desiredAngle) xMaxyMinHeaderRotX, _, _, _, _, _ = getCoordRotFwd( headerData.xmax, headerData.ymin, R_mat, xRotPt, yRotPt, desiredAngle) xMaxyMaxHeaderRotX, _, _, _, _, _ = getCoordRotFwd( headerData.xmax, headerData.ymax, R_mat, xRotPt, yRotPt, desiredAngle) # Find min/max x/y header points inside min/max x buffer points xMinyMinXPtsInBuffer = (xMinyMinHeaderRotX >= xRotL.min()) & \ (xMinyMinHeaderRotX <= xRotR.max()) xMinyMaxXPtsInBuffer = (xMinyMaxHeaderRotX >= xRotL.min()) & \ (xMinyMaxHeaderRotX <= xRotR.max()) xMaxyMinXPtsInBuffer = (xMaxyMinHeaderRotX >= xRotL.min()) & \ (xMaxyMinHeaderRotX <= xRotR.max()) xMaxyMaxXPtsInBuffer = (xMaxyMaxHeaderRotX >= xRotL.min()) & \ (xMaxyMaxHeaderRotX <= xRotR.max()) # Get header points inside MEASURED buffer points xHeaderPtsInBuffer = xMinyMinXPtsInBuffer | xMinyMaxXPtsInBuffer | xMaxyMinXPtsInBuffer | xMaxyMaxXPtsInBuffer # Find min/max x buffer points inside min/max x/y header points xyMinMaxHeaderRot = np.concatenate( (xMinyMinHeaderRotX, xMinyMaxHeaderRotX, xMaxyMinHeaderRotX, xMaxyMaxHeaderRotX), axis=1) xyMinHeaderRot = np.c_[np.amin(xyMinMaxHeaderRot, axis=1)] xyMaxHeaderRot = np.c_[np.amax(xyMinMaxHeaderRot, axis=1)] xMinBufferPtsInFile = (xRotL.min() >= xyMinHeaderRot) & ( xRotL.min() <= xyMaxHeaderRot) xMaxBufferPtsInFile = (xRotR.max() >= xyMinHeaderRot) & ( xRotR.max() <= xyMaxHeaderRot) # Get MEASURED buffer points inside header points xBufferPtsInHeader = xMinBufferPtsInFile | xMaxBufferPtsInFile # Get any points where buffer points are inside header and vice versa xPtsInFile = xHeaderPtsInBuffer | xBufferPtsInHeader # Get matching truth file names and x/y min/max points matchingFiles = headerData.tileName[xPtsInFile] matchingHeaderXmin = headerData.xmin[xPtsInFile] matchingHeaderXmax = headerData.xmax[xPtsInFile] matchingHeaderYmin = headerData.ymin[xPtsInFile] matchingHeaderYmax = headerData.ymax[xPtsInFile] # Get all MEASURED x,y buffer points xAll = np.concatenate((xL, xR)) yAll = np.concatenate((yL, yR)) # Initialize loop variables counter = 0 subData_x_old = [] subData_y_old = [] subData_z_old = [] subData_classification_old = [] subData_intensity_old = [] # Find matching truth files print(' Looking in Truth File Path: %s' % truthDir) print(' Matching Truth .las Files:') for i in range(0, len(matchingFiles)): # Determine TRUTH files where MEASURED data actually crosses over xPtsInFile = (xAll >= matchingHeaderXmin[i]) & ( xAll <= matchingHeaderXmax[i]) yPtsInFile = (yAll >= matchingHeaderYmin[i]) & ( yAll <= matchingHeaderYmax[i]) anyPtsInFile = any(xPtsInFile & yPtsInFile) # If a TRUTH file is a match, use it if (anyPtsInFile): # Print TRUTH file name to screen fileNum = counter + 1 print(' %s) %s' % (fileNum, matchingFiles[i])) # Build path to TRUTH file truthLasFile = os.path.normpath(truthDir + '/' + matchingFiles[i]) # Try to read .las file try: # Read TRUTH .las/.laz file # truthLasFile = truthLasFile.split('.')[0][:-3] + 'las.las' # print(truthLasFile) lasData = readLas(truthLasFile) except: # Print Error message print(' ERROR: Could not read file.') lasData = False # endTry # If there is .las data, process it if (lasData): # Covert to UTM coords if in lat/lon if (headerData.coordType == 'Lat/Lon'): lasDataNewX, lasDataNewY, _, _ = getLatLon2UTM( lasData.x, lasData.y) lasData = atlTruthStruct(lasDataNewX, lasDataNewY, lasData.z, lasData.classification, lasData.intensity, headerData.zone, headerData.hemi) # endif # Rotate TRUTH lasData to CT/AT plane xRotLasData, yRotLasData, _, _, _, _ = getCoordRotFwd( lasData.x, lasData.y, R_mat, xRotPt, yRotPt, desiredAngle) # Find TRUTH lasData points inside TRUTH buffer yRotLocalInds = (yRot >= yRotLasData.min()) & ( yRot <= yRotLasData.max()) xRotLocalL = xRotL[yRotLocalInds] xRotLocalR = xRotR[yRotLocalInds] xyBufferInds = (xRotLasData >= xRotLocalL.min()) & ( xRotLasData <= xRotLocalR.max()) # Extract TRUTH lasData points inside TRUTH buffer subData_x = lasData.x[xyBufferInds] subData_y = lasData.y[xyBufferInds] subData_z = lasData.z[xyBufferInds] subData_classification = lasData.classification[ xyBufferInds] subData_intensity = lasData.intensity[xyBufferInds] # Compute ACE subData_x, subData_y, subData_z, subData_classification, subData_intensity = get_ace( subData_x, subData_y, subData_z, subData_classification, subData_intensity) # Append TRUTH buffer points onto previous data subData_x_new = np.append(subData_x_old, subData_x) subData_y_new = np.append(subData_y_old, subData_y) subData_z_new = np.append(subData_z_old, subData_z) subData_classification_new = np.append( subData_classification_old, subData_classification) subData_intensity_new = np.append(subData_intensity_old, subData_intensity) # Store old TRUTH buffer data subData_x_old = subData_x_new subData_y_old = subData_y_new subData_z_old = subData_z_new subData_classification_old = subData_classification_new subData_intensity_old = subData_intensity_new # endIf # Increment counter counter += 1 # endIf # endFor # Store TRUTH swath data if it exists if ('subData_x_new' in locals()): # Rotate TRUTH data to CT/AT plane print(' Rotating Data to CT/AT Frame...') subData_x_newRot, subData_y_newRot, _, _, _, _ = getCoordRotFwd( subData_x_new, subData_y_new, R_mat, xRotPt, yRotPt, desiredAngle) # Store data in class structure atlTruthData = atlTruthStruct(subData_x_new, subData_y_new, subData_x_newRot, subData_y_newRot, subData_z_new, subData_classification_new, subData_intensity_new, headerData.zone, headerData.hemi) # Apply geoid model Z corrections if necessary regionName = atlMeasuredData.kmlRegionName # Initialize geoidFile variable geoidFile = False # Determine geoid model to use if ('sonoma' in regionName.lower() or 'indiana' in regionName.lower()): geoidFile = 'geoid12b_latlon.mat' elif ('finland' in regionName.lower()): geoidFile = 'geoidFin2005N00_latlon.mat' elif ('rangiora' in regionName.lower() or 'wellington' in regionName.lower()): geoidFile = 'geoidNZ_latlon.mat' # endif if (geoidFile): # Print status message print('') print( ' STATUS: Applying Truth Z Correction for %s (Geoid File = %s).' % (regionName, geoidFile)) # Load Geoid file geoidData = readGeoidFile(geoidFile) # Get geoidal heights and add to orthometric heights atlTruthData = getGeoidHeight(geoidData, atlTruthData) # EndIf else: # Set to false print(' STATUS: No Truth Data Stored for this Track.') atlTruthData = False # endIf # endIf # Create output file if (createTruthFile and atlTruthData): print(' Writing truth .las file...') outName = atlMeasuredData.atl03FileName + '_' + atlMeasuredData.gtNum + '_REFERENCE_' + str( buffer) + 'L' + str(buffer) + 'Rm_buffer.las' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Write .las file writeLas(np.ravel(atlTruthData.easting), np.ravel(atlTruthData.northing), np.ravel(atlTruthData.z), 'utm', outPath, np.ravel(atlTruthData.classification), np.ravel(atlTruthData.intensity), None, atlTruthData.hemi, atlTruthData.zone) # endIf # End timer timeEnd = runTime.time() timeElapsedTotal = timeEnd - timeStart timeElapsedMin = np.floor(timeElapsedTotal / 60) timeElapsedSec = timeElapsedTotal % 60 # Print completion message print(' Module Completed in %d min %d sec.' % (timeElapsedMin, timeElapsedSec)) print('\n') # Return object return atlTruthData
def getMeasurementError(atlMeasuredData, atlTruthData, rotationData, outFilePath, useMeasSigConf, filterData, offsets, createMeasCorrFile, makePlots, showPlots): # Start timer timeStart = runTime.time() # Get and print ground track number gtNum = atlMeasuredData.gtNum print(' Ground Track Number: %s' % gtNum) # Turn off runtime warnings (from np.nanmean) if not sys.warnoptions: warnings.simplefilter("ignore") # EndIf # Get array of offsets crossTrackBounds = offsets.crossTrackBounds alongTrackBounds = offsets.alongTrackBounds rasterResolutions = offsets.rasterResolutions # Get reduced MEASURED data (limited to bounds of TRUTH data) measInTruthInds = ( atlMeasuredData.alongTrack >= atlTruthData.alongTrack.min()) & ( atlMeasuredData.alongTrack <= atlTruthData.alongTrack.max()) atlMeasuredDataReducedX = atlMeasuredData.easting[measInTruthInds] atlMeasuredDataReducedY = atlMeasuredData.northing[measInTruthInds] atlMeasuredDataReducedZ = atlMeasuredData.z[measInTruthInds] atlMeasuredDataReducedXrot = atlMeasuredData.crossTrack[measInTruthInds] atlMeasuredDataReducedYrot = atlMeasuredData.alongTrack[measInTruthInds] atlMeasuredDataReducedTime = atlMeasuredData.time[measInTruthInds] atlMeasuredDataReducedClass = atlMeasuredData.classification[ measInTruthInds] atlMeasuredDataReducedSignal = atlMeasuredData.signalConf[measInTruthInds] # Get MEASURED reduced class atlMeasuredDataReduced = atlMeasuredDataReducedStruct(atlMeasuredDataReducedX, \ atlMeasuredDataReducedY, \ atlMeasuredDataReducedZ, \ atlMeasuredDataReducedXrot, \ atlMeasuredDataReducedYrot, \ atlMeasuredDataReducedTime, \ atlMeasuredDataReducedClass, \ atlMeasuredDataReducedSignal) # Filter Ground data from ATL08 if available if (useMeasSigConf): print(' Using Signal Confidence for Measurement Offset Computation') # Filter MEASURED data points based on signal confidence measGroundInds = ismember(atlMeasuredDataReduced.signalConf, filterData)[0] measXRot = atlMeasuredDataReduced.crossTrack[measGroundInds] measYRot = atlMeasuredDataReduced.alongTrack[measGroundInds] measZ = atlMeasuredDataReduced.z[measGroundInds] measT = atlMeasuredDataReduced.time[measGroundInds] # Do not filter ground TRUTH data points truthXRot = atlTruthData.crossTrack truthYRot = atlTruthData.alongTrack truthZ = atlTruthData.z else: print(' Using Ground Truth for Measurement Offset Computation') # Filter ground MEASURED data points icesatGroundIndex = 1 measGroundInds = ismember(atlMeasuredDataReduced.classification, icesatGroundIndex)[0] measXRot = atlMeasuredDataReduced.crossTrack[measGroundInds] measYRot = atlMeasuredDataReduced.alongTrack[measGroundInds] measZ = atlMeasuredDataReduced.z[measGroundInds] measT = atlMeasuredDataReduced.time[measGroundInds] # Filter ground TRUTH data points truthGroundInds = ismember(atlTruthData.classification, filterData)[0] truthXRot = atlTruthData.crossTrack[truthGroundInds] truthYRot = atlTruthData.alongTrack[truthGroundInds] truthZ = atlTruthData.z[truthGroundInds] # endIf if ((len(crossTrackBounds) > 1) and (len(alongTrackBounds) > 1)): # Initialize parameters correctionsCrossTrack = 0 correctionsAlongTrack = 0 crossTrackEdgeError = 0 alongTrackEdgeError = 0 numErr = 0 crossTrackBoundLimits = [-48, 48] alongTrackBoundLimits = [-1000, 1000] def limBounds(x, y, lim): # x = max([x, lim[0]]) # y = min([lim[1], y]) return x, y # Get array offsets crossTrackMinBound = ( np.fix(crossTrackBounds[0] / rasterResolutions[0]) * rasterResolutions[0]).astype(int) crossTrackMaxBound = (np.fix( (crossTrackBounds[-1]) / rasterResolutions[0]) * rasterResolutions[0]).astype(int) alongTrackMinBound = ( np.fix(alongTrackBounds[0] / rasterResolutions[0]) * rasterResolutions[0]).astype(int) alongTrackMaxBound = (np.fix( (alongTrackBounds[-1]) / rasterResolutions[0]) * rasterResolutions[0]).astype(int) # Loop through all raster resolutions (multi-resolutional approach) totRasterResolutions = len(rasterResolutions) # for i in range(0,totRasterResolutions): i = 0 while i < totRasterResolutions: # Get current raster resolution rasterResolution = rasterResolutions[i] # Set cross-track/along-track offsets if (i > 0): # Get previous raster resolution rasterResolutionPrev = rasterResolutions[i - 1] # Set cross-track offsets crossTrackBoundStart = correctionsCrossTrack - rasterResolutionPrev - rasterResolution + crossTrackEdgeError crossTrackBoundEnd = correctionsCrossTrack + rasterResolutionPrev + rasterResolution + crossTrackEdgeError crossTrackBoundStart, crossTrackBoundEnd = limBounds( crossTrackBoundStart, crossTrackBoundEnd, crossTrackBoundLimits) crossTrackMinBound = ( np.fix(crossTrackBoundStart / rasterResolution) * rasterResolution).astype(int) crossTrackMaxBound = ( np.fix(crossTrackBoundEnd / rasterResolution) * rasterResolution).astype(int) crossTrackOffsets = np.arange(crossTrackMinBound, crossTrackMaxBound + 1, rasterResolution) # Set along-track offsets alongTrackBoundStart = correctionsAlongTrack - rasterResolutionPrev - rasterResolution + alongTrackEdgeError alongTrackBoundEnd = correctionsAlongTrack + rasterResolutionPrev + rasterResolution + alongTrackEdgeError alongTrackBoundStart, alongTrackBoundEnd = limBounds( alongTrackBoundStart, alongTrackBoundEnd, alongTrackBoundLimits) alongTrackMinBound = ( np.fix(alongTrackBoundStart / rasterResolution) * rasterResolution).astype(int) alongTrackMaxBound = ( np.fix(alongTrackBoundEnd / rasterResolution) * rasterResolution).astype(int) alongTrackOffsets = np.arange(alongTrackMinBound, alongTrackMaxBound + 1, rasterResolution) else: # Set cross-track offsets crossTrackBoundStart = crossTrackBounds[0] + crossTrackEdgeError crossTrackBoundEnd = crossTrackBounds[-1] + crossTrackEdgeError crossTrackBoundStart, crossTrackBoundEnd = limBounds( crossTrackBoundStart, crossTrackBoundEnd, crossTrackBoundLimits) crossTrackMinBound = ( np.fix(crossTrackBoundStart / rasterResolutions[0]) * rasterResolutions[0]).astype(int) crossTrackMaxBound = ( np.fix(crossTrackBoundEnd / rasterResolutions[0]) * rasterResolutions[0]).astype(int) crossTrackOffsets = np.arange(crossTrackMinBound, crossTrackMaxBound + 1, rasterResolutions[0]) # Set along-track offsets alongTrackBoundStart = alongTrackBounds[0] + alongTrackEdgeError alongTrackBoundEnd = alongTrackBounds[-1] + alongTrackEdgeError alongTrackBoundStart, alongTrackBoundEnd = limBounds( alongTrackBoundStart, alongTrackBoundEnd, alongTrackBoundLimits) alongTrackMinBound = ( np.fix(alongTrackBoundStart / rasterResolutions[0]) * rasterResolutions[0]).astype(int) alongTrackMaxBound = ( np.fix(alongTrackBoundEnd / rasterResolutions[0]) * rasterResolutions[0]).astype(int) alongTrackOffsets = np.arange(alongTrackMinBound, alongTrackMaxBound + 1, rasterResolutions[0]) # endIf # Print message to screen print('') print(' Test Case %d of %d' % (i + 1, totRasterResolutions)) if not (crossTrackEdgeError == 0 and alongTrackEdgeError == 0): print(' Relocated search center to [%s, %s]' % (crossTrackEdgeError, alongTrackEdgeError)) print(' Raster Resolution = %s m' % rasterResolution) print(' Cross Track Offsets = [%s, %s]' % (crossTrackOffsets[0], crossTrackOffsets[-1])) print(' Along Track Offsets = [%s, %s]' % (alongTrackOffsets[0], alongTrackOffsets[-1])) # Make Offsets Int's and flip along-track offsets array so larger numbers at top crossTrackOffsets = crossTrackOffsets.astype(int) alongTrackOffsets = (np.flipud(alongTrackOffsets)).astype(int) # Format TRUTH data truthXRotReduced = np.ravel(truthXRot) truthYRotReduced = np.ravel(truthYRot) truthZReduced = np.ravel(truthZ) # Rasterize MEASURED data gridMethod = 'Mean' fillValue = np.nan print( ' Gridding Measured Data at %s m Resolution using %s Values...' % (rasterResolution, gridMethod)) measRasterRot = getRaster(measXRot, measYRot, measZ, rasterResolution, gridMethod, fillValue, measT) # Rasterize TRUTH data print( ' Gridding Truth Data at %s m Resolution using %s Values...' % (rasterResolution, gridMethod)) truthRasterRot = getRaster(truthXRotReduced, truthYRotReduced, truthZReduced, rasterResolution, gridMethod, fillValue) # Find offsets with minimum MAE print(' Finding Offsets with Minimum MAE...') # Store MEASURED data into array variables measRasterXRot = np.c_[np.ravel(measRasterRot.x)] measRasterYRot = np.c_[np.ravel(measRasterRot.y)] measRasterZ = np.c_[np.ravel(measRasterRot.grid)] measRasterT = np.c_[np.ravel(measRasterRot.t)] # Store TRUTH data into array variables truthRasterXRot = np.c_[np.ravel(truthRasterRot.x)] truthRasterYRot = np.c_[np.ravel(truthRasterRot.y)] truthRasterZ = np.c_[np.ravel(truthRasterRot.grid)] # Create 2 column array of all MEASURED X,Y data measXYall = np.column_stack([measRasterXRot, measRasterYRot]) # Create 2 column array of all TRUTH X,Y data truthXYall = np.column_stack([truthRasterXRot, truthRasterYRot]) # Find common TRUTH and MEASURED indices _, truthIndsCommon, measIndsCommon = getIntersection2d( truthXYall, measXYall, assume_unique=True) # Only use MEASURED indices in common with TRUTH indices measRasterXRot_common = measRasterXRot[measIndsCommon] measRasterYRot_common = measRasterYRot[measIndsCommon] measRasterZ_common = measRasterZ[measIndsCommon] measRasterT_common = measRasterT[measIndsCommon] # Find row, col for indices in TRUTH raster (that are in common with MEASURED raster) truthColsInit = (truthIndsCommon % np.shape(truthRasterRot.x)[1]).astype(int) truthRowsInit = (np.floor( truthIndsCommon / np.shape(truthRasterRot.x)[1])).astype(int) # Find min/max row, col for indicies in TRUTH raster truthMinXBounds = 0 truthMaxXBounds = np.shape(truthRasterRot.grid)[1] truthMinYBounds = 0 truthMaxYBounds = np.shape(truthRasterRot.grid)[0] # Pre-allocate results data numHorzCombos = np.size(crossTrackOffsets) numVertCombos = np.size(alongTrackOffsets) resultsCrossTrackShift = np.zeros((numVertCombos, numHorzCombos)) resultsAlongTrackShift = np.zeros((numVertCombos, numHorzCombos)) resultsVerticalShift = np.zeros((numVertCombos, numHorzCombos)) resultsMAE = np.zeros((numVertCombos, numHorzCombos)) resultsRMSE = np.zeros((numVertCombos, numHorzCombos)) resultsME = np.zeros((numVertCombos, numHorzCombos)) # Loop through all combos and find min MAE for I in range(0, numHorzCombos): for J in range(0, numVertCombos): # Get cross-track/along-track offset values crossTrackShift = crossTrackOffsets[I] alongTrackShift = alongTrackOffsets[J] # Get amount to shift X,Y indices moveIndsX = (crossTrackShift / rasterResolution).astype(int) moveIndsY = (alongTrackShift / rasterResolution).astype(int) # Get new TRUTH data from shifted X,Y indices truthColsCurrent = truthColsInit + moveIndsX truthRowsCurrent = truthRowsInit - moveIndsY # y array is inverted # Determine indices in bounds colsToKeep = (truthColsCurrent >= truthMinXBounds) & ( truthColsCurrent < truthMaxXBounds) rowsToKeep = (truthRowsCurrent >= truthMinYBounds) & ( truthRowsCurrent < truthMaxYBounds) indsToKeep = colsToKeep & rowsToKeep # Get current TRUTH Z points truthColsCurrent = truthColsCurrent[indsToKeep] truthRowsCurrent = truthRowsCurrent[indsToKeep] truthRasterZCurr = np.c_[np.ravel( truthRasterRot.grid[truthRowsCurrent, truthColsCurrent])] # Get current MEASURED Z points measRasterZCurr = measRasterZ_common[indsToKeep] # Find Z error zError = truthRasterZCurr - measRasterZCurr # Determine vertical shift if (offsets.useVerticalShift == 1): # Use defined vertical shift verticalShift = offsets.verticalShift else: # MAE is tied to median error (RMSE to mean error) verticalShift = np.nanmedian(zError) # Endif if (np.logical_not(np.isnan(verticalShift))): # Apply vertical shift measRasterZShifted = measRasterZCurr + verticalShift # Get new Z error zError = truthRasterZCurr - measRasterZShifted # Get Mean Absolute Error and RMSE MAE = np.nanmean(abs(zError)) RMSE = np.sqrt(np.nanmean(zError**2)) ME = np.nanmean(zError) else: # Populate with default values verticalShift = np.nan MAE = np.nan RMSE = np.nan ME = np.nan # endIf # Store results resultsCrossTrackShift[J, I] = crossTrackShift resultsAlongTrackShift[J, I] = alongTrackShift resultsVerticalShift[J, I] = verticalShift resultsMAE[J, I] = MAE resultsRMSE[J, I] = RMSE resultsME[J, I] = ME # endFor # endFor # # TEST!!! # # Set first/last rows/columns to NaN on first iteration to avoid choosing edge cases # if(i==0): # resultsMAE[0,:] = np.nan # resultsMAE[-1,:] = np.nan # resultsMAE[:,0] = np.nan # resultsMAE[:,-1] = np.nan # # endIf # Find row, col for min MAE minRow = np.where(resultsMAE == np.nanmin(resultsMAE))[0] minCol = np.where(resultsMAE == np.nanmin(resultsMAE))[1] if minRow == [] or minCol == []: print( 'Error: Gridding all NaN, ensure truth/meas files are correct' ) return [] # Check that estimate is not close to an edge nr, nc = np.shape(resultsMAE) dr, dc = 1, 1 # edge "closeness", index units inBounds = minRow in range(0 + dr, nr - dr) and minCol in range( 0 + dc, nc - dc) if not inBounds: # estimated an edge p = np.array([ float(resultsCrossTrackShift[minRow, minCol]), float(resultsAlongTrackShift[minRow, minCol]) ]) mid = np.array([ np.mean([crossTrackBoundStart, crossTrackBoundEnd]), np.mean([alongTrackBoundStart, alongTrackBoundEnd]) ]) vec_diff = p - mid numErr += 1 if numErr == 5: print( ' Error: Could not determine valid estimate, consider increasing search bounds' ) return [] else: crossTrackEdgeError += vec_diff[0] #p[0] alongTrackEdgeError += vec_diff[1] #p[1] print( ' Warning: Estimate on edge [%4.1f, %4.1f] m, relocating search and re-running test case %d...' % (p[0], p[1], i + 1)) continue # do not update i # Set edge error to zero if inBounds, this way # we center on estimate and zoom in since estimate is good numErr = 0 crossTrackEdgeError = 0 alongTrackEdgeError = 0 # Get output associated with min MAE correctionsCrossTrack = resultsCrossTrackShift[minRow, minCol] correctionsAlongTrack = resultsAlongTrackShift[minRow, minCol] correctionsVertical = resultsVerticalShift[minRow, minCol] correctionsMAE = resultsMAE[minRow, minCol] correctionsRMSE = resultsRMSE[minRow, minCol] correctionsME = resultsME[minRow, minCol] # Get CT/AT offsets in Easting/Northing plane R_mat = rotationData.R_mat xRotPt = rotationData.xRotPt yRotPt = rotationData.yRotPt correctionsEasting, correctionsNorthing, _, _, _ = getCoordRotRev( correctionsCrossTrack, correctionsAlongTrack, R_mat, xRotPt, yRotPt) correctionsEasting -= xRotPt correctionsNorthing -= yRotPt # Print results to screen print(' Cross-Track Correction = %d m (Easting = %0.1f m)' % (correctionsCrossTrack, correctionsEasting)) print(' Along-Track Correction = %d m (Northing = %0.1f m)' % (correctionsAlongTrack, correctionsNorthing)) print(' Vertical Correction = %0.1f m' % correctionsVertical) print(' MAE = %0.2f m' % correctionsMAE) print(' RMSE = %0.2f m' % correctionsRMSE) # Store data in class structure atlCorrections = atlCorrectionsStruct(correctionsCrossTrack, correctionsAlongTrack, \ correctionsEasting, correctionsNorthing, \ correctionsVertical, \ correctionsMAE, correctionsRMSE, correctionsME) # Get final MEASURED raster data measRasterXRotFinal = measRasterXRot + atlCorrections.crossTrack measRasterYRotFinal = measRasterYRot + atlCorrections.alongTrack measRasterZFinal = measRasterZ + atlCorrections.z measRasterTFinal = measRasterT # Get amount to shift X,Y indices moveIndsX = (atlCorrections.crossTrack / rasterResolution).astype(int) moveIndsY = (atlCorrections.alongTrack / rasterResolution).astype(int) # Get new TRUTH data from shifted X,Y indices truthColsCurrent = truthColsInit + moveIndsX truthRowsCurrent = truthRowsInit - moveIndsY # y array is inverted # Determine indices in bounds colsToKeep = (truthColsCurrent >= truthMinXBounds) & ( truthColsCurrent < truthMaxXBounds) rowsToKeep = (truthRowsCurrent >= truthMinYBounds) & ( truthRowsCurrent < truthMaxYBounds) indsToKeep = colsToKeep & rowsToKeep # Get final common TRUTH data truthColsCurrent = truthColsCurrent[indsToKeep] truthRowsCurrent = truthRowsCurrent[indsToKeep] truthRasterXRotCommonFinal = np.c_[np.ravel( truthRasterRot.x[truthRowsCurrent, truthColsCurrent])] truthRasterYRotCommonFinal = np.c_[np.ravel( truthRasterRot.y[truthRowsCurrent, truthColsCurrent])] truthRasterZCommonFinal = np.c_[np.ravel( truthRasterRot.grid[truthRowsCurrent, truthColsCurrent])] # Get final common MEASURED data measRasterXRotCommonFinal = measRasterXRot_common[ indsToKeep] + atlCorrections.crossTrack measRasterYRotCommonFinal = measRasterYRot_common[ indsToKeep] + atlCorrections.alongTrack measRasterZCommonFinal = measRasterZ_common[ indsToKeep] + atlCorrections.z measRasterTCommonFinal = measRasterT_common[indsToKeep] # Get Z Error raster (MEASURED - TRUTH) zErrorCommonFinal = measRasterZCommonFinal - truthRasterZCommonFinal # Get 100 m segment Z Mean Error data errorResolution = 100 gridMethod = 'Mean' fillValue = np.nan segmentError = getRaster(np.ravel(measRasterXRotCommonFinal), np.ravel(measRasterYRotCommonFinal), np.ravel(zErrorCommonFinal), errorResolution, gridMethod, fillValue) segmentErrorX = np.mean(segmentError.x, axis=1) segmentErrorY = np.mean(segmentError.y, axis=1) segmentErrorZ = np.nanmean(segmentError.grid, axis=1) # Format Y,Z values for plotting segmentErrorXYZ = np.column_stack( [segmentErrorX, segmentErrorY, segmentErrorZ]) segmentErrorXYZsorted = segmentErrorXYZ[ segmentErrorXYZ[:, 1].argsort(), ] segmentErrorLo = segmentErrorXYZsorted[:, 1] - errorResolution * 0.5 segmentErrorHi = segmentErrorXYZsorted[:, 1] + errorResolution * 0.5 segmentErrorXin = (np.column_stack( [segmentErrorXYZsorted[:, 0], segmentErrorXYZsorted[:, 0]])).flatten() segmentErrorYin = (np.column_stack( [segmentErrorLo, segmentErrorHi])).flatten() segmentErrorZin = (np.column_stack( [segmentErrorXYZsorted[:, 2], segmentErrorXYZsorted[:, 2]])).flatten() # Convert to easting/northing plane _, segmentErrorY_plot, _, _, _ = getCoordRotRev( segmentErrorXin, segmentErrorYin, R_mat, xRotPt, yRotPt) segmentErrorZ_plot = segmentErrorZin # Convert common measured and truth data back to Easting/Northing plane _, measRasterYCommonFinal, _, _, _ = getCoordRotRev( measRasterXRotCommonFinal, measRasterYRotCommonFinal, R_mat, xRotPt, yRotPt) _, truthRasterYCommonFinal, _, _, _ = getCoordRotRev( truthRasterXRotCommonFinal, truthRasterYRotCommonFinal, R_mat, xRotPt, yRotPt) # Make plots if (makePlots): # Make contour plot print(' Making Plots...') plotContour(resultsCrossTrackShift, resultsAlongTrackShift, resultsMAE, atlMeasuredData, atlCorrections, rasterResolution, showPlots, outFilePath, i) # Make ZY scatter plots plotZY(measRasterYCommonFinal, measRasterZCommonFinal, truthRasterYCommonFinal, truthRasterZCommonFinal, zErrorCommonFinal, segmentErrorY_plot, segmentErrorZ_plot, atlMeasuredData, atlCorrections, rasterResolution, showPlots, outFilePath, i) # Make ZT scatter plots plotZT(measRasterTCommonFinal, measRasterZCommonFinal, zErrorCommonFinal, atlMeasuredData, atlCorrections, rasterResolution, showPlots, outFilePath, i) # endIf i += 1 # endFor else: # Hard-coded offsets case print(' Using preset values...') # Set cross-track/along-track offsets correctionsCrossTrack = crossTrackBounds correctionsAlongTrack = alongTrackBounds # Get CT/AT offsets in Easting/Northing plane R_mat = rotationData.R_mat xRotPt = rotationData.xRotPt yRotPt = rotationData.yRotPt correctionsEasting, correctionsNorthing, _, _, _ = getCoordRotRev( correctionsCrossTrack, correctionsAlongTrack, R_mat, xRotPt, yRotPt) correctionsEasting -= xRotPt correctionsNorthing -= yRotPt # Get vertical offset if (offsets.useVerticalShift): correctionsVertical = np.array([float(offsets.verticalShift)]) else: correctionsVertical = np.array([0.0]) # endIf # Rasterize MEASURED data rasterResolution = rasterResolutions[-1] gridMethod = 'Mean' fillValue = -999 print( ' Gridding Measured Data at %s m Resolution using %s Values...' % (rasterResolution, gridMethod)) # Add in corrections measXRot += correctionsCrossTrack measYRot += correctionsAlongTrack measZ += correctionsVertical # Raster MEASURED data measRasterRot = getRaster(measXRot, measYRot, measZ, rasterResolution, gridMethod, fillValue, measT) # Reduce TRUTH data to width of cross-track offsets measWidth = (np.ceil( (measXRot.max() - measXRot.min()) / 2.0)).astype(int) minTruthBound = (crossTrackBounds - measWidth).astype(int) - 5 maxTruthBound = (crossTrackBounds + measWidth).astype(int) + 5 truthInOffsetsInds = (truthXRot >= minTruthBound) & (truthXRot <= maxTruthBound) truthXRotReduced = truthXRot[truthInOffsetsInds] truthYRotReduced = truthYRot[truthInOffsetsInds] truthZReduced = truthZ[truthInOffsetsInds] # Rasterize TRUTH data print(' Gridding Truth Data at %s m Resolution using %s Values...' % (rasterResolution, gridMethod)) truthRasterRot = getRaster(truthXRotReduced, truthYRotReduced, truthZReduced, rasterResolution, gridMethod, fillValue) # Remove NaN data (-999) in MEASURED raster measIndsToKeep = measRasterRot.grid != -999 measRasterXRot = measRasterRot.x[measIndsToKeep] measRasterYRot = measRasterRot.y[measIndsToKeep] measRasterZ = measRasterRot.grid[measIndsToKeep] measRasterT = measRasterRot.t[measIndsToKeep] # Get final MEASURED raster data measRasterXRotFinal = measRasterXRot measRasterYRotFinal = measRasterYRot measRasterZFinal = measRasterZ measRasterTFinal = measRasterT # Remove NaN data (-999) in TRUTH raster truthIndsToKeep = truthRasterRot.grid != -999 truthRasterXRot = truthRasterRot.x[truthIndsToKeep] truthRasterYRot = truthRasterRot.y[truthIndsToKeep] truthRasterZ = truthRasterRot.grid[truthIndsToKeep] # Get common MEASURED and TRUTH raster indices xyMeasRasterRotFinal = np.concatenate( (np.c_[measRasterXRotFinal], np.c_[measRasterYRotFinal]), axis=1) xyTruthRasterRot = np.concatenate( (np.c_[truthRasterXRot], np.c_[truthRasterYRot]), axis=1) commonValsFinal, xyCommonMeasIndsFinal, xyCommonTruthIndsFinal = getIntersection2d( xyMeasRasterRotFinal, xyTruthRasterRot, assume_unique=True) # Get common MEASURED raster points measRasterXRotCommonFinal = measRasterXRotFinal[xyCommonMeasIndsFinal] measRasterYRotCommonFinal = measRasterYRotFinal[xyCommonMeasIndsFinal] measRasterZCommonFinal = measRasterZFinal[xyCommonMeasIndsFinal] measRasterTCommonFinal = measRasterTFinal[xyCommonMeasIndsFinal] # Get common TRUTH raster points truthRasterXRotCommonFinal = truthRasterXRot[xyCommonTruthIndsFinal] truthRasterYRotCommonFinal = truthRasterYRot[xyCommonTruthIndsFinal] truthRasterZCommonFinal = truthRasterZ[xyCommonTruthIndsFinal] # Get Z Error raster (TRUTH - MEASURED) zErrorCommonFinal = measRasterZCommonFinal - truthRasterZCommonFinal # Get 100 m segment Z Mean Error data errorResolution = 100 gridMethod = 'Mean' fillValue = np.nan segmentError = getRaster(measRasterXRotCommonFinal, measRasterYRotCommonFinal, zErrorCommonFinal, errorResolution, gridMethod, fillValue) segmentErrorX = np.mean(segmentError.x, axis=1) segmentErrorY = np.mean(segmentError.y, axis=1) segmentErrorZ = np.nanmean(segmentError.grid, axis=1) # Format Y,Z values for plotting segmentErrorXYZ = np.column_stack( [segmentErrorX, segmentErrorY, segmentErrorZ]) segmentErrorXYZsorted = segmentErrorXYZ[segmentErrorXYZ[:, 1].argsort(), ] segmentErrorLo = segmentErrorXYZsorted[:, 1] - errorResolution * 0.5 segmentErrorHi = segmentErrorXYZsorted[:, 1] + errorResolution * 0.5 segmentErrorXin = (np.column_stack( [segmentErrorXYZsorted[:, 0], segmentErrorXYZsorted[:, 0]])).flatten() segmentErrorYin = (np.column_stack([segmentErrorLo, segmentErrorHi])).flatten() segmentErrorZin = (np.column_stack( [segmentErrorXYZsorted[:, 2], segmentErrorXYZsorted[:, 2]])).flatten() # Convert to easting/northing plane _, segmentErrorY_plot, _, _, _ = getCoordRotRev( segmentErrorXin, segmentErrorYin, R_mat, xRotPt, yRotPt) segmentErrorZ_plot = segmentErrorZin # Convert common measured and truth data back to Easting/Northing plane _, measRasterYCommonFinal, _, _, _ = getCoordRotRev( measRasterXRotCommonFinal, measRasterYRotCommonFinal, R_mat, xRotPt, yRotPt) _, truthRasterYCommonFinal, _, _, _ = getCoordRotRev( truthRasterXRotCommonFinal, truthRasterYRotCommonFinal, R_mat, xRotPt, yRotPt) MAE = np.mean(abs(zErrorCommonFinal)) RMSE = np.sqrt(np.mean(zErrorCommonFinal**2)) ME = np.mean(zErrorCommonFinal) # Set error values correctionsMAE = np.array([MAE]) correctionsRMSE = np.array([RMSE]) correctionsME = np.array([ME]) # Generate atlCorrections struct atlCorrections = atlCorrectionsStruct(correctionsCrossTrack, correctionsAlongTrack, \ correctionsEasting, correctionsNorthing, \ correctionsVertical, \ correctionsMAE, correctionsRMSE, correctionsME) # Make plots if (makePlots): # Make contour plot print(' Making Plots...') # Plot counter i = 0 # Make ZY scatter plots plotZY(measRasterYCommonFinal, measRasterZCommonFinal, truthRasterYCommonFinal, truthRasterZCommonFinal, zErrorCommonFinal, segmentErrorY_plot, segmentErrorZ_plot, atlMeasuredData, atlCorrections, rasterResolution, showPlots, outFilePath, i) # Make ZT scatter plots plotZT(measRasterTCommonFinal, measRasterZCommonFinal, zErrorCommonFinal, atlMeasuredData, atlCorrections, rasterResolution, showPlots, outFilePath, i) # endIf # endIf # Create output file if (createMeasCorrFile): # Get measured corrected data atlMeasCorrX = atlMeasuredData.easting + atlCorrections.easting atlMeasCorrY = atlMeasuredData.northing + atlCorrections.northing atlMeasCorrZ = atlMeasuredData.z + atlCorrections.z # Get E,N,U text for output file name xDirTxt = 'e' # east yDirTxt = 'n' # north zDirTxt = 'u' # up # Get directional text strings if (atlCorrections.easting < 0): xDirTxt = 'w' # west # endIf if (atlCorrections.northing < 0): yDirTxt = 's' # south # endIf if (atlCorrections.z < 0): zDirTxt = 'd' # down # endIf # Get measured corrected string text enuTxt = xDirTxt + '{0:0.1f}'.format(abs(atlCorrections.easting[0])) + '_' + \ yDirTxt + '{0:0.1f}'.format(abs(atlCorrections.northing[0])) + '_' + \ zDirTxt + '{0:0.1f}'.format(abs(atlCorrections.z[0])) # Get output path name print('') print(' Writing measured corrected .las file...') outName = atlMeasuredData.atl03FileName + '_' + atlMeasuredData.gtNum + '_SHIFTED_' + enuTxt + '.las' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # endIf # Write .las file writeLas(np.ravel(atlMeasCorrX), np.ravel(atlMeasCorrY), np.ravel(atlMeasCorrZ), 'utm', outPath, np.ravel(atlMeasuredData.classification), np.ravel(atlMeasuredData.intensity), np.ravel(atlMeasuredData.signalConf), atlMeasuredData.hemi, atlMeasuredData.zone) # endIf # End timer timeEnd = runTime.time() timeElapsedTotal = timeEnd - timeStart timeElapsedMin = np.floor(timeElapsedTotal / 60) timeElapsedSec = timeElapsedTotal % 60 # Print completion message print(' Module Completed in %d min %d sec.' % (timeElapsedMin, timeElapsedSec)) print('\n') # Return output data return atlCorrections
def getAtlMeasuredSwath(atl03FilePath=False, atl08FilePath=False, outFilePath=False, gtNum='gt1r', trimInfo='auto', createLasFile=False, createKmlFile=False, createCsvFile=False): # Initialize outputs atlMeasuredData = False headerData = False coordType = False rotationData = False # Only execute code if input ATL03 .h5 file and output path declared if (atl03FilePath and outFilePath): # Start timer timeStart = runTime.time() # Print message print(' Ground Track Number: %s' % gtNum) # Get ATL03 file path/name atl03FilePath = os.path.normpath(os.path.abspath(atl03FilePath)) atl03FileName = os.path.splitext(os.path.basename(atl03FilePath))[0] # Read ATL03 data from h5 file print(' Reading ATL03 .h5 file: %s' % atl03FilePath) lat_all = readAtl03H5(atl03FilePath, 'lat_ph', gtNum) lon_all = readAtl03H5(atl03FilePath, 'lon_ph', gtNum) z_all = readAtl03H5(atl03FilePath, 'h_ph', gtNum) deltaTime_all = readAtl03H5(atl03FilePath, 'delta_time', gtNum) signalConf_all = readAtl03H5(atl03FilePath, 'signal_conf_ph', gtNum) atl03_ph_index_beg, atl03_segment_id = readAtl03DataMapping( atl03FilePath, gtNum) # Get time from delta time time_all = deltaTime_all - np.min(deltaTime_all) # Get track direction if (lat_all[-1] > lat_all[0]): trackDirection = 'Ascending' else: trackDirection = 'Descending' # endIf # Extract metadata from ATL03 file name atl03h5Info = getNameParts(atl03FileName) # Map ATL08 to ATL03 ground photons if (atl08FilePath): # Get ATL08 file path/name atl08FilePath = os.path.normpath(os.path.abspath(atl08FilePath)) # Message to screen print(' Reading ATL08 .h5 file: %s' % atl08FilePath) atl08_classed_pc_indx, atl08_classed_pc_flag, atl08_segment_id = readAtl08DataMapping( atl08FilePath, gtNum) print(' Mapping ATL08 to ATL03 Ground Photons...') classification = getAtl08Mapping(atl03_ph_index_beg, atl03_segment_id, atl08_classed_pc_indx, atl08_classed_pc_flag, atl08_segment_id) # Get length to trim data by class_length = len(classification) lat_length = len(lat_all) data_length = np.min([class_length, lat_length]) # Trim ATL03 data down to size of classification array lat = lat_all[0:data_length] lon = lon_all[0:data_length] z = z_all[0:data_length] time = time_all[0:data_length] signalConf = signalConf_all[0:data_length] classification = classification[0:data_length] dataIsMapped = True else: # Message to screen print(' Not Mapping ATL08 to ATL03 Ground Photons') # Store data lat = lat_all lon = lon_all z = z_all time = time_all signalConf = signalConf_all classification = np.zeros(np.size(lat_all)) dataIsMapped = False # endIf # Get trim options trimParts = trimInfo.split(',') trimMode = trimParts[0] trimType = 'None' if (('manual' in trimMode.lower()) and (len(trimParts) > 1)): trimType = trimParts[1] trimMin = float(trimParts[2]) trimMax = float(trimParts[3]) # endIf # If selected to manually trim data, do this first if ('manual' in trimMode.lower()): # Trim by lat or time if ('lat' in trimType.lower()): print(' Manual Trim Mode (Min Lat: %s, Max Lat: %s)' % (trimMin, trimMax)) indsToKeep = (lat >= trimMin) & (lat <= trimMax) elif ('time' in trimType.lower()): print(' Manual Trim Mode (Min Time: %s, Max Time: %s)' % (trimMin, trimMax)) indsToKeep = (time >= trimMin) & (time <= trimMax) else: print(' Manual Trim Mode is Missing Args, Not Trimming Data') indsToKeep = np.ones(np.shape(lat), dtype=bool) # endif # Trim data lat = lat[indsToKeep] lon = lon[indsToKeep] z = z[indsToKeep] time = time[indsToKeep] signalConf = signalConf[indsToKeep] classification = classification[indsToKeep] # EndIf # Determine if ATL03 track goes over a lidar truth region kmlBoundsTextFile = 'kmlBounds.txt' kmlRegionName = False headerFilePath = False truthFilePath = False if (os.path.exists(kmlBoundsTextFile)): # Message to user print(' Finding Truth Region...') try: # Read kmlBounds.txt file and get contents kmlInfo = readTruthRegionsTxtFile(kmlBoundsTextFile) # Loop through kmlBounds.txt and find matching TRUTH area maxCounter = len(kmlInfo.regionName) counter = 0 while (not kmlRegionName): latInFile = (lat >= kmlInfo.latMin[counter]) & ( lat <= kmlInfo.latMax[counter]) lonInFile = (lon >= kmlInfo.lonMin[counter]) & ( lon <= kmlInfo.lonMax[counter]) trackInRegion = any(latInFile & lonInFile) if (trackInRegion): # Get truth region info kmlRegionName = kmlInfo.regionName[counter] headerFilePath = os.path.normpath( kmlInfo.headerFilePath[counter]) truthFilePath = os.path.normpath( kmlInfo.truthFilePath[counter]) kmlLatMin = kmlInfo.latMin[counter] kmlLatMax = kmlInfo.latMax[counter] kmlLonMin = kmlInfo.lonMin[counter] kmlLonMax = kmlInfo.lonMax[counter] # Print truth region print(' Truth File Region: %s' % kmlRegionName) # Read truth header .mat file headerData = readHeaderMatFile(headerFilePath) coordType = headerData.coordType # endIf # Increment counter counter += 1 if (counter >= maxCounter): # Send message to user print(' No Truth File Region Found in kmlBounds.txt') break # endIf # endWhile except: # Could not read kmlBounds.txt file print( ' Could not read truth header .mat file. Auto-assigning UTM zone.' ) # endIf # If selected to auto trim data, then trim if truth region exists if ('auto' in trimMode.lower()): if (kmlRegionName): # Trim data based on TRUTH region print( ' Auto Trim Mode (Trimming Data Based on Truth Region)...' ) indsInRegion = (lat >= kmlLatMin) & (lat <= kmlLatMax) & ( lon >= kmlLonMin) & (lon <= kmlLonMax) lat = lat[indsInRegion] lon = lon[indsInRegion] z = z[indsInRegion] time = time[indsInRegion] signalConf = signalConf[indsInRegion] classification = classification[indsInRegion] # endIf # endIf # Convert lat/lon coordinates to UTM print(' Converting Lat/Lon to UTM...') if (kmlRegionName and coordType == 'UTM'): # Force UTM zone to match region header file zoneToUse = headerData.zone hemiToUse = headerData.hemi easting, northing, zone, hemi = getLatLon2UTM( lon, lat, zoneToUse, hemiToUse) else: # Allow function to determine UTM zone easting, northing, zone, hemi = getLatLon2UTM(lon, lat) # endIf # Print UTM zone print(' UTM Zone: %s' % zone) # Transform MEASURED data to CT/AT plane print(' Computing CT/AT Frame Rotation...') desiredAngle = 90 crossTrack, alongTrack, R_mat, xRotPt, yRotPt, phi = getCoordRotFwd( easting, northing, [], [], [], desiredAngle) # Store rotation object rotationData = atlRotationStruct(R_mat, xRotPt, yRotPt, desiredAngle, phi) # Store data in class structure atlMeasuredData = atlMeasuredStruct(lat, lon, \ easting, northing, \ crossTrack, alongTrack, \ z, time, \ signalConf, classification, \ gtNum, zone, hemi, \ kmlRegionName, headerFilePath, truthFilePath, \ atl03FilePath, atl03FileName, \ atl08FilePath, atl03FileName, \ trackDirection, \ atl03h5Info, \ dataIsMapped) # Create output .las file if (createLasFile): print(' Writing measured .las file...') outName = atlMeasuredData.atl03FileName + '_' + atlMeasuredData.gtNum + '_MEASURED.las' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Write .las file writeLas(np.ravel(atlMeasuredData.easting), np.ravel(atlMeasuredData.northing), np.ravel(atlMeasuredData.z), 'utm', outPath, np.ravel(atlMeasuredData.classification), np.ravel(atlMeasuredData.intensity), atlMeasuredData.hemi, atlMeasuredData.zone) # endIf # Create output .kml file if (createKmlFile): print(' Writing measured .kml file...') outName = atlMeasuredData.atl03FileName + '_' + atlMeasuredData.gtNum + '_MEASURED.kml' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Get array of input time values timeStep = 1 # seconds timeVals = np.arange(np.min(atlMeasuredData.time), np.max(atlMeasuredData.time) + 1, timeStep) # Get closest time values from IceSat MEASURED data timeIn, indsToUse = getClosest(atlMeasuredData.time, timeVals) # Reduce lat/lon values to user-specified time scale lonsIn = atlMeasuredData.lon[indsToUse] latsIn = atlMeasuredData.lat[indsToUse] # Write .kml file writeKml(latsIn, lonsIn, timeIn, outPath) # endIf # Create output .csv file if (createCsvFile): print(' Writing measured .csv file...') outName = atlMeasuredData.atl03FileName + '_' + atlMeasuredData.gtNum + '_MEASURED.csv' outPath = os.path.normpath(outFilePath + '/' + outName) # If output directory does not exist, create it if (not os.path.exists(os.path.normpath(outFilePath))): os.mkdir(os.path.normpath(outFilePath)) # EndIf # Write .csv file namelist = ['Time (sec)', 'Latitude (deg)', 'Longitude (deg)', \ 'Easting (m)', 'Northing (m)', \ 'Cross-Track (m)', 'Along-Track (m)', \ 'Height (m)', \ 'Classification', 'Signal Confidence'] datalist = [atlMeasuredData.time, atlMeasuredData.lat, atlMeasuredData.lon, \ atlMeasuredData.easting, atlMeasuredData.northing, \ atlMeasuredData.crossTrack, atlMeasuredData.alongTrack,\ atlMeasuredData.z, \ atlMeasuredData.classification, atlMeasuredData.signalConf] writeArrayToCSV(outPath, namelist, datalist) # endIf # End timer timeEnd = runTime.time() timeElapsedTotal = timeEnd - timeStart timeElapsedMin = np.floor(timeElapsedTotal / 60) timeElapsedSec = timeElapsedTotal % 60 # Print completion message print(' Module Completed in %d min %d sec.' % (timeElapsedMin, timeElapsedSec)) print('\n') else: # Message to user print('Input correct ATL03 input .h5 file and/or output path.') # endIf # Return object return atlMeasuredData, headerData, rotationData