def getBoundingBox(fileList): """Return the bounding box for this data set in the format (minLon, maxLon, minLat, maxLat)""" if len(fileList) == 2: # Read BB from the label file return IrgGeoFunctions.getBoundingBoxFromIsisLabel(fileList[1]) else: # No label file, read it from the the main file return IrgGeoFunctions.getImageBoundingBox( fileList[0] ) # This information is also available in the IMG file header
def chooseLonCenter(self): '''Choose whether to align to the 0 centered or 180 centered basemap''' (minLon, maxLon, minLat, maxLat) = IrgGeoFunctions.getImageBoundingBox(self._inputHrscPaths[0]) meanLon = (minLon + maxLon) / 2 # TODO: Verify how these bounds work! # Detect if the image is located nearby the 180 degree line if (abs(abs(meanLon)-180) < 10) or (abs(maxLon - minLon) > 200): return 180 else: # Use the normal 0 centered image return 0
def chooseLonCenter(self): '''Choose whether to align to the 0 centered or 180 centered basemap''' (minLon, maxLon, minLat, maxLat) = IrgGeoFunctions.getImageBoundingBox(self._inputHrscPaths[0]) meanLon = (minLon + maxLon) / 2 # TODO: Verify how these bounds work! # Detect if the image is located nearby the 180 degree line if (abs(abs(meanLon) - 180) < 10) or (abs(maxLon - minLon) > 200): return 180 else: # Use the normal 0 centered image return 0
def main(): outputPath = '' try: try: usage = "usage: cube2kml.py [--help][--manual]\n " parser = optparse.OptionParser(usage=usage) parser.add_option("--manual", action="callback", callback=man, help="Read the manual.") parser.add_option( "-o", "--output-path", dest="outputPath", help="Output path (default replace extension with .kml") (options, args) = parser.parse_args() if not args: parser.error("need input cube file") cubePath = args[0] except optparse.OptionError, msg: raise Usage(msg) print "Beginning processing....." # Determine the output path if outputPath == '': outputPath = os.path.splitext( cubePath)[0] + '.kml' # Default output path outputFolder = os.path.dirname(outputPath) # Get the four corners of the cube bb = IrgGeoFunctions.getImageBoundingBox(cubePath) # Generate a kml plot of the cube generateKml(bb, outputPath) print "Finished" return 0
def main(): outputPath = "" try: try: usage = "usage: cube2kml.py [--help][--manual]\n " parser = optparse.OptionParser(usage=usage) parser.add_option("--manual", action="callback", callback=man, help="Read the manual.") parser.add_option( "-o", "--output-path", dest="outputPath", help="Output path (default replace extension with .kml" ) (options, args) = parser.parse_args() if not args: parser.error("need input cube file") cubePath = args[0] except optparse.OptionError, msg: raise Usage(msg) print "Beginning processing....." # Determine the output path if outputPath == "": outputPath = os.path.splitext(cubePath)[0] + ".kml" # Default output path outputFolder = os.path.dirname(outputPath) # Get the four corners of the cube bb = IrgGeoFunctions.getImageBoundingBox(cubePath) # Generate a kml plot of the cube generateKml(bb, outputPath) print "Finished" return 0
def writeLabelFile(imagePath, outputPath, dataSetName, versionId, description, extraData=None): """Write out a .LBL file formatted for the PDS""" # Call functions to automatically obtain some data from the referenced image imageSize = IrgGeoFunctions.getImageSize(imagePath) boundingBox = IrgGeoFunctions.getImageBoundingBox(imagePath) # Obtain the ASP version string aspVersionString = IrgAspFunctions.getAspVersionStrings() imageGeoInfo = IrgGeoFunctions.getImageGeoInfo(imagePath) projCenterLatitude = imageGeoInfo['standard_parallel_1'] projCenterLongitude = imageGeoInfo['central_meridian'] # Currently assuming pixels are the same size metersPerPixel = abs(imageGeoInfo['pixel size'][0]) # Compute pixels per degree lonSpanDegrees = boundingBox[1] - boundingBox[0] latSpanDegrees = boundingBox[3] - boundingBox[2] pixelsPerDegreeLon = imageSize[0] / lonSpanDegrees pixelsPerDegreeLat = imageSize[1] / latSpanDegrees pixelsPerDegree = (pixelsPerDegreeLat + pixelsPerDegreeLon) / 2.0 # Computed by dividing 'Origin' by 'Pixel Size' lineProjOffset = imageGeoInfo['origin'][0] / imageGeoInfo['pixel size'][1] sampleProjOffset = imageGeoInfo['origin'][1] / imageGeoInfo['pixel size'][0] labelFile = open(outputPath, 'w') labelFile.write('PDS_VERSION_ID = PDS3\n') labelFile.write('/* The source image data definition. */\n') labelFile.write('^IMAGE = ' + os.path.basename(outputPath) +'\n') labelFile.write('/* Identification Information */\n') labelFile.write('DATA_SET_ID = ""\n') # Someone will tell us what to put here labelFile.write('DATA_SET_NAME = ""\n') # Someone will tell us what to put here labelFile.write("VOLUME_ID = ''\n") # Someone will tell us what to put here labelFile.write('PRODUCER_INSTITUTION_NAME = "NASA AMES RESEARCH CENTER"\n') labelFile.write('PRODUCER_ID = NASA IRG\n') labelFile.write('PRODUCER_FULL_NAME = "ZACHARY MORATTO"\n') labelFile.write("PRODUCT_ID = " + dataSetName + "\n") labelFile.write("PRODUCT_VERSION_ID = " + versionId + "\n") labelFile.write('PRODUCT_TYPE = "RDR"\n') labelFile.write('INSTRUMENT_HOST_NAME = "LUNAR RECONNAISSANCE ORBITER"\n') labelFile.write('INSTRUMENT_HOST_ID = "LRO"\n') labelFile.write('INSTRUMENT_NAME = "LUNAR RECONNAISSANCE ORBITER CAMERA"\n') labelFile.write('INSTRUMENT_ID = "LROC"\n') labelFile.write('TARGET_NAME = MOON\n') labelFile.write('MISSION_PHASE_NAME = "NOMINAL MISSION"\n') labelFile.write("""RATIONALE_DESC = "Created at the request of NASA's Exploration\n""") labelFile.write(' Systems Mission Directorate to support future\n') labelFile.write(' human exploration"\n') labelFile.write('SOFTWARE_NAME = "'+ aspVersionString[0] +' | '+ aspVersionString[2] +'"\n') labelFile.write('DESCRIPTION = "' + description + '"\n') labelFile.write('\n') labelFile.write('/* Time Parameters */\n') labelFile.write('PRODUCT_CREATION_TIME = ' + time.strftime("%Y-%m-%dT%H:%M:%S") + '\n') labelFile.write('\n') labelFile.write('/* NOTE: */\n') labelFile.write('/* This raster image is composed of a set of pixels that represent finite */\n') labelFile.write('/* areas, and not discrete points. The center of the upper left pixel is */\n') labelFile.write('/* defined as line and sample (1.0,1.0). The */\n') labelFile.write('/* [LINE,SAMPLE]_PROJECTION_OFFSET elements are the pixel offset from line */\n') labelFile.write('/* and sample (1.0,1.0) to the map projection origin (defined by the */\n') labelFile.write('/* CENTER_LATITUDE and CENTER_LONGITUDE elements). These offset values */\n') labelFile.write('/* are positive when the map projection origin is to the right or below */\n') labelFile.write('/* the center of the upper left pixel. */\n') if extraData: # Location for additional notes labelFile.write(extraData) labelFile.write('\n') labelFile.write('OBJECT = IMAGE_MAP_PROJECTION\n') labelFile.write(' MAP_PROJECTION_TYPE = EQUIRECTANGULAR\n') # Specified by +proj=eqc labelFile.write(' PROJECTION_LATITUDE_TYPE = PLANETOCENTRIC\n') #From gdalinfo? labelFile.write(' A_AXIS_RADIUS = 1737.4 <KM>\n') # Fixed lunar radius labelFile.write(' B_AXIS_RADIUS = 1737.4 <KM>\n') labelFile.write(' C_AXIS_RADIUS = 1737.4 <KM>\n') labelFile.write(' COORDINATE_SYSTEM_NAME = PLANETOCENTRIC\n') #From gdalinfo? labelFile.write(' POSITIVE_LONGITUDE_DIRECTION = EAST\n') #From gdalinfo? labelFile.write(' KEYWORD_LATITUDE_TYPE = PLANETOCENTRIC\n') #From gdalinfo? labelFile.write(' /* NOTE: CENTER_LATITUDE and CENTER_LONGITUDE describe the location */\n') labelFile.write(' /* of the center of projection, which is not necessarily equal to the */\n') labelFile.write(' /* location of the center point of the image. */\n') labelFile.write(' CENTER_LATITUDE = ' + str(projCenterLatitude) + ' <DEG>\n') labelFile.write(' CENTER_LONGITUDE = ' + str(projCenterLongitude) + ' <DEG>\n') labelFile.write(' LINE_FIRST_PIXEL = 1\n') labelFile.write(' LINE_LAST_PIXEL = ' + str(imageSize[1] + 1) + '\n') labelFile.write(' SAMPLE_FIRST_PIXEL = 1\n') labelFile.write(' SAMPLE_LAST_PIXEL = ' + str(imageSize[0] + 1) + '\n') labelFile.write(' MAP_PROJECTION_ROTATION = 0.0 <DEG>\n') #From gdalinfo (probably always zero) labelFile.write(' MAP_RESOLUTION = ' + str(round(pixelsPerDegree,2)) +' <PIX/DEG>\n') labelFile.write(' MAP_SCALE = ' + str(round(metersPerPixel,4)) + ' <METERS/PIXEL>\n') labelFile.write(' MAXIMUM_LATITUDE = ' + str(boundingBox[3]) + ' <DEG>\n') labelFile.write(' MINIMUM_LATITUDE = ' + str(boundingBox[2]) + ' <DEG>\n') labelFile.write(' EASTERNMOST_LONGITUDE = ' + str(boundingBox[0]) + ' <DEG>\n') labelFile.write(' WESTERNMOST_LONGITUDE = ' + str(boundingBox[1]) + ' <DEG>\n') labelFile.write(' LINE_PROJECTION_OFFSET = ' + str(round(lineProjOffset,2)) +' <PIXEL>\n') labelFile.write(' SAMPLE_PROJECTION_OFFSET = ' + str(round(sampleProjOffset,2)) +' <PIXEL>\n') labelFile.write('END_OBJECT = IMAGE_MAP_PROJECTION\n') labelFile.write('\n') labelFile.write('END\n') labelFile.close() return True
def getBoundingBox(fileList): """Return the bounding box for this data set in the format (minLon, maxLon, minLat, maxLat)""" if len(fileList) == 2: # Read BB from the label file return IrgGeoFunctions.getBoundingBoxFromIsisLabel(fileList[1]) else: # No label file, read it from the the main file return IrgGeoFunctions.getImageBoundingBox(fileList[0]) # This information is also available in the IMG file header
def __init__(self, sourceFileInfoDict, outputFolder, basemapInstance, basemapInstance180, force=False, threadPool=None): '''Set up all the low resolution HRSC products.''' setName = sourceFileInfoDict['setName'] self._logger = logging.getLogger('hrscImageManager') # Echo logging to stdout echo = logging.StreamHandler(sys.stdout) echo.setLevel(logging.DEBUG) echo.setFormatter(logging.Formatter(MosaicUtilities.LOG_FORMAT_STR)) self._logger.addHandler(echo) self._logger.info('Initializing hrscImageManager for set ' + setName) # Initialize some values to empty in case they are accessed prematurely self._tileDict = None # # Set up some paths self._setName = setName self._threadPool = threadPool self._outputFolder = outputFolder self._hrscBasePathOut = os.path.join(outputFolder, setName) self._tileFolder = self._hrscBasePathOut + '_tiles' self._lowResMaskPath = self._hrscBasePathOut + '_low_res_mask.tif' self._highResBinaryMaskPath = self._hrscBasePathOut + '_high_res_binary_mask.tif' self._highResMaskPath = self._hrscBasePathOut + '_high_res_mask.tif' self._brightnessGainsPath = self._hrscBasePathOut + '_brightness_gains.csv' self._basemapCropPath = self._hrscBasePathOut + '_local_cropped_basemap.tif' # A crop of the basemap used in several places self._basemapGrayCropPath = self._hrscBasePathOut + '_local_gray_cropped_basemap.tif' #self._colorPairPath = self._hrscBasePathOut + '_low_res_color_pairs.csv' self._basemapSpatialRegistrationPath = self._hrscBasePathOut + '_low_res_spatial_transform_basemap.csv' # Transform to the low res basemap self._croppedRegionSpatialRegistrationPath = self._hrscBasePathOut + '_cropped_region_spatial_transform.csv' # Transform to cropped region of low res basemap self._highResSpatialRegistrationPath = self._hrscBasePathOut + '_high_res_spatial_transform_basemap.csv' self._lowResSpatialCroppedRegistrationPath = self._hrscBasePathOut + '_low_res_cropped_spatial_transform.csv' # Get full list of input paths from the input dictionary # - Sort them into a fixed order defined at the top of the file self._inputHrscPaths = [] rawList = sourceFileInfoDict['allChannelPaths'] self._inputHrscPaths.append( [s for s in rawList if 're3' in s][0] ) self._inputHrscPaths.append( [s for s in rawList if 'gr3' in s][0] ) self._inputHrscPaths.append( [s for s in rawList if 'bl3' in s][0] ) self._inputHrscPaths.append( [s for s in rawList if 'ir3' in s][0] ) self._inputHrscPaths.append( [s for s in rawList if 'nd3' in s][0] ) # TODO: Always store path to regular basemap? # Determine if a 180-centered basemap should be used for image preprocessing. self._isCentered180 = (self.chooseLonCenter() == 180) if self._isCentered180: self._logger.info('HRSC image is centered around 180') self._basemapInstance = basemapInstance180 else: # Normal case, use the 0 centered basemap self._basemapInstance = basemapInstance # Record input parameters self._basemapColorPath = self._basemapInstance.getColorBasemapPath() # Path to the color low res entire base map print 'Generating low res image copies...' # TODO: Warp to the correct basemap! # Generate a copy of each input HRSC channel at the low basemap resolution self._lowResWarpedPaths = [self._warpToProjection(path, outputFolder, '_basemap_res', self._basemapInstance.getLowResMpp(), force) for path in self._inputHrscPaths] # Build up a string containing all the low res paths for convenience self._lowResPathString = '' for path in self._lowResWarpedPaths: self._lowResPathString += path + ' ' print 'Generating low resolution mask...' # Make a mask at the low resolution cmd = './makeSimpleImageMask ' + self._lowResMaskPath +' '+ self._lowResPathString MosaicUtilities.cmdRunner(cmd, self._lowResMaskPath, force) self._lowResPathStringAndMask = self._lowResPathString +' '+ self._lowResMaskPath self._lowResMaskImageSize = IrgGeoFunctions.getImageSize(self._lowResMaskPath) # Compute the HRSC bounding box # - This is a pretty good estimate based on the metadata lowResNadirPath = self._lowResWarpedPaths[HRSC_NADIR] geoInfo = IrgGeoFunctions.getImageGeoInfo(lowResNadirPath) #print geoInfo['projection_bounds'] # print geoInfo['lonlat_bounds'] if 'lonlat_bounds' in geoInfo: (minLon, maxLon, minLat, maxLat) = geoInfo['lonlat_bounds'] else: # This function is not as reliable! (minLon, maxLon, minLat, maxLat) = IrgGeoFunctions.getImageBoundingBox(lowResNadirPath) hrscBoundingBoxDegrees = MosaicUtilities.Rectangle(minLon, maxLon, minLat, maxLat) if hrscBoundingBoxDegrees.maxX < hrscBoundingBoxDegrees.minX: hrscBoundingBoxDegrees.maxX += 360 # If needed, get both lon values into 0-360 degree range if (hrscBoundingBoxDegrees.minX < 0) and self._isCentered180: # If working in the 0-360 degree space, make sure the longitude values are positive hrscBoundingBoxDegrees.minX += 360 hrscBoundingBoxDegrees.maxX += 360 print 'Estimated HRSC bounds: ' + str(hrscBoundingBoxDegrees) # Cut out a region from the basemap around the location of the HRSC image # - We record the ROI in degrees and low res pixels print 'Generating low res base basemap region around HRSC data' CROP_BUFFER_LAT = 1.0 CROP_BUFFER_LON = 1.0 self._croppedRegionBoundingBoxDegrees = copy.copy(hrscBoundingBoxDegrees) self._croppedRegionBoundingBoxDegrees.expand(CROP_BUFFER_LON, CROP_BUFFER_LAT) self._croppedRegionBoundingBoxPixels = self._basemapInstance.degreeRoiToPixelRoi( self._croppedRegionBoundingBoxDegrees, False) self._basemapInstance.makeCroppedRegionDegrees(self._croppedRegionBoundingBoxDegrees, self._basemapCropPath, force) self._makeGrayscaleImage(self._basemapCropPath, self._basemapGrayCropPath) # Compute the spatial registration from the HRSC image to the base map self._computeBaseSpatialRegistration(self._basemapInstance, lowResNadirPath, force) # Compute the brightness scaling gains relative to the cropped base map # - This is done at low resolution # - The low resolution output is smoothed out later to avoid jagged edges. cmd = ('./computeBrightnessCorrection ' + self._basemapCropPath +' '+ self._lowResPathStringAndMask +' ' + self._lowResSpatialCroppedRegistrationPath +' '+ self._brightnessGainsPath) MosaicUtilities.cmdRunner(cmd, self._brightnessGainsPath, force) print 'Finished with low resolution processing for HRSC set ' + setName
def getBoundingBox(fileList): """Return the bounding box for this data set in the format (minLon, maxLon, minLat, maxLat)""" return IrgGeoFunctions.getImageBoundingBox(fileList[0])
def __init__(self, sourceFileInfoDict, outputFolder, basemapInstance, basemapInstance180, force=False, threadPool=None): '''Set up all the low resolution HRSC products.''' setName = sourceFileInfoDict['setName'] self._logger = logging.getLogger('hrscImageManager') # Echo logging to stdout echo = logging.StreamHandler(sys.stdout) echo.setLevel(logging.DEBUG) echo.setFormatter(logging.Formatter(MosaicUtilities.LOG_FORMAT_STR)) self._logger.addHandler(echo) self._logger.info('Initializing hrscImageManager for set ' + setName) # Initialize some values to empty in case they are accessed prematurely self._tileDict = None # # Set up some paths self._setName = setName self._threadPool = threadPool self._outputFolder = outputFolder self._hrscBasePathOut = os.path.join(outputFolder, setName) self._tileFolder = self._hrscBasePathOut + '_tiles' self._lowResMaskPath = self._hrscBasePathOut + '_low_res_mask.tif' self._highResBinaryMaskPath = self._hrscBasePathOut + '_high_res_binary_mask.tif' self._highResMaskPath = self._hrscBasePathOut + '_high_res_mask.tif' self._brightnessGainsPath = self._hrscBasePathOut + '_brightness_gains.csv' self._basemapCropPath = self._hrscBasePathOut + '_local_cropped_basemap.tif' # A crop of the basemap used in several places self._basemapGrayCropPath = self._hrscBasePathOut + '_local_gray_cropped_basemap.tif' #self._colorPairPath = self._hrscBasePathOut + '_low_res_color_pairs.csv' self._basemapSpatialRegistrationPath = self._hrscBasePathOut + '_low_res_spatial_transform_basemap.csv' # Transform to the low res basemap self._croppedRegionSpatialRegistrationPath = self._hrscBasePathOut + '_cropped_region_spatial_transform.csv' # Transform to cropped region of low res basemap self._highResSpatialRegistrationPath = self._hrscBasePathOut + '_high_res_spatial_transform_basemap.csv' self._lowResSpatialCroppedRegistrationPath = self._hrscBasePathOut + '_low_res_cropped_spatial_transform.csv' # Get full list of input paths from the input dictionary # - Sort them into a fixed order defined at the top of the file self._inputHrscPaths = [] rawList = sourceFileInfoDict['allChannelPaths'] self._inputHrscPaths.append([s for s in rawList if 're3' in s][0]) self._inputHrscPaths.append([s for s in rawList if 'gr3' in s][0]) self._inputHrscPaths.append([s for s in rawList if 'bl3' in s][0]) self._inputHrscPaths.append([s for s in rawList if 'ir3' in s][0]) self._inputHrscPaths.append([s for s in rawList if 'nd3' in s][0]) # TODO: Always store path to regular basemap? # Determine if a 180-centered basemap should be used for image preprocessing. self._isCentered180 = (self.chooseLonCenter() == 180) if self._isCentered180: self._logger.info('HRSC image is centered around 180') self._basemapInstance = basemapInstance180 else: # Normal case, use the 0 centered basemap self._basemapInstance = basemapInstance # Record input parameters self._basemapColorPath = self._basemapInstance.getColorBasemapPath( ) # Path to the color low res entire base map print 'Generating low res image copies...' # TODO: Warp to the correct basemap! # Generate a copy of each input HRSC channel at the low basemap resolution self._lowResWarpedPaths = [ self._warpToProjection(path, outputFolder, '_basemap_res', self._basemapInstance.getLowResMpp(), force) for path in self._inputHrscPaths ] # Build up a string containing all the low res paths for convenience self._lowResPathString = '' for path in self._lowResWarpedPaths: self._lowResPathString += path + ' ' print 'Generating low resolution mask...' # Make a mask at the low resolution cmd = './makeSimpleImageMask ' + self._lowResMaskPath + ' ' + self._lowResPathString MosaicUtilities.cmdRunner(cmd, self._lowResMaskPath, force) self._lowResPathStringAndMask = self._lowResPathString + ' ' + self._lowResMaskPath self._lowResMaskImageSize = IrgGeoFunctions.getImageSize( self._lowResMaskPath) # Compute the HRSC bounding box # - This is a pretty good estimate based on the metadata lowResNadirPath = self._lowResWarpedPaths[HRSC_NADIR] geoInfo = IrgGeoFunctions.getImageGeoInfo(lowResNadirPath) #print geoInfo['projection_bounds'] # print geoInfo['lonlat_bounds'] if 'lonlat_bounds' in geoInfo: (minLon, maxLon, minLat, maxLat) = geoInfo['lonlat_bounds'] else: # This function is not as reliable! (minLon, maxLon, minLat, maxLat) = IrgGeoFunctions.getImageBoundingBox(lowResNadirPath) hrscBoundingBoxDegrees = MosaicUtilities.Rectangle( minLon, maxLon, minLat, maxLat) if hrscBoundingBoxDegrees.maxX < hrscBoundingBoxDegrees.minX: hrscBoundingBoxDegrees.maxX += 360 # If needed, get both lon values into 0-360 degree range if (hrscBoundingBoxDegrees.minX < 0) and self._isCentered180: # If working in the 0-360 degree space, make sure the longitude values are positive hrscBoundingBoxDegrees.minX += 360 hrscBoundingBoxDegrees.maxX += 360 print 'Estimated HRSC bounds: ' + str(hrscBoundingBoxDegrees) # Cut out a region from the basemap around the location of the HRSC image # - We record the ROI in degrees and low res pixels print 'Generating low res base basemap region around HRSC data' CROP_BUFFER_LAT = 1.0 CROP_BUFFER_LON = 1.0 self._croppedRegionBoundingBoxDegrees = copy.copy( hrscBoundingBoxDegrees) self._croppedRegionBoundingBoxDegrees.expand(CROP_BUFFER_LON, CROP_BUFFER_LAT) self._croppedRegionBoundingBoxPixels = self._basemapInstance.degreeRoiToPixelRoi( self._croppedRegionBoundingBoxDegrees, False) self._basemapInstance.makeCroppedRegionDegrees( self._croppedRegionBoundingBoxDegrees, self._basemapCropPath, force) self._makeGrayscaleImage(self._basemapCropPath, self._basemapGrayCropPath) # Compute the spatial registration from the HRSC image to the base map self._computeBaseSpatialRegistration(self._basemapInstance, lowResNadirPath, force) # Compute the brightness scaling gains relative to the cropped base map # - This is done at low resolution # - The low resolution output is smoothed out later to avoid jagged edges. cmd = ('./computeBrightnessCorrection ' + self._basemapCropPath + ' ' + self._lowResPathStringAndMask + ' ' + self._lowResSpatialCroppedRegistrationPath + ' ' + self._brightnessGainsPath) MosaicUtilities.cmdRunner(cmd, self._brightnessGainsPath, force) print 'Finished with low resolution processing for HRSC set ' + setName