def getImageBoundingBox(filePath):
    """Returns (minLon, maxLon, minLat, maxLat) for a georeferenced image file"""

    extension = os.path.splitext(filePath)[1]
    if '.cub' in extension:
        return IrgIsisFunctions.getIsisBoundingBox(filePath)
    else: # Handle all other types
        return getGeoTiffBoundingBox(filePath)
Example #2
0
def getImageBoundingBox(filePath):
    """Returns (minLon, maxLon, minLat, maxLat) for a georeferenced image file"""

    extension = os.path.splitext(filePath)[1]
    if '.cub' in extension:
        return IrgIsisFunctions.getIsisBoundingBox(filePath)
    else:  # Handle all other types
        return getGeoTiffBoundingBox(filePath)
def main():

    val = IrgIsisFunctions.getCubeCenterLatitude('/u/smcmich1/data/lronacProduction/NAC_DTM_M115108088_M115114873/workDir/M115114873LE.correctedMosaic.cub')
    print val
    return 0


    try:
        try:
            usage = "usage: makePcAlignPlots.py [--help][--manual]\n  "
            parser = optparse.OptionParser(usage=usage)
            parser.add_option("-i", "--input", dest="inputPath",
                              help="Path to the input file.")
            parser.add_option("-o", "--output", dest="outputPath",
                              help="Where to write the output file.")
            parser.add_option("-s", "--skip", dest="skip",
                              help="Only plot every N points.")

            parser.add_option("--manual", action="callback", callback=man,
                              help="Read the manual.")
            (options, args) = parser.parse_args()

            if not options.inputPath:  parser.error("Need input file!")
            if not options.outputPath: parser.error("Need output file!")
            
            if not options.skip:  options.skip = 1
  
        except optparse.OptionError, msg:
            raise Usage(msg)

        print "Beginning processing....."

        #filePath   = '/home/smcmich1/data/stereoCorrectionTest/M120168714LE.corrected.cub_stereoCalibrationTemp2/pcAlignOutput-beg_errors.csv'
        #outputPath = '/home/smcmich1/data/stereoCorrectionTest/begErrors.png'

        fileContents = readErrorFile(options.inputPath)
    
        plotError(fileContents, options.outputPath, int(options.skip))

        print "Finished"
        return 0
def main(argsIn):

    print "Started lronacCameraRotationCorrector.py"

    try:
        try:
            usage = "usage: lronacCameraRotationCorrector.py [--output <path>][--manual]\n  "
            parser = optparse.OptionParser(usage=usage)
            parser.add_option("--left",  dest="leftPath",  help="Path to LE .cub file")
            parser.add_option("--right", dest="rightPath", help="Path to RE .cub file")

            parser.add_option("--rotation", dest="rotationPath", help="Path to already computed new rotation")

            parser.add_option("-g", "--gdcLogPath", dest="gdcLogPath",
                              help="Optional path to save computed GDC points to")

            parser.add_option("-s", "--spk", dest="spkPath",
                              help="Path to optional specified SPK (position) file to use.")
            parser.add_option("-c", "--ck", dest="ckPath",
                              help="Path to optional specified CK (orientation) file to use.")

            parser.add_option("--workDir", dest="workDir",  help="Folder to store temporary files in")

            parser.add_option("-o", "--output", dest="outputPath",
                              help="Where to write the output (RE) file.")
            parser.add_option("--manual", action="callback", callback=man,
                              help="Read the manual.")
            parser.add_option("--keep", action="store_true", dest="keep",
                              help="Do not delete the temporary files.")
            (options, args) = parser.parse_args(argsIn)

            if not options.leftPath: 
                parser.error("Need left input path")
            if not options.rightPath: 
                parser.error("Need right input path")
            if not options.outputPath: 
                parser.error("Need output path")

        except optparse.OptionError, msg:
            raise Usage(msg)

        print "Beginning processing....."

        startTime = time.time()

        outputFolder = os.path.dirname(options.outputPath)
        inputBaseName = os.path.basename(options.leftPath)
        tempFolder    = outputFolder + '/' + inputBaseName + '_rotCorrectTemp/'
        if options.workDir:
            tempFolder = options.workDir
        if not os.path.exists(tempFolder):
            os.mkdir(tempFolder) 

        # File must already have had spiceinit called


        # Copy the input file to the output location (only the RE image is modified
        cmd = "cp " + options.rightPath + " " + options.outputPath
        #print cmd
        os.system(cmd)

        # Call head -120 on file
        cmd = ['head', '-120', options.outputPath]
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        headText, err = p.communicate()

        # Parse output looking for the IK frame file
        print 'Looking for source frame file...'
        kernels = IrgIsisFunctions.parseHeadOutput(headText, options.outputPath)
        
        if not ('Frame' in kernels):
            os.remove(options.outputPath)
            raise Exception('Error! Unable to find frame file!')
        inputFramePath = kernels['Frame'][0]
        if not os.path.exists(inputFramePath):
            os.remove(options.outputPath)
            raise Exception('Unable to find any IK kernel file in ' + tempTextPath)
            

        if not options.rotationPath:
            # Make sure the output path does not already exist
            rotationAnglePath = os.path.join(tempFolder, "solvedRotationAngles.txt")
            if os.path.exists(rotationAnglePath):
                os.remove(rotationAnglePath)
            
            # Call lronacSpkParser to generate modified text file
            gdcText = ''
            if options.gdcLogPath: # Handle GDC point logging option
                gdcText = ' --gdcPointsOutPath ' + options.gdcLogPath
            cmd = 'lronacAngleSolver --outputPath '+ \
                      rotationAnglePath + gdcText + ' ' + options.leftPath + ' ' + options.rightPath
            print cmd
            os.system(cmd)
            if not os.path.exists(rotationAnglePath):
                os.remove(options.outputPath)
                raise Exception('Failed to solve for rotation angles!')

        else: # New rotation provided as a command line argument, skip computation
            rotationAnglePath = options.rotationPath
        
        # Read the rotation angles
        newRotation = readRotationFile(rotationAnglePath)

        # Generate a modified frame file
        tempIkPath = os.path.join(tempFolder, "angleCorrectedIkFile.tf")
        modifyFrameFile(inputFramePath, tempIkPath, newRotation)
        
        # Re-run spiceinit (on the copied RE file) using the new frame file
        cmd = ['spiceinit', 'attach=', 'true', 'from=', options.outputPath, 'fk=', tempIkPath]
        if (options.spkPath): # Add forced SPK path if needed
            cmd.append('spk=')
            cmd.append(options.spkPath)
        if (options.ckPath): # Add forced CK path if needed
            cmd.append('ck=')
            cmd.append(options.ckPath)
        #print cmd
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        outputText, err = p.communicate()
 
        if (outputText.find('ERROR') >= 0):
            print cmd
            print outputText
            raise Exception('Found error when calling spiceinit!')


        # Clean up temporary files
        if not options.keep:
            os.remove(rotationAnglePath)
            os.remove(tempIkPath)
            os.remove(options.spkPath)
      

        endTime = time.time()

        print "Finished in " + str(endTime - startTime) + " seconds."
        return 0
Example #5
0
def getImageSize(imagePath):
    return IrgIsisFunctions.getImageSize(imagePath)
