Esempio n. 1
0
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
Esempio n. 4
0
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
Esempio n. 5
0
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
Esempio n. 6
0
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