def fetchAndPrepFile(db, setName, subtype, remoteURL, workDir):
    '''Retrieves a remote file and prepares it for upload'''
    
    #print 'Uploading file ' + setName
    
    # Images with a center over this latitude will use polar stereographic projection
    #   instead of simple cylindrical projection.
    HIGH_LATITUDE_CUTOFF = 65 # Degrees
    
    asuImagePath  = os.path.join(workDir, setName + '_noGeo.jp2') # Map projected image from ASU
    asuLabelPath  = os.path.join(workDir, setName + '_noGeo.lbl') # Label from ASU
    edrPath       = os.path.join(workDir, setName + '.IMG')       # Raw image from PDS
    timePath      = os.path.join(workDir, setName + '.time')      # Contains only the file capture time string
    #cubPath     = os.path.join(workDir, setName + '.cub')        # Output of mroctx2isis
    #calPath     = os.path.join(workDir, setName + '.cal.cub')    # Output of ctxcal
    mapPath       = os.path.join(workDir, setName + '.map.cub')   # Output of cam2map
    mapLabelPath  = os.path.join(workDir, setName + '.map.pvl')   # Specify projection to cam2map
    
    # Generate the remote URLs from the data prefix and volume stored in these parameters
    asuImageUrl, asuLabelUrl, edrUrl = generatePdsPath(setName, subtype)
    
    if True: # Map project the EDR ourselves <-- This takes way too long!
        print 'Projecting the EDR image using ISIS...'

        localFilePath = os.path.join(workDir, setName + '.tif') # The output file we will upload

        # Check if this is a "flat" calibration image
        badImage = checkForBadFile(edrUrl)       
        if badImage:
            raise Exception('TODO: Remove bad images from the DB!')
        
        if not os.path.exists(edrPath):
            # Download the EDR file
            cmd = 'wget ' + edrUrl + ' -O ' + edrPath
            print cmd
            os.system(cmd)

        # Extract the image capture time from the .IMG file
        if not os.path.exists(timePath):
            timeString = getCreationTimeHelper(edrPath)
            f = open(timePath, 'w')
            f.write(timeString)
            f.close()
      
        # Convert and apply calibration to the CTX file
        calPath = IrgIsisFunctions.prepareCtxImage(edrPath, workDir, False)

        ## Find out the center latitude of the file and determine if it is high latitude
        centerLat = IrgIsisFunctions.getCubeCenterLatitude(calPath, workDir)
        print centerLat
        highLat   = abs(float(centerLat)) > HIGH_LATITUDE_CUTOFF

        if True:#not os.path.exists(mapLabelPath):
            # Generate the map label file           
            generateDefaultMappingPvl(mapLabelPath, highLat)
        
        
        if True:#not os.path.exists(mapPath):
            # Generate the map projected file
            cmd = ['timeout', '6h', 'cam2map', 'matchmap=','False', 'from=', calPath, 'to=', mapPath, 'map=', mapLabelPath]
            print cmd
            #os.system(cmd)
            p = subprocess.Popen(cmd)
            p.communicate()
            if (p.returncode != 0):
                raise Exception('Error or timeout running cam2map, returnCode = ' + str(p.returncode))
       
        if True: #not os.path.exists(localFilePath):
            # Generate the final image to upload
            cmd = 'gdal_translate -of GTiff ' + mapPath + ' ' + localFilePath
            print cmd
            os.system(cmd)
        
        # Clean up intermediate files    
        os.remove(mapLabelPath)
        os.remove(edrPath)
        os.remove(calPath)
        os.remove(mapPath)
        
        # Two local files are left around, the first should be uploaded.
        return [localFilePath, timePath]
        
    else: # Use the map projected image from the ASU web site
        print 'Using ASU projected image...'
        
        localFilePath = os.path.join(workDir, setName + '.jp2')  # The output file we will upload
        
        # Note: ASU seems to be missing some files!
        # We are using the label path in both projection cases
        if not os.path.exists(asuLabelPath):
            # Download the label file
            cmd = 'wget "' + asuLabelUrl + '" -O ' + asuLabelPath
            print cmd
            os.system(cmd)
        if not IrgFileFunctions.fileIsNonZero(asuLabelPath): # Try the alternate label path
            os.remove(asuLabelPath)
            asuLabelUrl  = asuLabelUrl.replace( '.scyl.', '.ps.')
            asuLabelPath = asuLabelPath.replace('.scyl.', '.ps.')
            print 'Trying alternate label path: ' + asuLabelUrl
            # Download the label file
            cmd = 'wget "' + asuLabelUrl + '" -O ' + asuLabelPath
            print cmd
            os.system(cmd)
            if not IrgFileFunctions.fileIsNonZero(asuLabelPath):
                raise Exception('Failed to download file label at URL: ' + asuLabelUrl)
        
        # Check the projection type
        projType = IrgGeoFunctions.getProjectionFromIsisLabel(asuLabelPath)
        if projType != 'SimpleCylindrical':
            print 'WARNING: projType = ' + projType
            print 'Maps Engine may fail to ingest this file!'
            #os.remove(asuLabelPath)
            #raise Exception(projType + ' images on hold until Google fixes a bug!')
        
        if not os.path.exists(asuImagePath):
            # Download the image file
            cmd = 'wget "' + asuImageUrl + '" -O ' + asuImagePath
            print cmd
            os.system(cmd)
        if not IrgFileFunctions.fileIsNonZero(asuImagePath):
            raise Exception('Failed to download image file at URL: ' + asuImageUrl)

        ## Correct the ISIS header if needed
        #fixedAsuHeaderPath = putIsisHeaderIn180(asuLabelPath)
        #if (fixedAsuHeaderPath != asuLabelPath):
        #    os.remove(asuLabelPath) # Delete replaced header

        if True: # This is fast now, so do it every time
            # Correct the file - The JP2 file from ASU needs the geo data from the label file!
            #cmd = 'addGeoToAsuCtxJp2.py --keep --label '+ asuLabelPath +' '+ asuImagePath +' '+ localFilePath
            #print cmd
            #os.system(cmd)
            # TODO: Remove unnecessary image copy here
            (correctedPath, sidecarPath) = addGeoDataToAsuJp2File(asuImagePath, asuLabelPath, localFilePath, keep=False)
            
            if not IrgFileFunctions.fileIsNonZero(sidecarPath):
                raise Exception('Script to add geo data to JP2 file failed!')
   
        # Clean up
        os.remove(asuImagePath)
        # Three local files are left around, the first should be uploaded.
        return [correctedPath, sidecarPath, asuLabelPath]
        #generateKmlFromGdcPoints(os.path.join(doubleCalWorkFolder, 'initialGdcCheck'),            tempFolder, 'dem-trans_source.csv', 'transformedPoints'              1000, 'blue', 'normal', , False)
        generateKmlFromGdcPoints(os.path.join(doubleCalWorkFolder, 'pcAlignOutput'),              tempFolder, 'dem-trans_reference.csv',       'transformedGdcPoints',          160,   'red',    'tiny',   carry)
        generateKmlFromGdcPoints(os.path.join(doubleCalWorkFolder, 'pcAlignOutput'),              tempFolder, 'dem-beg_errors.csv',            'beg-errors',                     2,    'yellow', 'tiny',   carry)
        generateKmlFromGdcPoints(os.path.join(doubleCalWorkFolder, 'pcAlignOutput'),              tempFolder, 'dem-end_errors.csv',            'end-errors',                     2,    'green',  'tiny',   carry)

        print 'Finished generating KML plots'

        # Delay check for left path to allow debug KML files to be generated
        if caughtException or not os.path.exists(leftCorrectedPath) or not os.path.exists(rightStereoCorrectedPath):
            raise Exception('Failed to run stereo calibration process - ' + exceptionText)

        print '\n-------------------------------------------------------------------------\n'

        # Generate a PVL file that we need for noproj
        pvlPath   = os.path.join(tempFolder, 'noprojInstruments.pvl')
        imageSize = IrgIsisFunctions.getImageSize(leftCorrectedPath)
        isHalfRes = imageSize[0] < 5000
        if not os.path.exists(pvlPath):
            print 'Writing PVL'
            IsisTools.writeLronacPvlFile(pvlPath, isHalfRes)

        correctTime = time.time()
        logging.info('Nav correction finished in %f seconds', correctTime - startTime)

        # Noproj the corrected data
        leftNoprojPath        = os.path.join(tempFolder, 'leftFinalCorrected.noproj.cub')
        rightNoprojPath       = os.path.join(tempFolder, 'rightFinalCorrected.noproj.cub')
        leftStereoNoprojPath  = os.path.join(tempFolder, 'leftStereoFinalCorrected.noproj.cub')
        rightStereoNoprojPath = os.path.join(tempFolder, 'rightStereoFinalCorrected.noproj.cub')

        # Set up thread objects
    except optparse.OptionError, msg:
        raise Usage(msg)

    startTime = time.time()
    
    # Determine if this is a main copy or a spawned copy
    spawnedCopy = ( (options.pixelStartX is not None) and (options.pixelStartY is not None) and
                    (options.pixelStopX  is not None) and (options.pixelStopY  is not None) and options.workDir )
    
    if spawnedCopy: # This copy was spawned to process a single tile
        
        return writeSingleTile(options) # Just call a function to handle this and then we are done!   

    # If the input image is NOT an ISIS image AND we are running on a single machine we can
    #  just use the multi-threading capability of the ordinary mapproject call.
    if (not IrgIsisFunctions.isIsisFile(options.imagePath)) and (not options.nodesListPath):
        cmd = ['mapproject',  options.imagePath, options.demPath, options.outputPath]    
        cmd = cmd + options.extraArgs
        subprocess.call(cmd)
        return 0


    # Otherwise this is the original called process and there are multiple steps to go through
    
    # Call mapproject on the input data using subprocess and record output
    cmd = ['mapproject',  '--query-projection', options.demPath, options.imagePath, options.outputPath]
    cmd = cmd + options.extraArgs # Append other options
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    projectionInfo, err = p.communicate()
    if not options.suppressOutput:
        print projectionInfo
def getImageSize(imagePath):
    return IrgIsisFunctions.getImageSize(imagePath)
Example #10
0
def fetchAndPrepFile(db, setName, subtype, remoteURL, workDir):
    '''Retrieves a remote file and prepares it for upload'''

    #print 'Uploading file ' + setName

    if subtype != 'DEM':  # Handles RED and COLOR images
        # The label file URL is the same as the image but with a different extension
        remoteLabelURL = getLabelPathFromImagePath(remoteURL)

        localFilePath = os.path.join(workDir, os.path.basename(remoteURL))
        localLabelPath = os.path.join(workDir,
                                      os.path.basename(remoteLabelURL))

        # Retrieve the header file
        if not os.path.exists(localLabelPath):
            # Try to get the label locally!
            pdsStart = remoteLabelURL.find('PDS')
            localPdsPath = os.path.join('/HiRISE/Data/',
                                        remoteLabelURL[pdsStart:])
            print localPdsPath
            if os.path.exists(
                    localPdsPath):  # File available locally, just copy it!
                cmd = 'cp ' + localPdsPath + ' ' + localLabelPath
            else:  # Download the image
                cmd = 'wget ' + remoteLabelURL + ' -O ' + localLabelPath
            print cmd
            os.system(cmd)

        # Retrieve the image file
        if not os.path.exists(localFilePath):  # Need to get it from somewhere
            # Try to get the image locally!
            pdsStart = remoteURL.find('PDS')
            localPdsPath = os.path.join('/HiRISE/Data/', remoteURL[pdsStart:])
            if os.path.exists(
                    localPdsPath):  # File available locally, just copy it!
                cmd = 'cp ' + localPdsPath + ' ' + localFilePath
            else:  # Download the image
                cmd = 'wget ' + remoteURL + ' -O ' + localFilePath
            print cmd
            os.system(cmd)

        if not IrgFileFunctions.fileIsNonZero(localFilePath):
            if not IrgFileFunctions.fileIsNonZero(localLabelPath):
                print 'Could not find label or image file, DELETING DATA RECORD!'
                common.removeDataRecord(db, common.SENSOR_TYPE_HiRISE, subtype,
                                        setName)
            raise Exception('Unable to download from URL: ' + remoteURL)

        # Check if there is geo data in the JP2 file
        jp2HasGeoData = IrgGeoFunctions.doesImageHaveGeoData(localFilePath)

        # If there is no label file, try to generate an artificial one.
        fakeLabelFile = False
        if not IrgFileFunctions.fileIsNonZero(localLabelPath):
            #raise Exception('Unable to download from URL: ' + remoteLabelURL)
            print 'WARNING: Unable to download from URL: ' + remoteLabelURL
            if not jp2HasGeoData:
                raise Exception(
                    'No geo data in JP2 and no Label file!  Cannot handle this!'
                )
            print 'Generating a fake LBL file to proceed...'
            localLabelPath = writeFakeLabelFromJp2(localFilePath)
            fakeLabelFile = True
        # At this point we always have a label file but it may be fake

        # Some images are missing geo information but we can add it from the label file
        localImageIsTiff = False
        localImagePath = localFilePath
        if not jp2HasGeoData:
            print 'Correcting JP2 file with no geo information!!!!'
            # Correct the local file, then remove the old (bad) file
            outputPrefix = localFilePath[0:-4]
            localImagePath = correctAndCropImage(localFilePath, localLabelPath,
                                                 outputPrefix)
            print 'Generated ' + localImagePath
            if localFilePath != localImagePath:
                print 'Deleting JP2 file without metadata!'
                os.remove(localFilePath)
            localImageIsTiff = (localImagePath[-4:] == ".TIF")

        if not localImageIsTiff:
            # Call code to fix the header information in the JP2 file!
            cmd = FIX_JP2_TOOL + ' ' + localImagePath
            print cmd
            os.system(cmd)

        # Check the projection type
        projType = IrgGeoFunctions.getProjectionFromIsisLabel(localLabelPath)
        (width, height) = IrgIsisFunctions.getImageSize(localImagePath)
        if (projType == 'POLAR STEREOGRAPHIC'
            ) and False:  #(width < MAX_POLAR_UPLOAD_WIDTH):
            # Google has trouble digesting these files so handle them differently.

            #os.remove(localLabelPath)
            #raise Exception('POLAR STEREOGRAPHIC images on hold until Google fixes a bug!')
            print 'Special handling for POLAR STEROGRAPHIC image!'

            if fakeLabelFile:
                print 'Cannot reprocess polar image without a label file!'
                print 'All we can do is upload the file and hope for the best.'
                # First file is for upload, second contains the timestamp.
                return [localImagePath, localLabelPath]

            # Compute how many chunks are needed for this image
            numChunks = ceil(width / POLAR_WIDTH_CHUNK_SIZE)

            # Determine which chunk this DB entry is for
            chunkNum = getChunkNum(setName)
            print 'This is chunk number ' + str(chunkNum)

            if chunkNum >= numChunks:  # Check for chunk number error
                raise Exception('Illegal chunk number: ' + setName)

            # If this is the main DB entry, we need to make sure the other DB entries exist!
            if chunkNum == 0:
                # Go ahead and try to add each chunk, the call will only go through if it does not already exist.
                for i in range(1, numChunks):
                    chunkSetName = makeChunkSetName(setName, i)
                    print 'Add chunk set name to DB: ' + chunkSetName
                    common.addDataRecord(db, common.SENSOR_TYPE_HiRISE,
                                         subtype, chunkSetName, remoteURL)

            raise Exception('DEBUG')

            # Now actually generate the desired chunk
            # - Need to use PIRL tools to extract a chunk to an IMG format, then convert that back to JP2 so that Google can read it.
            fileBasePath = os.path.splitext(localImagePath)[0]
            localImgPath = fileBasePath + '.IMG'
            localChunkPrefix = fileBasePath + '_' + str(chunkNum)
            chunkBB = getChunkBoundingBox(width, height, chunkNum)
            localChunkPath = correctAndCropImage(localImagePath,
                                                 localLabelPath,
                                                 localChunkPrefix, chunkBB)

            # Just use the same label file, we don't care if the DB has per-chunk boundaries.
            return [localChunkPath, localLabelPath]

        else:  # A normal, non-polar file.
            # First file is for upload, second contains the timestamp.
            return [localImagePath, localLabelPath]

    # TODO: Handle POLAR DEMS

    else:  # Handle DEMs

        # For DEMs there is no label file
        localFilePath = os.path.join(workDir, os.path.basename(remoteURL))
        if not os.path.exists(localFilePath):
            # Download the image
            cmd = 'wget ' + remoteURL + ' -O ' + localFilePath
            print cmd
            os.system(cmd)
        if not IrgFileFunctions.fileIsNonZero(
                localFilePath):  # Make sure we got the file
            raise Exception('Unable to download from URL: ' + remoteURL)

        # Generate a header file from the IMG file
        localLabelPath = localFilePath[:-4] + '.LBL'
        cmd = 'head -n 90 ' + localFilePath + ' >  ' + localLabelPath
        print cmd
        os.system(cmd)

        # Check if this is a polar stereographic image
        isPolar = False
        f = open(localLabelPath)
        for line in f:
            if ("MAP_PROJECTION_TYPE" in line) and ("POLAR STEREOGRAPHIC"
                                                    in line):
                isPolar = True
                print 'WARNING: POLAR STEREOGRAPHIC DEM MAY NOT UPLOAD PROPERLY'
                break
                #os.remove(localFilePath)
                #os.remove(localLabelPath)
                #raise Exception('POLAR STEREOGRAPHIC DEMs on hold until Google fixes a bug!')
        f.close()

        # Convert from IMG to TIF
        tiffFilePath = localFilePath[:-4] + '.TIF'
        if not os.path.exists(tiffFilePath):
            cmd = 'gdal_translate -of GTiff ' + localFilePath + ' ' + tiffFilePath
            print cmd
            os.system(cmd)

            if not isPolar:  # Only EQC files need to be corrected
                # Correct projected coordinates problems
                cmd = 'python /home/pirl/smcmich1/repo/Tools/geoTiffTool.py --normalize-eqc-lon ' + tiffFilePath
                print cmd
                os.system(cmd)

        os.remove(localFilePath)  # Clean up source image
        return [tiffFilePath, localLabelPath]
Example #11
0
def fetchAndPrepFile(db, setName, subtype, remoteURL, workDir):
    '''Retrieves a remote file and prepares it for upload'''

    #print 'Uploading file ' + setName

    # Images with a center over this latitude will use polar stereographic projection
    #   instead of simple cylindrical projection.
    HIGH_LATITUDE_CUTOFF = 65  # Degrees

    asuImagePath = os.path.join(workDir, setName +
                                '_noGeo.jp2')  # Map projected image from ASU
    asuLabelPath = os.path.join(workDir,
                                setName + '_noGeo.lbl')  # Label from ASU
    edrPath = os.path.join(workDir, setName + '.IMG')  # Raw image from PDS
    timePath = os.path.join(
        workDir,
        setName + '.time')  # Contains only the file capture time string
    #cubPath     = os.path.join(workDir, setName + '.cub')        # Output of mroctx2isis
    #calPath     = os.path.join(workDir, setName + '.cal.cub')    # Output of ctxcal
    mapPath = os.path.join(workDir, setName + '.map.cub')  # Output of cam2map
    mapLabelPath = os.path.join(workDir, setName +
                                '.map.pvl')  # Specify projection to cam2map

    # Generate the remote URLs from the data prefix and volume stored in these parameters
    asuImageUrl, asuLabelUrl, edrUrl = generatePdsPath(setName, subtype)

    if True:  # Map project the EDR ourselves <-- This takes way too long!
        print 'Projecting the EDR image using ISIS...'

        localFilePath = os.path.join(workDir, setName +
                                     '.tif')  # The output file we will upload

        # Check if this is a "flat" calibration image
        badImage = checkForBadFile(edrUrl)
        if badImage:
            raise Exception('TODO: Remove bad images from the DB!')

        if not os.path.exists(edrPath):
            # Download the EDR file
            cmd = 'wget ' + edrUrl + ' -O ' + edrPath
            print cmd
            os.system(cmd)

        # Extract the image capture time from the .IMG file
        if not os.path.exists(timePath):
            timeString = getCreationTimeHelper(edrPath)
            f = open(timePath, 'w')
            f.write(timeString)
            f.close()

        # Convert and apply calibration to the CTX file
        calPath = IrgIsisFunctions.prepareCtxImage(edrPath, workDir, False)

        ## Find out the center latitude of the file and determine if it is high latitude
        centerLat = IrgIsisFunctions.getCubeCenterLatitude(calPath, workDir)
        print centerLat
        highLat = abs(float(centerLat)) > HIGH_LATITUDE_CUTOFF

        if True:  #not os.path.exists(mapLabelPath):
            # Generate the map label file
            generateDefaultMappingPvl(mapLabelPath, highLat)

        if True:  #not os.path.exists(mapPath):
            # Generate the map projected file
            cmd = [
                'timeout', '6h', 'cam2map', 'matchmap=', 'False', 'from=',
                calPath, 'to=', mapPath, 'map=', mapLabelPath
            ]
            print cmd
            #os.system(cmd)
            p = subprocess.Popen(cmd)
            p.communicate()
            if (p.returncode != 0):
                raise Exception(
                    'Error or timeout running cam2map, returnCode = ' +
                    str(p.returncode))

        if True:  #not os.path.exists(localFilePath):
            # Generate the final image to upload
            cmd = 'gdal_translate -of GTiff ' + mapPath + ' ' + localFilePath
            print cmd
            os.system(cmd)

        # Clean up intermediate files
        os.remove(mapLabelPath)
        os.remove(edrPath)
        os.remove(calPath)
        os.remove(mapPath)

        # Two local files are left around, the first should be uploaded.
        return [localFilePath, timePath]

    else:  # Use the map projected image from the ASU web site
        print 'Using ASU projected image...'

        localFilePath = os.path.join(workDir, setName +
                                     '.jp2')  # The output file we will upload

        # Note: ASU seems to be missing some files!
        # We are using the label path in both projection cases
        if not os.path.exists(asuLabelPath):
            # Download the label file
            cmd = 'wget "' + asuLabelUrl + '" -O ' + asuLabelPath
            print cmd
            os.system(cmd)
        if not IrgFileFunctions.fileIsNonZero(
                asuLabelPath):  # Try the alternate label path
            os.remove(asuLabelPath)
            asuLabelUrl = asuLabelUrl.replace('.scyl.', '.ps.')
            asuLabelPath = asuLabelPath.replace('.scyl.', '.ps.')
            print 'Trying alternate label path: ' + asuLabelUrl
            # Download the label file
            cmd = 'wget "' + asuLabelUrl + '" -O ' + asuLabelPath
            print cmd
            os.system(cmd)
            if not IrgFileFunctions.fileIsNonZero(asuLabelPath):
                raise Exception('Failed to download file label at URL: ' +
                                asuLabelUrl)

        # Check the projection type
        projType = IrgGeoFunctions.getProjectionFromIsisLabel(asuLabelPath)
        if projType != 'SimpleCylindrical':
            print 'WARNING: projType = ' + projType
            print 'Maps Engine may fail to ingest this file!'
            #os.remove(asuLabelPath)
            #raise Exception(projType + ' images on hold until Google fixes a bug!')

        if not os.path.exists(asuImagePath):
            # Download the image file
            cmd = 'wget "' + asuImageUrl + '" -O ' + asuImagePath
            print cmd
            os.system(cmd)
        if not IrgFileFunctions.fileIsNonZero(asuImagePath):
            raise Exception('Failed to download image file at URL: ' +
                            asuImageUrl)

        ## Correct the ISIS header if needed
        #fixedAsuHeaderPath = putIsisHeaderIn180(asuLabelPath)
        #if (fixedAsuHeaderPath != asuLabelPath):
        #    os.remove(asuLabelPath) # Delete replaced header

        if True:  # This is fast now, so do it every time
            # Correct the file - The JP2 file from ASU needs the geo data from the label file!
            #cmd = 'addGeoToAsuCtxJp2.py --keep --label '+ asuLabelPath +' '+ asuImagePath +' '+ localFilePath
            #print cmd
            #os.system(cmd)
            # TODO: Remove unnecessary image copy here
            (correctedPath,
             sidecarPath) = addGeoDataToAsuJp2File(asuImagePath,
                                                   asuLabelPath,
                                                   localFilePath,
                                                   keep=False)

            if not IrgFileFunctions.fileIsNonZero(sidecarPath):
                raise Exception('Script to add geo data to JP2 file failed!')

        # Clean up
        os.remove(asuImagePath)
        # Three local files are left around, the first should be uploaded.
        return [correctedPath, sidecarPath, asuLabelPath]
def fetchAndPrepFile(db, setName, subtype, remoteURL, workDir):
    '''Retrieves a remote file and prepares it for upload'''
    
    #print 'Uploading file ' + setName
  
    if subtype != 'DEM': # Handles RED and COLOR images
        # The label file URL is the same as the image but with a different extension
        remoteLabelURL = getLabelPathFromImagePath(remoteURL)
    
        localFilePath  = os.path.join(workDir, os.path.basename(remoteURL))
        localLabelPath = os.path.join(workDir, os.path.basename(remoteLabelURL))

        # Retrieve the header file
        if not os.path.exists(localLabelPath):
            # Try to get the label locally!
            pdsStart     = remoteLabelURL.find('PDS')
            localPdsPath = os.path.join('/HiRISE/Data/', remoteLabelURL[pdsStart:])
            print localPdsPath
            if os.path.exists(localPdsPath): # File available locally, just copy it!
                cmd = 'cp ' + localPdsPath +' '+ localLabelPath
            else:  # Download the image
                cmd = 'wget ' + remoteLabelURL + ' -O ' + localLabelPath
            print cmd
            os.system(cmd)

        # Retrieve the image file
        if not os.path.exists(localFilePath): # Need to get it from somewhere
            # Try to get the image locally!
            pdsStart     = remoteURL.find('PDS')
            localPdsPath = os.path.join('/HiRISE/Data/', remoteURL[pdsStart:])
            if os.path.exists(localPdsPath): # File available locally, just copy it!
                cmd = 'cp ' + localPdsPath +' '+ localFilePath
            else:  # Download the image
                cmd = 'wget ' + remoteURL + ' -O ' + localFilePath
            print cmd
            os.system(cmd)

        if not IrgFileFunctions.fileIsNonZero(localFilePath):
            if not IrgFileFunctions.fileIsNonZero(localLabelPath):
                print 'Could not find label or image file, DELETING DATA RECORD!'
                common.removeDataRecord(db, common.SENSOR_TYPE_HiRISE, subtype, setName)
            raise Exception('Unable to download from URL: ' + remoteURL)

        # Check if there is geo data in the JP2 file
        jp2HasGeoData = IrgGeoFunctions.doesImageHaveGeoData(localFilePath)

        # If there is no label file, try to generate an artificial one.
        fakeLabelFile = False
        if not IrgFileFunctions.fileIsNonZero(localLabelPath):
            #raise Exception('Unable to download from URL: ' + remoteLabelURL)
            print 'WARNING: Unable to download from URL: ' + remoteLabelURL
            if not jp2HasGeoData:
                raise Exception('No geo data in JP2 and no Label file!  Cannot handle this!')
            print 'Generating a fake LBL file to proceed...'
            localLabelPath = writeFakeLabelFromJp2(localFilePath)
            fakeLabelFile  = True
        # At this point we always have a label file but it may be fake

        # Some images are missing geo information but we can add it from the label file
        localImageIsTiff = False
        localImagePath   = localFilePath
        if not jp2HasGeoData:
            print 'Correcting JP2 file with no geo information!!!!'
            # Correct the local file, then remove the old (bad) file
            outputPrefix   = localFilePath[0:-4]
            localImagePath = correctAndCropImage(localFilePath, localLabelPath, outputPrefix)
            print 'Generated ' + localImagePath
            if localFilePath != localImagePath:
                print 'Deleting JP2 file without metadata!'
                os.remove(localFilePath)
            localImageIsTiff = (localImagePath[-4:] == ".TIF")
    
        if not localImageIsTiff:
            # Call code to fix the header information in the JP2 file!
            cmd = FIX_JP2_TOOL +' '+ localImagePath
            print cmd
            os.system(cmd)


        # Check the projection type
        projType        = IrgGeoFunctions.getProjectionFromIsisLabel(localLabelPath)
        (width, height) = IrgIsisFunctions.getImageSize(localImagePath)
        if (projType == 'POLAR STEREOGRAPHIC') and False: #(width < MAX_POLAR_UPLOAD_WIDTH):
            # Google has trouble digesting these files so handle them differently.

            #os.remove(localLabelPath)
            #raise Exception('POLAR STEREOGRAPHIC images on hold until Google fixes a bug!')
            print 'Special handling for POLAR STEROGRAPHIC image!'

            if fakeLabelFile:
                print 'Cannot reprocess polar image without a label file!'
                print 'All we can do is upload the file and hope for the best.'
                # First file is for upload, second contains the timestamp.
                return [localImagePath, localLabelPath]
            
            # Compute how many chunks are needed for this image
            numChunks = ceil(width / POLAR_WIDTH_CHUNK_SIZE)
            
            # Determine which chunk this DB entry is for
            chunkNum = getChunkNum(setName)
            print 'This is chunk number ' + str(chunkNum)
            
            if chunkNum >= numChunks: # Check for chunk number error
                raise Exception('Illegal chunk number: ' + setName)
                
            # If this is the main DB entry, we need to make sure the other DB entries exist!
            if chunkNum == 0:
                # Go ahead and try to add each chunk, the call will only go through if it does not already exist.
                for i in range(1,numChunks):
                    chunkSetName = makeChunkSetName(setName, i)
                    print 'Add chunk set name to DB: ' + chunkSetName
                    common.addDataRecord(db, common.SENSOR_TYPE_HiRISE, subtype, chunkSetName, remoteURL)
                    
            raise Exception('DEBUG')
            
            # Now actually generate the desired chunk
            # - Need to use PIRL tools to extract a chunk to an IMG format, then convert that back to JP2 so that Google can read it.
            fileBasePath     = os.path.splitext(localImagePath)[0]
            localImgPath     = fileBasePath + '.IMG'
            localChunkPrefix = fileBasePath + '_' + str(chunkNum)
            chunkBB = getChunkBoundingBox(width, height, chunkNum)
            localChunkPath = correctAndCropImage(localImagePath, localLabelPath, localChunkPrefix, chunkBB)           
            
            # Just use the same label file, we don't care if the DB has per-chunk boundaries.
            return [localChunkPath, localLabelPath]
            
        else: # A normal, non-polar file.
            # First file is for upload, second contains the timestamp.
            return [localImagePath, localLabelPath]

    
    # TODO: Handle POLAR DEMS

    else: # Handle DEMs
        
        # For DEMs there is no label file
        localFilePath = os.path.join(workDir, os.path.basename(remoteURL))
        if not os.path.exists(localFilePath):
            # Download the image
            cmd = 'wget ' + remoteURL + ' -O ' + localFilePath
            print cmd
            os.system(cmd)
        if not IrgFileFunctions.fileIsNonZero(localFilePath): # Make sure we got the file
            raise Exception('Unable to download from URL: ' + remoteURL)

        # Generate a header file from the IMG file
        localLabelPath = localFilePath[:-4] + '.LBL'
        cmd = 'head -n 90 ' + localFilePath +' >  '+ localLabelPath
        print cmd
        os.system(cmd)

        # Check if this is a polar stereographic image
        isPolar = False 
        f = open(localLabelPath)
        for line in f:
            if ("MAP_PROJECTION_TYPE" in line) and ("POLAR STEREOGRAPHIC" in line):
                isPolar = True
                print 'WARNING: POLAR STEREOGRAPHIC DEM MAY NOT UPLOAD PROPERLY'
                break
                #os.remove(localFilePath)
                #os.remove(localLabelPath)
                #raise Exception('POLAR STEREOGRAPHIC DEMs on hold until Google fixes a bug!')
        f.close()
            
        # Convert from IMG to TIF
        tiffFilePath = localFilePath[:-4] + '.TIF'
        if not os.path.exists(tiffFilePath):
            cmd = 'gdal_translate -of GTiff ' + localFilePath +' '+ tiffFilePath
            print cmd
            os.system(cmd)
        
            if not isPolar: # Only EQC files need to be corrected
                # Correct projected coordinates problems
                cmd = 'python /home/pirl/smcmich1/repo/Tools/geoTiffTool.py --normalize-eqc-lon ' + tiffFilePath
                print cmd
                os.system(cmd)

        os.remove(localFilePath) # Clean up source image
        return [tiffFilePath, localLabelPath] 
def main(argsIn):

    print '#################################################################################'
    print "Running makeDemAndCompare.py"

    try:
        try:
            usage = "usage: makeDemAndCompare.py [--output <path>][--manual]\n  "
            parser = optparse.OptionParser(usage=usage)
            
            inputGroup = optparse.OptionGroup(parser, 'Input Paths')
            inputGroup.add_option("--left",  dest="leftPath",  help="Path to left  cube file")
            inputGroup.add_option("--right", dest="rightPath", help="Path to right cube file")            


            inputGroup.add_option("--lola",    dest="lolaPath", help="Path to LOLA DEM")
            inputGroup.add_option("--asu",     dest="asuPath",  help="Path to ASU DEM")
            
            inputGroup.add_option("--node-file", dest="nodeFilePath", 
                                  help="Path to file containing list of available nodes")

            parser.add_option_group(inputGroup)

            # The default working directory path is kind of ugly...
            parser.add_option("--workDir", dest="workDir",  help="Folder to store temporary files in")

            parser.add_option("--prefix",  dest="prefix",   help="Output prefix.")

            parser.add_option("--log-path",  dest="logPath",        
                              help="Where to write the output log file.")

            parser.add_option("--crop",  dest="cropAmount", 
                              help="Crops the output image to reduce processing time.")

            parser.add_option("--manual", action="callback", callback=man,
                              help="Read the manual.")
            parser.add_option("--keep", action="store_true", dest="keep",
                              help="Do not delete the temporary files.")
            (options, args) = parser.parse_args(argsIn)

            if not options.leftPath: 
                parser.error("Need left input path")
            if not options.rightPath: 
                parser.error("Need right input path")
            if not options.prefix: 
                parser.error("Need output prefix")            

        except optparse.OptionError, msg:
            raise Usage(msg)

        print "Beginning processing....."

        startTime = time.time()

        # Make sure we have all the functions we need
        functionStartupCheck()

        # Set this to true to force steps after it to activate
        carry = False

        # Set up the output folders
        outputFolder  = os.path.dirname(options.prefix)
        inputBaseName = os.path.basename(options.leftPath)
        tempFolder    = outputFolder + '/' + inputBaseName + '_stereoCalibrationTemp/'
        if (options.workDir):
            tempFolder = options.workDir
        if not os.path.exists(outputFolder):
            os.mkdir(outputFolder) 
        hadToCreateTempFolder = not os.path.exists(tempFolder)
        if not os.path.exists(tempFolder):
            os.mkdir(tempFolder) 
        
        # Set up logging
        if not options.logPath:
            options.logPath = options.prefix + '-Log.txt'
        logging.basicConfig(filename=options.logPath,level=logging.INFO)


        # Go ahead and set up all the output paths
        # -- Deliverables
        demPath               = options.prefix + '-DEM.tif'
        intersectionErrorPath = options.prefix + '-IntersectionErr.tif'
        hillshadePath         = options.prefix + '-Hillshade.tif'
        colormapPath          = options.prefix + '-Colormap.tif'
        colormapLegendPath    = options.prefix + '-ColormapLegend.csv'
        mapProjectLeftPath    = options.prefix + '-MapProjLeft.tif'
        mapProjectRightPath   = options.prefix + '-MapProjRight.tif'
        confidenceLevelPath   = options.prefix + '-Confidence.tif'
        confidenceLegendPath  = options.prefix + '-ConfidenceLegend.csv'
        # -- Diagnostic
        intersectionViewPathX    = options.prefix + '-IntersectionErrorX.tif'
        intersectionViewPathY    = options.prefix + '-IntersectionErrorY.tif'
        intersectionViewPathZ    = options.prefix + '-IntersectionErrorZ.tif'
        lolaDiffStatsPath        = options.prefix + '-LOLA_diff_stats.txt'
        lolaDiffPointsPath       = options.prefix + '-LOLA_diff_points.csv'
        lolaAsuDiffStatsPath     = options.prefix + '-ASU_LOLA_diff_stats.txt'
        lolaAsuDiffPointsPath    = options.prefix + '-ASU_LOLA_diff_points.csv'
        mapProjectLeftUint8Path  = options.prefix + '-MapProjLeftUint8.tif'
        mapProjectRightUint8Path = options.prefix + '-MapProjRightUint8.tif'


        # If specified, crop the inputs that will be passed into the stereo function to reduce processing time
        mainMosaicCroppedPath   = os.path.join(tempFolder, 'mainMosaicCropped.cub')
        stereoMosaicCroppedPath = os.path.join(tempFolder, 'stereoMosaicCropped.cub')
        if options.cropAmount and (options.cropAmount > 0):

            # Verify input files are present
            if not os.path.exists(options.leftPath):
                raise Exception('Input file ' + options.leftPath + ' not found!')
            if not os.path.exists(options.rightPath):
                raise Exception('Input file ' + options.rightPath + ' not found!')

            if (not os.path.exists(mainMosaicCroppedPath)) or carry:
                cmd = ('crop from= ' + options.leftPath   + ' to= ' + mainMosaicCroppedPath + 
                           ' nlines= ' + str(options.cropAmount))# + ' line=24200')
                print cmd
                os.system(cmd)
            if (not os.path.exists(stereoMosaicCroppedPath) or carry):
                cmd = ('crop from= ' + options.rightPath + ' to= ' + stereoMosaicCroppedPath + 
                           ' nlines= ' + str(options.cropAmount))#  + ' line=24200')
                print cmd
                os.system(cmd)
            options.leftPath  = mainMosaicCroppedPath
            options.rightPath = stereoMosaicCroppedPath

        print '\n-------------------------------------------------------------------------\n'

        # Call stereo to generate a point cloud from the two images
        # - This step takes a really long time.

        stereoOutputPrefix = os.path.join(tempFolder, 'stereoWorkDir/stereo')
        stereoOutputFolder = os.path.join(tempFolder, 'stereoWorkDir')
        pointCloudPath     = stereoOutputPrefix + '-PC.tif'
        
        stereoOptionString = ('--corr-timeout 400 --alignment-method AffineEpipolar --subpixel-mode ' + str(SUBPIXEL_MODE) + 
                              ' ' + options.leftPath + ' ' + options.rightPath + 
                              ' --job-size-w 4096 --job-size-h 4096 ' + # Reduce number of tile files created
                              ' ' + stereoOutputPrefix + ' --processes 10 --threads-multiprocess 4' +
                             ' --threads-singleprocess 32 --compute-error-vector' + ' --filter-mode 1' +
                              ' --erode-max-size 5000 --subpixel-kernel 35 35 --subpixel-max-levels 0')
  
        if (not os.path.exists(pointCloudPath) and not os.path.exists(demPath)) or carry:

            # Verify input files are present
            if not os.path.exists(options.leftPath):
                raise Exception('Input file ' + options.leftPath + ' not found!')
            if not os.path.exists(options.rightPath):
                raise Exception('Input file ' + options.rightPath + ' not found!')

            cmd = ('parallel_stereo ' + stereoOptionString)
            print cmd
            os.system(cmd)
 

            # Compute percentage of good pixels
            percentGood = IrgAspFunctions.getStereoGoodPixelPercentage(stereoOutputPrefix)
            print 'Stereo completed with good pixel percentage: ' + str(percentGood)
            logging.info('Final stereo completed with good pixel percentage: %s', str(percentGood))
                      
        else:
            print 'Stereo file ' + pointCloudPath + ' already exists, skipping stereo step.'


        stereoTime = time.time()
        logging.info('Stereo finished in %f seconds', stereoTime - startTime)


        # Find out the center latitude of the mosaic
        if os.path.exists(options.leftPath):
            centerLat = IrgIsisFunctions.getCubeCenterLatitude(options.leftPath, tempFolder)
        elif os.path.exists(demPath): # Input file has been deleted but we still have the info
            demInfo = IrgGeoFunctions.getImageGeoInfo(demPath, False)
            centerLat = demInfo['standard_parallel_1']
        else:
            raise Exception("Can't delete the input files before creating the DEM!")

        # Generate a DEM
        if (not os.path.exists(demPath)) or carry:
            
            # Equirectangular style projection
            # - Latitude of true scale = center latitude = lat_ts
            # - Latitude of origin = 0 = lat+0
            # - Longitude of projection center = Central meridian = lon+0
            cmd = ('point2dem --dem-hole-fill-len 15  --remove-outliers --errorimage -o ' + options.prefix + ' ' + pointCloudPath + 
                            ' -r moon --tr ' + str(DEM_METERS_PER_PIXEL) + ' --t_srs "+proj=eqc +lat_ts=' + str(centerLat) + 
                            ' +lat_0=0 +a='+str(MOON_RADIUS)+' +b='+str(MOON_RADIUS)+' +units=m" --nodata ' + str(DEM_NODATA))
            os.system(cmd)
        else:
            print 'DEM file ' + demPath + ' already exists, skipping point2dem step.'

        # Create a hillshade image to visualize the output
        if (not os.path.exists(hillshadePath)) or carry:
            cmd = 'hillshade ' + demPath + ' -o ' + hillshadePath
            print cmd
            os.system(cmd)
        else:
            print 'Output file ' + hillshadePath + ' already exists, skipping hillshade step.'

        # Create a colorized version of the hillshade
        # - Uses a blue-red color map from here: http://www.sandia.gov/~kmorel/documents/ColorMaps/
        if (not os.path.exists(colormapPath)) or (not os.path.exists(colormapLegendPath)) or carry:
            # The color LUT is kept with the source code
            lutFilePath = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'colorProfileBlueRed.csv')
            
            
            # Generate the initial version of colormap
            colormapTempPath = options.prefix + '-ColormapTemp.tif'
            cmd = 'colormap ' + demPath + ' -o ' + colormapTempPath + ' -s ' + hillshadePath + ' --lut-file ' + lutFilePath
            print cmd
            os.system(cmd)
            
            # Now convert to the final output version (remove transparency layer) and remove the temp file
            IrgFileFunctions.stripRgbImageAlphaChannel(colormapTempPath, colormapPath)            
            os.remove(colormapTempPath)
            
            # Generate another file storing the colormap info
            writeColorMapInfo(colormapPath, lutFilePath, demPath, colormapLegendPath)
        else:
            print 'Output file ' + colormapPath + ' already exists, skipping colormap step.'


        ## Create a 3d mesh of the point cloud
        #meshPath   = os.path.join(outputFolder, 'mesh.ive')
        #meshPrefix = os.path.join(outputFolder, 'mesh')
        #cmd = 'point2mesh ' + pointCloudPath + ' ' + options.leftPath + ' -o ' + meshPrefix
        #if not os.path.exists(meshPath):
        #    print cmd
        #    os.system(cmd)

        if not options.keep: # Remove stereo folder here to cut down on file count before mapproject calls
             IrgFileFunctions.removeFolderIfExists(stereoOutputFolder)


        # Convert the intersection error to a viewable format
        cmdX = 'gdal_translate -ot byte -scale 0 10 0 255 -outsize 50% 50% -b 1 ' + intersectionErrorPath + ' ' + intersectionViewPathX
        cmdY = 'gdal_translate -ot byte -scale 0 10 0 255 -outsize 50% 50% -b 2 ' + intersectionErrorPath + ' ' + intersectionViewPathY
        cmdZ = 'gdal_translate -ot byte -scale 0 10 0 255 -outsize 50% 50% -b 3 ' + intersectionErrorPath + ' ' + intersectionViewPathZ
        if not os.path.exists(intersectionViewPathX) or carry:
            print cmdX
            os.system(cmdX)
        if not os.path.exists(intersectionViewPathY) or carry:
            print cmdY
            os.system(cmdY)
        if not os.path.exists(intersectionViewPathZ) or carry:
            print cmdZ
            os.system(cmdZ)

        # Generate a confidence plot from the intersection error
        if not os.path.exists(confidenceLevelPath):
            thresholdString = str(PIXEL_ACCURACY_THRESHOLDS)[1:-1].replace(',', '')
            cmd = ('maskFromIntersectError ' + intersectionErrorPath + ' ' + confidenceLevelPath
                                             + ' --legend ' + confidenceLegendPath + 
                                        ' --scaleOutput --thresholds ' + thresholdString)
            print cmd
            os.system(cmd)

        hillshadeTime = time.time()
        logging.info('DEM and hillshade finished in %f seconds', hillshadeTime - stereoTime)


        # Call script to compare LOLA data with the DEM
        if options.lolaPath:
            compareDemToLola(options.lolaPath, demPath, lolaDiffStatsPath, lolaDiffPointsPath, carry)

        # Call script to compare LOLA data with the ASU DEM
        if options.asuPath:
            compareDemToLola(options.lolaPath, options.asuPath, lolaAsuDiffStatsPath, lolaAsuDiffPointsPath, carry)

        # Generate a map projected version of the left and right images
        # - This step is done last since it is so slow!
        mapProjectImage(options.leftPath,  demPath, mapProjectLeftPath,  MAP_PROJECT_METERS_PER_PIXEL, centerLat, options.nodeFilePath, carry)
        mapProjectImage(options.rightPath, demPath, mapProjectRightPath, MAP_PROJECT_METERS_PER_PIXEL, centerLat, options.nodeFilePath, carry)

        # Generate 8 bit versions of the mapproject files for debugging
        cmdLeft  = 'gdal_translate -scale -ot byte ' + mapProjectLeftPath  + ' ' + mapProjectLeftUint8Path
        cmdRight = 'gdal_translate -scale -ot byte ' + mapProjectRightPath + ' ' + mapProjectRightUint8Path
        if not os.path.exists(mapProjectLeftUint8Path) or carry:
            print cmdLeft
            os.system(cmdLeft)
        if not os.path.exists(mapProjectRightUint8Path) or carry:
            print cmdRight
            os.system(cmdRight)

        mapProjectTime = time.time()
        logging.info('Map project finished in %f seconds', mapProjectTime - hillshadeTime)

        # Clean up temporary files
        if not options.keep:
            print 'Removing temporary files'
            IrgFileFunctions.removeIfExists(mainMosaicCroppedPath)
            IrgFileFunctions.removeIfExists(stereoMosaicCroppedPath)
            #IrgFileFunctions.removeIntermediateStereoFiles(stereoOutputPrefix) # Limited clear
            IrgFileFunctions.removeFolderIfExists(stereoOutputFolder) # Larger clear
            #if (hadToCreateTempFolder): Not done since stereo output needs to be retained
            #    IrgFileFunctions.removeFolderIfExists(tempFolder)


        endTime = time.time()

        logging.info('makeDemAndCompare.py finished in %f seconds', endTime - startTime)
        print "Finished in " + str(endTime - startTime) + " seconds."
        print '#################################################################################'
        return 0
def main(argsIn):

    print "Started positionCorrector.py"

    try:
        try:
            usage = "usage: positionCorrector.py [--input <cube file>][--output <path>][--manual]\n  "
            parser = optparse.OptionParser(usage=usage)
            parser.add_option("-i", "--input", dest="inputPath",
                              help="Path to the input file.")
            parser.add_option("-o", "--output", dest="outputPath",
                              help="Where to write the output file.")

            # The default working directory path is kind of ugly...
            parser.add_option("--workDir", dest="workDir",  help="Folder to store temporary files in")

            parser.add_option("--manual", action="callback", callback=man,
                              help="Read the manual.")
            parser.add_option("--keep", action="store_true",
                              dest="keep",
                              help="Do not delete the temporary files.")
            parser.add_option("--spk", dest="spkPath", help='Output SPK path (always keep)')
            (options, args) = parser.parse_args(argsIn)

            if not options.inputPath:  parser.error("Need input path")
            if not options.outputPath: parser.error("Need output path")

        except optparse.OptionError, msg:
            raise Usage(msg)

        print "Beginning processing....."

        startTime = time.time()

        outputFolder  = os.path.dirname(options.outputPath)
        inputBaseName = os.path.basename(options.inputPath)
        tempFolder    = outputFolder + '/' + inputBaseName + '_posCorrectTemp/'
        if (options.workDir):
            tempFolder = options.workDir
        if not os.path.exists(tempFolder):
            os.mkdir(tempFolder) 

        # Copy the input file to the output location
        cmd = "cp " + options.inputPath + " " + options.outputPath
        #print cmd
        os.system(cmd)

        # Retrieve a list of all the kernels needed by the input cube file
        kernelDict = IrgIsisFunctions.getKernelsFromCube(options.outputPath)

        # Find the leap second file
        if not ('LeapSecond' in kernelDict):
            os.remove(options.outputPath)
            raise Exception('Error! Unable to find leap second file!')
        else:
            leapSecondFilePath = kernelDict['LeapSecond'][0] # Only deal with a single file
            

        # Convert the kernels into a list of strings to pass as arguments
        kernelStringList = []
        for k, v in kernelDict.iteritems(): # Iterate through dictionary entries
            # Add everything except the gigantic shape model (DEM)
            if k != 'ShapeModel':
                for i in v: # Iterate through type lists
                    kernelStringList.append(str(i))


        # Determine if the input file is LE or RE
        lePos = options.inputPath.rfind('LE')
        rePos = options.inputPath.rfind('RE')

        # Set the offsets from LRO spacecraft to the LRONAC cameras in meters
        if (lePos > rePos):
            lronacOffset = [1.3462, 0.8890, -0.1778] # LE
        else: 
            lronacOffset = [1.0160, 0.8890, -0.1778] # RE

        # Write out a file containing the LRONAC camera offset
        lronacOffsetPath = os.path.join(tempFolder, "lronacCameraOffset.csv")
        f = open(lronacOffsetPath, 'w')
        f.write('1 0 0 ' + str(lronacOffset[0]) + '\n')
        f.write('0 1 0 ' + str(lronacOffset[1]) + '\n')
        f.write('0 0 1 ' + str(lronacOffset[2]) + '\n')
        f.write('0 0 0 1')
        f.close()


        # Make sure the SPK data path does not already exist
        tempDataPrefix = os.path.join(tempFolder, "tempNavData")
        spkDataPath    = tempDataPrefix + "-spkData.txt"
        if os.path.exists(spkDataPath):
            os.remove(spkDataPath)

        # Call lronac spice editor tool to generate modified text file
        # - Call is silent unless there is an error
        cmd = ['spiceEditor', '--transformType', '1', '--transformFile', lronacOffsetPath, 
                              '--outputPrefix', tempDataPrefix, '--sourceCube', options.inputPath,
                              '--kernels'] + kernelStringList
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        outputText, err = p.communicate()       
        if not os.path.exists(spkDataPath):
            os.remove(options.outputPath)
            print cmd
            print outputText 
            raise Exception('Failed to create modified SPK data!')

        # Write the config file needed for the mkspk function
        #print 'Writing mkspk config file...'
        mkspkConfigPath = os.path.join(tempFolder, "spkConfig.txt")
        IsisTools.makeSpkSetupFile(leapSecondFilePath, mkspkConfigPath)

        # If the file already exists, delete it and rewrite it.
        if options.spkPath:
          tempSpkPath = options.spkPath
          #print 'Storing modified SPK file ' + tempSpkPath
        else:
          tempSpkPath = os.path.join(tempFolder, "modifiedLrocSpk.bsp")
        if os.path.exists(tempSpkPath):
            os.remove(tempSpkPath)

        #TODO: Temp trick to make sure this works for long paths

        # Create new SPK file using modified data
        # - Call is silent unless there is an error
        cmd = ['mkspk', '-setup', mkspkConfigPath, '-input', spkDataPath, '-output', tempSpkPath]
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        outputText, err = p.communicate()
        if not os.path.exists(tempSpkPath):
            os.remove(options.outputPath)
            print cmd
            print outputText
            raise Exception('Failed to create modified SPK file!')

        # Re-run spiceinit using the new SPK file
        cmd = ['spiceinit', 'attach=', 'true', 'from=', options.outputPath, "spk=", tempSpkPath]
        #print cmd
        p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        outputText, err = p.communicate()
 
        if (outputText.find('ERROR') >= 0):
            print cmd
            print outputText
            raise Exception('Found error when calling spiceinit!') 

        # Clean up temporary files
        if not options.keep:
            os.remove(spkDataPath)
            os.remove(mkspkConfigPath)

        endTime = time.time()

        print "Finished in " + str(endTime - startTime) + " seconds."
        return 0