<NME> ossimChipperUtil.cpp <BEF> //--- // // File: ossimChipperUtil.cpp // // License: MIT // // Author: David Burken // // Description: Utility class definition processing digital elevation // models(dems). // //--- // $Id$ #include <ossim/util/ossimChipperUtil.h> #include <ossim/base/ossimArgumentParser.h> #include <ossim/base/ossimApplicationUsage.h> #include <ossim/base/ossimConnectableContainer.h> #include <ossim/base/ossimConnectableObject.h> #include <ossim/base/ossimException.h> #include <ossim/base/ossimFilename.h> #include <ossim/base/ossimGeoPolygon.h> #include <ossim/base/ossimIrect.h> #include <ossim/base/ossimKeywordlist.h> #include <ossim/base/ossimKeywordNames.h> #include <ossim/base/ossimNotify.h> #include <ossim/base/ossimProperty.h> #include <ossim/base/ossimRefPtr.h> #include <ossim/base/ossimRefreshEvent.h> #include <ossim/base/ossimScalarTypeLut.h> #include <ossim/base/ossimStdOutProgress.h> #include <ossim/base/ossimStringProperty.h> #include <ossim/base/ossimTrace.h> #include <ossim/base/ossimVisitor.h> #include <ossim/base/ossimGrect.h> #include <ossim/imaging/ossimAnnotationFontObject.h> #include <ossim/imaging/ossimAnnotationLineObject.h> #include <ossim/imaging/ossimAnnotationSource.h> #include <ossim/imaging/ossimBrightnessContrastSource.h> #include <ossim/imaging/ossimBumpShadeTileSource.h> #include <ossim/imaging/ossimFilterResampler.h> #include <ossim/imaging/ossimFusionCombiner.h> #include <ossim/imaging/ossimGammaRemapper.h> #include <ossim/imaging/ossimImageData.h> #include <ossim/imaging/ossimImageFileWriter.h> #include <ossim/imaging/ossimImageGeometry.h> #include <ossim/imaging/ossimImageHandler.h> #include <ossim/imaging/ossimImageHistogramSource.h> #include <ossim/imaging/ossimImageMosaic.h> #include <ossim/imaging/ossimImageRenderer.h> #include <ossim/imaging/ossimImageSource.h> #include <ossim/imaging/ossimImageSourceFilter.h> #include <ossim/imaging/ossimImageToPlaneNormalFilter.h> #include <ossim/imaging/ossimImageWriterFactoryRegistry.h> #include <ossim/imaging/ossimIndexToRgbLutFilter.h> #include <ossim/imaging/ossimRectangleCutFilter.h> #include <ossim/imaging/ossimScalarRemapper.h> #include <ossim/imaging/ossimSFIMFusion.h> #include <ossim/imaging/ossimTwoColorView.h> #include <ossim/imaging/ossimImageSourceFactoryRegistry.h> #include <ossim/init/ossimInit.h> #include <ossim/projection/ossimEquDistCylProjection.h> #include <ossim/projection/ossimImageViewAffineTransform.h> #include <ossim/projection/ossimMapProjection.h> #include <ossim/projection/ossimProjection.h> #include <ossim/projection/ossimProjectionFactoryRegistry.h> #include <ossim/projection/ossimUtmProjection.h> #include <ossim/support_data/ossimSrcRecord.h> #include <cmath> #include <sstream> #include <string> using namespace std; static ossimTrace traceDebug("ossimChipperUtil:debug"); static ossimTrace traceLog("ossimChipperUtil:log"); static ossimTrace traceOptions("ossimChipperUtil:options"); static const std::string BRIGHTNESS_KW = "brightness"; static const std::string COLOR_BLUE_KW = "color_blue"; static const std::string COLOR_GREEN_KW = "color_green"; static const std::string COLOR_RED_KW = "color_red"; static const std::string CONTRAST_KW = "contrast"; static const std::string CLIP_WMS_BBOX_LL_KW = "clip_wms_bbox_ll"; static const std::string CLIP_POLY_LAT_LON_KW = "clip_poly_lat_lon"; static const std::string CUT_BBOX_XYWH_KW = "cut_bbox_xywh"; static const std::string CUT_WMS_BBOX_KW = "cut_wms_bbox"; static const std::string CUT_WMS_BBOX_LL_KW = "cut_wms_bbox_ll"; static const std::string CUT_CENTER_LAT_KW = "cut_center_lat"; static const std::string CUT_CENTER_LON_KW = "cut_center_lon"; static const std::string CUT_RADIUS_KW = "cut_radius"; // meters static const std::string CUT_HEIGHT_KW = "cut_height"; // pixels static const std::string CUT_MAX_LAT_KW = "cut_max_lat"; static const std::string CUT_MAX_LON_KW = "cut_max_lon"; static const std::string CUT_MIN_LAT_KW = "cut_min_lat"; static const std::string CUT_MIN_LON_KW = "cut_min_lon"; static const std::string CUT_WIDTH_KW = "cut_width"; // pixels static const std::string DEM_KW = "dem"; static const std::string GAIN_KW = "gain"; static const std::string GAMMA_KW = "gamma"; static const std::string FILE_KW = "file"; static const std::string FULLRES_XYS_KW = "fullres_xys"; static const std::string HIST_AOI_KW = "hist_aoi"; static const std::string HIST_CENTER_KW = "hist_center"; static const std::string HIST_LLWH_KW = "hist_llwh"; static const std::string HIST_OP_KW = "hist_op"; static const std::string HIST_LINEAR_CLIP_KW = "hist_linear_clip"; static const std::string HIST_LINEAR_NORM_CLIP_KW = "hist_linear_norm_clip"; static const std::string HIST_CENTER_CLIP_KW = "hist_center_clip"; static const std::string HIST_BIAS_FACTOR_KW = "hist_bias_factor"; static const std::string IMAGE_SPACE_SCALE_X_KW = "image_space_scale_x"; static const std::string IMAGE_SPACE_SCALE_Y_KW = "image_space_scale_y"; static const std::string IMG_KW = "image"; static const std::string LUT_FILE_KW = "lut_file"; static const std::string DEGREES_X_KW = "degrees_x"; static const std::string DEGREES_Y_KW = "degrees_y"; static const std::string METERS_KW = "meters"; static const std::string NORTH_UP_KW = "north_up"; // bool static const std::string OP_KW = "operation"; static const std::string OUTPUT_RADIOMETRY_KW = "output_radiometry"; static const std::string PAD_THUMBNAIL_KW = "pad_thumbnail"; // bool static const std::string READER_PROPERTY_KW = "reader_property"; static const std::string RESAMPLER_FILTER_KW = "resampler_filter"; static const std::string ROTATION_KW = "rotation"; static const std::string ROTATE_TO_INPUT = "rotate_to_input"; static const std::string RRDS_KW = "rrds"; static const std::string SCALE_2_8_BIT_KW = "scale_2_8_bit"; static const std::string SHARPEN_MODE_KW = "sharpen_mode"; static const std::string SHARPEN_PERCENT_KW = "sharpen_percent"; static const std::string SNAP_TIE_TO_ORIGIN_KW = "snap_tie_to_origin"; static const std::string SRC_FILE_KW = "src_file"; static const std::string SRS_KW = "srs"; static const std::string THREE_BAND_OUT_KW = "three_band_out"; // bool static const std::string THUMBNAIL_RESOLUTION_KW = "thumbnail_resolution"; // pixels static const std::string TILE_SIZE_KW = "tile_size"; // pixels static const std::string TRUE_KW = "true"; static const std::string UP_IS_UP_KW = "up_is_up"; // bool static const std::string WRITER_KW = "writer"; static const std::string WRITER_PROPERTY_KW = "writer_property"; static const std::string COMBINER_TYPE_KW = "combiner_type"; static const std::string NULL_PIXEL_FLIP_KW = "null_pixel_flip"; static const std::string TWOCMV_OLD_INPUT_BAND_KW = "2cmv_old_input_band"; static const std::string TWOCMV_NEW_INPUT_BAND_KW = "2cmv_new_input_band"; static const std::string TWOCMV_RED_OUTPUT_SOURCE_KW = "2cmv_red_output_source"; static const std::string TWOCMV_GREEN_OUTPUT_SOURCE_KW = "2cmv_green_output_source"; static const std::string TWOCMV_BLUE_OUTPUT_SOURCE_KW = "2cmv_blue_output_source"; ossimChipperUtil::ossimChipperUtil() : ossimReferenced(), m_operation(OSSIM_CHIPPER_OP_UNKNOWN), m_kwl(new ossimKeywordlist()), m_srcKwl(0), m_geom(0), m_ivt(0), m_demLayer(0), m_imgLayer(0), m_viewPortStretchEnabled(false) { // traceDebug.setTraceFlag(true); m_kwl->setExpandEnvVarsFlag(true); } // Private/hidden from use. ossimChipperUtil::ossimChipperUtil(const ossimChipperUtil & /* obj */) : m_operation(OSSIM_CHIPPER_OP_UNKNOWN) { } // Private/hidden from use. const ossimChipperUtil &ossimChipperUtil::operator=(const ossimChipperUtil & /* rhs */) { return *this; } ossimRefPtr<ossimImageSource> ossimChipperUtil::createCombiner() const { ossimRefPtr<ossimImageSource> result; ossimString combinerType = m_kwl->find(COMBINER_TYPE_KW.c_str()); if (combinerType.empty()) { combinerType = "ossimImageMosaic"; } result = ossimImageSourceFactoryRegistry::instance()->createImageSource(combinerType); if (!result.valid()) { result = new ossimImageMosaic(); } return result; } ossimChipperUtil::~ossimChipperUtil() { clear(); } void ossimChipperUtil::addArguments(ossimArgumentParser &ap) { ossimString usageString = ap.getApplicationName(); usageString += " [option]... [input-option]... <input-file(s)> <output-file>\nNote at least one input is required either from one of the input options, e.g. --input-dem <my-dem.hgt> or adding to command line in front of the output file in which case the code will try to ascertain what type of input it is.\n\nAvailable traces:\n-T \"ossimChipperUtil:debug\" - General debug trace to standard out.\n-T \"ossimChipperUtil:log\" - Writes a log file to output-file.log.\n-T \"ossimChipperUtil:options\" - Writes the options to output-file-options.kwl."; ossimApplicationUsage *au = ap.getApplicationUsage(); au->setCommandLineUsage(usageString); au->setDescription(ap.getApplicationName() + " Utility application for generating elevation products from dem data."); au->addCommandLineOption("--azimuth", "<azimuth>\nhillshade option - Light source azimuth angle for bump shade.\nRange: 0 to 360, Default = 180.0"); au->addCommandLineOption("-b or --bands <n,n...>", "Use the specified bands in given order: e.g. \"3,2,1\" will select bands 3, 2 and 1 of the input image.\nNote: it is 1 based"); au->addCommandLineOption("--brightness", "<brightness>\nApply brightness to input image(s). Valid range: -1.0 to 1.0"); au->addCommandLineOption("--central-meridian", "<central_meridian_in_decimal_degrees>\nNote if set this will be used for the central meridian of the projection. This can be used to lock the utm zone."); au->addCommandLineOption("--color", "<r> <g> <b>\nhillshade option - Set the red, green and blue color values to be used with hillshade.\nThis option can be used with or without an image source for color.\nRange 0 to 255, Default r=255, g=255, b=255"); au->addCommandLineOption("--color-table", "<color-table.kwl>\nhillshade or color-relief option - Keyword list containing color table for color-relief option."); au->addCommandLineOption("--contrast", "<contrast>\nApply contrast to input image(s). Valid range: -1.0 to 1.0"); au->addCommandLineOption("--cut-bbox-xywh", "<x>,<y>,<width>,<height>\nSpecify a comma separated bounding box."); au->addCommandLineOption("--cut-wms-bbox", "<minx>,<miny>,<maxx>,<maxy>\nSpecify a comma separated list in the format of a WMS BBOX.\nThe units are in the units of the projector defined by the --srs key"); au->addCommandLineOption("--cut-wms-bbox-ll", "<minx>,<miny>,<maxx>,<maxy>\nSpecify a comma separated list in the format of a WMS BBOX.\nThe units are always decimal degrees"); au->addCommandLineOption("--cut-width", "<width>\nSpecify the cut width in pixel"); au->addCommandLineOption("--cut-height", "<height>\nSpecify the cut height in pixel"); au->addCommandLineOption("--clip-wms-bbox-ll", "<minx>,<miny>,<maxx>,<maxy>\nSpecify a comma separated list in the format of a WMS BBOX.\nThe units are always decimal degrees"); au->addCommandLineOption("--clip-poly-lat-lon", "Polygon in the form of a string: (lat,lon),(lat,lon),...(lat,lon)"); au->addCommandLineOption("--cut-bbox-ll", "<min_lat> <min_lon> <max_lat> <max_lon>\nSpecify a bounding box with the minimum latitude/longitude and max latitude/longitude in decimal degrees.\nNote coordinates are edge to edge."); au->addCommandLineOption("--cut-bbox-llwh", "<min_lat> <min_lon> <max_lat> <max_lon> <width> <height>\nSpecify a bounding box with the minimum latitude/longitude, max latitude/longitude in decimal degrees and width/height in pixels.\nNote coordinates are edge to edge."); au->addCommandLineOption("--cut-center-llwh", "<latitude> <longitude> <width> <height>\nSpecify the center cut in latitude longitude space with width and height in pixels."); au->addCommandLineOption("--cut-center-llr", "<latitude> <longitude> <radius_in_meters>\nSpecify the center cut in latitude longitude space with radius in meters."); au->addCommandLineOption("--degrees", "<dpp_xy> | <dpp_x> <dpp_y>\nSpecifies an override for degrees per pixel. Takes either a single value applied equally to x and y directions, or two values applied correspondingly to x then y. This option takes precedence over the \"--meters\" option."); au->addCommandLineOption("--enable-null-pixel-flip", "Will add to the start of the chain and will flip interior pixels that are null to valid."); au->addCommandLineOption("--elevation", "<elevation>\nhillshade option - Light source elevation angle for bumb shade.\nRange: 0 to 90, Default = 45.0"); au->addCommandLineOption("-e or --entry", "<entry> For multi image handlers which entry do you wish to extract. For list of entries use: \"ossim-info -i <your_image>\" "); au->addCommandLineOption("--exaggeration", "<factor>\nMultiplier for elevation values when computing surface normals. Has the effect of lengthening shadows for oblique lighting.\nRange: .0001 to 50000, Default = 1.0"); au->addCommandLineOption("--fullres-xys", "<full res center x>,<full res center y>,<scale>[,<scale>]\nSpecify a full resolution x,y point (Used as pivot and center cut) and scale, comma separated with no spaces. If two scales are specified then first is x and second is y else x and y are set to equal scales"); au->addCommandLineOption("--gamma", "Good ranges are between .1 and 6. Probably try about .7, A value of 1 will not apply anything"); au->addCommandLineOption("-h or --help", "Display this help and exit."); au->addCommandLineOption("--hemisphere", "<hemisphere>\nSpecify a projection hemisphere if supported. E.g. UTM projection. This will lock the hemisphere even if input scene center is the other hemisphere. Valid values for UTM are \"N\" and \"S\""); au->addCommandLineOption("--histogram-aoi", "<x>,<y>,<width>,<height>\nSets single image region of interest(roi) to compute histogram from. Coordinates are zero based image space. Single image operation only. Comma separated, no spaces."); au->addCommandLineOption("--histogram-center-tile", "Will compute histogram from center tile of image."); au->addCommandLineOption("--histogram-llwh", "<latitude>,<longitude>,<width>,<height>\nSpecify the region of interest(roi) to compute histogram from. Latitude and longitude will be roi center space with width and height in pixels. Comma separated, no spaces."); au->addCommandLineOption("--histogram-op", "<operation>\nHistogram operation to perform. Valid operations are \"linear\" \"auto-minmax\", \"auto-percentile\", \"std-stretch-1\", \"std-stretch-2\" and \"std-stretch-3\"."); au->addCommandLineOption("--histogram-linear-clip", "<low>,<high> in actual DN value. If it's 8 bit then it will be values between 0 and 255"); au->addCommandLineOption("--histogram-linear-norm-clip", "<low>,<high> normalized value that range from 0 to 1. Example .2,.85"); au->addCommandLineOption("--histogram-center-clip", "<center> normalized value that range from 0 to 1. Default is 0.5 which is the center of the min max clip points."); au->addCommandLineOption("--histogram-bias-factor", "<bias factor> values are typically between 0 to 2. Allows small adjustments to the auto clip points. Currently only works for auto min max."); au->addCommandLineOption("--image-space-scale", "<x> <y>\nSpecifies an image space scale for x and y direction. \"chip\" operation only."); au->addCommandLineOption("--input-dem", "<dem> Input dem to process."); au->addCommandLineOption("--input-img", "<image> Input image to process."); au->addCommandLineOption("--input-src", "<file.src> Input source file list keyword list with list of dems or images or both to process."); au->addCommandLineOption("--meters", "<meters>\nSpecifies an override for the meters per pixel"); au->addCommandLineOption("-n or --north-up", "Rotates image North up. \"chip\" operation only."); au->addCommandLineOption("--op", "<operation>\nOperation to perform. Valid operations are \"chip\", \"color-relief\", \"hillshade\", \"psm\"(pan sharpened multispectral), \"2cmv\"(two color multi view) and \"ortho\".\nchip = input projection = output projection(image space), single image operation only."); au->addCommandLineOption("--options", "<options.kwl> This can be all or part of the application options. To get a template you can turn on trace to the ossimChipperUtil class by adding \"-T ossimChipperUtil\" to your command."); au->addCommandLineOption("--origin-latitude", "<latidude_in_decimal_degrees>\nNote if set this will be used for the origin latitude of the projection. Setting this to something other than 0.0 with a geographic projection creates a scaled geographic projection."); au->addCommandLineOption("--output-radiometry", "<R>\nSpecifies the desired product's pixel radiometry type. Possible values for <R> are: U8, U11, U16, S16, F32. Note this overrides the deprecated option \"scale-to-8-bit\"."); au->addCommandLineOption("--pad-thumbnail", "<boolean>\nIf true, output thumbnail dimensions will be padded in width or height to make square; else, it will have the aspect ratio of input, Default=false"); au->addCommandLineOption("--projection", "<output_projection> Valid projections: geo, geo-scaled, input or utm\ngeo = Equidistant Cylindrical, origin latitude = 0.0\ngeo-scaled = Equidistant Cylindrical, origin latitude = image center\ninput Use first images projection. Must be a map projecion.\nutm = Universal Tranverse Mercator\nIf input and multiple sources the projection of the first image will be used.\nIf utm the zone will be set from the scene center of first image.\nNOTE: --srs takes precedence over this option."); au->addCommandLineOption("--resample-filter", "<type>\nSpecify what resampler filter to use, e.g. nearest neighbor, bilinear, cubic, sinc.\nSee ossim-info --resampler-filters"); au->addCommandLineOption("-r or --rotate", "<degrees>\nRotate image by degrees. \"chip\" operation only."); au->addCommandLineOption("--reader-prop", "<string>Adds a property to send to the reader. format is name=value"); au->addCommandLineOption("--rotate-to-input", "\nOnly if the operation is \"ortho\", output a rotated map so that the top edge is oriented with the input image top edge. Only available for single-image products in GeoTIFF output format."); au->addCommandLineOption("--rrds", "<rrds> Reduced resolution data set where 0 is full resolution. \"chip\" operation only."); au->addCommandLineOption("--scale-to-8-bit", "Scales the output to unsigned eight bits per band. This option has been deprecated by the newer \"--output-radiometry\" option."); au->addCommandLineOption("--sharpen-mode", "<mode> Applies sharpness to image chain(s). Valid modes: \"light\", \"medium\",\"heavy\""); au->addCommandLineOption("--sharpen-percent", "<sharpen-percent> Applies sharpness to image chain(s). Valid percentage is between 0 and 1"); au->addCommandLineOption("--snap-tie-to-origin", "Snaps tie point to projection origin so that (tie-origin)/gsd come out on an even integer boundary."); au->addCommandLineOption("--srs", "<src_code>\nSpecify a spatial reference system(srs) code for the output projection. Example: --srs EPSG:4326"); au->addCommandLineOption("-t or --thumbnail", "<max_dimension>\nSpecify a thumbnail resolution.\nScale will be adjusted so the maximum dimension = argument given."); au->addCommandLineOption("--three-band-out", "Force three band output even if input is not. Attempts to map bands to RGB if possible."); au->addCommandLineOption("--tile-size", "<size_in_pixels>\nSets the output tile size if supported by writer. Notes: This sets both dimensions. Must be a multiple of 16, e.g. 1024."); au->addCommandLineOption("-u or --up-is-up", "Rotates image to up is up. \"chip\" operation only."); au->addCommandLineOption("-w or --writer", "<writer>\nSpecifies the output writer. Default uses output file extension to determine writer. For valid output writer types use: \"ossim-info --writers\"\n"); au->addCommandLineOption("--writer-prop", "<writer-property>\nPasses a name=value pair to the writer for setting it's property. Any number of these can appear on the line."); au->addCommandLineOption("--zone", "<zone>\nSpecify a projection zone if supported. E.g. UTM projection. This will lock the zone even if input scene center is in another zone. Valid values for UTM are \"1\" to \"60\""); au->addCommandLineOption("--2cmv-old-input-band", "<band>\nBand to use for two color multi view old input.\n"); au->addCommandLineOption("--2cmv-new-input-band", "<band>\nBand to use for two color multi view new input.\n"); au->addCommandLineOption("--2cmv-red-output-source", "<source>\nTwo color multi view source input for red output. Either, old, new, or mp(min pix). Default=old.\n"); au->addCommandLineOption("--2cmv-green-output-source", "<source>\nTwo color multi view source input for green output. Either, old, new, or mp(min pix). Default=new.\n"); au->addCommandLineOption("--2cmv-blue-output-source", "<source>\nTwo color multi view source input for blue output. Either, old, new, or mp(min pix). Default=new.\n"); au->addCommandLineOption("--combiner-type", "<type>\nossimBlendMosaic, ossimFeatherMosaic, ossimImageMosaic. Default: ossimImageMosaic. Example --combiner-type ossimImageMosaic\n"); } // End: ossimChipperUtil::addArguments void ossimChipperUtil::clear() { // Must disonnect chains so that they destroy. std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator i = m_imgLayer.begin(); while (i != m_imgLayer.end()) { (*i)->disconnect(); (*i) = 0; ++i; } m_imgLayer.clear(); i = m_demLayer.begin(); while (i != m_demLayer.end()) { (*i)->disconnect(); (*i) = 0; ++i; } m_demLayer.clear(); if (m_writer.valid()) { m_writer->disconnect(); m_writer = 0; } } bool ossimChipperUtil::initialize(ossimArgumentParser &ap) { static const char MODULE[] = "ossimChipperUtil::initialize(ossimArgumentParser&)"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } clear(); if (ap.read("-h") || ap.read("--help") || (ap.argc() == 1)) { usage(ap); return false; // Indicates process should be terminated to caller. } // Start with clean options keyword list. m_kwl->clear(); std::string tempString1; ossimArgumentParser::ossimParameter stringParam1(tempString1); std::string tempString2; ossimArgumentParser::ossimParameter stringParam2(tempString2); std::string tempString3; ossimArgumentParser::ossimParameter stringParam3(tempString3); std::string tempString4; ossimArgumentParser::ossimParameter stringParam4(tempString4); std::string tempString5; ossimArgumentParser::ossimParameter stringParam5(tempString5); std::string tempString6; ossimArgumentParser::ossimParameter stringParam6(tempString6); double tempDouble1; ossimArgumentParser::ossimParameter doubleParam1(tempDouble1); double tempDouble2; ossimArgumentParser::ossimParameter doubleParam2(tempDouble2); ossim_uint32 demIdx = 0; ossim_uint32 imgIdx = 0; ossim_uint32 readerPropIdx = 0; ossim_uint32 writerPropIdx = 0; ossimString key = ""; // Extract optional arguments and stuff them in a keyword list. if (ap.read("--azimuth", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::AZIMUTH_ANGLE_KW), tempString1); } if (ap.read("-b", stringParam1) || ap.read("--bands", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::BANDS_KW), tempString1); } if (ap.read("--brightness", stringParam1)) { m_kwl->addPair(BRIGHTNESS_KW, tempString1); } if (ap.read("--central-meridian", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::CENTRAL_MERIDIAN_KW), tempString1); } if (ap.read("--color", stringParam1, stringParam2, stringParam3)) { m_kwl->addPair(COLOR_RED_KW, tempString1); m_kwl->addPair(COLOR_GREEN_KW, tempString2); m_kwl->addPair(COLOR_BLUE_KW, tempString3); } if (ap.read("--color-table", stringParam1)) { m_kwl->addPair(LUT_FILE_KW, tempString1); } if (ap.read("--contrast", stringParam1)) { m_kwl->addPair(CONTRAST_KW, tempString1); } if (ap.read("--cut-width", stringParam1)) { m_kwl->addPair(CUT_WIDTH_KW, tempString1); } if (ap.read("--cut-height", stringParam1)) { m_kwl->addPair(CUT_HEIGHT_KW, tempString1); } if (ap.read("--cut-bbox-xywh", stringParam1)) { m_kwl->addPair(CUT_BBOX_XYWH_KW, tempString1); } if (ap.read("--cut-wms-bbox", stringParam1)) { m_kwl->addPair(CUT_WMS_BBOX_KW, tempString1); } if (ap.read("--cut-wms-bbox-ll", stringParam1)) { m_kwl->addPair(CUT_WMS_BBOX_LL_KW, tempString1); } if (ap.read("--clip-wms-bbox-ll", stringParam1)) { m_kwl->addPair(CLIP_WMS_BBOX_LL_KW, tempString1); } if (ap.read("--clip-poly-lat-lon", stringParam1)) { //std::vector<ossimGpt> result; //ossim::toVector(result, ossimString(tempString1)); //std::cout << result[0] << std::endl; //std::cout <<tempString1<<std::endl; //exit(0); m_kwl->addPair(CLIP_POLY_LAT_LON_KW, tempString1); } if (ap.read("--cut-bbox-ll", stringParam1, stringParam2, stringParam3, stringParam4)) { m_kwl->addPair(CUT_MIN_LAT_KW, tempString1); m_kwl->addPair(CUT_MIN_LON_KW, tempString2); m_kwl->addPair(CUT_MAX_LAT_KW, tempString3); m_kwl->addPair(CUT_MAX_LON_KW, tempString4); } if (ap.read("--cut-bbox-llwh", stringParam1, stringParam2, stringParam3, stringParam4, stringParam5, stringParam6)) { m_kwl->addPair(CUT_MIN_LAT_KW, tempString1); m_kwl->addPair(CUT_MIN_LON_KW, tempString2); m_kwl->addPair(CUT_MAX_LAT_KW, tempString3); m_kwl->addPair(CUT_MAX_LON_KW, tempString4); m_kwl->addPair(CUT_WIDTH_KW, tempString5); m_kwl->addPair(CUT_HEIGHT_KW, tempString6); } if (ap.read("--cut-center-llwh", stringParam1, stringParam2, stringParam3, stringParam4)) { m_kwl->addPair(CUT_CENTER_LAT_KW, tempString1); m_kwl->addPair(CUT_CENTER_LON_KW, tempString2); m_kwl->addPair(CUT_WIDTH_KW, tempString3); m_kwl->addPair(CUT_HEIGHT_KW, tempString4); } if (ap.read("--cut-center-llr", stringParam1, stringParam2, stringParam3)) { m_kwl->addPair(CUT_CENTER_LAT_KW, tempString1); m_kwl->addPair(CUT_CENTER_LON_KW, tempString2); m_kwl->addPair(CUT_RADIUS_KW, tempString3); } int num_params = ap.numberOfParams("--degrees", doubleParam1); if (num_params == 1) { ap.read("--degrees", doubleParam1); m_kwl->add(DEGREES_X_KW.c_str(), tempDouble1); m_kwl->add(DEGREES_Y_KW.c_str(), tempDouble1); } else if (num_params == 2) { ap.read("--degrees", doubleParam1, doubleParam2); m_kwl->add(DEGREES_X_KW.c_str(), tempDouble1); m_kwl->add(DEGREES_Y_KW.c_str(), tempDouble2); } if (ap.read("--elevation", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::ELEVATION_ANGLE_KW), tempString1); } if (ap.read("-e", stringParam1) || ap.read("--entry", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::ENTRY_KW), tempString1); } if(ap.read("--gamma", stringParam1)) { m_kwl->addPair(GAMMA_KW, tempString1); } if (ap.read("--exaggeration", stringParam1)) { m_kwl->addPair(GAIN_KW, tempString1); } if (ap.read("--fullres-xys", stringParam1)) { m_kwl->addPair(FULLRES_XYS_KW, tempString1); std::vector<ossimString> values; ossimString(tempString1).split(values, ","); double sx, sy; if (values.size() > 2) { sx = values[2].toDouble(); sy = sx; if (values.size() > 3) { sy = values[3].toDouble(); } m_kwl->add(IMAGE_SPACE_SCALE_X_KW.c_str(), sx); m_kwl->add(IMAGE_SPACE_SCALE_Y_KW.c_str(), sy); } } if (ap.read("--hemisphere", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::HEMISPHERE_KW), tempString1); } if (ap.read("--histogram-aoi", stringParam1)) { m_kwl->addPair(HIST_AOI_KW, tempString1); } if (ap.read("--enable-null-pixel-flip")) { m_kwl->addPair(NULL_PIXEL_FLIP_KW, TRUE_KW); } if (ap.read("--histogram-center-tile")) { m_kwl->addPair(HIST_CENTER_KW, TRUE_KW); } if (ap.read("--histogram-llwh", stringParam1)) { m_kwl->addPair(HIST_LLWH_KW, tempString1); } if (ap.read("--histogram-op", stringParam1)) { m_kwl->addPair(HIST_OP_KW, tempString1); } if (ap.read("--histogram-bias-factor", stringParam1)) { m_kwl->addPair(HIST_BIAS_FACTOR_KW, tempString1); } if (ap.read("--histogram-linear-clip", stringParam1)) { m_kwl->addPair(HIST_LINEAR_CLIP_KW, tempString1); } if (ap.read("--histogram-linear-norm-clip", stringParam1)) { m_kwl->addPair(HIST_LINEAR_NORM_CLIP_KW, tempString1); } if (ap.read("--histogram-center-clip", stringParam1)) { m_kwl->addPair(HIST_CENTER_CLIP_KW, tempString1); } if (ap.read("--image-space-scale", doubleParam1, doubleParam2)) { m_kwl->add(IMAGE_SPACE_SCALE_X_KW.c_str(), tempDouble1); m_kwl->add(IMAGE_SPACE_SCALE_Y_KW.c_str(), tempDouble2); } while (ap.read("--input-dem", stringParam1)) { key = DEM_KW; key += ossimString::toString(demIdx); key += "."; key += FILE_KW; m_kwl->addPair(key.string(), tempString1); ++demIdx; } while (ap.read("--input-img", stringParam1)) { key = IMG_KW; key += ossimString::toString(imgIdx); key += "."; key += FILE_KW; m_kwl->addPair(key.string(), tempString1); ++imgIdx; } if (ap.read("--input-src", stringParam1)) { m_kwl->addPair(SRC_FILE_KW, tempString1); } if (ap.read("--meters", stringParam1)) { m_kwl->addPair(METERS_KW, tempString1); } if (ap.read("-n") || ap.read("--north-up")) { m_kwl->addPair(NORTH_UP_KW, TRUE_KW); } if (ap.read("--op", stringParam1)) { m_kwl->addPair(OP_KW, tempString1); } //--- // Deprecated: "--options-keyword-list" //--- if (ap.read("--options", stringParam1)) { ossimFilename optionsKwl = tempString1; if (optionsKwl.exists()) { if (m_kwl->addFile(optionsKwl) == false) { std::string errMsg = "ERROR could not open options keyword list file: "; errMsg += optionsKwl.string(); throw ossimException(errMsg); } } else { std::string errMsg = "ERROR options keyword list file does not exists: "; errMsg += optionsKwl.string(); throw ossimException(errMsg); } } if (ap.read("--origin-latitude", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::ORIGIN_LATITUDE_KW), tempString1); } if (ap.read("--output-radiometry", stringParam1)) { m_kwl->addPair(OUTPUT_RADIOMETRY_KW, tempString1); } if (ap.read("--pad-thumbnail", stringParam1)) { m_kwl->addPair(PAD_THUMBNAIL_KW, tempString1); } if (ap.read("--projection", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::PROJECTION_KW), tempString1); } if (ap.read("--resample-filter", stringParam1)) { m_kwl->addPair(RESAMPLER_FILTER_KW, tempString1); } if (ap.read("-r", stringParam1) || ap.read("--rotate", stringParam1)) { m_kwl->addPair(ROTATION_KW, tempString1); } if (ap.read("--rotate-to-input")) { m_kwl->addPair(ROTATE_TO_INPUT, "true"); } while (ap.read("--reader-prop", stringParam1)) { key = READER_PROPERTY_KW; key += ossimString::toString(readerPropIdx); m_kwl->addPair(key.string(), tempString1); ++readerPropIdx; } if (ap.read("--rrds", stringParam1)) { m_kwl->addPair(RRDS_KW, tempString1); } if (ap.read("--scale-to-8-bit")) { m_kwl->addPair(SCALE_2_8_BIT_KW, TRUE_KW); } if (ap.read("--sharpen-mode", stringParam1)) { m_kwl->addPair(SHARPEN_MODE_KW, tempString1); } if (ap.read("--sharpen-percent", stringParam1)) { m_kwl->addPair(SHARPEN_PERCENT_KW, tempString1); } if (ap.read("--snap-tie-to-origin")) { m_kwl->addPair(SNAP_TIE_TO_ORIGIN_KW, TRUE_KW); } if (ap.read("--srs", stringParam1)) { ossimString os = tempString1; if (os.contains("EPSG:")) { os.gsub(ossimString("EPSG:"), ossimString("")); } m_kwl->addPair(SRS_KW, os.string()); } if (ap.read("-t", stringParam1) || ap.read("--thumbnail", stringParam1)) { m_kwl->addPair(THUMBNAIL_RESOLUTION_KW, tempString1); } if (ap.read("--three-band-out")) { m_kwl->addPair(THREE_BAND_OUT_KW, TRUE_KW); } if (ap.read("--tile-size", stringParam1)) { m_kwl->addPair(TILE_SIZE_KW, tempString1); } if (ap.read("-u") || ap.read("--up-is-up")) { m_kwl->addPair(UP_IS_UP_KW, TRUE_KW); } if (ap.read("-w", stringParam1) || ap.read("--writer", stringParam1)) { m_kwl->addPair(WRITER_KW, tempString1); } while (ap.read("--writer-prop", stringParam1)) { key = WRITER_PROPERTY_KW; key += ossimString::toString(writerPropIdx); m_kwl->addPair(key.string(), tempString1); ++writerPropIdx; } if (ap.read("--zone", stringParam1)) { m_kwl->addPair(std::string(ossimKeywordNames::ZONE_KW), tempString1); } if (ap.read("--2cmv-old-input-band", stringParam1)) { m_kwl->addPair(TWOCMV_OLD_INPUT_BAND_KW, tempString1); } if (ap.read("--2cmv-new-input-band", stringParam1)) { m_kwl->addPair(TWOCMV_NEW_INPUT_BAND_KW, tempString1); } if (ap.read("--2cmv-red-output-source", stringParam1)) { m_kwl->addPair(TWOCMV_RED_OUTPUT_SOURCE_KW, tempString1); } if (ap.read("--2cmv-green-output-source", stringParam1)) { m_kwl->addPair(TWOCMV_GREEN_OUTPUT_SOURCE_KW, tempString1); } if (ap.read("--2cmv-blue-output-source", stringParam1)) { m_kwl->addPair(TWOCMV_BLUE_OUTPUT_SOURCE_KW, tempString1); } if (ap.read("--combiner-type", stringParam1)) { m_kwl->addPair(COMBINER_TYPE_KW, tempString1); } // End of arg parsing. ap.reportRemainingOptionsAsUnrecognized(); if (ap.errors()) { ap.writeErrorMessages(ossimNotify(ossimNotifyLevel_NOTICE)); std::string errMsg = "Unknown option..."; throw ossimException(errMsg); } if (ap.argc() >= 2) { // Output file is last arg: m_kwl->add(ossimKeywordNames::OUTPUT_FILE_KW, ap[ap.argc() - 1]); } else { if (!m_kwl->find(ossimKeywordNames::OUTPUT_FILE_KW)) { ap.writeErrorMessages(ossimNotify(ossimNotifyLevel_NOTICE)); std::string errMsg = "Must supply an output file."; throw ossimException(errMsg); } } if (ap.argc() > 2) // User passed inputs in front of output file. { int pos = 1; // ap.argv[0] is application name. while (pos < (ap.argc() - 1)) { ossimFilename file = ap[pos]; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << "argv[" << pos << "]: " << file << "\n"; } if (isDemFile(file)) { key = DEM_KW; key += ossimString::toString(demIdx); key += "."; key += FILE_KW; m_kwl->addPair(key, file.string()); ++demIdx; } else if (isSrcFile(file)) { if (m_kwl->find(SRC_FILE_KW.c_str())) // --input-src used also { std::string errMsg = MODULE; errMsg += "ERROR Multiple src files passed in. Please combine into one."; throw ossimException(errMsg); } m_kwl->addPair(SRC_FILE_KW, file.string()); } else // Add as an input image. { key = IMG_KW; key += ossimString::toString(imgIdx); key += "."; key += FILE_KW; m_kwl->addPair(key.string(), file.string()); ++imgIdx; } ++pos; // Go to next arg... } // End: while ( pos < (ap.argc()-1) ) } // End: if ( ap.argc() > 2 ) initialize(); if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited..." << std::endl; } return true; } // End: void ossimChipperUtil::initialize(ossimArgumentParser& ap) void ossimChipperUtil::initialize(const ossimKeywordlist &kwl) { clear(); // Start with clean options keyword list. m_kwl->clear(); m_kwl->addList(kwl, true); initialize(); } void ossimChipperUtil::initialize() { static const char MODULE[] = "ossimChipperUtil::initialize()"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } if (traceOptions()) { ossimFilename optionsFile; getOutputFilename(optionsFile); optionsFile = optionsFile.noExtension(); optionsFile += "-options.kwl"; ossimString comment = " Can be use for --options argument."; m_kwl->write(optionsFile.c_str(), comment.c_str()); } // Determine the operation to do. std::string op = m_kwl->findKey(OP_KW); if (op.size()) { ossimString s = op; s.downcase(); if (s == "chip") { m_operation = OSSIM_CHIPPER_OP_CHIP; } else if (s == "hillshade") { m_operation = OSSIM_CHIPPER_OP_HILL_SHADE; } else if (s == "color-relief") { m_operation = OSSIM_CHIPPER_OP_COLOR_RELIEF; } else if (s == "ortho") { m_operation = OSSIM_CHIPPER_OP_ORTHO; } else if (s == "psm") { m_operation = OSSIM_CHIPPER_OP_PSM; } else if (s == "2cmv") { m_operation = OSSIM_CHIPPER_OP_2CMV; } else { std::string errMsg = "unknown operation: "; errMsg += s.string(); throw ossimException(errMsg); } } else { std::string errMsg = "keyword not found: "; errMsg += OP_KW; errMsg += "\nUse --op option to specify operation.\n"; throw ossimException(errMsg); } //--- // Populate the m_srcKwl if --src option was set. // Note do this before creating chains. //--- initializeSrcKwl(); // Check for required inputs. Do this after initializeSrcKwl. if (m_operation == OSSIM_CHIPPER_OP_CHIP) { if (getNumberOfInputs() != 1) { std::ostringstream errMsg; errMsg << op << " operation takes one input."; throw ossimException(errMsg.str()); } } if ((m_operation == OSSIM_CHIPPER_OP_2CMV) || (m_operation == OSSIM_CHIPPER_OP_PSM)) { if (getNumberOfInputs() != 2) { std::ostringstream errMsg; errMsg << op << " operation requires two inputs."; throw ossimException(errMsg.str()); } } // Sanity check rotation options. if (upIsUp() || northUp() || hasRotation()) { std::string option; ossim_uint32 rotationOptionCount = 0; if (upIsUp()) { option = UP_IS_UP_KW; ++rotationOptionCount; } if (northUp()) { option = NORTH_UP_KW; ++rotationOptionCount; } if (hasRotation()) { option = ROTATION_KW; ++rotationOptionCount; } // Can only do ONE rotation option. if (rotationOptionCount > 1) { std::ostringstream errMsg; if (upIsUp()) { errMsg << UP_IS_UP_KW << " is on.\n"; } if (northUp()) { errMsg << NORTH_UP_KW << " is on.\n"; } if (hasRotation()) { errMsg << ROTATION_KW << " is on.\n"; } errMsg << "Multiple rotation options do not make sense!"; throw ossimException(errMsg.str()); } // One input, chip operation only. if (getNumberOfInputs() != 1) { std::ostringstream errMsg; errMsg << option << " option takes one input."; throw ossimException(errMsg.str()); } if (m_operation != OSSIM_CHIPPER_OP_CHIP) { std::ostringstream errMsg; errMsg << option << " option only valid with \"chip\" operation."; throw ossimException(errMsg.str()); } } // Create chains for any dem sources. addDemSources(); // Create chains for any image sources. addImgSources(); // Initialize projection and propagate to chains. initializeOutputProjection(); if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << "options keyword list:\n" << *(m_kwl.get()) << "\n"; if (m_srcKwl.valid()) { ossimNotify(ossimNotifyLevel_DEBUG) << "support record keyword list:\n" << *(m_srcKwl.get()) << "\n"; } ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\n"; } } // End: void ossimChipperUtil::initialize() ossimRefPtr<ossimImageSource> ossimChipperUtil::initializeChain(ossimIrect &aoi) { static const char MODULE[] = "ossimChipperUtil::initializeChain"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } ossimString lookup; // used throughout... ossimRefPtr<ossimImageSource> source = 0; if (hasBumpShadeArg()) { // Create chain: source = initializeBumpShadeChain(); } else if (m_operation == OSSIM_CHIPPER_OP_COLOR_RELIEF) { source = initializeColorReliefChain(); } else if ((m_operation == OSSIM_CHIPPER_OP_CHIP) || (m_operation == OSSIM_CHIPPER_OP_ORTHO)) { source = combineLayers(); } else if (m_operation == OSSIM_CHIPPER_OP_2CMV) { source = initialize2CmvChain(); // Two Color Multiview. } else if (m_operation == OSSIM_CHIPPER_OP_PSM) { source = initializePsmChain(); // Pan sharpened multispectral. } if (source.valid()) { //--- // This is conditional. Output radiometry may of may not be set. This can also be set at // the ossimSingleImageChain level. //--- if ((getOutputScalarType() != OSSIM_SCALAR_UNKNOWN) && (source->getOutputScalarType() != getOutputScalarType())) { // source = addScalarRemapper(source, getOutputScalarType()); } //--- // Get the area of interest. This will be the scene bounding rect if not // explicitly set by user with one of the --cut options. // Need to get this before the thumbnail code. //--- getAreaOfInterest(source.get(), aoi); //--- // Set the image size here. Note must be set after combineLayers. This is needed for // the ossimImageGeometry::worldToLocal call for a geographic projection to handle wrapping // accross the date line. //--- if (m_geom.valid()) { m_geom->setImageSize(aoi.size()); } if (hasThumbnailResolution()) { //--- // Adjust the projection scale and get the new rect. // Note this will resize the ossimImageGeometry::m_imageSize is scale changes. //--- initializeThumbnailProjection(aoi, aoi); // Reset the source bounding rect if it changed. source->initialize(); } // GP 2019: we will add cutters at the end // Will remove temporarily bt leave the code hear just in // case I messed something up if (source.valid() && !aoi.hasNans()) { //--- // Add a cut filter. This will: // 1) Null out/clip any data pulled in. // 2) Speed up by not propagating get tile request outside the cut or "aoi" // to the left hand side(input). //--- // ossimRefPtr<ossimRectangleCutFilter> cutter = new ossimRectangleCutFilter(); // // Set the cut rectangle: // cutter->setRectangle(aoi); // // Null outside. // cutter->setCutType(ossimRectangleCutFilter::OSSIM_RECTANGLE_NULL_OUTSIDE); // // Connect cutter input to source chain. // cutter->connectMyInputTo(0, source.get()); // source = cutter.get(); // // Dependent on correct aoi so place after the cutter. // if (hasAnnotations()) // { // // Put annotations after scalar remapper. // ossimRefPtr<ossimImageSource> result = addAnnotations(source); // if (result.valid()) // { // source = result.get(); // } // } } } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\n"; } return source; } void ossimChipperUtil::setOptionsToChain( ossimIrect &aoi, const ossimKeywordlist & /* kwl */) { getAreaOfInterest(m_source.get(), aoi); } ossimRefPtr<ossimImageSource> ossimChipperUtil::initializeBumpShadeChain() { static const char MODULE[] = "ossimChipperUtil::initializeBumpShadeChain"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } ossimString lookup; ossimRefPtr<ossimImageSource> source = 0; // Combine the dems. ossimRefPtr<ossimImageSource> demSource = combineLayers(m_demLayer); // Set up the normal source. ossimRefPtr<ossimImageToPlaneNormalFilter> normSource = new ossimImageToPlaneNormalFilter; //--- // Set the track scale flag to true. This enables scaling the surface // normals by the GSD in order to maintain terrain proportions. //--- normSource->setTrackScaleFlag(true); // Connect to dems. normSource->connectMyInputTo(demSource.get()); // Set the smoothness factor. ossim_float64 gain = 1.0; lookup = m_kwl->findKey(GAIN_KW); if (lookup.size()) { gain = lookup.toFloat64(); } normSource->setSmoothnessFactor(gain); ossimRefPtr<ossimImageSource> colorSource = 0; if (hasLutFile()) { if (m_imgLayer.size()) { ossimNotify(ossimNotifyLevel_WARN) << MODULE << " WARNING:" << "\nBoth a color table and image(s) have been provided for a color source.\n" << "Choosing color table of image(s).\n"; } colorSource = addIndexToRgbLutFilter(demSource); } else { // Combine the images and set as color source for bump shade. colorSource = combineLayers(m_imgLayer); } // Create the bump shade. ossimRefPtr<ossimBumpShadeTileSource> bumpShade = new ossimBumpShadeTileSource; // Set the azimuth angle. ossim_float64 azimuthAngle = 180; lookup = m_kwl->findKey(ossimKeywordNames::AZIMUTH_ANGLE_KW); if (lookup.size()) { ossim_float64 f = lookup.toFloat64(); if ((f >= 0) && (f <= 360)) { azimuthAngle = f; } } bumpShade->setAzimuthAngle(azimuthAngle); // Set the elevation angle. ossim_float64 elevationAngle = 45.0; lookup = m_kwl->findKey(ossimKeywordNames::ELEVATION_ANGLE_KW); if (lookup.size()) { ossim_float64 f = lookup.toFloat64(); if ((f >= 0.0) && (f <= 90)) { elevationAngle = f; } } bumpShade->setElevationAngle(elevationAngle); if (!hasLutFile()) { // Set the color. ossim_uint8 r = 0xff; ossim_uint8 g = 0xff; ossim_uint8 b = 0xff; lookup = m_kwl->findKey(COLOR_RED_KW); if (lookup.size()) { r = lookup.toUInt8(); } lookup = m_kwl->findKey(COLOR_GREEN_KW); if (lookup.size()) { g = lookup.toUInt8(); } lookup = m_kwl->findKey(COLOR_BLUE_KW); if (lookup.size()) { b = lookup.toUInt8(); } bumpShade->setRgbColorSource(r, g, b); } // Connect the two sources. bumpShade->connectMyInputTo(0, normSource.get()); bumpShade->connectMyInputTo(1, colorSource.get()); if (traceDebug()) { ossim_uint8 r = 0xff; ossim_uint8 g = 0xff; ossim_uint8 b = 0xff; bumpShade->getRgbColorSource(r, g, b); ossimNotify(ossimNotifyLevel_DEBUG) << "\nazimuthAngle: " << azimuthAngle << "\nelevation angle: " << elevationAngle << "\ngain factor: " << gain << "\nr: " << int(r) << "\ng: " << int(g) << "\nb: " << int(b) << "\n"; } // Capture the pointer to give to the writer. source = bumpShade.get(); return source; } // End: ossimChipperUtil::initializeBumpShadeChain() ossimRefPtr<ossimImageSource> ossimChipperUtil::initializeColorReliefChain() { ossimRefPtr<ossimImageSource> result = combineLayers(); if (hasLutFile()) { result = addIndexToRgbLutFilter(result); } else { // No LUT file provided, so doing the default 8-bit linear stretch: if (result->getOutputScalarType() != OSSIM_UINT8) { //result = addScalarRemapper(result, OSSIM_UINT8); } } return result; } ossimRefPtr<ossimImageSource> ossimChipperUtil::initializePsmChain() { ossimRefPtr<ossimImageSource> result = 0; ossim_uint32 layerCount = (ossim_uint32)(m_demLayer.size() + m_imgLayer.size()); // Must have two and only two inputs. if (layerCount == 2) { ossimRefPtr<ossimSingleImageChain> input1 = 0; // First input should be color. ossimRefPtr<ossimSingleImageChain> input2 = 0; // Second input should be pan. // Most likely case, two image layers. if (m_imgLayer.size()) { input1 = m_imgLayer[0].get(); if (m_imgLayer.size() == 2) { input2 = m_imgLayer[1].get(); } } if (m_demLayer.size()) { if (!input1.valid()) { input1 = m_demLayer[0].get(); } if (!input2.valid()) { if (m_demLayer.size() == 1) { input2 = m_demLayer[0].get(); } else if (m_demLayer.size() == 2) { input2 = m_demLayer[1].get(); } } } if (input1.valid() && input2.valid()) { // Make the color input the first connection to the combiner. if (input1->getNumberOfOutputBands() == 1) { // Swap: ossimRefPtr<ossimSingleImageChain> tmpChain = input1; input1 = input2; input2 = tmpChain; } //--- // Check the pan source for one band: // This really shouldn't happen, i.e. caller should typically pass a // one band pan image that is higher resolution than the color source. //--- if (input2->getNumberOfOutputBands() > 1) { // Note this will add a band selector if there isn't one in chain. std::vector<ossim_uint32> bandList(1); bandList[0] = 0; input2->setBandSelection(bandList); } // TODO: Make dynamic by type. ossimRefPtr<ossimFusionCombiner> psm = new ossimSFIMFusion(); psm->connectMyInputTo(0, input1.get()); psm->connectMyInputTo(1, input2.get()); psm->initialize(); result = dynamic_cast<ossimImageSource *>(psm.get()); } } // Matches: if ( layerCount == 2 ) return result; } // End: ossimChipperUtil::initializePsmChain() void ossimChipperUtil::initializeOutputProjection() { if (isChipMode()) { createIdentityProjection(); } else { // Create the output projection. createOutputProjection(); // Setup the view in all the chains. propagateOutputProjectionToChains(); } } ossimIrect clampViewportStretch(const ossimIrect &viewRect) { ossimIrect result = viewRect; if (result.width() > 1024 || result.height() > 1024) { ossim_int64 w = ossim::min(static_cast<ossim_int64>(result.width()), static_cast<ossim_int64>(1024)); ossim_int64 h = ossim::min(static_cast<ossim_int64>(result.height()), static_cast<ossim_int64>(1024)); if (w < 256) w = 256; if (h < 256) h = 256; result = ossimIrect(ossimIpt(result.midPoint()), w, h); } return result; } void ossimChipperUtil::setStretch(ossimRefPtr<ossimHistogramRemapper> remapper)const { ossim_int32 mode = getHistoMode(); ossimString value; if (mode == ossimHistogramRemapper::LINEAR_ONE_PIECE) { value = m_kwl->findKey(HIST_LINEAR_CLIP_KW); std::vector<ossimString> splitValues; if (!value.empty()) { splitValues = value.split(","); if(splitValues.size() == 2) { remapper->setLowClipPoint(splitValues[0].toFloat32()); remapper->setHighClipPoint(splitValues[1].toFloat32()); } } else { value = m_kwl->findKey(HIST_LINEAR_NORM_CLIP_KW); if(!value.empty()) { splitValues = value.split(","); if (splitValues.size() == 2) { ossim_float32 low = splitValues[0].toFloat32(); ossim_float32 high = splitValues[1].toFloat32(); remapper->setLowNormalizedClipPoint(low); remapper->setHighNormalizedClipPoint(high); } } } } value = m_kwl->findKey(HIST_BIAS_FACTOR_KW); if(!value.empty()) { remapper->setBiasFactor(value.toDouble()); } value = m_kwl->findKey(HIST_CENTER_CLIP_KW); if (!value.empty()) { remapper->setMidPoint(value.toFloat64()); } } ossimRefPtr<ossimImageSource> ossimChipperUtil::getFinalInput(const ossimIrect& aoi, ossimRefPtr<ossimImageSource> currentSource) { ossimRefPtr<ossimImageSource> result = currentSource; if(m_container.valid()) { m_container->deleteAllChildren(); m_container->disconnect(); } else { m_container = new ossimConnectableContainer(); } ossimRefPtr<ossimRectangleCutFilter> cut = new ossimRectangleCutFilter(); cut->connectMyInputTo(result.get()); cut->setRectangle(aoi); m_container->addChild(cut.get()); result = cut.get(); if(m_viewPortStretchEnabled) { if(m_geom) { ossimDpt iptCenter; ossimDrect histogramSourceRect; ossimRefPtr<ossimImageHistogramSource> histoSource = new ossimImageHistogramSource(); // ossimTypeNameVisitor imageRendererVisitor("ossimImageRenderer", true); // currentSource->accept(imageRendererVisitor); ossim_float64 cutWidth = ossim::clamp<double>(aoi.width(), 256, 1024); ossim_float64 cutHeight = ossim::clamp<double>(aoi.height(), 256, 1024); ossimRefPtr<ossimRectangleCutFilter> viewportCut = new ossimRectangleCutFilter(); ossimIrect cutRect = ossimIrect(ossimIpt(aoi.midPoint()), cutWidth, cutHeight); m_container->addChild(viewportCut.get()); viewportCut->connectMyInputTo(currentSource.get()); viewportCut->setRectangle(cutRect); histoSource->connectMyInputTo(viewportCut.get()); // ossimRefPtr<ossimImageRenderer> rendererObj = imageRendererVisitor.getObjectAs<ossimImageRenderer>(0); // imageRendererVisitor.reset(); // need a rectangle cut that we will connect to an input source that is before the resampling // process so we will need to invert the view window center and then expand the sample rect to a // fixed size. Probably like 256x256 for now // // bool setupOk=false; // if (rendererObj) // { // ossimRefPtr<ossimImageViewTransform> trans = rendererObj->getImageViewTransform(); // if(trans) // { // m_container->addChild(viewportCut.get()); // trans->viewToImage(aoi.midPoint(), iptCenter); // histogramSourceRect = ossimIrect(ossimIpt(iptCenter), 1024, 1024); // viewportCut->connectMyInputTo(rendererObj->getInput(0)); // viewportCut->setRectangle(histogramSourceRect); // histoSource->connectMyInputTo(viewportCut.get()); // // ossimKeywordlist kwl; // // histoSource->getHistogram()->saveState(kwl); // //std::cout << "KWL: " << kwl << "\n"; // setupOk = true; // } // } // if(!setupOk) // { // histoSource->connectMyInputTo(result.get()); // } ossimRefPtr<ossimHistogramRemapper> remapper = new ossimHistogramRemapper(); remapper->connectMyInputTo(result.get()); remapper->connectMyInputTo(histoSource.get()); remapper->setStretchMode(static_cast<ossimHistogramRemapper::StretchMode>(getHistoMode())); remapper->setEnableFlag(true); result = remapper.get(); m_container->addChild(histoSource.get()); m_container->addChild(remapper.get()); setStretch(remapper); } } if(scaleToEightBit()) { if(result->getOutputScalarType() != OSSIM_UINT8) { ossimRefPtr<ossimScalarRemapper> scalarRemapper = new ossimScalarRemapper(); scalarRemapper->setOutputScalarType(OSSIM_UINT8); scalarRemapper->connectMyInputTo(result.get()); m_container->addChild(scalarRemapper.get()); result = scalarRemapper.get(); } } return result; } void ossimChipperUtil::execute() { static const char MODULE[] = "ossimChipperUtil::execute"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } ossimIrect aoi; ossimRefPtr<ossimImageSource> chain = initializeChain(aoi); // ossimRefPtr<ossimConnectableContainer> container = new ossimConnectableContainer(); ossimRefPtr<ossimImageSource> source = getFinalInput(aoi, chain);//chain.get(); if (source.valid() && !aoi.hasNans()) { // Set up the writer. m_writer = createNewWriter(); // Connect the writer to the cutter. m_writer->connectMyInputTo(0, source.get()); //--- // Set the area of interest. // NOTE: This must be called after the writer->connectMyInputTo as // ossimImageFileWriter::initialize incorrectly resets theAreaOfInterest // back to the bounding rect. //--- // m_writer->setAreaOfInterest(aoi); if (m_writer->getErrorStatus() == ossimErrorCodes::OSSIM_OK) { // Add a listener to get percent complete. ossimStdOutProgress prog(0, true); m_writer->addListener(&prog); if (traceLog()) { ossimKeywordlist logKwl; m_writer->saveStateOfAllInputs(logKwl); ossimFilename logFile; getOutputFilename(logFile); logFile.setExtension("log"); logKwl.write(logFile.c_str()); } // Write the file: m_writer->execute(); m_writer->removeListener(&prog); if (m_writer->isAborted()) { throw ossimException("Writer Process aborted!"); } m_writer->disconnect(); m_container->deleteAllChildren(); m_container->disconnect(); } else { throw ossimException("Unable to initialize writer for execution"); } } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\n"; } } void ossimChipperUtil::abort() { if (m_writer.valid()) { m_writer->abort(); } } ossimRefPtr<ossimImageData> ossimChipperUtil::getChip(const ossimKeywordlist &optionsKwl) { ossimRefPtr<ossimImageData> result = 0; ossimIrect aoi; if (optionsKwl.getSize() > 0) { m_kwl->addList(optionsKwl, true); } // (GP) // Until we add more ellaborate code to check for scale changes // as well as moving windows we will just always initialize // the output projection // initializeOutputProjection(); if (!m_source.valid()) { m_source = initializeChain(aoi); } if (optionsKwl.getSize() > 0) { //--- // Only do this if new options were passed in. This was causing an off by // one error when now options were passed in using thumbnail option. // The m_source->getBoundingRect(...) was returning 257 instead of 256. // Need to hunt that down but for right now this fixes problem. // (drb 20151222) //--- getAreaOfInterest(m_source.get(), aoi); m_geom->setImageSize(aoi.size()); } else { getAreaOfInterest(m_source.get(), aoi); } if (m_source.valid()) { ossimRefPtr<ossimImageSource> source = getFinalInput(aoi, m_source.get()); result = source->getTile(aoi); } return result; } void ossimChipperUtil::addDemSources() { static const char MODULE[] = "ossimChipperUtil::addDemSources"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } std::vector<ossimString> sortedList; m_kwl->getSortedList(sortedList, DEM_KW); for (auto record : sortedList) { ossimString key = record + "."; ossimString file = key + FILE_KW; ossimString entry = key + ossimKeywordNames::ENTRY_KW; ossimFilename f = m_kwl->findKey(file.string()); if (f.size()) { // Look for the entry key, e.g. dem0.entry: 10 ossim_uint32 entryIndex = 0; std::string value = m_kwl->findKey(entry.string()); if (value.size()) { entryIndex = ossimString(value).toUInt32(); } else { // Get global entry. Set by "-e" on command line apps. entryIndex = getEntryNumber(); } ossimSrcRecord srcRecord; srcRecord.setFilename(f); srcRecord.setEntryIndex(entryIndex); // addDemSource( f, entryIndex ); addDemSource(srcRecord); } } sortedList.clear(); if (m_srcKwl.valid()) { // Add stuff from src keyword list. m_srcKwl->getSortedList(sortedList, DEM_KW); for (auto record : sortedList) { ossimString prefix = record + "."; ossimSrcRecord src; if (src.loadState(*(m_srcKwl.get()), prefix)) { addDemSource(src); } } } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\n"; } } void ossimChipperUtil::addDemSource(const ossimFilename &file, ossim_uint32 entryIndex) { static const char MODULE[] = "ossimChipperUtil::addDemSource(const ossimFilename&)"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } ossimRefPtr<ossimSingleImageChain> ic = createChain(file, entryIndex, true); if (ic.valid()) { m_demLayer.push_back(ic); } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\n"; } } void ossimChipperUtil::addDemSource(const ossimSrcRecord &rec) { static const char MODULE[] = "ossimChipperUtil::addDemSource(const ossimSrcRecord&)"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } ossimRefPtr<ossimSingleImageChain> ic = createChain(rec, true); if (ic.valid()) { m_demLayer.push_back(ic); } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\n"; } } void ossimChipperUtil::addImgSources() { static const char MODULE[] = "ossimChipperUtil::addImgSources"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } std::vector<ossimString> sortedList; m_kwl->getSortedList(sortedList, IMG_KW); for (auto record : sortedList) { ossimString fileKey = record + "." + FILE_KW; ossimFilename f = m_kwl->findKey(fileKey.string()); if (!f.empty()) { ossimString imagePrefix = record + "."; std::shared_ptr<ossimSrcRecord> srcRecord = std::make_shared<ossimSrcRecord>(); if (srcRecord->loadState(*m_kwl, imagePrefix.c_str())) { addImgSource(*srcRecord); } } } sortedList.clear(); if (m_srcKwl.valid()) { m_srcKwl->getSortedList(sortedList, IMG_KW); for (auto record : sortedList) { ossimString prefix = record + "."; std::shared_ptr<ossimSrcRecord> src = std::make_shared<ossimSrcRecord>(); if (src->loadState(*(m_srcKwl.get()), prefix)) { addImgSource(*src); } } } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\n"; } } void ossimChipperUtil::addImgSource(const ossimFilename &file, ossim_uint32 entryIndex) { static const char MODULE[] = "ossimChipperUtil::addImgSource"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\nFile: " << file << "\n"; } ossimRefPtr<ossimSingleImageChain> ic = createChain(file, entryIndex, false); if (ic.valid()) { m_imgLayer.push_back(ic); } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\n"; } } void ossimChipperUtil::addImgSource(const ossimSrcRecord &rec) { static const char MODULE[] = "ossimChipperUtil::addImgSource(const ossimSrcRecord&)"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } ossimRefPtr<ossimSingleImageChain> ic = createChain(rec, false); if (ic.valid()) { m_imgLayer.push_back(ic); } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\n"; } } ossimRefPtr<ossimSingleImageChain> ossimChipperUtil::createChain(const ossimFilename &file, ossim_uint32 entryIndex, bool isDemSource) const { static const char MODULE[] = "ossimChipperUtil::createChain(const ossimFilename&"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered..." << "\nfile: " << file << "\nentry: " << entryIndex << "\nisDemSource: " << (isDemSource ? "true" : "false") << "\n"; } ossimRefPtr<ossimSingleImageChain> ic = 0; if (!file.empty() && file.exists()) { ic = new ossimSingleImageChain; if (ic->open(file)) { // Set any reader props: setReaderProps(ic->getImageHandler().get()); // we can't guarantee the state of the image handler at this point so // let's make sure that the entry is always set to the requested location // On Cib/Cadrg we were having problems. Removed the compare for entry 0 // if (setChainEntry(ic, entryIndex) == false) { std::ostringstream errMsg; errMsg << MODULE << " ERROR:\nEntry " << entryIndex << " out of range!" << std::endl; throw ossimException(errMsg.str()); } //--- // If PSM (pan sharpening) operation and this input is one band, don't // mess with its bands. //--- bool psmPanInput = false; if ((m_operation == OSSIM_CHIPPER_OP_PSM) && (ic->getNumberOfOutputBands() == 1)) { psmPanInput = true; } // Bands selection. Note: Not performed on PSM pan band. if (!psmPanInput) { if (isThreeBandOut()) { //--- // This will guarantee three bands out. Will put band // selector at the end of the chain if input is one band. If input image // handler has implemented a getRgbBandlist(...) it will also set the // rgb band order. //--- ic->setThreeBandFlag(true); } if (hasBandSelection()) { // User entered band list. std::vector<ossim_uint32> bandList(0); getBandList(bandList); if (bandList.size()) { ic->setBandSelection(bandList); } else { ic->setDefaultBandSelection(); } } } ossimString nullPixelFlip = m_kwl->find(NULL_PIXEL_FLIP_KW.c_str()); if (nullPixelFlip.toBool()) { ic->setAddNullPixelFlipFlag(true); } //--- // If multiple inputs and scaleToEightBit do it at the end of the processing // chain to alleviate un-even stretches between inputs. // // GP 2019: Going to move this to the end of the chain. Should always be full bit depth until the end //--- // const ossim_uint32 INPUT_COUNT = getNumberOfInputs(); // bool scaleFlag = (scaleToEightBit() && (INPUT_COUNT == 1)); //ic->setRemapToEightBitFlag(scaleFlag); // Always have resampler cache. ic->setAddResamplerCacheFlag(true); //--- // Don't need a chain cache as we're doing a sequential write. So the same tile // should never be visited more than once. //--- ic->setAddChainCacheFlag(false); //--- // Histogram: // Don't apply histogram stretch to dem sources for hill shade // operation. //--- if ((isDemSource == false) || (isDemSource && (m_operation != OSSIM_CHIPPER_OP_HILL_SHADE))) { ic->setAddHistogramFlag(hasHistogramOperation()); } if(hasGammaCorrection()) { ic->setAddGammaFlag(true); } // Brightness, contrast. Note in same filter. if (hasBrightnesContrastOperation()) { ic->setBrightnessContrastFlag(true); } std::string sharpnessMode = getSharpenMode(); std::string sharpenPercent = getSharpenPercent(); if (sharpnessMode.size()|| sharpenPercent.size()) { ic->setSharpenFlag(true); } // Create the chain. ic->createRenderedChain(); if(hasGammaCorrection()) { ic->getGammaRemapper()->setGamma(getGamma()); } // Set the filter type if needed. ossimString lookup = m_kwl->findKey(RESAMPLER_FILTER_KW); if (lookup.size()) { // Assumption image renderer is in chain: ic->getImageRenderer()->getResampler()->setFilterType(lookup); } // Histogram setup. if (hasHistogramOperation()) { setupChainHistogram(ic); } // Brightness contrast setup: if (hasBrightnesContrastOperation()) { // Assumption bright contrast filter in chain: ossim_float64 value = getBrightness(); ic->getBrightnessContrast()->setBrightness(value); value = getContrast(); ic->getBrightnessContrast()->setContrast(value); } // Sharpness: if (sharpnessMode.size()) { if (sharpnessMode == "light") { ic->getSharpenFilter()->setSharpenPercent(0.2); // ic->getSharpenFilter()->setWidthAndSigma(3, 0.5); } else if (sharpnessMode == "medium") { ic->getSharpenFilter()->setSharpenPercent(0.5); } else if (sharpnessMode == "heavy") { ic->getSharpenFilter()->setSharpenPercent(0.8); } } else if(sharpenPercent.size()) { ic->getSharpenFilter()->setSharpenPercent(ossimString(sharpenPercent).toDouble()); } if (hasGeoPolyCutterOption()) { ossimGeoPolygon polygon; getClipPolygon(polygon); if (polygon.size() > 0) { ic->addGeoPolyCutterPolygon(polygon); } } } else { ic = 0; } } if (!ic.valid()) { std::string errMsg = "Could not open: "; errMsg += file.string(); throw ossimException(errMsg); } if (traceDebug()) { ossimKeywordlist kwl; ic->saveState(kwl, 0); ossimNotify(ossimNotifyLevel_DEBUG) << "chain:\n" << kwl << "\n" << MODULE << " exiting...\n"; } return ic; } ossimRefPtr<ossimSingleImageChain> ossimChipperUtil::createChain(const ossimSrcRecord &rec, bool isDemSource) const { static const char MODULE[] = "ossimChipperUtil::createChain(const ossimSrcRecord&)"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } ossimRefPtr<ossimSingleImageChain> ic = new ossimSingleImageChain; if (ic->open(rec)) { // Set any reader props: setReaderProps(ic->getImageHandler().get()); // Entry is set in ossimSingleImageChain::open(src) //--- // If PSM (pan sharpening) operation and this input is one band, don't // mess with its bands. //--- bool psmPanInput = false; if ((m_operation == OSSIM_CHIPPER_OP_PSM) && (ic->getNumberOfOutputBands() == 1)) { psmPanInput = true; } // Bands selection. Note: Not performed on PSM pan band. if (!psmPanInput) { if (isThreeBandOut()) { //--- // This will guarantee three bands out. Will put band // selector at the end of the chain if input is one band. If input image // handler has implemented a getRgbBandlist(...) it will also set the // rgb band order. //--- ic->setThreeBandFlag(true); } if (hasBandSelection()) { // User entered band list. std::vector<ossim_uint32> bandList(0); getBandList(bandList); if (bandList.size()) { ic->setBandSelection(bandList); } else { ic->setDefaultBandSelection(); } } } ossimString nullPixelFlip = m_kwl->find(NULL_PIXEL_FLIP_KW.c_str()); if (nullPixelFlip.toBool()) { ic->setAddNullPixelFlipFlag(true); } //--- // If multiple inputs and scaleToEightBit do it at the end of the processing // chain to alleviate un-even stretches between inputs. // // GP 2019: Going to move this to the end of the chain. Should always be full bit depth until the end //--- // const ossim_uint32 INPUT_COUNT = getNumberOfInputs(); // bool scaleFlag = (scaleToEightBit() && (INPUT_COUNT == 1)); //ic->setRemapToEightBitFlag(scaleFlag); // Always have resampler cache. ic->setAddResamplerCacheFlag(true); //--- // Don't need a chain cache as we're doing a sequential write. So the same tile // should never be visited more than once. //--- ic->setAddChainCacheFlag(false); //--- // Histogram: // Don't apply histogram stretch to dem sources for hill shade // operation. //--- if ((isDemSource == false) || (isDemSource && (m_operation != OSSIM_CHIPPER_OP_HILL_SHADE))) { ic->setAddHistogramFlag(hasHistogramOperation()); } if(hasGammaCorrection()) { ic->setAddGammaFlag(true); } // Brightness, contrast. Note in same filter. if (hasBrightnesContrastOperation()) { ic->setBrightnessContrastFlag(true); } std::string sharpnessMode = getSharpenMode(); std::string sharpenPercent = getSharpenPercent(); if (sharpnessMode.size() || sharpenPercent.size()) { ic->setSharpenFlag(true); } //--- // Create the chain. // // NOTE: Histogram and band selector can be set in ic->createRenderedChain(rec) // if the right keywords are there. //--- ic->createRenderedChain(rec); // Set the filter type if needed. ossimString lookup = m_kwl->findKey(RESAMPLER_FILTER_KW); if (lookup.size()) { // Assumption image renderer is in chain: ic->getImageRenderer()->getResampler()->setFilterType(lookup); } // Histogram setup. if (hasHistogramOperation()) { setupChainHistogram(ic, std::make_shared<ossimSrcRecord>(rec)); } if(hasGammaCorrection()) { ic->getGammaRemapper()->setGamma(getGamma()); } // Brightness contrast setup: if (hasBrightnesContrastOperation()) { // Assumption bright contrast filter in chain: ossim_float64 value = getBrightness(); ic->getBrightnessContrast()->setBrightness(value); value = getContrast(); ic->getBrightnessContrast()->setContrast(value); } // Sharpness: if (sharpnessMode.size()) { if (sharpnessMode == "light") { ic->getSharpenFilter()->setSharpenPercent(0.2); } else if(sharpnessMode == "medium") { ic->getSharpenFilter()->setSharpenPercent(0.5); } else if (sharpnessMode == "heavy") { ic->getSharpenFilter()->setSharpenPercent(0.8); } } else if(sharpenPercent.size()) { ic->getSharpenFilter()->setSharpenPercent(ossimString(sharpenPercent).toDouble()); } if (hasGeoPolyCutterOption()) { ossimGeoPolygon polygon; getClipPolygon(polygon); if (polygon.size() > 0) { ic->addGeoPolyCutterPolygon(polygon); } } } else // Open failed. { std::string errMsg = "Could not open from src record!"; throw ossimException(errMsg); } if (traceDebug()) { ossimKeywordlist kwl; ic->saveState(kwl, 0); ossimNotify(ossimNotifyLevel_DEBUG) << "chain:\n" << kwl << "\n" << MODULE << " exiting...\n"; } return ic; } void ossimChipperUtil::createOutputProjection() { static const char MODULE[] = "ossimChipperUtil::createOutputProjection"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } std::string op = m_kwl->findKey(std::string(ossimKeywordNames::PROJECTION_KW)); std::string srs = m_kwl->findKey(SRS_KW); if (op.size() && srs.size()) { ossimNotify(ossimNotifyLevel_WARN) << MODULE << " WARNING:" << "\nBoth " << SRS_KW << " and " << ossimKeywordNames::PROJECTION_KW << " keywords are set!" << "\nsrs: " << srs << "\noutput_projection: " << op << "\nTaking " << srs << " over " << op << "\n"; } bool usingInput = false; ossimChipperOutputProjection projType = getOutputProjectionType(); ossimRefPtr<ossimMapProjection> proj = 0; // If an srs code use that first. if (srs.size()) { proj = getNewProjectionFromSrsCode(srs); } else if (op.size()) { switch (projType) { case ossimChipperUtil::OSSIM_CHIPPER_PROJ_GEO: { proj = getNewGeoProjection(); break; } case ossimChipperUtil::OSSIM_CHIPPER_PROJ_GEO_SCALED: { proj = getNewGeoScaledProjection(); break; } case ossimChipperUtil::OSSIM_CHIPPER_PROJ_INPUT: { proj = getFirstInputProjection(); usingInput = true; break; } case ossimChipperUtil::OSSIM_CHIPPER_PROJ_UTM: { proj = getNewUtmProjection(); break; } default: { break; // Just for un-handled type warning. } } } // Check for identity projection: ossimRefPtr<ossimMapProjection> inputProj = getFirstInputProjection(); if (proj.valid() && inputProj.valid()) { if (!usingInput && (*(inputProj.get()) == *(proj.get()))) { if (projType == OSSIM_CHIPPER_PROJ_GEO_SCALED) { // Get the origin used for scaling. ossimGpt origin = proj->getOrigin(); // Copy the input projection to our projection. Has the tie and scale we need. proj = inputProj; // Set the origin for scaling. proj->setOrigin(origin); } else { proj = inputProj; } usingInput = true; } } if (!proj.valid()) { // Try first input. If map projected use that. if (inputProj.valid()) { proj = inputProj; usingInput = true; if (traceDebug()) { ossimNotify(ossimNotifyLevel_WARN) << "WARNING: No projection set!" << "\nDefaulting to first input's projection.\n"; } } else { proj = getNewGeoScaledProjection(); if (traceDebug()) { ossimNotify(ossimNotifyLevel_WARN) << "WARNING: No projection set!" << "\nDefaulting to scaled geographic at scene center.\n"; } } } // Create our ossimImageGeometry with projection (no transform). m_geom = new ossimImageGeometry(0, proj.get()); //--- // If the input is the same as output projection do not modify; else, set // the gsd to user selected "METERS_KW" or the best resolution of the inputs, // set the tie and then snap it to the projection origin. //--- if (!usingInput || hasScaleOption()) { // Set the scale. initializeProjectionGsd(); } // Set the tie. initializeProjectionTiePoint(); if (snapTieToOrigin()) { // Adjust the projection tie to the origin. proj->snapTiePointToOrigin(); } // May need to rotate the output map if requested: bool rotateMap=false; m_kwl->getBoolKeywordValue(rotateMap, ROTATE_TO_INPUT.c_str()); if (rotateMap) { rotateMapToInput(); } if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << "using input projection: " << (usingInput ? "true" : "false") << "\noutput image geometry:\n"; m_geom->print(ossimNotify(ossimNotifyLevel_DEBUG)); ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\n"; } } // End: ossimChipperUtil::createOutputProjection() void ossimChipperUtil::rotateMapToInput() { static const char MODULE[] = "ossimChipperUtil::rotateMapToInput"; if (traceDebug()) ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; try { if (!m_geom) throw ossimException("Null projection geometry encountered."); ossimRefPtr<ossimMapProjection> mapProj = dynamic_cast<ossimMapProjection*>(m_geom->getProjection()); if (!mapProj) throw ossimException("Output projection must be a map projection."); if (m_imgLayer.size() != 1) throw ossimException("Optimal rotation output requested but this feature is not available for mosaics."); ossimRefPtr<ossimImageHandler> ih = m_imgLayer[0]->getImageHandler(); if (!ih) throw ossimException("Null image handler encountered."); ossimRefPtr<ossimImageGeometry> inputGeom = ih->getImageGeometry(); if (!inputGeom) throw ossimException("Null image geometry encountered."); // Determine azimuth of +X axis: ossimIpt inputImgSize (inputGeom->getImageSize()); if (inputImgSize.x == 0) throw ossimException("Image size is zero."); ossimDpt pUL (0,0); ossimDpt pUR (inputImgSize.x-1, 0); ossimDpt pLR (inputImgSize.x-1, inputImgSize.y-1); ossimDpt pLL (0, inputImgSize.y-1); ossimGpt gUL, gUR, gLR, gLL; inputGeom->localToWorld(pUL, gUL); inputGeom->localToWorld(pUR, gUR); inputGeom->localToWorld(pLR, gLR); inputGeom->localToWorld(pLL, gLL); // The -y axis relative to North is the number we need to rotate, so subtract 90 deg from // x-axis orientation. double mapRotation = gLL.azimuthTo(gUL); cout << "\n Rotating map by "<<mapRotation<<" deg\n"<<endl; //### mapProj->applyRotation(mapRotation); // Not sure where the UL of the rotated map will be, so assume it will coincide with the image // UL for now, then scan for corners outside: mapProj->setUlGpt(gUL); mapProj->worldToLineSample(gUR, pUR); mapProj->worldToLineSample(gLR, pLR); mapProj->worldToLineSample(gLL, pLL); ossimDrect drect (pUL, pUR, pLR, pLL); // A new output image rect has been established, find the corresponding UL ground point // and set projection's tiepoint: mapProj->lineSampleToWorld(drect.ul(), gUL); mapProj->setUlGpt(gUL); } catch (ossimException& e) { ossimNotify(ossimNotifyLevel_WARN) << "ossimChipperUtil::rotateMapToInput() -- Exception: " << e.what() <<" Ignoring request.\n"; } return; } void ossimChipperUtil::createIdentityProjection() { static const char MODULE[] = "ossimChipperUtil::createIdentityProjection"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\n"; } // Get the singe image chain. Sould be only one. ossimRefPtr<ossimSingleImageChain> sic = 0; if (m_demLayer.size()) { sic = m_demLayer[0]; } else if (m_imgLayer.size()) { sic = m_imgLayer[0]; } if (sic.valid()) { // Get the image handler. ossimRefPtr<ossimImageHandler> ih = sic->getImageHandler(); // Resampler: ossimRefPtr<ossimImageRenderer> resampler = sic->getImageRenderer(); if (ih.valid()) { //--- // Get the geometry from the image handler. Since we're in "identity" // mode use the inputs for the outputs. //--- m_geom = ih->getImageGeometry(); if (m_geom.valid()) { ossim_float64 rotation = 0.0; if (upIsUp()) { ossimRefPtr<ossimProjection> proj = m_geom->getProjection(); if (proj.valid()) { rotation = m_geom->upIsUpAngle(); } else { ossimNotify(ossimNotifyLevel_WARN) << "WARNING: No projection to compute the up is up angle!" << std::endl; } } else if (northUp()) { ossimRefPtr<ossimProjection> proj = m_geom->getProjection(); if (proj.valid()) { rotation = m_geom->northUpAngle(); } else { ossimNotify(ossimNotifyLevel_WARN) << "WARNING: No projection to compute the North up angle!" << std::endl; } } else if (hasRotation()) { rotation = getRotation(); } if (ossim::isnan(rotation)) { rotation = 0.0; } ossimDpt imageSpaceScale; getImageSpaceScale(imageSpaceScale); //ossimDrect rect; //m_geom->getBoundingRect(rect); ossimDpt midPt; // = rect.midPoint(); getImageSpacePivot(midPt); if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << "\nAffine transform parameters:" << "\nrotation: " << rotation << "\nmid point: " << midPt << std::endl; } m_ivt = new ossimImageViewAffineTransform(-rotation, imageSpaceScale.x, // image space scale x imageSpaceScale.y, // image space scale y 1.0, 1.0, //scale x and y 0.0, 0.0, // translate x,y midPt.x * imageSpaceScale.x, midPt.y * imageSpaceScale.y); // pivot point if (m_kwl->hasKey(METERS_KW) || m_kwl->hasKey(DEGREES_X_KW) || m_kwl->hasKey(RRDS_KW)) { // Set the image view transform scale. initializeIvtScale(); } resampler->setImageViewTransform(m_ivt.get()); } // Matches: if ( m_geom.valid() ) } // Matches: if ( ih.valid() ) } // Matches: if ( sic.valid() } // End: createIdentityProjection() void ossimChipperUtil::initializeIvtScale() { if (isChipMode() && m_ivt.valid() && m_geom.valid()) { ossimDpt scale; scale.makeNan(); // Check for GSD spec. Degrees/pixel takes priority over meters/pixel: ossimString lookup; lookup.string() = m_kwl->findKey(DEGREES_X_KW); if (lookup.size()) { ossimDpt outputDpp; outputDpp.makeNan(); outputDpp.x = lookup.toFloat64(); lookup.string() = m_kwl->findKey(DEGREES_Y_KW); if (lookup.size()) { outputDpp.y = lookup.toFloat64(); } if (!outputDpp.hasNans()) { // Input degress per pixel. Consider this a scale of 1.0. ossimDpt inputDpp; m_geom->getDegreesPerPixel(inputDpp); if (!inputDpp.hasNans()) { scale.x = inputDpp.x / outputDpp.x; scale.y = inputDpp.y / outputDpp.y; } } } if (scale.hasNans()) { lookup = m_kwl->findKey(METERS_KW); if (lookup.size()) { ossimDpt outputMpp; outputMpp.makeNan(); outputMpp.x = lookup.toFloat64(); outputMpp.y = outputMpp.x; if (!outputMpp.hasNans()) { // Input meters per pixel. Consider this a scale of 1.0. ossimDpt inputMpp; m_geom->getMetersPerPixel(inputMpp); if (!inputMpp.hasNans()) { scale.x = inputMpp.x / outputMpp.x; scale.y = inputMpp.y / outputMpp.y; } } } } if (scale.hasNans()) { lookup = m_kwl->findKey(RRDS_KW); if (lookup.size()) { ossim_float64 d = lookup.toInt32(); if (d == 0.0) { scale.x = 1.0; } else { scale.x = 1.0 / std::pow(2.0, d); } scale.y = scale.x; } } if (!scale.hasNans()) { m_ivt->scale(scale.x, scale.y); } else { std::string errMsg = "ossimChipperUtil::initializeIvtScale failed!"; throw ossimException(errMsg); } } // Matches: if ( isChipMode() && ... ) } // End: ossimChipperUtil::initializeIvtScale() void ossimChipperUtil::initializeProjectionTiePoint () { static const char MODULE[] = "ossimChipperUtil::initializeProjectionTiePoint()"; if (traceDebug()) { ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << "
{'commit': 'diff --git a/src/util/ossimChipperUtil.cpp b/src/util/ossimChipperUtil.cpp\nindex 5d8fe842..39d36f30 100644\n--- a/src/util/ossimChipperUtil.cpp\n+++ b/src/util/ossimChipperUtil.cpp\n@@ -1898,6 +1898,11 @@ void ossimChipperUtil::addImgSources()\n          std::shared_ptr<ossimSrcRecord> srcRecord = std::make_shared<ossimSrcRecord>();\n          if (srcRecord->loadState(*m_kwl, imagePrefix.c_str()))\n          {\n+            // Use the global entry only if not set already.\n+            if ( srcRecord->getEntryIndex() == -1 )\n+            {\n+               srcRecord->setEntryIndex( (ossim_int32)getEntryNumber() );\n+            }\n             addImgSource(*srcRecord);\n          }\n       }\n', 'commit_subject': 'Merge pull request #283 from ossimlabs/drb-20220828', 'repo_name': 'ossimlabs/ossim', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1661779449', 'tz_offset': '-240', 'date': {'seconds': '1661779449'}}, 'commit_tree': 'd187df0b73f0c40270e64ddccffb5ac934cecf28', 'commit_message': 'Merge pull request #283 from ossimlabs/drb-20220828\n\nFix for ossimChipperUtil::addImgSources() not honoring -e (entry) option.', 'language_name': 'HTML', 'license': 'mit', 'hunks': '@@ -1898,6 +1898,11 @@ void ossimChipperUtil::addImgSources()\n          std::shared_ptr<ossimSrcRecord> srcRecord = std::make_shared<ossimSrcRecord>();\n          if (srcRecord->loadState(*m_kwl, imagePrefix.c_str()))\n          {\n+            // Use the global entry only if not set already.\n+            if ( srcRecord->getEntryIndex() == -1 )\n+            {\n+               srcRecord->setEntryIndex( (ossim_int32)getEntryNumber() );\n+            }\n             addImgSource(*srcRecord);\n          }\n       }\n', 'addition_count': 5, 'deletion_count': 0, 'src_file': 'a/src/util/ossimChipperUtil.cpp', 'tgt_file': 'b/src/util/ossimChipperUtil.cpp', 'file_extension': '.cpp', 'before_file': '//---\n//\n// File: ossimChipperUtil.cpp\n//\n// License: MIT\n//\n// Author:  David Burken\n//\n// Description: Utility class definition processing digital elevation\n// models(dems).\n//\n//---\n// $Id$\n\n#include <ossim/util/ossimChipperUtil.h>\n\n#include <ossim/base/ossimArgumentParser.h>\n#include <ossim/base/ossimApplicationUsage.h>\n#include <ossim/base/ossimConnectableContainer.h>\n#include <ossim/base/ossimConnectableObject.h>\n#include <ossim/base/ossimException.h>\n#include <ossim/base/ossimFilename.h>\n#include <ossim/base/ossimGeoPolygon.h>\n#include <ossim/base/ossimIrect.h>\n#include <ossim/base/ossimKeywordlist.h>\n#include <ossim/base/ossimKeywordNames.h>\n#include <ossim/base/ossimNotify.h>\n#include <ossim/base/ossimProperty.h>\n#include <ossim/base/ossimRefPtr.h>\n#include <ossim/base/ossimRefreshEvent.h>\n#include <ossim/base/ossimScalarTypeLut.h>\n#include <ossim/base/ossimStdOutProgress.h>\n#include <ossim/base/ossimStringProperty.h>\n#include <ossim/base/ossimTrace.h>\n#include <ossim/base/ossimVisitor.h>\n#include <ossim/base/ossimGrect.h>\n\n#include <ossim/imaging/ossimAnnotationFontObject.h>\n#include <ossim/imaging/ossimAnnotationLineObject.h>\n#include <ossim/imaging/ossimAnnotationSource.h>\n#include <ossim/imaging/ossimBrightnessContrastSource.h>\n#include <ossim/imaging/ossimBumpShadeTileSource.h>\n#include <ossim/imaging/ossimFilterResampler.h>\n#include <ossim/imaging/ossimFusionCombiner.h>\n#include <ossim/imaging/ossimGammaRemapper.h>\n#include <ossim/imaging/ossimImageData.h>\n#include <ossim/imaging/ossimImageFileWriter.h>\n#include <ossim/imaging/ossimImageGeometry.h>\n#include <ossim/imaging/ossimImageHandler.h>\n#include <ossim/imaging/ossimImageHistogramSource.h>\n#include <ossim/imaging/ossimImageMosaic.h>\n#include <ossim/imaging/ossimImageRenderer.h>\n#include <ossim/imaging/ossimImageSource.h>\n#include <ossim/imaging/ossimImageSourceFilter.h>\n#include <ossim/imaging/ossimImageToPlaneNormalFilter.h>\n#include <ossim/imaging/ossimImageWriterFactoryRegistry.h>\n#include <ossim/imaging/ossimIndexToRgbLutFilter.h>\n#include <ossim/imaging/ossimRectangleCutFilter.h>\n#include <ossim/imaging/ossimScalarRemapper.h>\n#include <ossim/imaging/ossimSFIMFusion.h>\n#include <ossim/imaging/ossimTwoColorView.h>\n#include <ossim/imaging/ossimImageSourceFactoryRegistry.h>\n#include <ossim/init/ossimInit.h>\n\n#include <ossim/projection/ossimEquDistCylProjection.h>\n#include <ossim/projection/ossimImageViewAffineTransform.h>\n#include <ossim/projection/ossimMapProjection.h>\n#include <ossim/projection/ossimProjection.h>\n#include <ossim/projection/ossimProjectionFactoryRegistry.h>\n#include <ossim/projection/ossimUtmProjection.h>\n\n#include <ossim/support_data/ossimSrcRecord.h>\n\n#include <cmath>\n#include <sstream>\n#include <string>\n\nusing namespace std;\n\nstatic ossimTrace traceDebug("ossimChipperUtil:debug");\nstatic ossimTrace traceLog("ossimChipperUtil:log");\nstatic ossimTrace traceOptions("ossimChipperUtil:options");\n\nstatic const std::string BRIGHTNESS_KW = "brightness";\nstatic const std::string COLOR_BLUE_KW = "color_blue";\nstatic const std::string COLOR_GREEN_KW = "color_green";\nstatic const std::string COLOR_RED_KW = "color_red";\nstatic const std::string CONTRAST_KW = "contrast";\nstatic const std::string CLIP_WMS_BBOX_LL_KW = "clip_wms_bbox_ll";\nstatic const std::string CLIP_POLY_LAT_LON_KW = "clip_poly_lat_lon";\nstatic const std::string CUT_BBOX_XYWH_KW = "cut_bbox_xywh";\nstatic const std::string CUT_WMS_BBOX_KW = "cut_wms_bbox";\nstatic const std::string CUT_WMS_BBOX_LL_KW = "cut_wms_bbox_ll";\nstatic const std::string CUT_CENTER_LAT_KW = "cut_center_lat";\nstatic const std::string CUT_CENTER_LON_KW = "cut_center_lon";\nstatic const std::string CUT_RADIUS_KW = "cut_radius"; // meters\nstatic const std::string CUT_HEIGHT_KW = "cut_height"; // pixels\nstatic const std::string CUT_MAX_LAT_KW = "cut_max_lat";\nstatic const std::string CUT_MAX_LON_KW = "cut_max_lon";\nstatic const std::string CUT_MIN_LAT_KW = "cut_min_lat";\nstatic const std::string CUT_MIN_LON_KW = "cut_min_lon";\nstatic const std::string CUT_WIDTH_KW = "cut_width"; // pixels\nstatic const std::string DEM_KW = "dem";\nstatic const std::string GAIN_KW = "gain";\nstatic const std::string GAMMA_KW = "gamma";\nstatic const std::string FILE_KW = "file";\nstatic const std::string FULLRES_XYS_KW = "fullres_xys";\nstatic const std::string HIST_AOI_KW = "hist_aoi";\nstatic const std::string HIST_CENTER_KW = "hist_center";\nstatic const std::string HIST_LLWH_KW = "hist_llwh";\nstatic const std::string HIST_OP_KW = "hist_op";\nstatic const std::string HIST_LINEAR_CLIP_KW = "hist_linear_clip";\nstatic const std::string HIST_LINEAR_NORM_CLIP_KW = "hist_linear_norm_clip";\nstatic const std::string HIST_CENTER_CLIP_KW = "hist_center_clip";\nstatic const std::string HIST_BIAS_FACTOR_KW = "hist_bias_factor";\nstatic const std::string IMAGE_SPACE_SCALE_X_KW = "image_space_scale_x";\nstatic const std::string IMAGE_SPACE_SCALE_Y_KW = "image_space_scale_y";\nstatic const std::string IMG_KW = "image";\nstatic const std::string LUT_FILE_KW = "lut_file";\nstatic const std::string DEGREES_X_KW = "degrees_x";\nstatic const std::string DEGREES_Y_KW = "degrees_y";\nstatic const std::string METERS_KW = "meters";\nstatic const std::string NORTH_UP_KW = "north_up"; // bool\nstatic const std::string OP_KW = "operation";\nstatic const std::string OUTPUT_RADIOMETRY_KW = "output_radiometry";\nstatic const std::string PAD_THUMBNAIL_KW = "pad_thumbnail"; // bool\nstatic const std::string READER_PROPERTY_KW = "reader_property";\nstatic const std::string RESAMPLER_FILTER_KW = "resampler_filter";\nstatic const std::string ROTATION_KW = "rotation";\nstatic const std::string ROTATE_TO_INPUT = "rotate_to_input";\nstatic const std::string RRDS_KW = "rrds";\nstatic const std::string SCALE_2_8_BIT_KW = "scale_2_8_bit";\nstatic const std::string SHARPEN_MODE_KW = "sharpen_mode";\nstatic const std::string SHARPEN_PERCENT_KW = "sharpen_percent";\nstatic const std::string SNAP_TIE_TO_ORIGIN_KW = "snap_tie_to_origin";\nstatic const std::string SRC_FILE_KW = "src_file";\nstatic const std::string SRS_KW = "srs";\nstatic const std::string THREE_BAND_OUT_KW = "three_band_out";\t\t\t\t\t// bool\nstatic const std::string THUMBNAIL_RESOLUTION_KW = "thumbnail_resolution"; // pixels\nstatic const std::string TILE_SIZE_KW = "tile_size";\t\t\t\t\t\t\t\t// pixels\nstatic const std::string TRUE_KW = "true";\nstatic const std::string UP_IS_UP_KW = "up_is_up"; // bool\nstatic const std::string WRITER_KW = "writer";\nstatic const std::string WRITER_PROPERTY_KW = "writer_property";\nstatic const std::string COMBINER_TYPE_KW = "combiner_type";\nstatic const std::string NULL_PIXEL_FLIP_KW = "null_pixel_flip";\n\nstatic const std::string TWOCMV_OLD_INPUT_BAND_KW = "2cmv_old_input_band";\nstatic const std::string TWOCMV_NEW_INPUT_BAND_KW = "2cmv_new_input_band";\nstatic const std::string TWOCMV_RED_OUTPUT_SOURCE_KW = "2cmv_red_output_source";\nstatic const std::string TWOCMV_GREEN_OUTPUT_SOURCE_KW = "2cmv_green_output_source";\nstatic const std::string TWOCMV_BLUE_OUTPUT_SOURCE_KW = "2cmv_blue_output_source";\n\nossimChipperUtil::ossimChipperUtil()\n    : ossimReferenced(),\n      m_operation(OSSIM_CHIPPER_OP_UNKNOWN),\n      m_kwl(new ossimKeywordlist()),\n      m_srcKwl(0),\n      m_geom(0),\n      m_ivt(0),\n      m_demLayer(0),\n      m_imgLayer(0),\n      m_viewPortStretchEnabled(false)\n{\n   // traceDebug.setTraceFlag(true);\n\n   m_kwl->setExpandEnvVarsFlag(true);\n}\n\n// Private/hidden from use.\nossimChipperUtil::ossimChipperUtil(const ossimChipperUtil & /* obj */)\n   : m_operation(OSSIM_CHIPPER_OP_UNKNOWN)\n{\n}\n\n// Private/hidden from use.\nconst ossimChipperUtil &ossimChipperUtil::operator=(const ossimChipperUtil & /* rhs */)\n{\n   return *this;\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::createCombiner() const\n{\n   ossimRefPtr<ossimImageSource> result;\n   ossimString combinerType = m_kwl->find(COMBINER_TYPE_KW.c_str());\n   if (combinerType.empty())\n   {\n      combinerType = "ossimImageMosaic";\n   }\n\n   result = ossimImageSourceFactoryRegistry::instance()->createImageSource(combinerType);\n   if (!result.valid())\n   {\n      result = new ossimImageMosaic();\n   }\n\n   return result;\n}\n\nossimChipperUtil::~ossimChipperUtil()\n{\n   clear();\n}\n\nvoid ossimChipperUtil::addArguments(ossimArgumentParser &ap)\n{\n   ossimString usageString = ap.getApplicationName();\n   usageString += " [option]... [input-option]... <input-file(s)> <output-file>\\nNote at least one input is required either from one of the input options, e.g. --input-dem <my-dem.hgt> or adding to command line in front of the output file in which case the code will try to ascertain what type of input it is.\\n\\nAvailable traces:\\n-T \\"ossimChipperUtil:debug\\"   - General debug trace to standard out.\\n-T \\"ossimChipperUtil:log\\"     - Writes a log file to output-file.log.\\n-T \\"ossimChipperUtil:options\\" - Writes the options to output-file-options.kwl.";\n\n   ossimApplicationUsage *au = ap.getApplicationUsage();\n\n   au->setCommandLineUsage(usageString);\n\n   au->setDescription(ap.getApplicationName() + " Utility application for generating elevation products from dem data.");\n\n   au->addCommandLineOption("--azimuth", "<azimuth>\\nhillshade option - Light source azimuth angle for bump shade.\\nRange: 0 to 360, Default = 180.0");\n\n   au->addCommandLineOption("-b or --bands <n,n...>", "Use the specified bands in given order: e.g. \\"3,2,1\\" will select bands 3, 2 and 1 of the input image.\\nNote: it is 1 based");\n\n   au->addCommandLineOption("--brightness", "<brightness>\\nApply brightness to input image(s). Valid range: -1.0 to 1.0");\n\n   au->addCommandLineOption("--central-meridian", "<central_meridian_in_decimal_degrees>\\nNote if set this will be used for the central meridian of the projection.  This can be used to lock the utm zone.");\n\n   au->addCommandLineOption("--color", "<r> <g> <b>\\nhillshade option - Set the red, green and blue color values to be used with hillshade.\\nThis option can be used with or without an image source for color.\\nRange 0 to 255, Default r=255, g=255, b=255");\n\n   au->addCommandLineOption("--color-table", "<color-table.kwl>\\nhillshade or color-relief option - Keyword list containing color table for color-relief option.");\n\n   au->addCommandLineOption("--contrast", "<contrast>\\nApply contrast to input image(s). Valid range: -1.0 to 1.0");\n\n   au->addCommandLineOption("--cut-bbox-xywh", "<x>,<y>,<width>,<height>\\nSpecify a comma separated bounding box.");\n\n   au->addCommandLineOption("--cut-wms-bbox", "<minx>,<miny>,<maxx>,<maxy>\\nSpecify a comma separated list in the format of a WMS BBOX.\\nThe units are in the units of the projector defined by the --srs key");\n\n   au->addCommandLineOption("--cut-wms-bbox-ll", "<minx>,<miny>,<maxx>,<maxy>\\nSpecify a comma separated list in the format of a WMS BBOX.\\nThe units are always decimal degrees");\n\n   au->addCommandLineOption("--cut-width", "<width>\\nSpecify the cut width in pixel");\n\n   au->addCommandLineOption("--cut-height", "<height>\\nSpecify the cut height in pixel");\n\n   au->addCommandLineOption("--clip-wms-bbox-ll", "<minx>,<miny>,<maxx>,<maxy>\\nSpecify a comma separated list in the format of a WMS BBOX.\\nThe units are always decimal degrees");\n\n   au->addCommandLineOption("--clip-poly-lat-lon", "Polygon in the form of a string: (lat,lon),(lat,lon),...(lat,lon)");\n\n   au->addCommandLineOption("--cut-bbox-ll", "<min_lat> <min_lon> <max_lat> <max_lon>\\nSpecify a bounding box with the minimum latitude/longitude and max latitude/longitude in decimal degrees.\\nNote coordinates are edge to edge.");\n\n   au->addCommandLineOption("--cut-bbox-llwh", "<min_lat> <min_lon> <max_lat> <max_lon> <width> <height>\\nSpecify a bounding box with the minimum latitude/longitude, max latitude/longitude in decimal degrees and width/height in pixels.\\nNote coordinates are edge to edge.");\n\n   au->addCommandLineOption("--cut-center-llwh", "<latitude> <longitude> <width> <height>\\nSpecify the center cut in latitude longitude space with width and height in pixels.");\n\n   au->addCommandLineOption("--cut-center-llr", "<latitude> <longitude> <radius_in_meters>\\nSpecify the center cut in latitude longitude space with radius in meters.");\n\n   au->addCommandLineOption("--degrees", "<dpp_xy> | <dpp_x> <dpp_y>\\nSpecifies an override for degrees per pixel. Takes either a single value applied equally to x and y directions, or two values applied correspondingly to x then y. This option takes precedence over the \\"--meters\\" option.");\n\n   au->addCommandLineOption("--enable-null-pixel-flip", "Will add to the start of the chain and will flip interior pixels that are null to valid.");\n   au->addCommandLineOption("--elevation", "<elevation>\\nhillshade option - Light source elevation angle for bumb shade.\\nRange: 0 to 90, Default = 45.0");\n\n   au->addCommandLineOption("-e or --entry", "<entry> For multi image handlers which entry do you wish to extract. For list of entries use: \\"ossim-info -i <your_image>\\" ");\n\n   au->addCommandLineOption("--exaggeration", "<factor>\\nMultiplier for elevation values when computing surface normals. Has the effect of lengthening shadows for oblique lighting.\\nRange: .0001 to 50000, Default = 1.0");\n\n   au->addCommandLineOption("--fullres-xys", "<full res center x>,<full res center y>,<scale>[,<scale>]\\nSpecify a full resolution x,y point (Used as pivot and center cut) and scale, comma separated with no spaces.  If two scales are specified then first is x and second is y else x and y are set to equal scales");\n\n   au->addCommandLineOption("--gamma", "Good ranges are between .1 and 6.  Probably try about .7,  A value of 1 will not apply anything");\n\n   au->addCommandLineOption("-h or --help", "Display this help and exit.");\n\n   au->addCommandLineOption("--hemisphere", "<hemisphere>\\nSpecify a projection hemisphere if supported. E.g. UTM projection. This will lock the hemisphere even if input scene center is the other hemisphere. Valid values for UTM are \\"N\\" and \\"S\\"");\n\n   au->addCommandLineOption("--histogram-aoi", "<x>,<y>,<width>,<height>\\nSets single image region of interest(roi) to compute histogram from. Coordinates are zero based image space.  Single image operation only. Comma separated, no spaces.");\n\n   au->addCommandLineOption("--histogram-center-tile", "Will compute histogram from center tile of image.");\n\n   au->addCommandLineOption("--histogram-llwh", "<latitude>,<longitude>,<width>,<height>\\nSpecify the region of interest(roi) to compute histogram from. Latitude and longitude will be roi center space with width and height in pixels. Comma separated, no spaces.");\n\n   au->addCommandLineOption("--histogram-op", "<operation>\\nHistogram operation to perform. Valid operations are \\"linear\\" \\"auto-minmax\\", \\"auto-percentile\\", \\"std-stretch-1\\", \\"std-stretch-2\\" and \\"std-stretch-3\\".");\n\n   au->addCommandLineOption("--histogram-linear-clip", "<low>,<high> in actual DN value.  If it\'s 8 bit then it will be values between 0 and 255");\n   au->addCommandLineOption("--histogram-linear-norm-clip", "<low>,<high> normalized value that range from 0 to 1. Example .2,.85");\n   au->addCommandLineOption("--histogram-center-clip", "<center> normalized value that range from 0 to 1. Default is 0.5 which is the center of the min max clip points.");\n   au->addCommandLineOption("--histogram-bias-factor", "<bias factor> values are typically between 0 to 2.  Allows small adjustments to the auto clip points.  Currently only works for auto min max.");\n\n   au->addCommandLineOption("--image-space-scale", "<x> <y>\\nSpecifies an image space scale for x and y direction. \\"chip\\" operation only.");\n\n   au->addCommandLineOption("--input-dem", "<dem> Input dem to process.");\n\n   au->addCommandLineOption("--input-img", "<image> Input image to process.");\n\n   au->addCommandLineOption("--input-src", "<file.src> Input source file list keyword list with list of dems or images or both to process.");\n\n   au->addCommandLineOption("--meters", "<meters>\\nSpecifies an override for the meters per pixel");\n\n   au->addCommandLineOption("-n or --north-up", "Rotates image North up. \\"chip\\" operation only.");\n\n   au->addCommandLineOption("--op", "<operation>\\nOperation to perform. Valid operations are \\"chip\\", \\"color-relief\\", \\"hillshade\\", \\"psm\\"(pan sharpened multispectral), \\"2cmv\\"(two color multi view) and \\"ortho\\".\\nchip = input projection = output projection(image space), single image operation only.");\n\n   au->addCommandLineOption("--options", "<options.kwl>  This can be all or part of the application options.  To get a template you can turn on trace to the ossimChipperUtil class by adding \\"-T ossimChipperUtil\\" to your command.");\n\n   au->addCommandLineOption("--origin-latitude", "<latidude_in_decimal_degrees>\\nNote if set this will be used for the origin latitude of the projection.  Setting this to something other than 0.0 with a geographic projection creates a scaled geographic projection.");\n\n   au->addCommandLineOption("--output-radiometry", "<R>\\nSpecifies the desired product\'s pixel radiometry type. Possible values for <R> are: U8, U11, U16, S16, F32. Note this overrides the deprecated option \\"scale-to-8-bit\\".");\n\n   au->addCommandLineOption("--pad-thumbnail", "<boolean>\\nIf true, output thumbnail dimensions will be padded in width or height to make square; else, it will have the aspect ratio of input,  Default=false");\n\n   au->addCommandLineOption("--projection", "<output_projection> Valid projections: geo, geo-scaled, input or utm\\ngeo = Equidistant Cylindrical, origin latitude = 0.0\\ngeo-scaled = Equidistant Cylindrical, origin latitude = image center\\ninput Use first images projection. Must be a map projecion.\\nutm = Universal Tranverse Mercator\\nIf input and multiple sources the projection of the first image will be used.\\nIf utm the zone will be set from the scene center of first image.\\nNOTE: --srs takes precedence over this option.");\n\n   au->addCommandLineOption("--resample-filter", "<type>\\nSpecify what resampler filter to use, e.g. nearest neighbor, bilinear, cubic, sinc.\\nSee ossim-info --resampler-filters");\n\n   au->addCommandLineOption("-r or --rotate", "<degrees>\\nRotate image by degrees. \\"chip\\" operation only.");\n\n   au->addCommandLineOption("--reader-prop", "<string>Adds a property to send to the reader. format is name=value");\n\n   au->addCommandLineOption("--rotate-to-input", "\\nOnly if the operation is \\"ortho\\", output a rotated map so that the top edge is oriented with the input image top edge. Only available for single-image products in GeoTIFF output format.");\n\n   au->addCommandLineOption("--rrds", "<rrds> Reduced resolution data set where 0 is full resolution. \\"chip\\" operation only.");\n\n   au->addCommandLineOption("--scale-to-8-bit", "Scales the output to unsigned eight bits per band. This option has been deprecated by the newer \\"--output-radiometry\\" option.");\n\n   au->addCommandLineOption("--sharpen-mode", "<mode> Applies sharpness to image chain(s). Valid modes: \\"light\\", \\"medium\\",\\"heavy\\"");\n   au->addCommandLineOption("--sharpen-percent", "<sharpen-percent> Applies sharpness to image chain(s). Valid percentage is between 0 and 1");\n\n   au->addCommandLineOption("--snap-tie-to-origin",\n                            "Snaps tie point to projection origin so that (tie-origin)/gsd come out on an even integer boundary.");\n\n   au->addCommandLineOption("--srs", "<src_code>\\nSpecify a spatial reference system(srs) code for the output projection. Example: --srs EPSG:4326");\n\n   au->addCommandLineOption("-t or --thumbnail", "<max_dimension>\\nSpecify a thumbnail resolution.\\nScale will be adjusted so the maximum dimension = argument given.");\n\n   au->addCommandLineOption("--three-band-out", "Force three band output even if input is not. Attempts to map bands to RGB if possible.");\n\n   au->addCommandLineOption("--tile-size", "<size_in_pixels>\\nSets the output tile size if supported by writer.  Notes: This sets both dimensions. Must be a multiple of 16, e.g. 1024.");\n\n   au->addCommandLineOption("-u or --up-is-up", "Rotates image to up is up. \\"chip\\" operation only.");\n\n   au->addCommandLineOption("-w or --writer", "<writer>\\nSpecifies the output writer.  Default uses output file extension to determine writer. For valid output writer types use: \\"ossim-info --writers\\"\\n");\n\n   au->addCommandLineOption("--writer-prop", "<writer-property>\\nPasses a name=value pair to the writer for setting it\'s property. Any number of these can appear on the line.");\n\n   au->addCommandLineOption("--zone", "<zone>\\nSpecify a projection zone if supported.  E.g. UTM projection. This will lock the zone even if input scene center is in another zone. Valid values for UTM are \\"1\\" to \\"60\\"");\n\n   au->addCommandLineOption("--2cmv-old-input-band", "<band>\\nBand to use for two color multi view old input.\\n");\n   au->addCommandLineOption("--2cmv-new-input-band", "<band>\\nBand to use for two color multi view new input.\\n");\n\n   au->addCommandLineOption("--2cmv-red-output-source", "<source>\\nTwo color multi view source input for red output.  Either, old, new, or mp(min pix).  Default=old.\\n");\n\n   au->addCommandLineOption("--2cmv-green-output-source", "<source>\\nTwo color multi view source input for green output.  Either, old, new, or mp(min pix).  Default=new.\\n");\n\n   au->addCommandLineOption("--2cmv-blue-output-source", "<source>\\nTwo color multi view source input for blue output.  Either, old, new, or mp(min pix).  Default=new.\\n");\n   au->addCommandLineOption("--combiner-type", "<type>\\nossimBlendMosaic, ossimFeatherMosaic, ossimImageMosaic.  Default: ossimImageMosaic.  Example --combiner-type ossimImageMosaic\\n");\n\n} // End: ossimChipperUtil::addArguments\n\nvoid ossimChipperUtil::clear()\n{\n   // Must disonnect chains so that they destroy.\n   std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator i = m_imgLayer.begin();\n   while (i != m_imgLayer.end())\n   {\n      (*i)->disconnect();\n      (*i) = 0;\n      ++i;\n   }\n   m_imgLayer.clear();\n\n   i = m_demLayer.begin();\n   while (i != m_demLayer.end())\n   {\n      (*i)->disconnect();\n      (*i) = 0;\n      ++i;\n   }\n   m_demLayer.clear();\n\n   if (m_writer.valid())\n   {\n      m_writer->disconnect();\n      m_writer = 0;\n   }\n}\n\nbool ossimChipperUtil::initialize(ossimArgumentParser &ap)\n{\n   static const char MODULE[] = "ossimChipperUtil::initialize(ossimArgumentParser&)";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   clear();\n   if (ap.read("-h") || ap.read("--help") || (ap.argc() == 1))\n   {\n      usage(ap);\n\n      return false; // Indicates process should be terminated to caller.\n   }\n\n   // Start with clean options keyword list.\n   m_kwl->clear();\n\n   std::string tempString1;\n   ossimArgumentParser::ossimParameter stringParam1(tempString1);\n   std::string tempString2;\n   ossimArgumentParser::ossimParameter stringParam2(tempString2);\n   std::string tempString3;\n   ossimArgumentParser::ossimParameter stringParam3(tempString3);\n   std::string tempString4;\n   ossimArgumentParser::ossimParameter stringParam4(tempString4);\n   std::string tempString5;\n   ossimArgumentParser::ossimParameter stringParam5(tempString5);\n   std::string tempString6;\n   ossimArgumentParser::ossimParameter stringParam6(tempString6);\n   double tempDouble1;\n   ossimArgumentParser::ossimParameter doubleParam1(tempDouble1);\n   double tempDouble2;\n   ossimArgumentParser::ossimParameter doubleParam2(tempDouble2);\n\n   ossim_uint32 demIdx = 0;\n   ossim_uint32 imgIdx = 0;\n   ossim_uint32 readerPropIdx = 0;\n   ossim_uint32 writerPropIdx = 0;\n   ossimString key = "";\n\n   // Extract optional arguments and stuff them in a keyword list.\n   if (ap.read("--azimuth", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::AZIMUTH_ANGLE_KW), tempString1);\n   }\n\n   if (ap.read("-b", stringParam1) || ap.read("--bands", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::BANDS_KW), tempString1);\n   }\n\n   if (ap.read("--brightness", stringParam1))\n   {\n      m_kwl->addPair(BRIGHTNESS_KW, tempString1);\n   }\n\n   if (ap.read("--central-meridian", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::CENTRAL_MERIDIAN_KW), tempString1);\n   }\n\n   if (ap.read("--color", stringParam1, stringParam2, stringParam3))\n   {\n      m_kwl->addPair(COLOR_RED_KW, tempString1);\n      m_kwl->addPair(COLOR_GREEN_KW, tempString2);\n      m_kwl->addPair(COLOR_BLUE_KW, tempString3);\n   }\n\n   if (ap.read("--color-table", stringParam1))\n   {\n      m_kwl->addPair(LUT_FILE_KW, tempString1);\n   }\n\n   if (ap.read("--contrast", stringParam1))\n   {\n      m_kwl->addPair(CONTRAST_KW, tempString1);\n   }\n\n   if (ap.read("--cut-width", stringParam1))\n   {\n      m_kwl->addPair(CUT_WIDTH_KW, tempString1);\n   }\n   if (ap.read("--cut-height", stringParam1))\n   {\n      m_kwl->addPair(CUT_HEIGHT_KW, tempString1);\n   }\n   if (ap.read("--cut-bbox-xywh", stringParam1))\n   {\n      m_kwl->addPair(CUT_BBOX_XYWH_KW, tempString1);\n   }\n   if (ap.read("--cut-wms-bbox", stringParam1))\n   {\n      m_kwl->addPair(CUT_WMS_BBOX_KW, tempString1);\n   }\n   if (ap.read("--cut-wms-bbox-ll", stringParam1))\n   {\n      m_kwl->addPair(CUT_WMS_BBOX_LL_KW, tempString1);\n   }\n\n   if (ap.read("--clip-wms-bbox-ll", stringParam1))\n   {\n      m_kwl->addPair(CLIP_WMS_BBOX_LL_KW, tempString1);\n   }\n\n   if (ap.read("--clip-poly-lat-lon", stringParam1))\n   {\n      //std::vector<ossimGpt> result;\n      //ossim::toVector(result, ossimString(tempString1));\n      //std::cout << result[0] << std::endl;\n      //std::cout <<tempString1<<std::endl;\n      //exit(0);\n      m_kwl->addPair(CLIP_POLY_LAT_LON_KW, tempString1);\n   }\n\n   if (ap.read("--cut-bbox-ll", stringParam1, stringParam2, stringParam3, stringParam4))\n   {\n      m_kwl->addPair(CUT_MIN_LAT_KW, tempString1);\n      m_kwl->addPair(CUT_MIN_LON_KW, tempString2);\n      m_kwl->addPair(CUT_MAX_LAT_KW, tempString3);\n      m_kwl->addPair(CUT_MAX_LON_KW, tempString4);\n   }\n\n   if (ap.read("--cut-bbox-llwh", stringParam1, stringParam2, stringParam3,\n               stringParam4, stringParam5, stringParam6))\n   {\n      m_kwl->addPair(CUT_MIN_LAT_KW, tempString1);\n      m_kwl->addPair(CUT_MIN_LON_KW, tempString2);\n      m_kwl->addPair(CUT_MAX_LAT_KW, tempString3);\n      m_kwl->addPair(CUT_MAX_LON_KW, tempString4);\n      m_kwl->addPair(CUT_WIDTH_KW, tempString5);\n      m_kwl->addPair(CUT_HEIGHT_KW, tempString6);\n   }\n\n   if (ap.read("--cut-center-llwh", stringParam1, stringParam2, stringParam3, stringParam4))\n   {\n      m_kwl->addPair(CUT_CENTER_LAT_KW, tempString1);\n      m_kwl->addPair(CUT_CENTER_LON_KW, tempString2);\n      m_kwl->addPair(CUT_WIDTH_KW, tempString3);\n      m_kwl->addPair(CUT_HEIGHT_KW, tempString4);\n   }\n\n   if (ap.read("--cut-center-llr", stringParam1, stringParam2, stringParam3))\n   {\n      m_kwl->addPair(CUT_CENTER_LAT_KW, tempString1);\n      m_kwl->addPair(CUT_CENTER_LON_KW, tempString2);\n      m_kwl->addPair(CUT_RADIUS_KW, tempString3);\n   }\n\n   int num_params = ap.numberOfParams("--degrees", doubleParam1);\n   if (num_params == 1)\n   {\n      ap.read("--degrees", doubleParam1);\n      m_kwl->add(DEGREES_X_KW.c_str(), tempDouble1);\n      m_kwl->add(DEGREES_Y_KW.c_str(), tempDouble1);\n   }\n   else if (num_params == 2)\n   {\n      ap.read("--degrees", doubleParam1, doubleParam2);\n      m_kwl->add(DEGREES_X_KW.c_str(), tempDouble1);\n      m_kwl->add(DEGREES_Y_KW.c_str(), tempDouble2);\n   }\n\n   if (ap.read("--elevation", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::ELEVATION_ANGLE_KW), tempString1);\n   }\n\n   if (ap.read("-e", stringParam1) || ap.read("--entry", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::ENTRY_KW), tempString1);\n   }\n\n   if(ap.read("--gamma", stringParam1))\n   {\n      m_kwl->addPair(GAMMA_KW, tempString1);\n   }\n\n   if (ap.read("--exaggeration", stringParam1))\n   {\n      m_kwl->addPair(GAIN_KW, tempString1);\n   }\n\n   if (ap.read("--fullres-xys", stringParam1))\n   {\n      m_kwl->addPair(FULLRES_XYS_KW, tempString1);\n\n      std::vector<ossimString> values;\n      ossimString(tempString1).split(values, ",");\n      double sx, sy;\n      if (values.size() > 2)\n      {\n         sx = values[2].toDouble();\n         sy = sx;\n         if (values.size() > 3)\n         {\n            sy = values[3].toDouble();\n         }\n         m_kwl->add(IMAGE_SPACE_SCALE_X_KW.c_str(), sx);\n         m_kwl->add(IMAGE_SPACE_SCALE_Y_KW.c_str(), sy);\n      }\n   }\n\n   if (ap.read("--hemisphere", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::HEMISPHERE_KW), tempString1);\n   }\n\n   if (ap.read("--histogram-aoi", stringParam1))\n   {\n      m_kwl->addPair(HIST_AOI_KW, tempString1);\n   }\n\n   if (ap.read("--enable-null-pixel-flip"))\n   {\n      m_kwl->addPair(NULL_PIXEL_FLIP_KW, TRUE_KW);\n   }\n\n   if (ap.read("--histogram-center-tile"))\n   {\n      m_kwl->addPair(HIST_CENTER_KW, TRUE_KW);\n   }\n\n   if (ap.read("--histogram-llwh", stringParam1))\n   {\n      m_kwl->addPair(HIST_LLWH_KW, tempString1);\n   }\n\n   if (ap.read("--histogram-op", stringParam1))\n   {\n      m_kwl->addPair(HIST_OP_KW, tempString1);\n   }\n\n   if (ap.read("--histogram-bias-factor", stringParam1))\n   {\n      m_kwl->addPair(HIST_BIAS_FACTOR_KW, tempString1);\n   }\n   if (ap.read("--histogram-linear-clip", stringParam1))\n   {\n      m_kwl->addPair(HIST_LINEAR_CLIP_KW, tempString1);\n   }\n   if (ap.read("--histogram-linear-norm-clip", stringParam1))\n   {\n      m_kwl->addPair(HIST_LINEAR_NORM_CLIP_KW, tempString1);\n   }\n   if (ap.read("--histogram-center-clip", stringParam1))\n   {\n      m_kwl->addPair(HIST_CENTER_CLIP_KW, tempString1);\n   }\n   if (ap.read("--image-space-scale", doubleParam1, doubleParam2))\n   {\n      m_kwl->add(IMAGE_SPACE_SCALE_X_KW.c_str(), tempDouble1);\n      m_kwl->add(IMAGE_SPACE_SCALE_Y_KW.c_str(), tempDouble2);\n   }\n\n   while (ap.read("--input-dem", stringParam1))\n   {\n      key = DEM_KW;\n      key += ossimString::toString(demIdx);\n      key += ".";\n      key += FILE_KW;\n      m_kwl->addPair(key.string(), tempString1);\n      ++demIdx;\n   }\n\n   while (ap.read("--input-img", stringParam1))\n   {\n      key = IMG_KW;\n      key += ossimString::toString(imgIdx);\n      key += ".";\n      key += FILE_KW;\n      m_kwl->addPair(key.string(), tempString1);\n      ++imgIdx;\n   }\n\n   if (ap.read("--input-src", stringParam1))\n   {\n      m_kwl->addPair(SRC_FILE_KW, tempString1);\n   }\n\n   if (ap.read("--meters", stringParam1))\n   {\n      m_kwl->addPair(METERS_KW, tempString1);\n   }\n\n   if (ap.read("-n") || ap.read("--north-up"))\n   {\n      m_kwl->addPair(NORTH_UP_KW, TRUE_KW);\n   }\n\n   if (ap.read("--op", stringParam1))\n   {\n      m_kwl->addPair(OP_KW, tempString1);\n   }\n\n   //---\n   // Deprecated: "--options-keyword-list"\n   //---\n   if (ap.read("--options", stringParam1))\n   {\n      ossimFilename optionsKwl = tempString1;\n      if (optionsKwl.exists())\n      {\n         if (m_kwl->addFile(optionsKwl) == false)\n         {\n            std::string errMsg = "ERROR could not open options keyword list file: ";\n            errMsg += optionsKwl.string();\n            throw ossimException(errMsg);\n         }\n      }\n      else\n      {\n         std::string errMsg = "ERROR options keyword list file does not exists: ";\n         errMsg += optionsKwl.string();\n         throw ossimException(errMsg);\n      }\n   }\n\n   if (ap.read("--origin-latitude", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::ORIGIN_LATITUDE_KW), tempString1);\n   }\n\n   if (ap.read("--output-radiometry", stringParam1))\n   {\n      m_kwl->addPair(OUTPUT_RADIOMETRY_KW, tempString1);\n   }\n\n   if (ap.read("--pad-thumbnail", stringParam1))\n   {\n      m_kwl->addPair(PAD_THUMBNAIL_KW, tempString1);\n   }\n\n   if (ap.read("--projection", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::PROJECTION_KW), tempString1);\n   }\n\n   if (ap.read("--resample-filter", stringParam1))\n   {\n      m_kwl->addPair(RESAMPLER_FILTER_KW, tempString1);\n   }\n\n   if (ap.read("-r", stringParam1) || ap.read("--rotate", stringParam1))\n   {\n      m_kwl->addPair(ROTATION_KW, tempString1);\n   }\n\n   if (ap.read("--rotate-to-input"))\n   {\n      m_kwl->addPair(ROTATE_TO_INPUT, "true");\n   }\n\n   while (ap.read("--reader-prop", stringParam1))\n   {\n      key = READER_PROPERTY_KW;\n      key += ossimString::toString(readerPropIdx);\n      m_kwl->addPair(key.string(), tempString1);\n      ++readerPropIdx;\n   }\n\n   if (ap.read("--rrds", stringParam1))\n   {\n      m_kwl->addPair(RRDS_KW, tempString1);\n   }\n\n   if (ap.read("--scale-to-8-bit"))\n   {\n      m_kwl->addPair(SCALE_2_8_BIT_KW, TRUE_KW);\n   }\n\n   if (ap.read("--sharpen-mode", stringParam1))\n   {\n      m_kwl->addPair(SHARPEN_MODE_KW, tempString1);\n   }\n\n   if (ap.read("--sharpen-percent", stringParam1))\n   {\n      m_kwl->addPair(SHARPEN_PERCENT_KW, tempString1);\n   }\n\n   if (ap.read("--snap-tie-to-origin"))\n   {\n      m_kwl->addPair(SNAP_TIE_TO_ORIGIN_KW, TRUE_KW);\n   }\n\n   if (ap.read("--srs", stringParam1))\n   {\n      ossimString os = tempString1;\n      if (os.contains("EPSG:"))\n      {\n         os.gsub(ossimString("EPSG:"), ossimString(""));\n      }\n      m_kwl->addPair(SRS_KW, os.string());\n   }\n\n   if (ap.read("-t", stringParam1) || ap.read("--thumbnail", stringParam1))\n   {\n      m_kwl->addPair(THUMBNAIL_RESOLUTION_KW, tempString1);\n   }\n\n   if (ap.read("--three-band-out"))\n   {\n      m_kwl->addPair(THREE_BAND_OUT_KW, TRUE_KW);\n   }\n\n   if (ap.read("--tile-size", stringParam1))\n   {\n      m_kwl->addPair(TILE_SIZE_KW, tempString1);\n   }\n\n   if (ap.read("-u") || ap.read("--up-is-up"))\n   {\n      m_kwl->addPair(UP_IS_UP_KW, TRUE_KW);\n   }\n\n   if (ap.read("-w", stringParam1) || ap.read("--writer", stringParam1))\n   {\n      m_kwl->addPair(WRITER_KW, tempString1);\n   }\n\n   while (ap.read("--writer-prop", stringParam1))\n   {\n      key = WRITER_PROPERTY_KW;\n      key += ossimString::toString(writerPropIdx);\n      m_kwl->addPair(key.string(), tempString1);\n      ++writerPropIdx;\n   }\n\n   if (ap.read("--zone", stringParam1))\n   {\n      m_kwl->addPair(std::string(ossimKeywordNames::ZONE_KW), tempString1);\n   }\n\n   if (ap.read("--2cmv-old-input-band", stringParam1))\n   {\n      m_kwl->addPair(TWOCMV_OLD_INPUT_BAND_KW, tempString1);\n   }\n\n   if (ap.read("--2cmv-new-input-band", stringParam1))\n   {\n      m_kwl->addPair(TWOCMV_NEW_INPUT_BAND_KW, tempString1);\n   }\n   if (ap.read("--2cmv-red-output-source", stringParam1))\n   {\n      m_kwl->addPair(TWOCMV_RED_OUTPUT_SOURCE_KW, tempString1);\n   }\n\n   if (ap.read("--2cmv-green-output-source", stringParam1))\n   {\n      m_kwl->addPair(TWOCMV_GREEN_OUTPUT_SOURCE_KW, tempString1);\n   }\n\n   if (ap.read("--2cmv-blue-output-source", stringParam1))\n   {\n      m_kwl->addPair(TWOCMV_BLUE_OUTPUT_SOURCE_KW, tempString1);\n   }\n   if (ap.read("--combiner-type", stringParam1))\n   {\n      m_kwl->addPair(COMBINER_TYPE_KW, tempString1);\n   }\n   // End of arg parsing.\n   ap.reportRemainingOptionsAsUnrecognized();\n   if (ap.errors())\n   {\n      ap.writeErrorMessages(ossimNotify(ossimNotifyLevel_NOTICE));\n      std::string errMsg = "Unknown option...";\n      throw ossimException(errMsg);\n   }\n\n   if (ap.argc() >= 2)\n   {\n      // Output file is last arg:\n      m_kwl->add(ossimKeywordNames::OUTPUT_FILE_KW, ap[ap.argc() - 1]);\n   }\n   else\n   {\n      if (!m_kwl->find(ossimKeywordNames::OUTPUT_FILE_KW))\n      {\n         ap.writeErrorMessages(ossimNotify(ossimNotifyLevel_NOTICE));\n         std::string errMsg = "Must supply an output file.";\n         throw ossimException(errMsg);\n      }\n   }\n\n   if (ap.argc() > 2) // User passed inputs in front of output file.\n   {\n      int pos = 1; // ap.argv[0] is application name.\n      while (pos < (ap.argc() - 1))\n      {\n         ossimFilename file = ap[pos];\n         if (traceDebug())\n         {\n            ossimNotify(ossimNotifyLevel_DEBUG)\n               << "argv[" << pos << "]: " << file << "\\n";\n         }\n\n         if (isDemFile(file))\n         {\n            key = DEM_KW;\n            key += ossimString::toString(demIdx);\n            key += ".";\n            key += FILE_KW;\n            m_kwl->addPair(key, file.string());\n            ++demIdx;\n         }\n         else if (isSrcFile(file))\n         {\n            if (m_kwl->find(SRC_FILE_KW.c_str())) // --input-src used also\n            {\n               std::string errMsg = MODULE;\n               errMsg += "ERROR Multiple src files passed in.  Please combine into one.";\n               throw ossimException(errMsg);\n            }\n\n            m_kwl->addPair(SRC_FILE_KW, file.string());\n         }\n         else // Add as an input image.\n         {\n            key = IMG_KW;\n            key += ossimString::toString(imgIdx);\n            key += ".";\n            key += FILE_KW;\n            m_kwl->addPair(key.string(), file.string());\n            ++imgIdx;\n         }\n\n         ++pos; // Go to next arg...\n\n      } // End: while ( pos < (ap.argc()-1) )\n\n   } // End: if ( ap.argc() > 2 )\n\n   initialize();\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited..." << std::endl;\n   }\n   return true;\n\n} // End: void ossimChipperUtil::initialize(ossimArgumentParser& ap)\n\nvoid ossimChipperUtil::initialize(const ossimKeywordlist &kwl)\n{\n   clear();\n\n   // Start with clean options keyword list.\n   m_kwl->clear();\n\n   m_kwl->addList(kwl, true);\n\n   initialize();\n}\n\nvoid ossimChipperUtil::initialize()\n{\n   static const char MODULE[] = "ossimChipperUtil::initialize()";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   if (traceOptions())\n   {\n      ossimFilename optionsFile;\n      getOutputFilename(optionsFile);\n      optionsFile = optionsFile.noExtension();\n      optionsFile += "-options.kwl";\n      ossimString comment = " Can be use for --options argument.";\n      m_kwl->write(optionsFile.c_str(), comment.c_str());\n   }\n\n   // Determine the operation to do.\n   std::string op = m_kwl->findKey(OP_KW);\n   if (op.size())\n   {\n      ossimString s = op;\n      s.downcase();\n\n      if (s == "chip")\n      {\n         m_operation = OSSIM_CHIPPER_OP_CHIP;\n      }\n      else if (s == "hillshade")\n      {\n         m_operation = OSSIM_CHIPPER_OP_HILL_SHADE;\n      }\n\n      else if (s == "color-relief")\n      {\n         m_operation = OSSIM_CHIPPER_OP_COLOR_RELIEF;\n      }\n      else if (s == "ortho")\n      {\n         m_operation = OSSIM_CHIPPER_OP_ORTHO;\n      }\n      else if (s == "psm")\n      {\n         m_operation = OSSIM_CHIPPER_OP_PSM;\n      }\n      else if (s == "2cmv")\n      {\n         m_operation = OSSIM_CHIPPER_OP_2CMV;\n      }\n      else\n      {\n         std::string errMsg = "unknown operation: ";\n         errMsg += s.string();\n         throw ossimException(errMsg);\n      }\n   }\n   else\n   {\n      std::string errMsg = "keyword not found: ";\n      errMsg += OP_KW;\n      errMsg += "\\nUse --op option to specify operation.\\n";\n      throw ossimException(errMsg);\n   }\n\n   //---\n   // Populate the m_srcKwl if --src option was set.\n   // Note do this before creating chains.\n   //---\n   initializeSrcKwl();\n\n   // Check for required inputs. Do this after initializeSrcKwl.\n   if (m_operation == OSSIM_CHIPPER_OP_CHIP)\n   {\n      if (getNumberOfInputs() != 1)\n      {\n         std::ostringstream errMsg;\n         errMsg << op << " operation takes one input.";\n         throw ossimException(errMsg.str());\n      }\n   }\n\n   if ((m_operation == OSSIM_CHIPPER_OP_2CMV) || (m_operation == OSSIM_CHIPPER_OP_PSM))\n   {\n      if (getNumberOfInputs() != 2)\n      {\n         std::ostringstream errMsg;\n         errMsg << op << " operation requires two inputs.";\n         throw ossimException(errMsg.str());\n      }\n   }\n\n   // Sanity check rotation options.\n   if (upIsUp() || northUp() || hasRotation())\n   {\n      std::string option;\n      ossim_uint32 rotationOptionCount = 0;\n      if (upIsUp())\n      {\n         option = UP_IS_UP_KW;\n         ++rotationOptionCount;\n      }\n      if (northUp())\n      {\n         option = NORTH_UP_KW;\n         ++rotationOptionCount;\n      }\n      if (hasRotation())\n      {\n         option = ROTATION_KW;\n         ++rotationOptionCount;\n      }\n\n      // Can only do ONE rotation option.\n      if (rotationOptionCount > 1)\n      {\n         std::ostringstream errMsg;\n         if (upIsUp())\n         {\n            errMsg << UP_IS_UP_KW << " is on.\\n";\n         }\n         if (northUp())\n         {\n            errMsg << NORTH_UP_KW << " is on.\\n";\n         }\n         if (hasRotation())\n         {\n            errMsg << ROTATION_KW << " is on.\\n";\n         }\n         errMsg << "Multiple rotation options do not make sense!";\n         throw ossimException(errMsg.str());\n      }\n\n      // One input, chip operation only.\n      if (getNumberOfInputs() != 1)\n      {\n         std::ostringstream errMsg;\n         errMsg << option << " option takes one input.";\n         throw ossimException(errMsg.str());\n      }\n\n      if (m_operation != OSSIM_CHIPPER_OP_CHIP)\n      {\n         std::ostringstream errMsg;\n         errMsg << option << " option only valid with \\"chip\\" operation.";\n         throw ossimException(errMsg.str());\n      }\n   }\n\n   // Create chains for any dem sources.\n   addDemSources();\n\n   // Create chains for any image sources.\n   addImgSources();\n\n   // Initialize projection and propagate to chains.\n   initializeOutputProjection();\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "options keyword list:\\n"\n         << *(m_kwl.get()) << "\\n";\n\n      if (m_srcKwl.valid())\n      {\n         ossimNotify(ossimNotifyLevel_DEBUG)\n            << "support record keyword list:\\n"\n            << *(m_srcKwl.get()) << "\\n";\n      }\n\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n} // End: void ossimChipperUtil::initialize()\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::initializeChain(ossimIrect &aoi)\n{\n   static const char MODULE[] = "ossimChipperUtil::initializeChain";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimString lookup; // used throughout...\n\n   ossimRefPtr<ossimImageSource> source = 0;\n\n   if (hasBumpShadeArg())\n   {\n      // Create chain:\n      source = initializeBumpShadeChain();\n   }\n   else if (m_operation == OSSIM_CHIPPER_OP_COLOR_RELIEF)\n   {\n      source = initializeColorReliefChain();\n   }\n   else if ((m_operation == OSSIM_CHIPPER_OP_CHIP) ||\n            (m_operation == OSSIM_CHIPPER_OP_ORTHO))\n   {\n      source = combineLayers();\n   }\n   else if (m_operation == OSSIM_CHIPPER_OP_2CMV)\n   {\n      source = initialize2CmvChain(); // Two Color Multiview.\n   }\n   else if (m_operation == OSSIM_CHIPPER_OP_PSM)\n   {\n      source = initializePsmChain(); // Pan sharpened multispectral.\n   }\n\n   if (source.valid())\n   {\n      //---\n      // This is conditional.  Output radiometry may of may not be set.  This can also be set at\n      // the ossimSingleImageChain level.\n      //---\n      if ((getOutputScalarType() != OSSIM_SCALAR_UNKNOWN) &&\n          (source->getOutputScalarType() != getOutputScalarType()))\n      {\n         // source = addScalarRemapper(source, getOutputScalarType());\n      }\n\n      //---\n      // Get the area of interest. This will be the scene bounding rect if not\n      // explicitly set by user with one of the --cut options.\n      //  Need to get this before the thumbnail code.\n      //---\n      getAreaOfInterest(source.get(), aoi);\n\n      //---\n      // Set the image size here.  Note must be set after combineLayers.  This is needed for\n      // the ossimImageGeometry::worldToLocal call for a geographic projection to handle wrapping\n      // accross the date line.\n      //---\n      if (m_geom.valid())\n      {\n         m_geom->setImageSize(aoi.size());\n      }\n\n      if (hasThumbnailResolution())\n      {\n         //---\n         // Adjust the projection scale and get the new rect.\n         // Note this will resize the ossimImageGeometry::m_imageSize is scale changes.\n         //---\n         initializeThumbnailProjection(aoi, aoi);\n\n         // Reset the source bounding rect if it changed.\n         source->initialize();\n      }\n\n      // GP 2019: we will add cutters at the end\n      // Will remove temporarily bt leave the code hear just in\n      // case I messed something up\n      if (source.valid() && !aoi.hasNans())\n      {\n         //---\n         // Add a cut filter. This will:\n         // 1) Null out/clip any data pulled in.\n         // 2) Speed up by not propagating get tile request outside the cut or "aoi"\n         //    to the left hand side(input).\n         //---\n         // ossimRefPtr<ossimRectangleCutFilter> cutter = new ossimRectangleCutFilter();\n\n         // // Set the cut rectangle:\n         // cutter->setRectangle(aoi);\n\n         // // Null outside.\n         // cutter->setCutType(ossimRectangleCutFilter::OSSIM_RECTANGLE_NULL_OUTSIDE);\n\n         // // Connect cutter input to source chain.\n         // cutter->connectMyInputTo(0, source.get());\n\n         // source = cutter.get();\n\n         // // Dependent on correct aoi so place after the cutter.\n         // if (hasAnnotations())\n         // {\n         //    // Put annotations after scalar remapper.\n         //    ossimRefPtr<ossimImageSource> result = addAnnotations(source);\n         //    if (result.valid())\n         //    {\n         //       source = result.get();\n         //    }\n         // }\n      }\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n   return source;\n}\n\nvoid ossimChipperUtil::setOptionsToChain(\n   ossimIrect &aoi, const ossimKeywordlist & /* kwl */)\n{\n   getAreaOfInterest(m_source.get(), aoi);\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::initializeBumpShadeChain()\n{\n   static const char MODULE[] = "ossimChipperUtil::initializeBumpShadeChain";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimString lookup;\n   ossimRefPtr<ossimImageSource> source = 0;\n\n   // Combine the dems.\n   ossimRefPtr<ossimImageSource> demSource = combineLayers(m_demLayer);\n\n   // Set up the normal source.\n   ossimRefPtr<ossimImageToPlaneNormalFilter> normSource = new ossimImageToPlaneNormalFilter;\n\n   //---\n   // Set the track scale flag to true.  This enables scaling the surface\n   // normals by the GSD in order to maintain terrain proportions.\n   //---\n   normSource->setTrackScaleFlag(true);\n\n   // Connect to dems.\n   normSource->connectMyInputTo(demSource.get());\n\n   // Set the smoothness factor.\n   ossim_float64 gain = 1.0;\n   lookup = m_kwl->findKey(GAIN_KW);\n   if (lookup.size())\n   {\n      gain = lookup.toFloat64();\n   }\n   normSource->setSmoothnessFactor(gain);\n\n   ossimRefPtr<ossimImageSource> colorSource = 0;\n   if (hasLutFile())\n   {\n      if (m_imgLayer.size())\n      {\n         ossimNotify(ossimNotifyLevel_WARN)\n            << MODULE << " WARNING:"\n            << "\\nBoth a color table and image(s) have been provided for a color source.\\n"\n            << "Choosing color table of image(s).\\n";\n      }\n\n      colorSource = addIndexToRgbLutFilter(demSource);\n   }\n   else\n   {\n      // Combine the images and set as color source for bump shade.\n      colorSource = combineLayers(m_imgLayer);\n   }\n\n   // Create the bump shade.\n   ossimRefPtr<ossimBumpShadeTileSource> bumpShade = new ossimBumpShadeTileSource;\n\n   // Set the azimuth angle.\n   ossim_float64 azimuthAngle = 180;\n   lookup = m_kwl->findKey(ossimKeywordNames::AZIMUTH_ANGLE_KW);\n   if (lookup.size())\n   {\n      ossim_float64 f = lookup.toFloat64();\n      if ((f >= 0) && (f <= 360))\n      {\n         azimuthAngle = f;\n      }\n   }\n   bumpShade->setAzimuthAngle(azimuthAngle);\n\n   // Set the elevation angle.\n   ossim_float64 elevationAngle = 45.0;\n   lookup = m_kwl->findKey(ossimKeywordNames::ELEVATION_ANGLE_KW);\n   if (lookup.size())\n   {\n      ossim_float64 f = lookup.toFloat64();\n      if ((f >= 0.0) && (f <= 90))\n      {\n         elevationAngle = f;\n      }\n   }\n   bumpShade->setElevationAngle(elevationAngle);\n\n   if (!hasLutFile())\n   {\n      // Set the color.\n      ossim_uint8 r = 0xff;\n      ossim_uint8 g = 0xff;\n      ossim_uint8 b = 0xff;\n      lookup = m_kwl->findKey(COLOR_RED_KW);\n      if (lookup.size())\n      {\n         r = lookup.toUInt8();\n      }\n      lookup = m_kwl->findKey(COLOR_GREEN_KW);\n      if (lookup.size())\n      {\n         g = lookup.toUInt8();\n      }\n      lookup = m_kwl->findKey(COLOR_BLUE_KW);\n      if (lookup.size())\n      {\n         b = lookup.toUInt8();\n      }\n      bumpShade->setRgbColorSource(r, g, b);\n   }\n\n   // Connect the two sources.\n   bumpShade->connectMyInputTo(0, normSource.get());\n   bumpShade->connectMyInputTo(1, colorSource.get());\n\n   if (traceDebug())\n   {\n      ossim_uint8 r = 0xff;\n      ossim_uint8 g = 0xff;\n      ossim_uint8 b = 0xff;\n      bumpShade->getRgbColorSource(r, g, b);\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "\\nazimuthAngle:      " << azimuthAngle\n         << "\\nelevation angle:   " << elevationAngle\n         << "\\ngain factor:       " << gain\n         << "\\nr:                 " << int(r)\n         << "\\ng:                 " << int(g)\n         << "\\nb:                 " << int(b)\n         << "\\n";\n   }\n\n   // Capture the pointer to give to the writer.\n   source = bumpShade.get();\n\n   return source;\n\n} // End: ossimChipperUtil::initializeBumpShadeChain()\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::initializeColorReliefChain()\n{\n   ossimRefPtr<ossimImageSource> result = combineLayers();\n   if (hasLutFile())\n   {\n      result = addIndexToRgbLutFilter(result);\n   }\n   else\n   {\n      // No LUT file provided, so doing the default 8-bit linear stretch:\n      if (result->getOutputScalarType() != OSSIM_UINT8)\n      {\n         //result = addScalarRemapper(result, OSSIM_UINT8);\n      }\n   }\n   return result;\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::initializePsmChain()\n{\n   ossimRefPtr<ossimImageSource> result = 0;\n\n   ossim_uint32 layerCount = (ossim_uint32)(m_demLayer.size() + m_imgLayer.size());\n\n   // Must have two and only two inputs.\n   if (layerCount == 2)\n   {\n      ossimRefPtr<ossimSingleImageChain> input1 = 0; // First input should be color.\n      ossimRefPtr<ossimSingleImageChain> input2 = 0; // Second input should be pan.\n\n      // Most likely case, two image layers.\n      if (m_imgLayer.size())\n      {\n         input1 = m_imgLayer[0].get();\n\n         if (m_imgLayer.size() == 2)\n         {\n            input2 = m_imgLayer[1].get();\n         }\n      }\n\n      if (m_demLayer.size())\n      {\n         if (!input1.valid())\n         {\n            input1 = m_demLayer[0].get();\n         }\n\n         if (!input2.valid())\n         {\n            if (m_demLayer.size() == 1)\n            {\n               input2 = m_demLayer[0].get();\n            }\n            else if (m_demLayer.size() == 2)\n            {\n               input2 = m_demLayer[1].get();\n            }\n         }\n      }\n\n      if (input1.valid() && input2.valid())\n      {\n         // Make the color input the first connection to the combiner.\n         if (input1->getNumberOfOutputBands() == 1)\n         {\n            // Swap:\n            ossimRefPtr<ossimSingleImageChain> tmpChain = input1;\n            input1 = input2;\n            input2 = tmpChain;\n         }\n\n         //---\n         // Check the pan source for one band:\n         // This really shouldn\'t happen, i.e. caller should typically pass a\n         // one band pan image that is higher resolution than the color source.\n         //---\n         if (input2->getNumberOfOutputBands() > 1)\n         {\n            // Note this will add a band selector if there isn\'t one in chain.\n            std::vector<ossim_uint32> bandList(1);\n            bandList[0] = 0;\n            input2->setBandSelection(bandList);\n         }\n\n         // TODO: Make dynamic by type.\n         ossimRefPtr<ossimFusionCombiner> psm = new ossimSFIMFusion();\n         psm->connectMyInputTo(0, input1.get());\n         psm->connectMyInputTo(1, input2.get());\n         psm->initialize();\n         result = dynamic_cast<ossimImageSource *>(psm.get());\n      }\n\n   } // Matches: if ( layerCount == 2 )\n\n   return result;\n\n} // End: ossimChipperUtil::initializePsmChain()\n\nvoid ossimChipperUtil::initializeOutputProjection()\n{\n   if (isChipMode())\n   {\n      createIdentityProjection();\n   }\n   else\n   {\n      // Create the output projection.\n      createOutputProjection();\n\n      // Setup the view in all the chains.\n      propagateOutputProjectionToChains();\n   }\n}\n\nossimIrect clampViewportStretch(const ossimIrect &viewRect)\n{\n   ossimIrect result = viewRect;\n\n   if (result.width() > 1024 || result.height() > 1024)\n   {\n      ossim_int64 w = ossim::min(static_cast<ossim_int64>(result.width()), static_cast<ossim_int64>(1024));\n      ossim_int64 h = ossim::min(static_cast<ossim_int64>(result.height()), static_cast<ossim_int64>(1024));\n      if (w < 256)\n         w = 256;\n      if (h < 256)\n         h = 256;\n\n      result = ossimIrect(ossimIpt(result.midPoint()), w, h);\n   }\n   return result;\n}\n\nvoid ossimChipperUtil::setStretch(ossimRefPtr<ossimHistogramRemapper> remapper)const\n{\n   ossim_int32 mode = getHistoMode();\n   ossimString value;\n   if (mode == ossimHistogramRemapper::LINEAR_ONE_PIECE)\n   {\n      value = m_kwl->findKey(HIST_LINEAR_CLIP_KW);\n      std::vector<ossimString> splitValues;\n      if (!value.empty())\n      {\n         splitValues = value.split(",");\n         if(splitValues.size() == 2)\n         {\n            remapper->setLowClipPoint(splitValues[0].toFloat32());\n            remapper->setHighClipPoint(splitValues[1].toFloat32());\n         }\n      }\n      else\n      {\n         value = m_kwl->findKey(HIST_LINEAR_NORM_CLIP_KW);\n         if(!value.empty())\n         {\n            splitValues = value.split(",");\n            if (splitValues.size() == 2)\n            {\n               ossim_float32 low = splitValues[0].toFloat32();\n               ossim_float32 high = splitValues[1].toFloat32();\n               remapper->setLowNormalizedClipPoint(low);\n               remapper->setHighNormalizedClipPoint(high);\n            }\n         }\n      }\n   }\n   value = m_kwl->findKey(HIST_BIAS_FACTOR_KW);\n   if(!value.empty())\n   {\n      remapper->setBiasFactor(value.toDouble());\n   }\n\n   value = m_kwl->findKey(HIST_CENTER_CLIP_KW);\n   if (!value.empty())\n   {\n      remapper->setMidPoint(value.toFloat64());\n   }\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::getFinalInput(const ossimIrect& aoi, \n                                                              ossimRefPtr<ossimImageSource> currentSource)\n{\n   ossimRefPtr<ossimImageSource> result = currentSource;\n\n   if(m_container.valid())\n   {\n      m_container->deleteAllChildren();\n      m_container->disconnect();\n   }\n   else\n   {\n      m_container = new ossimConnectableContainer();\n   }\n   ossimRefPtr<ossimRectangleCutFilter> cut = new ossimRectangleCutFilter();\n   cut->connectMyInputTo(result.get());\n   cut->setRectangle(aoi);\n   m_container->addChild(cut.get());\n   result = cut.get();\n   if(m_viewPortStretchEnabled)\n   {\n      if(m_geom)\n      {\n         ossimDpt iptCenter;\n         ossimDrect histogramSourceRect;\n         ossimRefPtr<ossimImageHistogramSource>\n             histoSource = new ossimImageHistogramSource();\n         // ossimTypeNameVisitor imageRendererVisitor("ossimImageRenderer", true);\n         // currentSource->accept(imageRendererVisitor);\n         ossim_float64 cutWidth  = ossim::clamp<double>(aoi.width(), 256, 1024);\n         ossim_float64 cutHeight = ossim::clamp<double>(aoi.height(), 256, 1024);\n         ossimRefPtr<ossimRectangleCutFilter> viewportCut = new ossimRectangleCutFilter();\n         ossimIrect cutRect = ossimIrect(ossimIpt(aoi.midPoint()), cutWidth, cutHeight);\n         m_container->addChild(viewportCut.get());\n         viewportCut->connectMyInputTo(currentSource.get());\n         viewportCut->setRectangle(cutRect);\n         histoSource->connectMyInputTo(viewportCut.get());\n\n         // ossimRefPtr<ossimImageRenderer> rendererObj = imageRendererVisitor.getObjectAs<ossimImageRenderer>(0);\n         // imageRendererVisitor.reset();\n         // need a rectangle cut that we will connect to an input source that is before the resampling\n         // process so we will need to invert the view window center and then expand the sample rect to a\n         // fixed size.  Probably like 256x256 for now\n         //\n \n         // bool setupOk=false;\n         // if (rendererObj)\n         // {\n         //    ossimRefPtr<ossimImageViewTransform> trans = rendererObj->getImageViewTransform();\n         //    if(trans)\n         //    {\n         //       m_container->addChild(viewportCut.get());\n         //       trans->viewToImage(aoi.midPoint(), iptCenter);\n         //       histogramSourceRect = ossimIrect(ossimIpt(iptCenter), 1024, 1024);\n         //       viewportCut->connectMyInputTo(rendererObj->getInput(0));\n         //       viewportCut->setRectangle(histogramSourceRect);\n         //       histoSource->connectMyInputTo(viewportCut.get());\n         //       // ossimKeywordlist  kwl;\n         //       // histoSource->getHistogram()->saveState(kwl);\n         //       //std::cout << "KWL: " <<  kwl << "\\n";\n         //       setupOk = true;\n         //    }\n         // }\n         // if(!setupOk)\n         // {\n         //   histoSource->connectMyInputTo(result.get());\n         // }\n         ossimRefPtr<ossimHistogramRemapper> remapper = new ossimHistogramRemapper();\n         remapper->connectMyInputTo(result.get());\n         remapper->connectMyInputTo(histoSource.get());\n         remapper->setStretchMode(static_cast<ossimHistogramRemapper::StretchMode>(getHistoMode()));\n         remapper->setEnableFlag(true);\n\n         result = remapper.get();\n         m_container->addChild(histoSource.get());\n         m_container->addChild(remapper.get());\n         setStretch(remapper);\n      }\n   }\n   if(scaleToEightBit())\n   {\n      if(result->getOutputScalarType() != OSSIM_UINT8)\n      {\n         ossimRefPtr<ossimScalarRemapper>  scalarRemapper = new ossimScalarRemapper();\n         scalarRemapper->setOutputScalarType(OSSIM_UINT8);\n         scalarRemapper->connectMyInputTo(result.get());\n         m_container->addChild(scalarRemapper.get());\n         result = scalarRemapper.get();\n      }\n   }\n   return result;\n}\n\nvoid ossimChipperUtil::execute()\n{\n   static const char MODULE[] = "ossimChipperUtil::execute";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimIrect aoi;\n   ossimRefPtr<ossimImageSource> chain = initializeChain(aoi);\n //  ossimRefPtr<ossimConnectableContainer> container = new ossimConnectableContainer();\n   ossimRefPtr<ossimImageSource> source = getFinalInput(aoi, chain);//chain.get();\n   if (source.valid() && !aoi.hasNans())\n   {\n      // Set up the writer.\n      m_writer = createNewWriter();\n\n      // Connect the writer to the cutter.\n      m_writer->connectMyInputTo(0, source.get());\n\n      //---\n      // Set the area of interest.\n      // NOTE: This must be called after the writer->connectMyInputTo as\n      // ossimImageFileWriter::initialize incorrectly resets theAreaOfInterest\n      // back to the bounding rect.\n      //---\n      // m_writer->setAreaOfInterest(aoi);\n\n      if (m_writer->getErrorStatus() == ossimErrorCodes::OSSIM_OK)\n      {\n         // Add a listener to get percent complete.\n         ossimStdOutProgress prog(0, true);\n         m_writer->addListener(&prog);\n\n         if (traceLog())\n         {\n            ossimKeywordlist logKwl;\n            m_writer->saveStateOfAllInputs(logKwl);\n\n            ossimFilename logFile;\n            getOutputFilename(logFile);\n            logFile.setExtension("log");\n\n            logKwl.write(logFile.c_str());\n         }\n\n         // Write the file:\n         m_writer->execute();\n\n         m_writer->removeListener(&prog);\n\n         if (m_writer->isAborted())\n         {\n            throw ossimException("Writer Process aborted!");\n         }\n         m_writer->disconnect();\n         m_container->deleteAllChildren();\n         m_container->disconnect();\n      }\n      else\n      {\n         throw ossimException("Unable to initialize writer for execution");\n      }\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n}\nvoid ossimChipperUtil::abort()\n{\n   if (m_writer.valid())\n   {\n      m_writer->abort();\n   }\n}\n\nossimRefPtr<ossimImageData> ossimChipperUtil::getChip(const ossimKeywordlist &optionsKwl)\n{\n   ossimRefPtr<ossimImageData> result = 0;\n\n   ossimIrect aoi;\n\n   if (optionsKwl.getSize() > 0)\n   {\n      m_kwl->addList(optionsKwl, true);\n   }\n   \n\n   // (GP)\n   // Until we add more ellaborate code to check for scale changes\n   // as well as moving windows we will just always initialize\n   // the output projection\n   //\n\n   initializeOutputProjection();\n\n   if (!m_source.valid())\n   {\n      m_source = initializeChain(aoi);\n   }\n\n   if (optionsKwl.getSize() > 0)\n   {\n      \n      //---\n      // Only do this if new options were passed in. This was causing an off by\n      // one error when now options were passed in using thumbnail option.\n      // The m_source->getBoundingRect(...) was returning 257 instead of 256.\n      // Need to hunt that down but for right now this fixes problem.\n      // (drb 20151222)\n      //---\n      getAreaOfInterest(m_source.get(), aoi);\n\n      m_geom->setImageSize(aoi.size());\n   }\n   else\n   {\n      getAreaOfInterest(m_source.get(), aoi);\n   }\n\n   if (m_source.valid())\n   {\n      ossimRefPtr<ossimImageSource> source = getFinalInput(aoi, m_source.get());\n      result = source->getTile(aoi);\n   }\n\n   return result;\n}\n\nvoid ossimChipperUtil::addDemSources()\n{\n   static const char MODULE[] = "ossimChipperUtil::addDemSources";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   std::vector<ossimString> sortedList;\n   m_kwl->getSortedList(sortedList, DEM_KW);\n   for (auto record : sortedList)\n   {\n      ossimString key = record + ".";\n      ossimString file = key + FILE_KW;\n      ossimString entry = key + ossimKeywordNames::ENTRY_KW;\n      ossimFilename f = m_kwl->findKey(file.string());\n      if (f.size())\n      {\n         // Look for the entry key, e.g. dem0.entry: 10\n         ossim_uint32 entryIndex = 0;\n         std::string value = m_kwl->findKey(entry.string());\n         if (value.size())\n         {\n            entryIndex = ossimString(value).toUInt32();\n         }\n         else\n         {\n            // Get global entry.  Set by "-e" on command line apps.\n            entryIndex = getEntryNumber();\n         }\n         ossimSrcRecord srcRecord;\n         srcRecord.setFilename(f);\n         srcRecord.setEntryIndex(entryIndex);\n         // addDemSource( f, entryIndex );\n         addDemSource(srcRecord);\n      }\n   }\n   sortedList.clear();\n   if (m_srcKwl.valid())\n   {\n      // Add stuff from src keyword list.\n      m_srcKwl->getSortedList(sortedList, DEM_KW);\n      for (auto record : sortedList)\n      {\n         ossimString prefix = record + ".";\n         ossimSrcRecord src;\n         if (src.loadState(*(m_srcKwl.get()), prefix))\n         {\n            addDemSource(src);\n         }\n      }\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::addDemSource(const ossimFilename &file, ossim_uint32 entryIndex)\n{\n   static const char MODULE[] = "ossimChipperUtil::addDemSource(const ossimFilename&)";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimSingleImageChain> ic = createChain(file, entryIndex, true);\n   if (ic.valid())\n   {\n      m_demLayer.push_back(ic);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\\n";\n   }\n}\n\nvoid ossimChipperUtil::addDemSource(const ossimSrcRecord &rec)\n{\n   static const char MODULE[] = "ossimChipperUtil::addDemSource(const ossimSrcRecord&)";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimSingleImageChain> ic = createChain(rec, true);\n   if (ic.valid())\n   {\n      m_demLayer.push_back(ic);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\\n";\n   }\n}\n\nvoid ossimChipperUtil::addImgSources()\n{\n   static const char MODULE[] = "ossimChipperUtil::addImgSources";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n   std::vector<ossimString> sortedList;\n   m_kwl->getSortedList(sortedList, IMG_KW);\n   for (auto record : sortedList)\n   {\n      ossimString fileKey = record + "." + FILE_KW;\n      ossimFilename f = m_kwl->findKey(fileKey.string());\n      if (!f.empty())\n      {\n         ossimString imagePrefix = record + ".";\n         std::shared_ptr<ossimSrcRecord> srcRecord = std::make_shared<ossimSrcRecord>();\n         if (srcRecord->loadState(*m_kwl, imagePrefix.c_str()))\n         {\n            addImgSource(*srcRecord);\n         }\n      }\n   }\n   sortedList.clear();\n   if (m_srcKwl.valid())\n   {\n      m_srcKwl->getSortedList(sortedList, IMG_KW);\n      for (auto record : sortedList)\n      {\n         ossimString prefix = record + ".";\n         std::shared_ptr<ossimSrcRecord> src = std::make_shared<ossimSrcRecord>();\n         if (src->loadState(*(m_srcKwl.get()), prefix))\n         {\n            addImgSource(*src);\n         }\n      }\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::addImgSource(const ossimFilename &file, ossim_uint32 entryIndex)\n{\n   static const char MODULE[] = "ossimChipperUtil::addImgSource";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << MODULE << " entered...\\nFile: " << file << "\\n";\n   }\n\n   ossimRefPtr<ossimSingleImageChain> ic = createChain(file, entryIndex, false);\n   if (ic.valid())\n   {\n      m_imgLayer.push_back(ic);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\\n";\n   }\n}\n\nvoid ossimChipperUtil::addImgSource(const ossimSrcRecord &rec)\n{\n   static const char MODULE[] = "ossimChipperUtil::addImgSource(const ossimSrcRecord&)";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimSingleImageChain> ic = createChain(rec, false);\n   if (ic.valid())\n   {\n      m_imgLayer.push_back(ic);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exiting...\\n";\n   }\n}\n\nossimRefPtr<ossimSingleImageChain> ossimChipperUtil::createChain(const ossimFilename &file,\n                                                                 ossim_uint32 entryIndex,\n                                                                 bool isDemSource) const\n{\n   static const char MODULE[] = "ossimChipperUtil::createChain(const ossimFilename&";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << MODULE << " entered..."\n         << "\\nfile: " << file\n         << "\\nentry: " << entryIndex\n         << "\\nisDemSource: " << (isDemSource ? "true" : "false")\n         << "\\n";\n   }\n\n   ossimRefPtr<ossimSingleImageChain> ic = 0;\n\n   if (!file.empty() && file.exists())\n   {\n      ic = new ossimSingleImageChain;\n      if (ic->open(file))\n      {\n         // Set any reader props:\n         setReaderProps(ic->getImageHandler().get());\n\n         // we can\'t guarantee the state of the image handler at this point so\n         // let\'s make sure that the entry is always set to the requested location\n         //  On Cib/Cadrg we were having problems.  Removed the compare for entry 0\n         //\n\n         if (setChainEntry(ic, entryIndex) == false)\n         {\n            std::ostringstream errMsg;\n            errMsg << MODULE << " ERROR:\\nEntry " << entryIndex << " out of range!"\n                   << std::endl;\n            throw ossimException(errMsg.str());\n         }\n\n         //---\n         // If PSM (pan sharpening) operation and this input is one band, don\'t\n         // mess with its bands.\n         //---\n         bool psmPanInput = false;\n         if ((m_operation == OSSIM_CHIPPER_OP_PSM) && (ic->getNumberOfOutputBands() == 1))\n         {\n            psmPanInput = true;\n         }\n\n         // Bands selection.  Note: Not performed on PSM pan band.\n         if (!psmPanInput)\n         {\n            if (isThreeBandOut())\n            {\n               //---\n               // This will guarantee three bands out.  Will put band\n               // selector at the end of the chain if input is one band. If input image\n               // handler has implemented a getRgbBandlist(...) it will also set the\n               // rgb band order.\n               //---\n               ic->setThreeBandFlag(true);\n            }\n\n            if (hasBandSelection())\n            {\n               // User entered band list.\n               std::vector<ossim_uint32> bandList(0);\n               getBandList(bandList);\n               if (bandList.size())\n               {\n                  ic->setBandSelection(bandList);\n               }\n               else\n               {\n                  ic->setDefaultBandSelection();\n               }\n            }\n         }\n\n         ossimString nullPixelFlip = m_kwl->find(NULL_PIXEL_FLIP_KW.c_str());\n         if (nullPixelFlip.toBool())\n         {\n            ic->setAddNullPixelFlipFlag(true);\n         }\n         //---\n         // If multiple inputs and scaleToEightBit do it at the end of the processing\n         // chain to alleviate un-even stretches between inputs.\n         //\n         // GP 2019:  Going to move this to the end of the chain.  Should always be full bit depth until the end\n         //---\n         // const ossim_uint32 INPUT_COUNT = getNumberOfInputs();\n         // bool scaleFlag = (scaleToEightBit() && (INPUT_COUNT == 1));\n         //ic->setRemapToEightBitFlag(scaleFlag);\n\n         // Always have resampler cache.\n         ic->setAddResamplerCacheFlag(true);\n\n         //---\n         // Don\'t need a chain cache as we\'re doing a sequential write.  So the same tile\n         // should never be visited more than once.\n         //---\n         ic->setAddChainCacheFlag(false);\n\n         //---\n         // Histogram:\n         // Don\'t apply histogram stretch to dem sources for hill shade\n         // operation.\n         //---\n         if ((isDemSource == false) ||\n             (isDemSource && (m_operation != OSSIM_CHIPPER_OP_HILL_SHADE)))\n         {\n            ic->setAddHistogramFlag(hasHistogramOperation());\n         }\n\n         if(hasGammaCorrection())\n         {\n            ic->setAddGammaFlag(true);\n         }\n         // Brightness, contrast. Note in same filter.\n         if (hasBrightnesContrastOperation())\n         {\n            ic->setBrightnessContrastFlag(true);\n         }\n\n         std::string sharpnessMode = getSharpenMode();\n         std::string sharpenPercent = getSharpenPercent();\n         if (sharpnessMode.size()|| sharpenPercent.size())\n         {\n            ic->setSharpenFlag(true);\n         }\n\n         // Create the chain.\n         ic->createRenderedChain();\n\n         if(hasGammaCorrection())\n         {\n            ic->getGammaRemapper()->setGamma(getGamma());\n         }\n         // Set the filter type if needed.\n         ossimString lookup = m_kwl->findKey(RESAMPLER_FILTER_KW);\n         if (lookup.size())\n         {\n            // Assumption image renderer is in chain:\n            ic->getImageRenderer()->getResampler()->setFilterType(lookup);\n         }\n\n         // Histogram setup.\n         if (hasHistogramOperation())\n         {\n            setupChainHistogram(ic);\n         }\n\n         // Brightness contrast setup:\n         if (hasBrightnesContrastOperation())\n         {\n            // Assumption bright contrast filter in chain:\n\n            ossim_float64 value = getBrightness();\n            ic->getBrightnessContrast()->setBrightness(value);\n\n            value = getContrast();\n            ic->getBrightnessContrast()->setContrast(value);\n         }\n\n         // Sharpness:\n         if (sharpnessMode.size())\n         {\n            if (sharpnessMode == "light")\n            {\n               ic->getSharpenFilter()->setSharpenPercent(0.2);\n               // ic->getSharpenFilter()->setWidthAndSigma(3, 0.5);\n            }\n            else if (sharpnessMode == "medium")\n            {\n               ic->getSharpenFilter()->setSharpenPercent(0.5);\n            }\n            else if (sharpnessMode == "heavy")\n            {\n               ic->getSharpenFilter()->setSharpenPercent(0.8);\n            }\n         }\n         else if(sharpenPercent.size())\n         {\n             ic->getSharpenFilter()->setSharpenPercent(ossimString(sharpenPercent).toDouble());\n         }\n\n         if (hasGeoPolyCutterOption())\n         {\n            ossimGeoPolygon polygon;\n            getClipPolygon(polygon);\n            if (polygon.size() > 0)\n            {\n               ic->addGeoPolyCutterPolygon(polygon);\n            }\n         }\n      }\n      else\n      {\n         ic = 0;\n      }\n   }\n\n   if (!ic.valid())\n   {\n      std::string errMsg = "Could not open: ";\n      errMsg += file.string();\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimKeywordlist kwl;\n      ic->saveState(kwl, 0);\n\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "chain:\\n"\n         << kwl << "\\n"\n         << MODULE << " exiting...\\n";\n   }\n\n   return ic;\n}\n\nossimRefPtr<ossimSingleImageChain> ossimChipperUtil::createChain(const ossimSrcRecord &rec,\n                                                                 bool isDemSource) const\n{\n   static const char MODULE[] = "ossimChipperUtil::createChain(const ossimSrcRecord&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimSingleImageChain> ic = new ossimSingleImageChain;\n   if (ic->open(rec))\n   {\n      // Set any reader props:\n      setReaderProps(ic->getImageHandler().get());\n\n      // Entry is set in ossimSingleImageChain::open(src)\n\n      //---\n      // If PSM (pan sharpening) operation and this input is one band, don\'t\n      // mess with its bands.\n      //---\n      bool psmPanInput = false;\n      if ((m_operation == OSSIM_CHIPPER_OP_PSM) && (ic->getNumberOfOutputBands() == 1))\n      {\n         psmPanInput = true;\n      }\n\n      // Bands selection.  Note: Not performed on PSM pan band.\n      if (!psmPanInput)\n      {\n         if (isThreeBandOut())\n         {\n            //---\n            // This will guarantee three bands out.  Will put band\n            // selector at the end of the chain if input is one band. If input image\n            // handler has implemented a getRgbBandlist(...) it will also set the\n            // rgb band order.\n            //---\n            ic->setThreeBandFlag(true);\n         }\n\n         if (hasBandSelection())\n         {\n            // User entered band list.\n            std::vector<ossim_uint32> bandList(0);\n            getBandList(bandList);\n            if (bandList.size())\n            {\n               ic->setBandSelection(bandList);\n            }\n            else\n            {\n               ic->setDefaultBandSelection();\n            }\n         }\n      }\n      ossimString nullPixelFlip = m_kwl->find(NULL_PIXEL_FLIP_KW.c_str());\n      if (nullPixelFlip.toBool())\n      {\n         ic->setAddNullPixelFlipFlag(true);\n      }\n\n      //---\n      // If multiple inputs and scaleToEightBit do it at the end of the processing\n      // chain to alleviate un-even stretches between inputs.\n      //\n      // GP 2019:  Going to move this to the end of the chain.  Should always be full bit depth until the end\n      //---\n      // const ossim_uint32 INPUT_COUNT = getNumberOfInputs();\n      // bool scaleFlag = (scaleToEightBit() && (INPUT_COUNT == 1));\n      //ic->setRemapToEightBitFlag(scaleFlag);\n\n      // Always have resampler cache.\n      ic->setAddResamplerCacheFlag(true);\n\n      //---\n      // Don\'t need a chain cache as we\'re doing a sequential write.  So the same tile\n      // should never be visited more than once.\n      //---\n      ic->setAddChainCacheFlag(false);\n\n      //---\n      // Histogram:\n      // Don\'t apply histogram stretch to dem sources for hill shade\n      // operation.\n      //---\n      if ((isDemSource == false) ||\n          (isDemSource && (m_operation != OSSIM_CHIPPER_OP_HILL_SHADE)))\n      {\n         ic->setAddHistogramFlag(hasHistogramOperation());\n      }\n\n      if(hasGammaCorrection())\n      {\n         ic->setAddGammaFlag(true);\n      }\n      // Brightness, contrast. Note in same filter.\n      if (hasBrightnesContrastOperation())\n      {\n         ic->setBrightnessContrastFlag(true);\n      }\n\n      std::string sharpnessMode = getSharpenMode();\n      std::string sharpenPercent = getSharpenPercent();\n      if (sharpnessMode.size() || sharpenPercent.size())\n      {\n         ic->setSharpenFlag(true);\n      }\n\n      //---\n      // Create the chain.\n      //\n      // NOTE: Histogram and band selector can be set in ic->createRenderedChain(rec)\n      // if the right keywords are there.\n      //---\n      ic->createRenderedChain(rec);\n\n      // Set the filter type if needed.\n      ossimString lookup = m_kwl->findKey(RESAMPLER_FILTER_KW);\n      if (lookup.size())\n      {\n         // Assumption image renderer is in chain:\n         ic->getImageRenderer()->getResampler()->setFilterType(lookup);\n      }\n\n      // Histogram setup.\n      if (hasHistogramOperation())\n      {\n         setupChainHistogram(ic, std::make_shared<ossimSrcRecord>(rec));\n      }\n\n      if(hasGammaCorrection())\n      {\n         ic->getGammaRemapper()->setGamma(getGamma());\n      }\n      // Brightness contrast setup:\n      if (hasBrightnesContrastOperation())\n      {\n         // Assumption bright contrast filter in chain:\n\n         ossim_float64 value = getBrightness();\n         ic->getBrightnessContrast()->setBrightness(value);\n\n         value = getContrast();\n         ic->getBrightnessContrast()->setContrast(value);\n      }\n\n      // Sharpness:\n      if (sharpnessMode.size())\n      {\n         if (sharpnessMode == "light")\n         {\n            ic->getSharpenFilter()->setSharpenPercent(0.2);\n         }\n         else if(sharpnessMode == "medium")\n         {\n            ic->getSharpenFilter()->setSharpenPercent(0.5);\n         }\n         else if (sharpnessMode == "heavy")\n         {\n            ic->getSharpenFilter()->setSharpenPercent(0.8);\n         }\n      }\n      else if(sharpenPercent.size())\n      {\n         ic->getSharpenFilter()->setSharpenPercent(ossimString(sharpenPercent).toDouble());\n      }\n\n      if (hasGeoPolyCutterOption())\n      {\n         ossimGeoPolygon polygon;\n         getClipPolygon(polygon);\n         if (polygon.size() > 0)\n         {\n            ic->addGeoPolyCutterPolygon(polygon);\n         }\n      }\n   }\n   else // Open failed.\n   {\n      std::string errMsg = "Could not open from src record!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimKeywordlist kwl;\n      ic->saveState(kwl, 0);\n\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "chain:\\n"\n         << kwl << "\\n"\n         << MODULE << " exiting...\\n";\n   }\n\n   return ic;\n}\n\nvoid ossimChipperUtil::createOutputProjection()\n{\n   static const char MODULE[] = "ossimChipperUtil::createOutputProjection";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   std::string op = m_kwl->findKey(std::string(ossimKeywordNames::PROJECTION_KW));\n   std::string srs = m_kwl->findKey(SRS_KW);\n\n   if (op.size() && srs.size())\n   {\n      ossimNotify(ossimNotifyLevel_WARN)\n         << MODULE << " WARNING:"\n         << "\\nBoth " << SRS_KW << " and " << ossimKeywordNames::PROJECTION_KW\n         << " keywords are set!"\n         << "\\nsrs:               " << srs\n         << "\\noutput_projection: " << op\n         << "\\nTaking " << srs << " over " << op << "\\n";\n   }\n\n   bool usingInput = false;\n   ossimChipperOutputProjection projType = getOutputProjectionType();\n   ossimRefPtr<ossimMapProjection> proj = 0;\n\n   // If an srs code use that first.\n   if (srs.size())\n   {\n      proj = getNewProjectionFromSrsCode(srs);\n   }\n   else if (op.size())\n   {\n      switch (projType)\n      {\n         case ossimChipperUtil::OSSIM_CHIPPER_PROJ_GEO:\n         {\n            proj = getNewGeoProjection();\n            break;\n         }\n         case ossimChipperUtil::OSSIM_CHIPPER_PROJ_GEO_SCALED:\n         {\n            proj = getNewGeoScaledProjection();\n            break;\n         }\n         case ossimChipperUtil::OSSIM_CHIPPER_PROJ_INPUT:\n         {\n            proj = getFirstInputProjection();\n            usingInput = true;\n            break;\n         }\n         case ossimChipperUtil::OSSIM_CHIPPER_PROJ_UTM:\n         {\n            proj = getNewUtmProjection();\n            break;\n         }\n         default:\n         {\n            break; // Just for un-handled type warning.\n         }\n      }\n   }\n\n\n   // Check for identity projection:\n   ossimRefPtr<ossimMapProjection> inputProj = getFirstInputProjection();\n   if (proj.valid() && inputProj.valid())\n   {\n      if (!usingInput && (*(inputProj.get()) == *(proj.get())))\n      {\n         if (projType == OSSIM_CHIPPER_PROJ_GEO_SCALED)\n         {\n            // Get the origin used for scaling.\n            ossimGpt origin = proj->getOrigin();\n\n            // Copy the input projection to our projection.  Has the tie and scale we need.\n            proj = inputProj;\n\n            // Set the origin for scaling.\n            proj->setOrigin(origin);\n         }\n         else\n         {\n            proj = inputProj;\n         }\n         usingInput = true;\n      }\n   }\n   if (!proj.valid())\n   {\n      // Try first input. If map projected use that.\n      if (inputProj.valid())\n      {\n         proj = inputProj;\n         usingInput = true;\n         if (traceDebug())\n         {\n            ossimNotify(ossimNotifyLevel_WARN)\n               << "WARNING: No projection set!"\n               << "\\nDefaulting to first input\'s projection.\\n";\n         }\n      }\n      else\n      {\n         proj = getNewGeoScaledProjection();\n         if (traceDebug())\n         {\n            ossimNotify(ossimNotifyLevel_WARN)\n               << "WARNING: No projection set!"\n               << "\\nDefaulting to scaled geographic at scene center.\\n";\n         }\n      }\n   }\n\n   // Create our ossimImageGeometry with projection (no transform).\n   m_geom = new ossimImageGeometry(0, proj.get());\n\n   //---\n   // If the input is the same as output projection do not modify; else, set\n   // the gsd to user selected "METERS_KW" or the best resolution of the inputs,\n   // set the tie and then snap it to the projection origin.\n   //---\n   if (!usingInput || hasScaleOption())\n   {\n      // Set the scale.\n      initializeProjectionGsd();\n   }\n\n   // Set the tie.\n   initializeProjectionTiePoint();\n\n   if (snapTieToOrigin())\n   {\n      // Adjust the projection tie to the origin.\n      proj->snapTiePointToOrigin();\n   }\n\n   // May need to rotate the output map if requested:\n   bool rotateMap=false;\n   m_kwl->getBoolKeywordValue(rotateMap, ROTATE_TO_INPUT.c_str());\n   if (rotateMap)\n   {\n      rotateMapToInput();\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "using input projection: " << (usingInput ? "true" : "false")\n         << "\\noutput image geometry:\\n";\n\n      m_geom->print(ossimNotify(ossimNotifyLevel_DEBUG));\n\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n} // End: ossimChipperUtil::createOutputProjection()\n\nvoid ossimChipperUtil::rotateMapToInput()\n{\n   static const char MODULE[] = "ossimChipperUtil::rotateMapToInput";\n   if (traceDebug())\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n\n   try\n   {\n      if (!m_geom)\n         throw ossimException("Null projection geometry encountered.");\n      ossimRefPtr<ossimMapProjection> mapProj =\n            dynamic_cast<ossimMapProjection*>(m_geom->getProjection());\n      if (!mapProj)\n         throw ossimException("Output projection must be a map projection.");\n      if (m_imgLayer.size() != 1)\n         throw ossimException("Optimal rotation output requested but this feature is not available for mosaics.");\n      ossimRefPtr<ossimImageHandler> ih = m_imgLayer[0]->getImageHandler();\n      if (!ih)\n         throw ossimException("Null image handler encountered.");\n      ossimRefPtr<ossimImageGeometry> inputGeom = ih->getImageGeometry();\n      if (!inputGeom)\n         throw ossimException("Null image geometry encountered.");\n\n      // Determine azimuth of +X axis:\n      ossimIpt inputImgSize (inputGeom->getImageSize());\n      if (inputImgSize.x == 0)\n         throw ossimException("Image size is zero.");\n\n      ossimDpt pUL (0,0);\n      ossimDpt pUR (inputImgSize.x-1, 0);\n      ossimDpt pLR (inputImgSize.x-1, inputImgSize.y-1);\n      ossimDpt pLL (0, inputImgSize.y-1);\n      ossimGpt gUL, gUR, gLR, gLL;\n      inputGeom->localToWorld(pUL, gUL);\n      inputGeom->localToWorld(pUR, gUR);\n      inputGeom->localToWorld(pLR, gLR);\n      inputGeom->localToWorld(pLL, gLL);\n\n      // The -y axis relative to North is the number we need to rotate, so subtract 90 deg from\n      // x-axis orientation.\n      double mapRotation = gLL.azimuthTo(gUL);\n      cout << "\\n Rotating map by "<<mapRotation<<" deg\\n"<<endl; //###\n      mapProj->applyRotation(mapRotation);\n\n      // Not sure where the UL of the rotated map will be, so assume it will coincide with the image\n      // UL for now, then scan for corners outside:\n      mapProj->setUlGpt(gUL);\n      mapProj->worldToLineSample(gUR, pUR);\n      mapProj->worldToLineSample(gLR, pLR);\n      mapProj->worldToLineSample(gLL, pLL);\n      ossimDrect drect (pUL, pUR, pLR, pLL);\n\n      // A new output image rect has been established, find the corresponding UL ground point\n      // and set projection\'s tiepoint:\n      mapProj->lineSampleToWorld(drect.ul(), gUL);\n      mapProj->setUlGpt(gUL);\n   }\n   catch (ossimException& e)\n   {\n      ossimNotify(ossimNotifyLevel_WARN) << "ossimChipperUtil::rotateMapToInput() -- Exception: "\n                                         << e.what() <<" Ignoring request.\\n";\n   }\n   return;\n}\n\nvoid ossimChipperUtil::createIdentityProjection()\n{\n   static const char MODULE[] = "ossimChipperUtil::createIdentityProjection";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   // Get the singe image chain.  Sould be only one.\n   ossimRefPtr<ossimSingleImageChain> sic = 0;\n   if (m_demLayer.size())\n   {\n      sic = m_demLayer[0];\n   }\n   else if (m_imgLayer.size())\n   {\n      sic = m_imgLayer[0];\n   }\n\n   if (sic.valid())\n   {\n      // Get the image handler.\n      ossimRefPtr<ossimImageHandler> ih = sic->getImageHandler();\n\n      // Resampler:\n      ossimRefPtr<ossimImageRenderer> resampler = sic->getImageRenderer();\n\n      if (ih.valid())\n      {\n         //---\n         // Get the geometry from the image handler.  Since we\'re in "identity"\n         // mode use the inputs for the outputs.\n         //---\n         m_geom = ih->getImageGeometry();\n\n         if (m_geom.valid())\n         {\n            ossim_float64 rotation = 0.0;\n\n            if (upIsUp())\n            {\n               ossimRefPtr<ossimProjection> proj = m_geom->getProjection();\n               if (proj.valid())\n               {\n                  rotation = m_geom->upIsUpAngle();\n               }\n               else\n               {\n                  ossimNotify(ossimNotifyLevel_WARN)\n                     << "WARNING: No projection to compute the up is up angle!" << std::endl;\n               }\n            }\n            else if (northUp())\n            {\n               ossimRefPtr<ossimProjection> proj = m_geom->getProjection();\n               if (proj.valid())\n               {\n                  rotation = m_geom->northUpAngle();\n               }\n               else\n               {\n                  ossimNotify(ossimNotifyLevel_WARN)\n                     << "WARNING: No projection to compute the North up angle!" << std::endl;\n               }\n            }\n            else if (hasRotation())\n            {\n               rotation = getRotation();\n            }\n\n            if (ossim::isnan(rotation))\n            {\n               rotation = 0.0;\n            }\n\n            ossimDpt imageSpaceScale;\n            getImageSpaceScale(imageSpaceScale);\n\n            //ossimDrect rect;\n            //m_geom->getBoundingRect(rect);\n            ossimDpt midPt; // = rect.midPoint();\n\n            getImageSpacePivot(midPt);\n\n            if (traceDebug())\n            {\n               ossimNotify(ossimNotifyLevel_DEBUG)\n                  << MODULE\n                  << "\\nAffine transform parameters:"\n                  << "\\nrotation:  " << rotation\n                  << "\\nmid point: " << midPt << std::endl;\n            }\n\n            m_ivt = new ossimImageViewAffineTransform(-rotation,\n                                                      imageSpaceScale.x, // image space scale x\n                                                      imageSpaceScale.y, // image space scale y\n                                                      1.0, 1.0,\t\t\t //scale x and y\n                                                      0.0, 0.0,\t\t\t // translate x,y\n                                                      midPt.x * imageSpaceScale.x,\n                                                      midPt.y * imageSpaceScale.y); // pivot point\n\n            if (m_kwl->hasKey(METERS_KW) ||\n                m_kwl->hasKey(DEGREES_X_KW) ||\n                m_kwl->hasKey(RRDS_KW))\n            {\n               // Set the image view transform scale.\n               initializeIvtScale();\n            }\n\n            resampler->setImageViewTransform(m_ivt.get());\n\n         } // Matches: if ( m_geom.valid() )\n\n      } // Matches: if ( ih.valid() )\n\n   } // Matches: if ( sic.valid()\n\n} // End: createIdentityProjection()\n\nvoid ossimChipperUtil::initializeIvtScale()\n{\n   if (isChipMode() && m_ivt.valid() && m_geom.valid())\n   {\n      ossimDpt scale;\n      scale.makeNan();\n\n      // Check for GSD spec. Degrees/pixel takes priority over meters/pixel:\n      ossimString lookup;\n      lookup.string() = m_kwl->findKey(DEGREES_X_KW);\n      if (lookup.size())\n      {\n         ossimDpt outputDpp;\n         outputDpp.makeNan();\n\n         outputDpp.x = lookup.toFloat64();\n\n         lookup.string() = m_kwl->findKey(DEGREES_Y_KW);\n         if (lookup.size())\n         {\n            outputDpp.y = lookup.toFloat64();\n         }\n\n         if (!outputDpp.hasNans())\n         {\n            // Input degress per pixel.  Consider this a scale of 1.0.\n            ossimDpt inputDpp;\n            m_geom->getDegreesPerPixel(inputDpp);\n\n            if (!inputDpp.hasNans())\n            {\n               scale.x = inputDpp.x / outputDpp.x;\n               scale.y = inputDpp.y / outputDpp.y;\n            }\n         }\n      }\n\n      if (scale.hasNans())\n      {\n         lookup = m_kwl->findKey(METERS_KW);\n         if (lookup.size())\n         {\n            ossimDpt outputMpp;\n            outputMpp.makeNan();\n            outputMpp.x = lookup.toFloat64();\n            outputMpp.y = outputMpp.x;\n\n            if (!outputMpp.hasNans())\n            {\n               // Input meters per pixel.  Consider this a scale of 1.0.\n               ossimDpt inputMpp;\n               m_geom->getMetersPerPixel(inputMpp);\n\n               if (!inputMpp.hasNans())\n               {\n                  scale.x = inputMpp.x / outputMpp.x;\n                  scale.y = inputMpp.y / outputMpp.y;\n               }\n            }\n         }\n      }\n\n      if (scale.hasNans())\n      {\n         lookup = m_kwl->findKey(RRDS_KW);\n         if (lookup.size())\n         {\n            ossim_float64 d = lookup.toInt32();\n            if (d == 0.0)\n            {\n               scale.x = 1.0;\n            }\n            else\n            {\n               scale.x = 1.0 / std::pow(2.0, d);\n            }\n            scale.y = scale.x;\n         }\n      }\n\n      if (!scale.hasNans())\n      {\n         m_ivt->scale(scale.x, scale.y);\n      }\n      else\n      {\n         std::string errMsg = "ossimChipperUtil::initializeIvtScale failed!";\n         throw ossimException(errMsg);\n      }\n\n   } // Matches: if ( isChipMode() && ... )\n\n} // End: ossimChipperUtil::initializeIvtScale()\n\nvoid ossimChipperUtil::initializeProjectionTiePoint ()\n{\n   static const char MODULE[] = "ossimChipperUtil::initializeProjectionTiePoint()";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   // Get the map projection from the output geometry:\n   ossimRefPtr<ossimMapProjection> mapProj = getMapProjection();\n\n   if (mapProj.valid())\n   {\n      //---\n      // If the output is geographic of there are sensor model inputs, get the tie\n      // using the ground point.\n      //---\n      if (mapProj->isGeographic() || hasSensorModelInput())\n      {\n         ossimGpt tiePoint;\n         tiePoint.makeNan();\n         getTiePoint(tiePoint);\n\n         if (!tiePoint.hasNans())\n         {\n            //---\n            // The tie point coordinates currently reflect the UL edge of the UL pixel.\n            // We\'ll need to shift the tie point bac from the edge to the center base on the\n            // output gsd.\n            //---\n            ossimDpt half_pixel_shift = m_geom->getDegreesPerPixel() * 0.5;\n            tiePoint.lat -= half_pixel_shift.lat;\n            tiePoint.lon += half_pixel_shift.lon;\n            mapProj->setUlTiePoints(tiePoint);\n         }\n         else\n         {\n            std::string errMsg = MODULE;\n            errMsg += " tie point has nans!";\n            throw(ossimException(errMsg));\n         }\n\n         if (traceDebug())\n         {\n            ossimNotify(ossimNotifyLevel_DEBUG)\n               << "projection tie point: " << tiePoint << "\\n"\n               << MODULE << " exited...\\n";\n         }\n      }\n      else\n      {\n         //---\n         // TODO: Add test for like input projections and use above geographic tie\n         // code if not.\n         //---\n         ossimDpt tiePoint;\n         tiePoint.makeNan();\n         getTiePoint(tiePoint);\n\n         if (!tiePoint.hasNans())\n         {\n            //---\n            // The tie point coordinates currently reflect the UL edge of the UL pixel.\n            // We\'ll need to shift the tie point bac from the edge to the center base on the\n            // output gsd.\n            //---\n            ossimDpt half_pixel_shift = m_geom->getMetersPerPixel() * 0.5;\n            tiePoint.y -= half_pixel_shift.y;\n            tiePoint.x += half_pixel_shift.x;\n            mapProj->setUlTiePoints(tiePoint);\n         }\n         else\n         {\n            std::string errMsg = MODULE;\n            errMsg += " tie point has nans!";\n            throw(ossimException(errMsg));\n         }\n\n         if (traceDebug())\n         {\n            ossimNotify(ossimNotifyLevel_DEBUG)\n               << "projection tie point: " << tiePoint << "\\n"\n               << MODULE << " exited...\\n";\n         }\n      }\n\n   } // Matches: if ( mapProj.valid() )\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += "m_projection is null!";\n      throw(ossimException(errMsg));\n   }\n}\n\nvoid ossimChipperUtil::initializeProjectionGsd()\n{\n   static const char MODULE[] = "ossimChipperUtil::initializeProjectionGsd()";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n   ossimRefPtr<ossimMapProjection> mapProj = getMapProjection();\n   if (!mapProj.valid())\n   {\n      std::string errMsg = MODULE;\n      errMsg += " projection is null!";\n      throw(ossimException(errMsg));\n   }\n\n   ossimDpt gsd;\n   gsd.makeNan();\n\n   ossimString degreesX;\n   ossimString degreesY;\n   ossimString meters;\n   degreesX.string() = m_kwl->findKey(DEGREES_X_KW);\n   degreesY.string() = m_kwl->findKey(DEGREES_Y_KW);\n   meters.string() = m_kwl->findKey(METERS_KW);\n\n   if (hasCutBoxWidthHeight())\n   {\n      // --cut-bbox-llwh Implies a scale...\n      if (degreesX.size() || degreesY.size() || meters.size())\n      {\n         std::ostringstream errMsg;\n         errMsg << MODULE << " ERROR: Ambiguous scale keywords!\\n"\n                << "Do not combine meters or degrees with cut box with a width and height.\\n";\n         throw(ossimException(errMsg.str()));\n      }\n\n      ossimString cutMinLat;\n      ossimString cutMinLon;\n      ossimString cutMaxLat;\n      ossimString cutMaxLon;\n      ossimString cutWidth;\n      ossimString cutHeight;\n      cutMinLat.string() = m_kwl->findKey(CUT_MIN_LAT_KW);\n      cutMinLon.string() = m_kwl->findKey(CUT_MIN_LON_KW);\n      cutMaxLat.string() = m_kwl->findKey(CUT_MAX_LAT_KW);\n      cutMaxLon.string() = m_kwl->findKey(CUT_MAX_LON_KW);\n      cutWidth.string() = m_kwl->findKey(CUT_WIDTH_KW);\n      cutHeight.string() = m_kwl->findKey(CUT_HEIGHT_KW);\n      if (cutMinLat.size() && cutMinLon.size() && cutMaxLat.size() &&\n          cutMaxLon.size() && cutWidth.size() && cutHeight.size())\n      {\n         ossim_float64 minLat = cutMinLat.toFloat64();\n         ossim_float64 minLon = cutMinLon.toFloat64();\n         ossim_float64 maxLat = cutMaxLat.toFloat64();\n         ossim_float64 maxLon = cutMaxLon.toFloat64();\n         ossim_float64 width = cutWidth.toFloat64();\n         ossim_float64 height = cutHeight.toFloat64();\n         if (!ossim::isnan(minLat) && !ossim::isnan(minLon) && !ossim::isnan(maxLat) &&\n             !ossim::isnan(maxLon) && !ossim::isnan(width) && !ossim::isnan(height))\n         {\n            gsd.x = std::fabs(maxLon - minLon) / width;\n            gsd.y = std::fabs(maxLat - minLat) / height;\n\n            mapProj->setDecimalDegreesPerPixel(gsd);\n         }\n      }\n   }\n   else if (hasWmsBboxCutWidthHeight())\n   {\n      ossimString cutWidth;\n      ossimString cutHeight;\n      ossimString cutWmsBbox;\n\n      cutWidth.string() = m_kwl->findKey(CUT_WIDTH_KW);\n      cutHeight.string() = m_kwl->findKey(CUT_HEIGHT_KW);\n      cutWmsBbox.string() = m_kwl->findKey(CUT_WMS_BBOX_KW);\n\n      cutWmsBbox = cutWmsBbox.upcase().replaceAllThatMatch("BBOX:", "");\n      std::vector<ossimString> cutBox = cutWmsBbox.split(",");\n      if (cutBox.size() == 4)\n      {\n         ossim_float64 minx = cutBox[0].toFloat64();\n         ossim_float64 miny = cutBox[1].toFloat64();\n         ossim_float64 maxx = cutBox[2].toFloat64();\n         ossim_float64 maxy = cutBox[3].toFloat64();\n         ossim_float64 width = cutWidth.toFloat64();\n         ossim_float64 height = cutHeight.toFloat64();\n         gsd.x = std::fabs(maxx - minx) / width;\n         gsd.y = std::fabs(maxy - miny) / height;\n\n         // bbox is in the units of the projector\n         if (mapProj->isGeographic())\n         {\n\n            mapProj->setDecimalDegreesPerPixel(gsd);\n         }\n         else\n         {\n            mapProj->setMetersPerPixel(gsd);\n         }\n      }\n      else\n      {\n         std::ostringstream errMsg;\n         errMsg << MODULE << " ERROR: cut box does not have 4 values!\\n";\n         throw(ossimException(errMsg.str()));\n      }\n   }\n   else\n   {\n      if (meters.size() && (degreesX.size() || degreesY.size()))\n      {\n         std::ostringstream errMsg;\n         errMsg << MODULE << " ERROR: Ambiguous scale keywords!\\n"\n                << "Do not combine meters with degrees.\\n";\n         throw(ossimException(errMsg.str()));\n      }\n\n      if (degreesX.size())\n      {\n         // --degrees\n         gsd.x = degreesX.toFloat64();\n\n         if (degreesY.size())\n         {\n            gsd.y = degreesY.toFloat64();\n         }\n         if (!gsd.hasNans())\n         {\n            mapProj->setDecimalDegreesPerPixel(gsd);\n         }\n      }\n      else if (meters.size())\n      {\n         // --meters\n         gsd.x = meters.toFloat64();\n         gsd.y = gsd.x;\n         if (!gsd.hasNans())\n         {\n            mapProj->setMetersPerPixel(gsd);\n         }\n      }\n   }\n\n   if (gsd.hasNans())\n   {\n      // Get the best resolution from the inputs.\n      getMetersPerPixel(gsd);\n\n      // See if the output projection is geo-scaled; if so, make the pixels square in meters.\n      if (getOutputProjectionType() == ossimChipperUtil::OSSIM_CHIPPER_PROJ_GEO_SCALED)\n      {\n         // Pick the best resolution and make them both the same.\n         gsd.x = ossim::min<ossim_float64>(gsd.x, gsd.y);\n         gsd.y = gsd.x;\n      }\n\n      // Set to input gsd.\n      mapProj->setMetersPerPixel(gsd);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "projection gsd: " << gsd << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::getTiePoint(ossimGpt &tie)\n{\n   static const char MODULE[] = "ossimChipperUtil::getTiePoint(ossimGpt&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx;\n\n   tie.lat = ossim::nan();\n   tie.lon = ossim::nan();\n   tie.hgt = 0.0;\n\n   // Loop through dem layers.\n   ossimGpt chainTiePoint;\n   chainIdx = m_demLayer.begin();\n   while (chainIdx != m_demLayer.end())\n   {\n      getTiePoint((*chainIdx).get(), chainTiePoint);\n      if (tie.hasNans())\n      {\n         tie = chainTiePoint;\n      }\n      else\n      {\n         if (chainTiePoint.lat > tie.lat)\n         {\n            tie.lat = chainTiePoint.lat;\n         }\n         if (chainTiePoint.lon < tie.lon)\n         {\n            tie.lon = chainTiePoint.lon;\n         }\n      }\n      ++chainIdx;\n   }\n\n   // Loop through image layers.\n   chainIdx = m_imgLayer.begin();\n   while (chainIdx != m_imgLayer.end())\n   {\n      getTiePoint((*chainIdx).get(), chainTiePoint);\n      if (tie.hasNans())\n      {\n         tie = chainTiePoint;\n      }\n      else\n      {\n         if (chainTiePoint.lat > tie.lat)\n         {\n            tie.lat = chainTiePoint.lat;\n         }\n         if (chainTiePoint.lon < tie.lon)\n         {\n            tie.lon = chainTiePoint.lon;\n         }\n      }\n      ++chainIdx;\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "tie point: " << tie << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::getTiePoint(ossimSingleImageChain *chain, ossimGpt &tie)\n{\n   static const char MODULE[] = "ossimChipperUtil::getTiePoint(ossimSingleImageChain*,ossimGpt&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   if (chain && m_geom.valid())\n   {\n      //---\n      // The view is not set yet in the chain so we get the tie point from the\n      // image handler geometry not from the chain which will come from the\n      // ossimImageRenderer.\n      //---\n      ossimRefPtr<ossimImageHandler> ih = chain->getImageHandler();\n      if (ih.valid())\n      {\n         ossimRefPtr<ossimImageGeometry> geom = ih->getImageGeometry();\n         if (geom.valid())\n         {\n            geom->getTiePoint(tie, true);\n         }\n\n         // Set height to 0.0 even though it\'s not used so hasNans test works.\n         tie.hgt = 0.0;\n\n         if (tie.hasNans())\n         {\n            std::string errMsg = MODULE;\n            errMsg += "\\ngeom->localToWorld returned nan for chain.";\n            errMsg += "\\nChain: ";\n            errMsg += chain->getFilename().string();\n            throw ossimException(errMsg);\n         }\n      }\n      else\n      {\n         std::string errMsg = MODULE;\n         errMsg += "\\nNo geometry for chain: ";\n         errMsg += chain->getFilename().string();\n         throw ossimException(errMsg);\n      }\n   }\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR: Null chain passed to method!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "chain name: " << chain->getFilename()\n         << "\\ntie point:  " << tie << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::getTiePoint(ossimDpt &tie)\n{\n   static const char MODULE[] = "ossimChipperUtil::getTiePoint(ossimDpt&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx;\n\n   tie.makeNan();\n\n   // Loop through dem layers.\n   ossimDpt chainTiePoint;\n   chainIdx = m_demLayer.begin();\n   while (chainIdx != m_demLayer.end())\n   {\n      getTiePoint((*chainIdx).get(), chainTiePoint);\n      if (tie.hasNans())\n      {\n         tie = chainTiePoint;\n      }\n      else\n      {\n         if (chainTiePoint.y > tie.y)\n         {\n            tie.y = chainTiePoint.y;\n         }\n         if (chainTiePoint.x < tie.x)\n         {\n            tie.x = chainTiePoint.x;\n         }\n      }\n      ++chainIdx;\n   }\n\n   // Loop through image layers.\n   chainIdx = m_imgLayer.begin();\n   while (chainIdx != m_imgLayer.end())\n   {\n      getTiePoint((*chainIdx).get(), chainTiePoint);\n      if (tie.hasNans())\n      {\n         tie = chainTiePoint;\n      }\n      else\n      {\n         if (chainTiePoint.y > tie.y)\n         {\n            tie.y = chainTiePoint.y;\n         }\n         if (chainTiePoint.x < tie.x)\n         {\n            tie.x = chainTiePoint.x;\n         }\n      }\n      ++chainIdx;\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "tie point: " << tie << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::getTiePoint(ossimSingleImageChain *chain, ossimDpt &tie)\n{\n   static const char MODULE[] = "ossimChipperUtil::getTiePoint(ossimSingleImageChain*,ossimDpt&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   if (chain && m_geom.valid())\n   {\n      //---\n      // The view is not set yet in the chain so we get the tie point from the\n      // image handler geometry not from the chain which will come from the\n      // ossimImageRenderer.\n      //---\n      ossimRefPtr<ossimImageHandler> ih = chain->getImageHandler();\n      if (ih.valid())\n      {\n         ossimRefPtr<ossimImageGeometry> geom = ih->getImageGeometry();\n         if (geom.valid())\n         {\n            geom->getTiePoint(tie, true);\n         }\n\n         if (tie.hasNans())\n         {\n            std::string errMsg = MODULE;\n            errMsg += "\\ngeom->localToWorld returned nan for chain.";\n            errMsg += "\\nChain: ";\n            errMsg += chain->getFilename().string();\n            throw ossimException(errMsg);\n         }\n      }\n      else\n      {\n         std::string errMsg = MODULE;\n         errMsg += "\\nNo geometry for chain: ";\n         errMsg += chain->getFilename().string();\n         throw ossimException(errMsg);\n      }\n   }\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR: Null chain passed to method!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "chain name: " << chain->getFilename()\n         << "\\ntie point:  " << tie << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::getMetersPerPixel(ossimDpt &gsd)\n{\n   static const char MODULE[] = "ossimChipperUtil::getMetersPerPixel(ossimDpt&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   gsd.makeNan();\n\n   ossimDpt chainGsd;\n   std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx;\n\n   // Loop through dem layers.\n   chainIdx = m_demLayer.begin();\n   while (chainIdx != m_demLayer.end())\n   {\n      getMetersPerPixel((*chainIdx).get(), chainGsd);\n      if (gsd.hasNans() || (chainGsd.x < gsd.x))\n      {\n         gsd = chainGsd;\n      }\n      ++chainIdx;\n   }\n\n   // Loop through image layers.\n   chainIdx = m_imgLayer.begin();\n   while (chainIdx != m_imgLayer.end())\n   {\n      getMetersPerPixel((*chainIdx).get(), chainGsd);\n      if (gsd.hasNans() || (chainGsd.x < gsd.x))\n      {\n         gsd = chainGsd;\n      }\n      ++chainIdx;\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "gsd: " << gsd << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::getMetersPerPixel(ossimSingleImageChain *chain, ossimDpt &gsd)\n{\n   static const char MODULE[] = "ossimChipperUtil::getMetersPerPixel(ossimSingleImageChain*,ossimDpt&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   if (chain)\n   {\n      ossimRefPtr<ossimImageGeometry> geom = chain->getImageGeometry();\n      if (geom.valid())\n      {\n         geom->getMetersPerPixel(gsd);\n         if (gsd.hasNans())\n         {\n            std::string errMsg = MODULE;\n            errMsg += "\\ngeom->getMetersPerPixel returned nan for chain.";\n            errMsg += "\\nChain: ";\n            errMsg += chain->getFilename().string();\n            throw ossimException(errMsg);\n         }\n      }\n      else\n      {\n         std::string errMsg = MODULE;\n         errMsg += "\\nNo geometry for chain: ";\n         errMsg += chain->getFilename().string();\n         throw ossimException(errMsg);\n      }\n   }\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR: Null chain passed to method!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "chain name: " << chain->getFilename()\n         << "\\nmeters per pixel: " << gsd << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nossim_float64 ossimChipperUtil::getCentralMeridian() const\n{\n   ossim_float64 result = ossim::nan();\n   ossimString lookup = m_kwl->findKey(std::string(ossimKeywordNames::CENTRAL_MERIDIAN_KW));\n   if (lookup.size())\n   {\n      result = lookup.toFloat64();\n      if ((result < -180.0) || (result > 180.0))\n      {\n         std::string errMsg = "central meridian range error!";\n         errMsg += " Valid range: -180 to 180";\n         throw ossimException(errMsg);\n      }\n   }\n   return result;\n}\n\nossim_float64 ossimChipperUtil::getOriginLatitude() const\n{\n   ossim_float64 result = ossim::nan();\n   ossimString lookup = m_kwl->find(ossimKeywordNames::ORIGIN_LATITUDE_KW);\n   if (lookup.size())\n   {\n      result = lookup.toFloat64();\n      if ((result < -90) || (result > 90.0))\n      {\n         std::string errMsg = "origin latitude range error!";\n         errMsg += " Valid range: -90 to 90";\n         throw ossimException(errMsg);\n      }\n   }\n   return result;\n}\n\nvoid ossimChipperUtil::getSceneCenter(ossimGpt &gpt)\n{\n   static const char MODULE[] = "ossimChipperUtil::getSceneCenter(ossimGpt&)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   std::vector<ossimGpt> centerGptArray;\n   ossimGpt centerGpt;\n\n   std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx;\n\n   // Loop through dem layers.\n   chainIdx = m_demLayer.begin();\n   while (chainIdx != m_demLayer.end())\n   {\n      getSceneCenter((*chainIdx).get(), centerGpt);\n      if (!centerGpt.hasNans())\n      {\n         centerGptArray.push_back(centerGpt);\n      }\n      ++chainIdx;\n   }\n\n   // Loop through image layers.\n   chainIdx = m_imgLayer.begin();\n   while (chainIdx != m_imgLayer.end())\n   {\n      getSceneCenter((*chainIdx).get(), centerGpt);\n      if (!centerGpt.hasNans())\n      {\n         centerGptArray.push_back(centerGpt);\n      }\n      ++chainIdx;\n   }\n\n   ossim_float64 lat = 0.0;\n   ossim_float64 lon = 0.0;\n\n   std::vector<ossimGpt>::const_iterator pointIdx = centerGptArray.begin();\n   while (pointIdx != centerGptArray.end())\n   {\n      lat += (*pointIdx).lat;\n      lon += (*pointIdx).lon;\n      ++pointIdx;\n   }\n\n   lat /= centerGptArray.size();\n   lon /= centerGptArray.size();\n\n   if ((lat >= -90.0) && (lat <= 90.0) && (lon >= -180.0) && (lon <= 180.0))\n   {\n      gpt.lat = lat;\n      gpt.lon = lon;\n   }\n   else\n   {\n      std::ostringstream errMsg;\n      errMsg << MODULE << " range error!\\nlatitude = "\n             << ossimString::toString(lat).string()\n             << "\\nlongitude = "\n             << ossimString::toString(lon).string();\n      throw ossimException(errMsg.str());\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "scene center: " << gpt << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nvoid ossimChipperUtil::getSceneCenter(ossimSingleImageChain *chain, ossimGpt &gpt)\n{\n   static const char MODULE[] =\n      "ossimChipperUtil::getSceneCenter(const ossimSingleImageChain*,ossimGpt&)";\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   if (chain)\n   {\n      ossimRefPtr<ossimImageGeometry> geom = chain->getImageGeometry();\n      if (geom.valid())\n      {\n         ossimIrect boundingRect = chain->getBoundingRect();\n         ossimDpt midPoint = boundingRect.midPoint();\n         geom->localToWorld(midPoint, gpt);\n         gpt.hgt = 0.0;\n\n         if (gpt.hasNans())\n         {\n            std::string errMsg = MODULE;\n            errMsg += "\\ngeom->localToWorld returned nan for chain.";\n            errMsg += "\\nChain: ";\n            errMsg += chain->getFilename().string();\n            throw ossimException(errMsg);\n         }\n      }\n      else\n      {\n         std::string errMsg = MODULE;\n         errMsg += "\\nNo geometry for chain: ";\n         errMsg += chain->getFilename().string();\n         throw ossimException(errMsg);\n      }\n   }\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR: Null chain passed to method!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "chain name: " << chain->getFilename()\n         << "\\nscene center: " << gpt << "\\n"\n         << MODULE << " exited...\\n";\n   }\n}\n\nossimRefPtr<ossimMapProjection> ossimChipperUtil::getFirstInputProjection()\n{\n   static const char MODULE[] = "ossimChipperUtil::getFirstInputProjection";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimImageHandler> ih = 0;\n   ossimRefPtr<ossimMapProjection> result = 0;\n\n   // Get the first image handler.\n   if (m_demLayer.size())\n   {\n      ih = m_demLayer[0]->getImageHandler();\n   }\n   else if (m_imgLayer.size())\n   {\n      ih = m_imgLayer[0]->getImageHandler();\n   }\n\n   if (ih.valid())\n   {\n      // Get the geometry from the first image handler.\n      ossimRefPtr<ossimImageGeometry> geom = ih->getImageGeometry();\n      if (geom.valid())\n      {\n         // Get the image projection.\n         ossimRefPtr<ossimProjection> proj = geom->getProjection();\n         if (proj.valid())\n         {\n            // Cast and assign to result.\n            ossimMapProjection *mapProj = dynamic_cast<ossimMapProjection*>(proj.get());\n            if (mapProj)\n            {\n               // Must duplicate in case the output projection gets modified.\n               result = (ossimMapProjection *)mapProj->dup();\n            }\n            if (!result.valid() && traceDebug())\n            {\n               ossimNotify(ossimNotifyLevel_WARN) << "Could not cast to map projection.\\n";\n            }\n         }\n         else if (traceDebug())\n         {\n            ossimNotify(ossimNotifyLevel_WARN) << "No projection in first chain...\\n";\n         }\n      }\n   }\n   else if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_WARN) << "No image handler in first chain...\\n";\n   }\n\n   if (traceDebug())\n   {\n      if (result.valid())\n      {\n         result->print(ossimNotify(ossimNotifyLevel_DEBUG));\n      }\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n   return result;\n}\n\nossimRefPtr<ossimMapProjection> ossimChipperUtil::getNewGeoProjection()\n{\n   return ossimRefPtr<ossimMapProjection>(new ossimEquDistCylProjection());\n}\n\nossimRefPtr<ossimMapProjection> ossimChipperUtil::getNewGeoScaledProjection()\n{\n   // Make projection:\n   ossimRefPtr<ossimMapProjection> result = getNewGeoProjection();\n\n   // Set the origin for scaling:\n\n   // First check for user set "central_meridian" and "origin_latitude":\n   ossimGpt origin;\n   origin.lat = getOriginLatitude();\n   origin.lon = getCentralMeridian();\n   origin.hgt = 0.0;\n\n   if (origin.hasNans())\n   {\n      // Use the scene center from the input.\n      getSceneCenter(origin);\n\n      //---\n      // Note only latitude used for scaling, origin kept at 0.0.\n      // This is a fix/hack for ossimEquDistCylProjection wrapping issues.\n      //---\n      origin.lon = 0.0;\n   }\n\n   if (!origin.hasNans())\n   {\n      result->setOrigin(origin);\n   }\n   else\n   {\n      std::string errMsg = "ossimChipperUtil::getNewGeoScaledProjection ERROR";\n      errMsg += "\\nOrigin has nans!";\n      throw ossimException(errMsg);\n   }\n\n   return result;\n}\n\nossimRefPtr<ossimMapProjection> ossimChipperUtil::getNewProjectionFromSrsCode(\n   const std::string &code)\n{\n   ossimRefPtr<ossimMapProjection> result = 0;\n\n   ossimString os = code;\n   os.downcase();\n\n   if ((os == "epsg:4326") || (code == "4326"))\n   {\n      // Avoid factory call for this.\n      result = new ossimEquDistCylProjection();\n   }\n   else\n   {\n      ossimRefPtr<ossimProjection> proj = ossimProjectionFactoryRegistry::instance()->createProjection(code);\n      if (proj.valid())\n      {\n         result = PTR_CAST(ossimMapProjection, proj.get());\n      }\n   }\n   return result;\n}\n\nossimRefPtr<ossimMapProjection> ossimChipperUtil::getNewUtmProjection()\n{\n   // Make projection:\n   ossimRefPtr<ossimUtmProjection> utm = new ossimUtmProjection;\n\n   // Set the zone from keyword option:\n   bool setZone = false;\n   ossim_int32 zone = getZone();\n   if ((zone > 0) && (zone < 61))\n   {\n      utm->setZone(zone);\n      setZone = true;\n   }\n\n   // Set the hemisphere from keyword option:\n   bool setHemisphere = false;\n   std::string hemisphere = getHemisphere();\n   if (hemisphere.size())\n   {\n      ossimString h(hemisphere);\n      h.upcase();\n      if ((h == "N") || (h == "NORTH"))\n      {\n         char c = \'N\';\n         utm->setHemisphere(c);\n         setHemisphere = true;\n      }\n      if ((h == "S") || (h == "SOUTH"))\n      {\n         char c = \'S\';\n         utm->setHemisphere(c);\n         setHemisphere = true;\n      }\n   }\n\n   if (!setZone || !setHemisphere)\n   {\n      // First check for user set "central_meridian" and "origin_latitude":\n      ossimGpt origin;\n      origin.lat = getOriginLatitude();\n      origin.lon = getCentralMeridian();\n      origin.hgt = 0.0;\n\n      if (origin.hasNans())\n      {\n         // Use the scene center from the input.\n         getSceneCenter(origin);\n      }\n\n      if (!origin.hasNans())\n      {\n         if (!setZone)\n         {\n            utm->setZone(origin);\n         }\n         if (!setHemisphere)\n         {\n            utm->setHemisphere(origin);\n         }\n      }\n      else\n      {\n         std::string errMsg = "ossimChipperUtil::getNewUtmProjection ERROR";\n         errMsg += "\\nOrigin has nans!";\n         throw ossimException(errMsg);\n      }\n   }\n\n   return ossimRefPtr<ossimMapProjection>(utm.get());\n}\n\nossimRefPtr<ossimMapProjection> ossimChipperUtil::getMapProjection()\n{\n   ossimRefPtr<ossimMapProjection> mp = 0;\n   if (m_geom.valid())\n   {\n      mp = dynamic_cast<ossimMapProjection *>(m_geom->getProjection());\n   }\n   return mp;\n}\n\nossimRefPtr<ossimImageFileWriter> ossimChipperUtil::createNewWriter() const\n{\n   static const char MODULE[] = "ossimChipperUtil::createNewWriter()";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimFilename outputFile;\n   getOutputFilename(outputFile);\n\n   if (outputFile == ossimFilename::NIL)\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR no output file name!";\n      throw ossimException(errMsg);\n   }\n\n   ossimRefPtr<ossimImageFileWriter> writer = 0;\n\n   ossimString lookup = m_kwl->findKey(WRITER_KW);\n   if (lookup.size())\n   {\n      writer = ossimImageWriterFactoryRegistry::instance()->createWriter(lookup);\n      if (!writer.valid())\n      {\n         std::string errMsg = MODULE;\n         errMsg += " ERROR creating writer: ";\n         errMsg += lookup.string();\n         throw ossimException(errMsg);\n      }\n   }\n   else // Create from output file extension.\n   {\n      writer = ossimImageWriterFactoryRegistry::instance()->createWriterFromExtension(outputFile.ext());\n\n      if (!writer.valid())\n      {\n         std::string errMsg = MODULE;\n         errMsg += " ERROR creating writer from extension: ";\n         errMsg += outputFile.ext().string();\n         throw ossimException(errMsg);\n      }\n   }\n\n   // Set the output name.\n   writer->setFilename(outputFile);\n\n   // Add any writer props.\n   ossim_uint32 count = m_kwl->numberOf(WRITER_PROPERTY_KW.c_str());\n   for (ossim_uint32 i = 0; i < count; ++i)\n   {\n      ossimString key = WRITER_PROPERTY_KW;\n      key += ossimString::toString(i);\n      lookup = m_kwl->findKey(key.string());\n      if (lookup.size())\n      {\n         std::vector<ossimString> splitArray;\n         lookup.split(splitArray, "=");\n         if (splitArray.size() == 2)\n         {\n            ossimRefPtr<ossimProperty> prop =\n               new ossimStringProperty(splitArray[0], splitArray[1]);\n\n            if (traceDebug())\n            {\n               ossimNotify(ossimNotifyLevel_DEBUG)\n                  << "Setting writer prop: " << splitArray[0] << "=" << splitArray[1] << "\\n";\n            }\n\n            writer->setProperty(prop);\n         }\n      }\n   }\n\n   // Output tile size:\n   lookup = m_kwl->findKey(TILE_SIZE_KW);\n   if (lookup.size())\n   {\n      ossimIpt tileSize;\n      tileSize.x = lookup.toInt32();\n      if ((tileSize.x % 16) == 0)\n      {\n         tileSize.y = tileSize.x;\n         writer->setTileSize(tileSize);\n      }\n      else if (traceDebug())\n      {\n         ossimNotify(ossimNotifyLevel_NOTICE)\n            << MODULE << " NOTICE:"\n            << "\\nTile width must be a multiple of 16! Using default.."\n            << std::endl;\n      }\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "writer name: " << writer->getClassName() << "\\n"\n         << MODULE << " exited...\\n";\n   }\n\n   return writer;\n}\nossimIrect viewToImage(ossimRefPtr<ossimImageViewTransform> ivt, const ossimIrect& viewRect)\n{\n   ossimIrect result;\n   ossimDpt ul,ur,lr,ll;\n\n   ivt->viewToImage(viewRect.ul(), ul);\n   ivt->viewToImage(viewRect.ur(), ur);\n   ivt->viewToImage(viewRect.lr(), lr);\n   ivt->viewToImage(viewRect.ll(), ll);\n\n   result = ossimDrect(ul, ur, lr, ll);\n\n   if(result.width() > 1024 || result.height() > 1024)\n   {\n      ossim_int64 w = ossim::min(static_cast<ossim_int64>(result.width()), static_cast<ossim_int64>(1024));\n      ossim_int64 h = ossim::min(static_cast<ossim_int64>(result.height()), static_cast<ossim_int64>(1024));\n      if(w < 64) w = 64;\n      if(h < 64) h = 64;\n\n      result = ossimIrect(ossimIpt(result.midPoint()), w, h);\n   }\n   return result;\n}\n\nvoid ossimChipperUtil::propagateViewportStretch(const ossimIrect& rect)\n{\n   //for now we will do it on each image chaing\n   std::vector<ossimRefPtr<ossimSingleImageChain> >::iterator chainIdx;\n\n   // Loop through dem layers.\n   chainIdx = m_demLayer.begin();\n   while (chainIdx != m_demLayer.end())\n   {\n      ossimRefPtr<ossimHistogramRemapper> remapper = (*chainIdx)->getHistogramRemapper();\n      ossimRefPtr<ossimImageRenderer> resampler = (*chainIdx)->getImageRenderer();\n      if (resampler.valid())\n      {\n         ossimRefPtr<ossimImageViewTransform> ivt = resampler->getImageViewTransform();\n         if (ivt.valid() && remapper.valid())\n         {\n            ossimIrect imageRect = viewToImage(ivt, rect);\n            remapper->setEnableFlag(true);\n            remapper->computeHistogram(imageRect);\n         }\n      }\n      ++chainIdx;\n   }\n\n   // Loop through image layers.\n   chainIdx = m_imgLayer.begin();\n   while (chainIdx != m_imgLayer.end())\n   {\n      ossimRefPtr<ossimHistogramRemapper> remapper = (*chainIdx)->getHistogramRemapper();\n      ossimRefPtr<ossimImageRenderer>     resampler = (*chainIdx)->getImageRenderer();\n      if (resampler.valid())\n      {\n         ossimRefPtr<ossimImageViewTransform> ivt = resampler->getImageViewTransform();\n         if (ivt.valid()&&remapper.valid())\n         {\n            ossimIrect imageRect = viewToImage(ivt, rect);\n            remapper->setEnableFlag(true);\n            remapper->computeHistogram(imageRect);\n         \n         }\n      }\n      ++chainIdx;\n   }\n}\n\nvoid ossimChipperUtil::propagateOutputProjectionToChains()\n{\n   static const char MODULE[] = "ossimChipperUtil::propagateOutputProjectionToChains()";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx;\n\n   // we need to make sure the outputs are refreshed so they can reset themselves\n   // Needed when we are doing interactive update to the GSD and clip window\n   ossimRefPtr<ossimRefreshEvent> refreshEvent = new ossimRefreshEvent();\n   ossimEventVisitor eventVisitor(refreshEvent.get());\n   ossimViewInterfaceVisitor viewVisitor(m_geom.get());\n   // Loop through dem layers.\n   chainIdx = m_demLayer.begin();\n   while (chainIdx != m_demLayer.end())\n   {\n      viewVisitor.reset();\n      eventVisitor.reset();\n      (*chainIdx)->accept(viewVisitor);\n      (*chainIdx)->accept(eventVisitor);\n\n      ossimRefPtr<ossimImageRenderer> resampler = (*chainIdx)->getImageRenderer();\n      if (resampler.valid())\n      {\n         //resampler->setView( m_geom.get() );\n         // resampler->propagateEventToOutputs(refreshEvent);\n      }\n      else\n      {\n         std::string errMsg = MODULE;\n         errMsg += " chain has no resampler!";\n         throw(ossimException(errMsg));\n      }\n      ++chainIdx;\n   }\n\n   // Loop through image layers.\n   chainIdx = m_imgLayer.begin();\n   while (chainIdx != m_imgLayer.end())\n   {\n      ossimRefPtr<ossimImageRenderer> resampler = (*chainIdx)->getImageRenderer();\n      eventVisitor.reset();\n      viewVisitor.reset();\n      (*chainIdx)->accept(viewVisitor);\n      (*chainIdx)->accept(eventVisitor);\n\n      if (resampler.valid())\n      {\n         //resampler->setView( m_geom.get() );\n         //resampler->propagateEventToOutputs(refreshEvent);\n      }\n      else\n      {\n         std::string errMsg = MODULE;\n         errMsg += " chain has no resampler!";\n         throw(ossimException(errMsg));\n      }\n      ++chainIdx;\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::combineLayers(\n   std::vector<ossimRefPtr<ossimSingleImageChain>> &layers) const\n{\n   static const char MODULE[] = "ossimChipperUtil::combineLayers(layers)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimImageSource> result = 0;\n\n   if (layers.size() == 1)\n   {\n      result = layers[0].get();\n   }\n   else if (layers.size() > 1)\n   {\n\n      result = createCombiner(); //new ossimImageMosaic;\n\n      std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx = layers.begin();\n      while (chainIdx != layers.end())\n      {\n         result->connectMyInputTo((*chainIdx).get());\n         ++chainIdx;\n      }\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n   return result;\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::combineLayers()\n{\n   static const char MODULE[] = "ossimChipperUtil::combineLayers()";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimImageSource> result = 0;\n\n   ossim_uint32 layerCount = (ossim_uint32)(m_demLayer.size() + m_imgLayer.size());\n\n   if (layerCount)\n   {\n      if (layerCount == 1)\n      {\n         if (m_imgLayer.size())\n         {\n            result = m_imgLayer[0].get();\n         }\n         else\n         {\n            result = m_demLayer[0].get();\n         }\n      }\n      else\n      {\n         result = createCombiner(); //new ossimImageMosaic;\n\n         // Combine the images.  Note we\'ll put the images on top of the dems.\n         if (m_imgLayer.size())\n         {\n            std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx =\n               m_imgLayer.begin();\n            while (chainIdx != m_imgLayer.end())\n            {\n               result->connectMyInputTo((*chainIdx).get());\n               ++chainIdx;\n            }\n         }\n         if (m_demLayer.size()) // Combine any dem layers.\n         {\n            std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx =\n               m_demLayer.begin();\n            while (chainIdx != m_demLayer.end())\n            {\n               result->connectMyInputTo((*chainIdx).get());\n               ++chainIdx;\n            }\n         }\n      }\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n   return result;\n\n} // End: ossimChipperUtil::combineLayers\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::initialize2CmvChain()\n{\n   ossimRefPtr<ossimImageSource> result = 0;\n\n   ossim_uint32 layerCount = (ossim_uint32)(m_demLayer.size() + m_imgLayer.size());\n\n   // Must have two and only two inputs.\n   if (layerCount == 2)\n   {\n      ossimRefPtr<ossimConnectableObject> oldImg = 0;\n      ossimRefPtr<ossimConnectableObject> newImg = 0;\n\n      //---\n      // Expecting two image layers.  We\'ll code it for flexabilty though...\n      // - Take old and new from m_imgLayer if present.\n      // - Use m_demLayer only if missing.\n      // - Using first image found as old, second new.\n      //---\n\n      // Most likely case, two image layers.\n      if (m_imgLayer.size())\n      {\n         oldImg = m_imgLayer[0].get();\n\n         if (m_imgLayer.size() == 2)\n         {\n            newImg = m_imgLayer[1].get();\n         }\n      }\n\n      if (m_demLayer.size())\n      {\n         if (!oldImg.valid())\n         {\n            oldImg = m_demLayer[0].get();\n         }\n\n         if (!newImg.valid())\n         {\n            if (m_demLayer.size() == 1)\n            {\n               newImg = m_demLayer[0].get();\n            }\n            else if (m_demLayer.size() == 2)\n            {\n               newImg = m_demLayer[1].get();\n            }\n         }\n      }\n\n      if (newImg.valid() && oldImg.valid())\n      {\n         // Input 0 is old, 1 is new.\n         ossimRefPtr<ossimTwoColorView> tcmv = new ossimTwoColorView;\n         tcmv->connectMyInputTo(0, oldImg.get());\n         tcmv->connectMyInputTo(1, newImg.get());\n\n         // Look for 2cmv options.\n         ossim_uint32 oldInputBandIndex = 0;\n         ossim_uint32 newInputBandIndex = 0;\n         ossimTwoColorView::ossimTwoColorMultiViewOutputSource redOutputSource =\n            ossimTwoColorView::OLD;\n         ossimTwoColorView::ossimTwoColorMultiViewOutputSource grnOutputSource =\n            ossimTwoColorView::NEW;\n         ossimTwoColorView::ossimTwoColorMultiViewOutputSource bluOutputSource =\n            ossimTwoColorView::NEW;\n\n         ossimString os;\n         std::string key = TWOCMV_OLD_INPUT_BAND_KW;\n         std::string val = m_kwl->findKey(key);\n\n         if (val.size())\n         {\n            os = val;\n            oldInputBandIndex = os.toUInt32();\n         }\n\n         key = TWOCMV_NEW_INPUT_BAND_KW;\n         val = m_kwl->findKey(key);\n         if (val.size())\n         {\n            os = val;\n            newInputBandIndex = os.toUInt32();\n         }\n\n         key = TWOCMV_RED_OUTPUT_SOURCE_KW;\n         val = m_kwl->findKey(key);\n         if (val.size())\n         {\n            os = val;\n            os.downcase();\n\n            if (os == "new")\n            {\n               redOutputSource = ossimTwoColorView::NEW;\n            }\n            else if (os == "MIN")\n            {\n               redOutputSource = ossimTwoColorView::MIN;\n            }\n         }\n\n         key = TWOCMV_GREEN_OUTPUT_SOURCE_KW;\n         val = m_kwl->findKey(key);\n         if (val.size())\n         {\n            os = val;\n            os.downcase();\n\n            if (os == "old")\n            {\n               grnOutputSource = ossimTwoColorView::OLD;\n            }\n            else if (os == "MIN")\n            {\n               grnOutputSource = ossimTwoColorView::MIN;\n            }\n         }\n\n         key = TWOCMV_BLUE_OUTPUT_SOURCE_KW;\n         val = m_kwl->findKey(key);\n         if (val.size())\n         {\n            os = val;\n            os.downcase();\n\n            if (os == "old")\n            {\n               bluOutputSource = ossimTwoColorView::OLD;\n            }\n            else if (os == "MIN")\n            {\n               bluOutputSource = ossimTwoColorView::MIN;\n            }\n         }\n\n         // Set options.\n         tcmv->setBandIndexMapping(oldInputBandIndex,\n                                   newInputBandIndex,\n                                   redOutputSource,\n                                   grnOutputSource,\n                                   bluOutputSource);\n         tcmv->initialize();\n\n         result = tcmv.get();\n      }\n   }\n\n   return result;\n\n} // ossimChipperUtil::initialize2CmvChain()\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::addIndexToRgbLutFilter(\n   ossimRefPtr<ossimImageSource> &source) const\n{\n   static const char MODULE[] = "ossimChipperUtil::addIndexToRgbLutFilter(source)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimImageSource> result = 0;\n\n   if (source.valid())\n   {\n      ossimRefPtr<ossimIndexToRgbLutFilter> lut = new ossimIndexToRgbLutFilter();\n      ossimFilename lutFile;\n      lutFile.string() = m_kwl->findKey(LUT_FILE_KW);\n      if (lutFile.exists())\n      {\n         //---\n         // Connect to dems:\n         // Must do this first so that the min and max get set from the input\n         // connection prior to initializing the lut.\n         //---\n         lut->connectMyInputTo(source.get());\n\n         // Note sure about this.  Make option maybe? (drb)\n         lut->setMode(ossimIndexToRgbLutFilter::REGULAR);\n\n         lut->setLut(lutFile);\n\n         // Set as color source for bump shade.\n         result = lut.get();\n      }\n      else\n      {\n         std::string errMsg = MODULE;\n         errMsg += " color table does not exists: ";\n         errMsg += lutFile.string();\n         throw ossimException(errMsg);\n      }\n   }\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR: Null source passed to method!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n   return result;\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::addScalarRemapper(\n   ossimRefPtr<ossimImageSource> &source, ossimScalarType scalar) const\n{\n   static const char MODULE[] = "ossimChipperUtil::addScalarRemapper(source)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimImageSource> result = 0;\n\n   if (source.valid())\n   {\n      if ((scalar != OSSIM_SCALAR_UNKNOWN) && (source->getOutputScalarType() != scalar))\n      {\n         ossimRefPtr<ossimScalarRemapper> remapper = new ossimScalarRemapper();\n         remapper->setOutputScalarType(scalar);\n         remapper->connectMyInputTo(source.get());\n         result = remapper.get();\n\n         if (traceDebug())\n         {\n            ossimNotify(ossimNotifyLevel_DEBUG)\n               << "\\nOutput remapped to: "\n               << ossimScalarTypeLut::instance()->getEntryString(scalar) << "\\n";\n         }\n      }\n      else\n      {\n         result = source;\n      }\n   }\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR: Null source passed to method!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << MODULE << " exited...\\n";\n   }\n\n   return result;\n}\n\nossimRefPtr<ossimImageSource> ossimChipperUtil::addAnnotations(\n   ossimRefPtr<ossimImageSource> &source) const\n{\n   static const char MODULE[] = "ossimChipperUtil::addAnnotations(source)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   ossimRefPtr<ossimImageSource> result = 0;\n\n   if (source.valid() & m_geom.valid())\n   {\n      ossimString regularExpression = "annotation[0-9]*\\\\.type";\n      const ossim_uint32 COUNT = m_kwl->getNumberOfKeysThatMatch(regularExpression);\n      if (COUNT > 0)\n      {\n         // Create annotator:\n         ossimRefPtr<ossimAnnotationSource> annotator = new ossimAnnotationSource();\n\n         // Connect it up:\n         annotator->connectMyInputTo(source.get());\n\n         // Loop through keyword list and add all annotation objects.\n         ossim_uint32 i = 0;\n         ossim_uint32 found = 0;\n         std::string prefixBase = "annotation";\n         std::string prefix;\n         std::string key;\n         std::string value;\n         while ((found < COUNT) && (i < (COUNT + 100)))\n         {\n            prefix = prefixBase + ossimString::toString(i).string() + ".";\n            key = "type";\n            value = m_kwl->findKey(prefix, key);\n            if (value.size())\n            {\n               ++found;\n\n               if (value == "cross_hair")\n               {\n                  addCrossHairAnnotation(annotator, prefix);\n               }\n            }\n            ++i;\n         }\n\n         annotator->initialize();\n\n         // Assign return result:\n         result = annotator.get();\n      }\n   }\n   else\n   {\n      std::string errMsg = MODULE;\n      errMsg += " ERROR: Null source passed to method!";\n      throw ossimException(errMsg);\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n   return result;\n}\n\nvoid ossimChipperUtil::addCrossHairAnnotation(\n   ossimRefPtr<ossimAnnotationSource> annotator, const std::string &prefix) const\n{\n   if (annotator.valid() && prefix.size())\n   {\n      std::string linePrefix = prefix + std::string("line.");\n      std::string textPrefix = prefix + std::string("text.");\n      ossimDpt centerPt;\n      centerPt.makeNan();\n      ossim_float64 lineSize = 8.0;\n      ossimDpt start;\n      ossimDpt end;\n\n      // Cross hair line location:\n      std::string key = "location";\n      std::string value = m_kwl->findKey(linePrefix, key);\n      if (value.size())\n      {\n         if (value == "scene_center")\n         {\n            ossimIrect boundingRect = annotator->getBoundingRect(0);\n            centerPt = boundingRect.midPoint();\n         }\n      }\n      else\n      {\n         key = "location.gpt";\n         value = m_kwl->findKey(linePrefix, key);\n         if (value.size())\n         {\n            ossimGpt gpt;\n            gpt.toPoint(value);\n            if (gpt.hasNans() == false)\n            {\n               // Convert to view coordinate.\n               ossimDpt imgPt;\n               m_geom->worldToLocal(gpt, imgPt);\n               if (m_ivt.valid())\n               {\n                  m_ivt->imageToView(imgPt, centerPt);\n               }\n               else\n               {\n                  centerPt = imgPt;\n               }\n            }\n         }\n      }\n\n      if (centerPt.hasNans() == false)\n      {\n         // Cross hair size:\n         key = "size";\n         value = m_kwl->findKey(linePrefix, key);\n         if (value.size())\n         {\n            lineSize = ossimString::toInt32(ossimString(value));\n         }\n         ossim_float64 halfLine = lineSize / 2.0;\n\n         // Create lines.\n         ossimRefPtr<ossimAnnotationLineObject> line1 = new ossimAnnotationLineObject();\n\n         // Pick up r,g,b,thickness.\n         line1->loadState(*(m_kwl.get()), linePrefix.c_str());\n\n         start.x = centerPt.x;\n         start.y = centerPt.y - halfLine;\n         end.x = centerPt.x;\n         end.y = centerPt.y + halfLine;\n\n         // ossimAnnotationLineObject::setLine recomputes the bounding box.\n         line1->setLine(start, end);\n\n         annotator->addObject(line1.get());\n\n         ossimRefPtr<ossimAnnotationLineObject> line2 = new ossimAnnotationLineObject();\n\n         // This will pick up r,g,b,thickness.\n         line2->loadState(*(m_kwl.get()), linePrefix.c_str());\n\n         start.x = centerPt.x - halfLine;\n         start.y = centerPt.y;\n         end.x = centerPt.x + halfLine;\n         end.y = centerPt.y;\n\n         // ossimAnnotationLineObject::setLine recomputes the bounding box.\n         line2->setLine(start, end);\n\n         annotator->addObject(line2.get());\n\n         // Set text if any:\n         key = "string";\n         value = m_kwl->findKey(textPrefix, key);\n         if (value.size())\n         {\n            ossimRefPtr<ossimAnnotationFontObject> text = new ossimAnnotationFontObject();\n            text->loadState(*(m_kwl.get()), textPrefix.c_str());\n\n            text->setString(ossimString(value));\n\n            // text scale:\n            key = "scale";\n            value = m_kwl->findKey(textPrefix, key);\n            if (value.size())\n            {\n               ossimDpt scale;\n               scale.toPoint(value);\n               text->setScale(scale);\n            }\n\n            // text point size:\n            key = "point_size";\n            value = m_kwl->findKey(textPrefix, key);\n            if (value.size())\n            {\n               ossimIpt ptSize;\n               ptSize.toPoint(value);\n               text->setPointSize(ptSize);\n            }\n\n            // text location:\n            key = "location";\n            value = m_kwl->findKey(textPrefix, key);\n            if (value.size())\n            {\n               if (value == "bottom")\n               {\n                  ossimIpt ipt;\n                  ipt.x = (ossim_int32)centerPt.x;\n                  ipt.y = (ossim_int32)(centerPt.y + halfLine + 10);\n                  text->setUpperLeftPosition(ipt);\n\n                  ossimDrect rect;\n                  text->getBoundingRect(rect);\n                  ipt.x = ipt.x - rect.width() / 2;\n                  text->setUpperLeftPosition(ipt);\n               }\n            }\n\n            annotator->addObject(text.get());\n         }\n      }\n   }\n\n} // End: ossimChipperUtil::addCrossHairAnnotations( ... )\n\nbool ossimChipperUtil::setupChainHistogram(ossimRefPtr<ossimSingleImageChain> &chain,\n                                           std::shared_ptr<ossimSrcRecord> srcRecordPtr) const\n{\n   static const char MODULE[] = "ossimChipperUtil::setupChainHistogram(chain)";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   bool result = false;\n\n   ossimHistogramRemapper::StretchMode mode =\n      (ossimHistogramRemapper::StretchMode)getHistoMode();\n\n   if (chain.valid())\n   {\n      ossimRefPtr<ossimImageHandler> ih = chain->getImageHandler();\n      ossimRefPtr<ossimHistogramRemapper> remapper = chain->getHistogramRemapper();\n\n      if (ih.valid() && remapper.valid() &&\n          (mode != ossimHistogramRemapper::STRETCH_UNKNOWN))\n      {\n         result = true;\n         ossimString histCenterKw = m_kwl->findKey(HIST_CENTER_KW);\n         if (histCenterKw.empty())\n         {\n            histCenterKw = "false";\n         }\n         bool roiStretch = (m_kwl->hasKey(HIST_AOI_KW) || m_kwl->hasKey(HIST_LLWH_KW) ||\n                            histCenterKw.toBool());\n\n         if (!roiStretch)\n         {\n            bool openedHistogram = false;\n            if (remapper->getHistogramFile() == ossimFilename::NIL)\n            {\n               ossimFilename f;\n               if (srcRecordPtr)\n               {\n                  f = srcRecordPtr->getHistogramPath();\n               }\n               // Open histogram file.\n               if (f.empty() || !f.exists())\n               {\n                  f = ih->getFilenameWithThisExtension(ossimString("his"));\n                  if (f.empty() || (f.exists() == false))\n                  {\n                     // For backward compatibility check if single entry and _e0.his\n                     f = ih->getFilenameWithThisExtension(ossimString("his"), true);\n                     if (!f.exists())\n                        f.clear();\n                  }\n               }\n\n               if (!f.empty())\n               {\n                  openedHistogram = remapper->openHistogram(f);\n                  if (!openedHistogram && traceDebug())\n                  {\n                     ossimNotify(ossimNotifyLevel_WARN)\n                        << MODULE << " WARNING:"\n                        << "\\nCould not open:  " << f << "\\n";\n                  }\n               }\n\n               if (!openedHistogram)\n               {\n                  //---\n                  // User requested a histogram operation but does not have\n                  // external histogram file. This will cause downstream code\n                  // to computer from center tile of image.\n                  //---\n                  roiStretch = true;\n               }\n            }\n         }\n\n         // Enable and set mode:\n         remapper->setEnableFlag(true);\n         remapper->setStretchMode(mode);\n\n         if (roiStretch)\n         {\n            ossimIrect aoi;\n            std::string value = m_kwl->findKey(HIST_AOI_KW);\n            if (value.size())\n            {\n               result = getIrect(value, aoi);\n            }\n            else\n            {\n               value = m_kwl->findKey(HIST_LLWH_KW);\n               if (value.size())\n               {\n                  result = getIrect(chain, value, aoi);\n               }\n               else //  Use center of image.\n               {\n                  // Will need to mark it to do a true computation later\n                  // for each chain.  We do not set the request rect yet.\n                  //\n                  m_viewPortStretchEnabled = true;\n                  result = false;//getIrect(chain, aoi);\n               }\n            }\n\n            if (traceDebug())\n            {\n               ossimNotify(ossimNotifyLevel_DEBUG)\n                  << "ROI of histogram: " << aoi << std::endl;\n            }\n\n            if (result)\n            {\n               ossimIrect newAoi = aoi;   \n               ossimDpt centerPoint;\n\n               // we will clamp the region of interest if too large\n               if ( aoi.width() > 2048 || aoi.height() > 2048)\n               {\n                  ossim_int64 w = ossim::min(static_cast<ossim_int64>(aoi.width()), static_cast<ossim_int64>(2048));\n                  ossim_int64 h = ossim::min(static_cast<ossim_int64>(aoi.height()), static_cast<ossim_int64>(2048));\n                  aoi.getCenter(centerPoint);\n                  newAoi = ossimIrect(ossimIpt(centerPoint), w, h);\n               }\n               //---\n               // Note: both of these sections work.\n               // Going with separate connection for now. drb - 20 Feb. 2016\n               //\n               //---\n#if 1\n               ossimRefPtr<ossimImageHistogramSource> ihist =\n                  new ossimImageHistogramSource();\n               ihist->setAreaOfInterest(aoi);\n               ihist->connectMyInputTo(remapper->getInput()); //ih.get());\n               remapper->connectMyInputTo(ihist.get());\n#else\n               remapper->computeHistogram(newAoi);\n#endif\n            }\n         }\n\n         if (!result)\n         {\n            remapper->setEnableFlag(false);\n         }\n         setStretch(remapper);\n      } // Matches: if ( ih.valid() && remapper.valid() && mode... )\n   } // Matches: if ( chain.valid() )\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n\n   return result;\n}\n\nbool ossimChipperUtil::setChainEntry(\n   ossimRefPtr<ossimSingleImageChain> &chain, ossim_uint32 entryIndex) const\n{\n   bool result = false;\n   if (chain.valid())\n   {\n      ossimRefPtr<ossimImageHandler> ih = chain->getImageHandler();\n      if (ih.valid())\n      {\n         result = ih->setCurrentEntry(entryIndex);\n      }\n   }\n   return result;\n}\n\nvoid ossimChipperUtil::getOutputFilename(ossimFilename &f) const\n{\n   f.string() = m_kwl->findKey(std::string(ossimKeywordNames::OUTPUT_FILE_KW));\n}\n\nvoid ossimChipperUtil::getAreaOfInterest(ossimImageSource *source, ossimIrect &rect) const\n{\n   static const char MODULE[] = "ossimChipperUtil::getAreaOfInterest()";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " entered...\\n";\n   }\n\n   // Nan rect for starters.\n   rect.makeNan();\n\n   if (source)\n   {\n      source->initialize(); // Ensure all bounding rectangles are set.\n\n      if (m_kwl->hasKey(CUT_BBOX_XYWH_KW))\n      {\n         // <x>,<y>,<w>,<h>\n         std::string cutBbox = m_kwl->findKey(CUT_BBOX_XYWH_KW);\n         getIrect(cutBbox, rect);\n      }\n      if (rect.hasNans() &&\n          m_kwl->hasKey(FULLRES_XYS_KW) &&\n          m_kwl->hasKey(CUT_WIDTH_KW) &&\n          m_kwl->hasKey(CUT_HEIGHT_KW))\n      {\n         ossimString tempFullXys = m_kwl->findKey(FULLRES_XYS_KW);\n         ossimString tempWidth = m_kwl->findKey(CUT_WIDTH_KW);\n         ossimString tempHeight = m_kwl->findKey(CUT_HEIGHT_KW);\n         if (m_geom && m_ivt)\n         {\n            std::vector<ossimString> values;\n            tempFullXys.split(values, ",");\n            ossimDpt scale;\n            ossimDpt location;\n            scale.makeNan();\n            location.makeNan();\n            double w = tempWidth.toDouble();\n            double h = tempHeight.toDouble();\n            if (values.size() > 2)\n            {\n               location.x = values[0].toDouble();\n               location.y = values[1].toDouble();\n               ossimDpt mid;\n               m_ivt->imageToView(location, mid);\n               ossimIpt ul(ossim::round<int>(mid.x - (w / 2)),\n                           ossim::round<int>(mid.y - (h / 2)));\n               ossimIpt lr((ul.x + w - 1), ul.y + h - 1);\n               rect = ossimIrect(ul, lr);\n            }\n         }\n      }\n      if (rect.hasNans())\n      {\n         if (m_geom.valid())\n         {\n            if (m_kwl->find(CUT_CENTER_LAT_KW.c_str()))\n            {\n               // "Cut Center" with: --cut-center-llwh or --cut-center-llr:\n\n               ossimString latStr = m_kwl->findKey(CUT_CENTER_LAT_KW);\n               ossimString lonStr = m_kwl->findKey(CUT_CENTER_LON_KW);\n               if (latStr.size() && lonStr.size())\n               {\n                  ossimGpt centerGpt;\n\n                  //---\n                  // Want the height nan going into worldToLocal call so it gets picked\n                  // up by the elevation manager.\n                  //---\n                  centerGpt.makeNan();\n\n                  centerGpt.lat = latStr.toFloat64();\n                  centerGpt.lon = lonStr.toFloat64();\n\n                  if (!centerGpt.isLatNan() && !centerGpt.isLonNan())\n                  {\n                     // Ground "cut center" to view:\n                     ossimDpt centerDpt(0.0, 0.0);\n                     m_geom->worldToLocal(centerGpt, centerDpt);\n\n                     if (!centerDpt.hasNans())\n                     {\n                        if (isChipMode() && m_ivt.valid()) // Chipping in image space.\n                        {\n                           // Tranform image center point to view:\n                           ossimDpt ipt = centerDpt;\n                           m_ivt->imageToView(ipt, centerDpt);\n                        }\n\n                        // --cut-center-llwh:\n                        ossimString widthStr = m_kwl->findKey(CUT_WIDTH_KW);\n                        ossimString heightStr = m_kwl->findKey(CUT_HEIGHT_KW);\n                        if (widthStr.size() && heightStr.size())\n                        {\n                           ossim_int32 width = widthStr.toInt32();\n                           ossim_int32 height = heightStr.toInt32();\n                           if (width && height)\n                           {\n                              ossimIpt ul(ossim::round<int>(centerDpt.x - (width / 2)),\n                                          ossim::round<int>(centerDpt.y - (height / 2)));\n                              ossimIpt lr((ul.x + width - 1), ul.y + height - 1);\n                              rect = ossimIrect(ul, lr);\n                           }\n                        }\n                        else // --cut-center-llr:\n                        {\n                           ossimString radiusStr = m_kwl->findKey(CUT_RADIUS_KW);\n                           if (radiusStr.size())\n                           {\n                              ossim_float64 radius = radiusStr.toFloat64();\n                              if (radius)\n                              {\n                                 ossimDpt mpp;\n                                 m_geom->getMetersPerPixel(mpp);\n\n                                 if (!mpp.hasNans())\n                                 {\n                                    ossim_float64 rx = radius / mpp.x;\n                                    ossim_float64 ry = radius / mpp.y;\n\n                                    ossimIpt ul(ossim::round<int>(centerDpt.x - rx),\n                                                ossim::round<int>(centerDpt.y - ry));\n                                    ossimIpt lr(ossim::round<int>(centerDpt.x + rx),\n                                                ossim::round<int>(centerDpt.y + ry));\n                                    rect = ossimIrect(ul, lr);\n                                 }\n                              }\n                           }\n                        }\n                     }\n\n                  } // Matches: if ( !centerGpt.hasNans() )\n\n               } // Matches: if ( latStr && lonStr )\n\n            } // Matches: if ( m_kwl->find( CUT_CENTER_LAT_KW ) )\n\n            else if (m_kwl->find(CUT_MAX_LAT_KW.c_str()) ||\n                     m_kwl->find(CUT_WMS_BBOX_LL_KW.c_str()))\n            {\n               ossimString maxLat;\n               ossimString maxLon;\n               ossimString minLat;\n               ossimString minLon;\n\n               // --cut-bbox-ll or --cut-bbox-llwh\n               if (m_kwl->find(CUT_MAX_LAT_KW.c_str()))\n               {\n                  maxLat = m_kwl->findKey(CUT_MAX_LAT_KW);\n                  maxLon = m_kwl->findKey(CUT_MAX_LON_KW);\n                  minLat = m_kwl->findKey(CUT_MIN_LAT_KW);\n                  minLon = m_kwl->findKey(CUT_MIN_LON_KW);\n               }\n               else\n               {\n                  ossimString cutBbox = m_kwl->findKey(CUT_WMS_BBOX_LL_KW);\n\n                  cutBbox = cutBbox.upcase().replaceAllThatMatch("BBOX:");\n                  std::vector<ossimString> cutBox = cutBbox.split(",");\n                  if (cutBox.size() > 3)\n                  {\n                     minLon = cutBox[0];\n                     minLat = cutBox[1];\n                     maxLon = cutBox[2];\n                     maxLat = cutBox[3];\n                  }\n               }\n\n               if (maxLat.size() && maxLon.size() && minLat.size() && minLon.size())\n               {\n                  ossim_float64 minLatF = minLat.toFloat64();\n                  ossim_float64 maxLatF = maxLat.toFloat64();\n                  ossim_float64 minLonF = minLon.toFloat64();\n                  ossim_float64 maxLonF = maxLon.toFloat64();\n\n                  //---\n                  // Check for swap so we don\'t get a negative height.\n                  // Note no swap check for longitude as box could cross date line.\n                  //---\n                  if (minLatF > maxLatF)\n                  {\n                     ossim_float64 tmpF = minLatF;\n                     minLatF = maxLatF;\n                     maxLatF = tmpF;\n                  }\n\n                  //---\n                  // Assume cut box is edge to edge or "Pixel Is Area". Our\n                  // AOI(area of interest) uses center of pixel or "Pixel Is Point"\n                  // so get the degrees per pixel and shift AOI to center.\n                  //---\n                  ossimDpt halfDpp;\n                  m_geom->getDegreesPerPixel(halfDpp);\n                  halfDpp = halfDpp / 2.0;\n\n                  ossimGpt gpt(0.0, 0.0, 0.0);\n                  ossimDpt ulPt;\n                  ossimDpt lrPt;\n\n                  // Upper left:\n                  gpt.lat = maxLatF - halfDpp.y;\n                  gpt.lon = minLonF + halfDpp.x;\n                  m_geom->worldToLocal(gpt, ulPt);\n\n                  // Lower right:\n                  gpt.lat = minLatF + halfDpp.y;\n                  gpt.lon = maxLonF - halfDpp.x;\n                  m_geom->worldToLocal(gpt, lrPt);\n\n                  if (isChipMode() && m_ivt.valid())\n                  {\n                     // Chipping in image space:\n\n                     // Tranform image ul point to view:\n                     ossimDpt ipt = ulPt;\n                     m_ivt->imageToView(ipt, ulPt);\n\n                     // Tranform image lr point to view:\n                     ipt = lrPt;\n                     m_ivt->imageToView(ipt, lrPt);\n                  }\n\n                  rect = ossimIrect(ossimIpt(ulPt), ossimIpt(lrPt));\n               }\n            }\n            else if (m_kwl->find(CUT_WMS_BBOX_KW.c_str()))\n            {\n               ossimString cutBbox = m_kwl->findKey(CUT_WMS_BBOX_KW);\n\n               cutBbox = cutBbox.upcase().replaceAllThatMatch("BBOX:");\n               std::vector<ossimString> cutBox = cutBbox.split(",");\n               if (cutBox.size() == 4)\n               {\n\n                  ossim_float64 minx = cutBox[0].toFloat64();\n                  ossim_float64 miny = cutBox[1].toFloat64();\n                  ossim_float64 maxx = cutBox[2].toFloat64();\n                  ossim_float64 maxy = cutBox[3].toFloat64();\n\n                  const ossimMapProjection *mapProj = m_geom->getAsMapProjection();\n                  if (mapProj)\n                  {\n                     std::vector<ossimDpt> pts(4);\n                     ossimDpt *ptsArray = &pts.front();\n                     if (mapProj->isGeographic())\n                     {\n                        ossimDpt halfDpp;\n                        m_geom->getDegreesPerPixel(halfDpp);\n                        halfDpp = halfDpp / 2.0;\n                        ossimGpt gpt(0.0, 0.0, 0.0);\n                        ossimDpt ulPt;\n                        ossimDpt lrPt;\n\n                        // Upper left:\n                        gpt.lat = maxy - halfDpp.y;\n                        gpt.lon = minx + halfDpp.x;\n                        m_geom->worldToLocal(gpt, ptsArray[0]);\n                        // Upper right:\n                        gpt.lat = maxy - halfDpp.y;\n                        gpt.lon = maxx - halfDpp.x;\n                        m_geom->worldToLocal(gpt, ptsArray[1]);\n\n                        // Lower right:\n                        gpt.lat = miny + halfDpp.y;\n                        gpt.lon = maxx - halfDpp.x;\n                        m_geom->worldToLocal(gpt, ptsArray[2]);\n\n                        //Lower left\n                        gpt.lat = miny + halfDpp.y;\n                        gpt.lon = minx + halfDpp.x;\n                        m_geom->worldToLocal(gpt, ptsArray[3]);\n                     }\n                     else\n                     {\n                        ossimDpt halfMpp;\n                        ossimDpt eastingNorthing;\n                        m_geom->getMetersPerPixel(halfMpp);\n                        halfMpp = halfMpp / 2.0;\n\n                        eastingNorthing.x = minx + halfMpp.x;\n                        eastingNorthing.y = miny + halfMpp.y;\n                        mapProj->eastingNorthingToLineSample(eastingNorthing, ptsArray[0]);\n                        eastingNorthing.x = minx + halfMpp.x;\n                        eastingNorthing.y = maxy - halfMpp.y;\n                        mapProj->eastingNorthingToLineSample(eastingNorthing, ptsArray[1]);\n                        eastingNorthing.x = maxx - halfMpp.x;\n                        eastingNorthing.y = maxy - halfMpp.y;\n                        mapProj->eastingNorthingToLineSample(eastingNorthing, ptsArray[2]);\n                        eastingNorthing.x = maxx - halfMpp.x;\n                        eastingNorthing.y = miny + halfMpp.y;\n                        mapProj->eastingNorthingToLineSample(eastingNorthing, ptsArray[3]);\n                     }\n                     rect = ossimIrect(pts);\n                  }\n               }\n            }\n\n         } // if ( m_getOutputGeometry.valid() )\n         else\n         {\n            // Should never happer...\n            std::string errMsg = MODULE;\n            if (!source)\n            {\n               errMsg += " image source null!";\n               throw(ossimException(errMsg));\n            }\n            // do not have to have a projection to output in image space\n            // So commenting out here\n            //\n            // else\n            // {\n            //    errMsg += " output projection null!";\n            // }\n         }\n\n         // If no user defined rect set to scene bounding rect.\n         if (rect.hasNans())\n         {\n            // Get the rectangle from the input chain:\n            rect = source->getBoundingRect(0);\n         }\n\n      } // if ( rect.hasNans() )\n\n   } // if ( source )\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << "aoi: " << rect << "\\n"\n         << MODULE << " exited...\\n";\n   }\n} // End: ossimChipperUtil::getAreaOfInterest\n\nbool ossimChipperUtil::getIrect(const std::string &s, ossimIrect &rect) const\n{\n   bool result = false;\n   if (s.size())\n   {\n      // <x>,<y>,<w>,<h>\n      ossimString cutBbox = s;\n      std::vector<ossimString> keys;\n      cutBbox.split(keys, ",");\n      if (keys.size() > 3)\n      {\n         ossimIpt ul;\n         ossimIpt lr;\n         ul.x = keys[0].toInt32();\n         ul.y = keys[1].toInt32();\n         lr.x = ul.x + keys[2].toInt32() - 1;\n         lr.y = ul.y + keys[3].toInt32() - 1;\n         rect = ossimIrect(ul, lr);\n         result = true;\n      }\n   }\n   return result;\n}\n\nbool ossimChipperUtil::getIrect(ossimRefPtr<ossimSingleImageChain> &chain,\n                                const std::string &s,\n                                ossimIrect &rect) const\n{\n   bool result = false;\n\n   if (chain.valid())\n   {\n      ossimRefPtr<ossimImageHandler> ih = chain->getImageHandler();\n      if (ih.valid())\n      {\n         // Get the geometry from the image handler.\n         ossimRefPtr<ossimImageGeometry> geom = ih->getImageGeometry();\n         if (geom.valid())\n         {\n            // <lat>,<lon>,<w>,<h>\n            ossimString cutBbox = s;\n            std::vector<ossimString> keys;\n            cutBbox.split(keys, ",");\n            if (keys.size() > 3)\n            {\n               ossimGpt gpt;\n               gpt.lat = keys[0].toFloat64();\n               gpt.lon = keys[1].toFloat64();\n               ossim_int32 w = keys[2].toInt32();\n               ossim_int32 h = keys[3].toInt32();\n               ossimDpt dpt;\n               geom->worldToLocal(gpt, dpt);\n               ossimIpt ul = dpt;\n               ul.x -= w / 2;\n               ul.y -= h / 2;\n               ossimIpt lr(ul.x + w - 1, ul.y + h - 1);\n               rect = ossimIrect(ul, lr);\n               result = true;\n            }\n         }\n      }\n   }\n\n   return result;\n}\n\nbool ossimChipperUtil::getIrect(ossimRefPtr<ossimSingleImageChain> &chain,\n                                ossimIrect &rect) const\n{\n   bool result = false;\n   if (chain.valid())\n   {\n      \n      ossimRefPtr<ossimImageHandler> ih = chain->getImageHandler();\n      if (ih.valid())\n      {\n         const ossim_int32 MAX = 512;\n         ossimIrect r = ih->getImageRectangle();\n         ossimIpt size = r.size();\n         ossim_int32 w = ossim::min(MAX, size.x);\n         ossim_int32 h = ossim::min(MAX, size.y);\n         ossimIpt ul = r.midPoint();\n         ul.x -= w / 2;\n         ul.y -= h / 2;\n         ossimIpt lr(ul.x + w - 1, ul.y + h - 1);\n         rect = ossimIrect(ul, lr);\n         result = true;\n      }\n   }\n   return result;\n}\n\nvoid ossimChipperUtil::initializeThumbnailProjection(const ossimIrect &originalRect,\n                                                     ossimIrect &adjustedRect)\n{\n   static const char MODULE[] = "ossimChipperUtil::initializeThumbnailProjection";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << MODULE << " entered...\\n"\n         << "origial rect:  " << originalRect << "\\n";\n\n      if (m_geom.valid())\n      {\n         m_geom->print(ossimNotify(ossimNotifyLevel_DEBUG));\n      }\n   }\n\n   if (!originalRect.hasNans() && m_geom.valid())\n   {\n      //---\n      // Thumbnail setup:\n      //---\n      ossimString thumbRes = m_kwl->findKey(THUMBNAIL_RESOLUTION_KW);\n      if (thumbRes.size())\n      {\n         ossim_float64 thumbSize = thumbRes.toFloat64();\n         ossim_float64 maxRectDimension =\n            ossim::max(originalRect.width(), originalRect.height());\n         ossim_uint32 tw = originalRect.width();\n         ossim_uint32 th = originalRect.height();\n         ossim_float64 scale = 1.0;\n         if (maxRectDimension > thumbSize)\n         {\n            // Need to adjust scale:\n\n            // Get the corners before the scale change:\n            ossimGpt ulGpt;\n            ossimGpt lrGpt;\n\n            m_geom->localToWorld(ossimDpt(originalRect.ul()), ulGpt);\n            m_geom->localToWorld(ossimDpt(originalRect.lr()), lrGpt);\n\n            if (isChipMode() && m_ivt.valid()) // Chipping in image space.)\n            {\n               scale = thumbSize / maxRectDimension;\n               if (m_ivt->getScale().hasNans())\n               {\n                  m_ivt->scale(scale, scale);\n               }\n               else\n               {\n                  m_ivt->scale(m_ivt->getScale().x * scale, m_ivt->getScale().y * scale);\n               }\n               tw *= scale;\n               th *= scale;\n            }\n            else\n            {\n               scale = maxRectDimension / thumbSize;\n               //---\n               // Adjust the projection scale.  Note the "true" is to recenter\n               // the tie point so it falls relative to the projection origin.\n               //\n               // This call also scales: ossimImageGeometry::m_imageSize\n               //---\n               m_geom->applyScale(ossimDpt(scale, scale), true);\n               tw /= scale;\n               th /= scale;\n            }\n            if (tw < 1)\n               tw = 1;\n            if (th < 1)\n               th = 1;\n            // Must call to reset the ossimImageRenderer\'s bounding rect for each input.\n            propagateOutputProjectionToChains();\n\n            // Get the new upper left in view space.\n            ossimDpt dpt;\n            m_geom->worldToLocal(ulGpt, dpt);\n            ossimIpt ul(dpt);\n\n            // Get the new lower right in view space.\n            m_geom->worldToLocal(lrGpt, dpt);\n            ossimIpt lr(dpt);\n\n            //---\n            // Clamp to thumbnail bounds with padding if turned on.\n            // Padding is optional. If padding turned on alway make square.\n            //---\n            ossim_int32 ts = thumbSize;\n            bool pad = padThumbnail();\n            if (pad)\n            {\n               lr.x = ul.x + ts - 1;\n               lr.y = ul.y + ts - 1;\n            }\n            else // let it vary\n            {\n               lr.x = ul.x + tw - 1;\n               lr.y = ul.y + th - 1;\n            }\n\n            adjustedRect = ossimIrect(ul, lr);\n         }\n      }\n\n   } // if ( !originalRect.hasNans() && m_geom.valid() )\n   else\n   {\n      // Should never happer...\n      std::string errMsg = MODULE;\n      if (originalRect.hasNans())\n      {\n         errMsg += " passed in rect has nans!";\n      }\n      else\n      {\n         errMsg += " output projection null!";\n      }\n      throw(ossimException(errMsg));\n   }\n\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG) << "\\nadjusted rect: " << adjustedRect << "\\n";\n      if (m_geom.valid())\n      {\n         m_geom->print(ossimNotify(ossimNotifyLevel_DEBUG));\n      }\n      ossimNotify(ossimNotifyLevel_DEBUG) << MODULE << " exited...\\n";\n   }\n}\n\nbool ossimChipperUtil::hasBandSelection() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      result = m_kwl->hasKey(std::string(ossimKeywordNames::BANDS_KW));\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasWmsBboxCutWidthHeight() const\n{\n   bool result = false;\n\n   if (m_kwl.valid())\n   {\n      result = (m_kwl->hasKey(CUT_HEIGHT_KW) &&\n                m_kwl->hasKey(CUT_WIDTH_KW) &&\n                (m_kwl->hasKey(CUT_WMS_BBOX_KW) ||\n                 m_kwl->hasKey(CUT_WMS_BBOX_LL_KW)));\n   }\n\n   return result;\n}\n\nbool ossimChipperUtil::hasCutBoxWidthHeight() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      if (m_kwl->hasKey(CUT_HEIGHT_KW))\n      {\n         if (m_kwl->hasKey(CUT_WIDTH_KW))\n         {\n            if (m_kwl->hasKey(CUT_MIN_LAT_KW))\n            {\n               if (m_kwl->hasKey(CUT_MIN_LON_KW))\n               {\n                  if (m_kwl->hasKey(CUT_MAX_LAT_KW))\n                  {\n                     if (m_kwl->hasKey(CUT_MAX_LON_KW))\n                     {\n                        result = true;\n                     }\n                  }\n               }\n            } // if lat and lon WMS style bbox is specified then we will behave the same as above\n            else if (m_kwl->hasKey(CUT_WMS_BBOX_LL_KW))\n            {\n               result = true;\n            }\n         }\n      }\n   }\n\n   return result;\n}\n\nbool ossimChipperUtil::hasScaleOption() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      if (m_kwl->hasKey(METERS_KW.c_str()) ||\n          m_kwl->hasKey(DEGREES_X_KW.c_str()) ||\n          hasCutBoxWidthHeight() ||\n          hasWmsBboxCutWidthHeight())\n      {\n         result = true;\n      }\n   }\n   return result;\n}\n\nbool ossimChipperUtil::isThreeBandOut() const\n{\n   return keyIsTrue(THREE_BAND_OUT_KW);\n}\n\nbool ossimChipperUtil::padThumbnail() const\n{\n   return keyIsTrue(PAD_THUMBNAIL_KW);\n}\n\nvoid ossimChipperUtil::setReaderProps(ossimImageHandler *ih) const\n{\n   if (ih && m_kwl.valid())\n   {\n      ossim_uint32 count = m_kwl->numberOf(READER_PROPERTY_KW.c_str());\n      for (ossim_uint32 i = 0; i < count; ++i)\n      {\n         ossimString key = READER_PROPERTY_KW;\n         key += ossimString::toString(i);\n         ossimString value = m_kwl->findKey(key.string());\n         if (value.size())\n         {\n            std::vector<ossimString> splitArray;\n            value.split(splitArray, "=");\n            if (splitArray.size() == 2)\n            {\n               ossimRefPtr<ossimProperty> prop =\n                  new ossimStringProperty(splitArray[0], splitArray[1]);\n\n               ih->setProperty(prop);\n            }\n         }\n      }\n   }\n}\n\nvoid ossimChipperUtil::getBandList(std::vector<ossim_uint32> &bandList) const\n{\n   bandList.clear();\n   if (m_kwl.valid())\n   {\n      ossimString os;\n      os.string() = m_kwl->findKey(std::string(ossimKeywordNames::BANDS_KW));\n      if (os.size() && (os != "default"))\n      {\n         std::vector<ossimString> band_list(0);\n         os.split(band_list, ossimString(","), false);\n         if (band_list.size())\n         {\n            std::vector<ossimString>::const_iterator i = band_list.begin();\n            while (i != band_list.end())\n            {\n               ossim_uint32 band = (*i).toUInt32();\n               if (band) // One based so we need to subtract.\n               {\n                  bandList.push_back(band - 1);\n               }\n               ++i;\n            }\n         }\n      }\n   }\n\n} // End: ossimChipperUtil::getBandList\n\nbool ossimChipperUtil::hasAnnotations() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      ossimString regularExpression = "annotation[0-9]*\\\\.type";\n      ossim_uint32 count = m_kwl->getNumberOfKeysThatMatch(regularExpression);\n      if (count > 0)\n      {\n         result = true;\n      }\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasLutFile() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      result = (m_kwl->find(LUT_FILE_KW.c_str()) != 0);\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasBrightnesContrastOperation() const\n{\n   bool result = false;\n   std::string value = m_kwl->findKey(BRIGHTNESS_KW);\n   if (value.size())\n   {\n      result = true;\n   }\n   else\n   {\n      value = m_kwl->findKey(CONTRAST_KW);\n      if (value.size())\n      {\n         result = true;\n      }\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasGeoPolyCutterOption() const\n{\n   bool result = (m_kwl->find(CLIP_WMS_BBOX_LL_KW.c_str()) ||\n                  m_kwl->find(CLIP_POLY_LAT_LON_KW.c_str()));\n\n   return result;\n}\n\nbool ossimChipperUtil::hasBumpShadeArg() const\n{\n   bool result = (m_operation == OSSIM_CHIPPER_OP_HILL_SHADE);\n   if (!result && m_kwl.valid())\n   {\n      result = (m_kwl->find(ossimKeywordNames::AZIMUTH_ANGLE_KW) ||\n                m_kwl->find(COLOR_RED_KW.c_str()) ||\n                m_kwl->find(COLOR_GREEN_KW.c_str()) ||\n                m_kwl->find(COLOR_BLUE_KW.c_str()) ||\n                m_kwl->find(ossimKeywordNames::ELEVATION_ANGLE_KW) ||\n                m_kwl->find(GAIN_KW.c_str()));\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasThumbnailResolution() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      result = (m_kwl->find(THUMBNAIL_RESOLUTION_KW.c_str()) != 0);\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasHistogramOperation() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      result = m_kwl->hasKey(HIST_OP_KW);\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasGammaCorrection() const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      result = m_kwl->hasKey(GAMMA_KW);\n   }\n   return result;\n\n}\n\nbool ossimChipperUtil::isDemFile(const ossimFilename &file) const\n{\n   bool result = false;\n   ossimString ext = file.ext();\n   if (ext.size() >= 2)\n   {\n      ext.downcase();\n      if ((ext == "hgt") || (ext == "dem") ||\n          (((*ext.begin()) == \'d\') && ((*(ext.begin() + 1)) == \'t\')))\n      {\n         result = true;\n      }\n   }\n   return result;\n}\n\nbool ossimChipperUtil::isSrcFile(const ossimFilename &file) const\n{\n   bool result = false;\n   ossimString ext = file.ext();\n   ext.downcase();\n   if (ext == "src")\n   {\n      result = true;\n   }\n   return result;\n}\n\nossimScalarType ossimChipperUtil::getOutputScalarType() const\n{\n   ossimScalarType scalar = OSSIM_SCALAR_UNKNOWN;\n   ossimString lookup = m_kwl->findKey(OUTPUT_RADIOMETRY_KW);\n   if (lookup.size())\n   {\n      scalar = ossimScalarTypeLut::instance()->getScalarTypeFromString(lookup);\n   }\n   if (scalar == OSSIM_SCALAR_UNKNOWN)\n   {\n      // deprecated keyword...\n      if (keyIsTrue(std::string(SCALE_2_8_BIT_KW)))\n      {\n         scalar = OSSIM_UINT8;\n      }\n   }\n   return scalar;\n}\n\nbool ossimChipperUtil::scaleToEightBit() const\n{\n   bool result = false;\n   if (getOutputScalarType() == OSSIM_UINT8)\n   {\n      result = true;\n   }\n   return result;\n}\n\nbool ossimChipperUtil::snapTieToOrigin() const\n{\n   return keyIsTrue(SNAP_TIE_TO_ORIGIN_KW);\n}\n\nvoid ossimChipperUtil::getImageSpaceScale(ossimDpt &imageSpaceScale) const\n{\n   imageSpaceScale.x = 1.0;\n   imageSpaceScale.y = 1.0;\n   ossimString lookup;\n\n   if (m_kwl->hasKey(FULLRES_XYS_KW))\n   {\n      lookup = m_kwl->findKey(FULLRES_XYS_KW);\n      std::vector<ossimString> values;\n      lookup.trim().split(values, ",");\n      if (values.size() > 2)\n      {\n         imageSpaceScale.x = values[2].toDouble();\n         imageSpaceScale.y = imageSpaceScale.x;\n         if (values.size() > 3)\n         {\n            imageSpaceScale.y = values[3].toDouble();\n         }\n      }\n   }\n   else\n   {\n      std::string value = m_kwl->findKey(IMAGE_SPACE_SCALE_X_KW);\n      if (value.size())\n      {\n         imageSpaceScale.x = ossimString(value).toFloat64();\n      }\n      else\n      {\n         imageSpaceScale.x = 1.0;\n      }\n      value = m_kwl->findKey(IMAGE_SPACE_SCALE_Y_KW);\n      if (value.size())\n      {\n         imageSpaceScale.y = ossimString(value).toFloat64();\n      }\n      else\n      {\n         imageSpaceScale.y = 1.0;\n      }\n   }\n}\n\nvoid ossimChipperUtil::getImageSpacePivot(ossimDpt &imageSpacePivot) const\n{\n   ossimString lookup;\n   imageSpacePivot.makeNan();\n   if (m_kwl->hasKey(FULLRES_XYS_KW))\n   {\n      lookup = m_kwl->findKey(FULLRES_XYS_KW);\n      std::vector<ossimString> values;\n      lookup.trim().split(values, ",");\n\n      if (values.size() > 1)\n      {\n         imageSpacePivot.x = values[0].toDouble();\n         imageSpacePivot.y = values[1].toDouble();\n      }\n   }\n   else\n   {\n      if (m_geom)\n      {\n         ossimDrect rect;\n         m_geom->getBoundingRect(rect);\n         imageSpacePivot = rect.midPoint();\n      }\n   }\n}\n\nossim_float64 ossimChipperUtil::getRotation() const\n{\n   ossim_float64 result = ossim::nan();\n   if (m_kwl.valid())\n   {\n      std::string value = m_kwl->findKey(ROTATION_KW);\n      if (value.size())\n      {\n         result = ossimString(value).toFloat64();\n         if (result < 0)\n         {\n            result += 360.0;\n         }\n\n         // Range check:\n         if ((result < 0.0) || (result > 360.0))\n         {\n            std::ostringstream errMsg;\n            errMsg << "ossimChipperUtil::getRotation range error!\\n"\n                   << "rotation = " << result\n                   << "\\nMust be between 0 and 360.";\n            throw ossimException(errMsg.str());\n         }\n      }\n   }\n   return result;\n}\n\nbool ossimChipperUtil::upIsUp() const\n{\n   return keyIsTrue(std::string(UP_IS_UP_KW));\n}\n\nbool ossimChipperUtil::hasRotation() const\n{\n   bool result = false;\n   std::string value = m_kwl->findKey(std::string(ROTATION_KW));\n   if (value.size())\n   {\n      result = true;\n   }\n   return result;\n}\n\nbool ossimChipperUtil::northUp() const\n{\n   return keyIsTrue(std::string(NORTH_UP_KW));\n}\n\nbool ossimChipperUtil::isChipMode() const\n{\n   return (m_operation == OSSIM_CHIPPER_OP_CHIP);\n}\n\nbool ossimChipperUtil::keyIsTrue(const std::string &key) const\n{\n   bool result = false;\n   if (m_kwl.valid())\n   {\n      std::string value = m_kwl->findKey(key);\n      if (value.size())\n      {\n         result = ossimString(value).toBool();\n      }\n   }\n   return result;\n}\n\nossim_uint32 ossimChipperUtil::getEntryNumber() const\n{\n   ossim_uint32 result = 0;\n   if (m_kwl.valid())\n   {\n      std::string value = m_kwl->findKey(std::string(ossimKeywordNames::ENTRY_KW));\n      if (value.size())\n      {\n         result = ossimString(value).toUInt32();\n      }\n   }\n   return result;\n}\n\nossim_int32 ossimChipperUtil::getZone() const\n{\n   ossim_int32 result = 0;\n   if (m_kwl.valid())\n   {\n      std::string value = m_kwl->findKey(std::string(ossimKeywordNames::ZONE_KW));\n      if (value.size())\n      {\n         result = ossimString(value).toUInt32();\n      }\n   }\n   return result;\n}\n\nstd::string ossimChipperUtil::getHemisphere() const\n{\n   std::string result;\n   if (m_kwl.valid())\n   {\n      result = m_kwl->findKey(std::string(ossimKeywordNames::HEMISPHERE_KW));\n   }\n   return result;\n}\n\nbool ossimChipperUtil::hasSensorModelInput()\n{\n   bool result = false;\n\n   // Test image layers.\n   std::vector<ossimRefPtr<ossimSingleImageChain>>::iterator chainIdx = m_imgLayer.begin();\n   while (chainIdx != m_imgLayer.end())\n   {\n      // Get the image handler:\n      ossimRefPtr<ossimImageHandler> ih = (*chainIdx)->getImageHandler();\n      if (ih.valid())\n      {\n         // Get the geometry from the first image handler.\n         ossimRefPtr<ossimImageGeometry> geom = ih->getImageGeometry();\n         if (geom.valid())\n         {\n            // Get the image projection.\n            ossimRefPtr<ossimProjection> proj = geom->getProjection();\n            if (proj.valid())\n            {\n               // Cast and assign to result.\n               ossimMapProjection *mapProj = PTR_CAST(ossimMapProjection, proj.get());\n               if (!mapProj)\n               {\n                  result = true;\n                  break;\n               }\n            }\n         }\n      }\n      ++chainIdx;\n   }\n\n   if (!result)\n   {\n      // Test dem layers.\n      chainIdx = m_demLayer.begin();\n      while (chainIdx != m_demLayer.end())\n      {\n         // Get the image handler:\n         ossimRefPtr<ossimImageHandler> ih = (*chainIdx)->getImageHandler();\n         if (ih.valid())\n         {\n            // Get the geometry from the first image handler.\n            ossimRefPtr<ossimImageGeometry> geom = ih->getImageGeometry();\n            if (geom.valid())\n            {\n               // Get the image projection.\n               ossimRefPtr<ossimProjection> proj = geom->getProjection();\n               if (proj.valid())\n               {\n                  // Cast and assign to result.\n                  ossimMapProjection *mapProj = PTR_CAST(ossimMapProjection, proj.get());\n                  if (!mapProj)\n                  {\n                     result = true;\n                     break;\n                  }\n               }\n            }\n         }\n         ++chainIdx;\n      }\n   }\n\n   return result;\n}\n\nvoid ossimChipperUtil::initializeSrcKwl()\n{\n   static const char MODULE[] = "ossimChipperUtil::initializeSrcKwl";\n   if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << MODULE << " entered...\\n";\n   }\n\n   std::string value = m_kwl->findKey(std::string(SRC_FILE_KW));\n   if (value.size())\n   {\n      m_srcKwl = new ossimKeywordlist();\n      m_srcKwl->setExpandEnvVarsFlag(true);\n      if (m_srcKwl->addFile(value.c_str()) == false)\n      {\n         m_srcKwl = 0;\n      }\n   }\n   else\n   {\n      m_srcKwl = 0;\n   }\n\n   if (traceDebug())\n   {\n      if (m_srcKwl.valid())\n      {\n         ossimNotify(ossimNotifyLevel_DEBUG)\n            << "src keyword list:\\n"\n            << *(m_srcKwl.get()) << "\\n";\n      }\n      ossimNotify(ossimNotifyLevel_DEBUG)\n         << MODULE << " exited...\\n";\n   }\n}\n\nossim_uint32 ossimChipperUtil::getNumberOfInputs() const\n{\n   ossim_uint32 result = 0;\n\n   ossimString demRegExpr = "dem[0-9]*\\\\.file";\n   ossimString imgRegExpr = "image[0-9]*\\\\.file";\n\n   if (m_kwl.valid())\n   {\n      // Look for dems, e.g. dem0.file: foo.tif\n      result = m_kwl->getNumberOfKeysThatMatch(demRegExpr);\n\n      // Look for images, e.g. image0.file: foo.tif\n      result += m_kwl->getNumberOfKeysThatMatch(imgRegExpr);\n   }\n\n   if (m_srcKwl.valid())\n   {\n      // Look for dems, e.g. dem0.file: foo.tif\n      result += m_srcKwl->getNumberOfKeysThatMatch(demRegExpr);\n\n      // Look for images, e.g. image0.file: foo.tif\n      result += m_srcKwl->getNumberOfKeysThatMatch(imgRegExpr);\n   }\n\n   return result;\n}\n\nossimChipperUtil::ossimChipperOutputProjection ossimChipperUtil::getOutputProjectionType() const\n{\n   ossimChipperOutputProjection result = ossimChipperUtil::OSSIM_CHIPPER_PROJ_UNKNOWN;\n   const char *op = m_kwl->find(ossimKeywordNames::PROJECTION_KW);\n   if (op)\n   {\n      ossimString os = op;\n      os.downcase();\n      if (os == "geo")\n      {\n         result = ossimChipperUtil::OSSIM_CHIPPER_PROJ_GEO;\n      }\n      else if (os == "geo-scaled")\n      {\n         result = ossimChipperUtil::OSSIM_CHIPPER_PROJ_GEO_SCALED;\n      }\n      else if (os == "input")\n      {\n         result = ossimChipperUtil::OSSIM_CHIPPER_PROJ_INPUT;\n      }\n      else if ((os == "utm") || (os == "ossimutmprojection"))\n      {\n         result = ossimChipperUtil::OSSIM_CHIPPER_PROJ_UTM;\n      }\n   }\n   return result;\n}\n\nvoid ossimChipperUtil::getClipPolygon(ossimGeoPolygon &polygon) const\n{\n   ossimString param = m_kwl->find(CLIP_WMS_BBOX_LL_KW.c_str());\n   if (!param.empty())\n   {\n      if (!polygon.addWmsBbox(param))\n      {\n         polygon.clear();\n      }\n   }\n   else\n   {\n      param = m_kwl->find(CLIP_POLY_LAT_LON_KW.c_str());\n      if (!param.empty())\n      {\n         std::vector<ossimGpt> points;\n         ossim::toVector(points, param);\n         if (!points.empty())\n         {\n            polygon = points;\n         }\n      }\n   }\n}\n\nossim_float64 ossimChipperUtil::getBrightness() const\n{\n   ossim_float64 brightness = 0.0;\n   std::string value = m_kwl->findKey(BRIGHTNESS_KW);\n   if (value.size())\n   {\n      brightness = ossimString(value).toFloat64();\n\n      // Range check it:\n      if ((brightness < -1.0) || (brightness > 1.0))\n      {\n         ossimNotify(ossimNotifyLevel_WARN)\n            << "ossimChipperUtil::getBrightness range error!"\n            << "\\nbrightness: " << brightness\n            << "\\nvalid range: -1.0 to 1.0"\n            << "\\nReturned brightness has been reset to: 0.0"\n            << std::endl;\n\n         brightness = 0.0;\n      }\n   }\n   return brightness;\n}\n\nossim_float64 ossimChipperUtil::getContrast() const\n{\n   ossim_float64 contrast = 1.0;\n   std::string value = m_kwl->findKey(CONTRAST_KW);\n   if (value.size())\n   {\n      contrast = ossimString(value).toFloat64();\n\n      // Range check it:\n      if ((contrast < 0.0) || (contrast > 20.0))\n      {\n         ossimNotify(ossimNotifyLevel_WARN)\n            << "ossimChipperUtil::getContrast range error!"\n            << "\\ncontrast: " << contrast\n            << "\\nvalid range: 0 to 20.0"\n            << "\\nReturned contrast has been reset to: 1.0"\n            << std::endl;\n\n         contrast = 1.0;\n      }\n   }\n   return contrast;\n}\n\nossim_float64 ossimChipperUtil::getGamma() const\n{\n   ossim_float64 gamma = 1.0;\n   std::string value = m_kwl->findKey(GAMMA_KW);\n   if (value.size())\n   {\n      gamma = ossimString(value).toFloat64();\n\n      // Range check it:\n   }\n   return gamma;\n\n}\n\nstd::string ossimChipperUtil::getSharpenMode() const\n{\n   ossimString mode = m_kwl->findKey(SHARPEN_MODE_KW);\n   if (mode.size())\n   {\n      mode.downcase();\n      if ((mode != "light") && (mode != "heavy") && (mode != "medium") && (mode != "none"))\n      {\n         ossimNotify(ossimNotifyLevel_WARN)\n            << "ossimChipperUtil::getSharpnessMode WARNING!"\n            << "\\nInvalid sharpness mode: " << mode\n            << "\\nValid modes: \\"light\\", \\"medium\\", and \\"heavy\\""\n            << std::endl;\n         mode = "";\n      }\n      if (mode == "none")\n         mode = "";\n   }\n\n   return mode.string();\n}\n\nstd::string ossimChipperUtil::getSharpenPercent() const\n{\n   ossimString percent = m_kwl->findKey(SHARPEN_PERCENT_KW);\n   if (percent.size())\n   {\n      ossim_float64 value = percent.toDouble();\n      if (value < 0 || value > 1)\n      {\n         ossimNotify(ossimNotifyLevel_WARN)\n             << "ossimChipperUtil::getSharpenPercent WARNING!"\n             << "\\nInvalid sharpen percent: " << percent\n             << "\\nValid percent: Between 0 and 1\\n";\n         percent = "";\n      }\n   }\n   return percent;\n}\n\nint ossimChipperUtil::getHistoMode() const\n{\n   int result = ossimHistogramRemapper::STRETCH_UNKNOWN;\n\n   ossimString op = m_kwl->findKey(HIST_OP_KW);\n   op.downcase();\n\n   if ((op.string() == "auto-minmax"))\n   {\n      result = ossimHistogramRemapper::LINEAR_AUTO_MIN_MAX;\n   }\n   else if ((op.string() == "auto-percentile"))\n   {\n      result = ossimHistogramRemapper::LINEAR_AUTO_PERCENTILE;\n   }\n   else if ((op == "std-stretch-1") || (op == "std-stretch 1"))\n   {\n      result = ossimHistogramRemapper::LINEAR_1STD_FROM_MEAN;\n   }\n   else if ((op == "std-stretch-2") || (op == "std-stretch 2"))\n   {\n      result = ossimHistogramRemapper::LINEAR_2STD_FROM_MEAN;\n   }\n   else if ((op == "std-stretch-3") || (op == "std-stretch 3"))\n   {\n      result = ossimHistogramRemapper::LINEAR_3STD_FROM_MEAN;\n   }\n   else if((op == "linear"))\n   {\n      result = ossimHistogramRemapper::LINEAR_ONE_PIECE;\n   }\n   else if (traceDebug())\n   {\n      ossimNotify(ossimNotifyLevel_WARN)\n         << "ossimChipperUtil::getHistoMode: Unhandled operation: " << op << "\\n";\n   }\n\n   return result;\n}\n\nvoid ossimChipperUtil::usage(ossimArgumentParser &ap)\n{\n   // Add global usage options.\n   ossimInit::instance()->addOptions(ap);\n\n   // Set app name.\n   std::string appName = ap.getApplicationName();\n   ap.getApplicationUsage()->setApplicationName(ossimString(appName));\n\n   // Add options.\n   addArguments(ap);\n\n   // Write usage.\n   ap.getApplicationUsage()->write(ossimNotify(ossimNotifyLevel_INFO));\n\n   // Keeping single line in tact for examples for cut and paste purposes.\n   ossimNotify(ossimNotifyLevel_INFO)\n\n      << "NOTES:\\n"\n      << "1) Never use same base name in the same directory! Example is you have a Chicago.tif\\n"\n      << "   and you want a Chicago.jp2, output Chicago.jp2 to its own directory.\\n"\n\n      << "\\nExample commands:\\n"\n\n      << "\\n// File conversion: Convert geotiff to a jp2 file.\\n"\n      << appName << " --op chip -w ossim_kakadu_jp2 Chicago.tif outputs/Chicago.jp2\\n"\n\n      << "\\n// Orthorectification: Orthorectify a nitf with RPC model out to a geotiff.\\n"\n      << appName << " --op ortho 5V090205P0001912264B220000100282M_001508507.ntf outputs/ortho.tif\\n"\n\n      << "\\n// Mosaic: Mosaic multiple images together and output to a geotiff.\\n"\n      << appName << " --combiner-type ossimImageMosaic --op ortho f1.tif f2.tif f3.tif outputs/mosaic.tif\\n"\n\n      << "\\n// Mosaic: Feather Mosaic multiple images together and output to a geotiff.\\n"\n      << appName << " --combiner-type ossimFeatherMosaic --op ortho f1.tif f2.tif f3.tif outputs/feather.tif\\n"\n\n      << "\\n// Color relief: Colorize two DEMs from a lut, output to a geotiff.\\n"\n      << appName << " --op color-relief --color-table ossim-dem-color-table-template.kwl N37W123.hgt N38W123.hgt outputs/color-relief.tif\\n"\n\n      << "\\n// Color relief: Colorize two DEMs from a lut, output to a png thumbnail.\\n"\n      << appName << " --op color-relief --color-table ossim-dem-color-table-template.kwl -t 1024 -w ossim_png N37W123.hgt N38W123.hgt outputs/color-relief.png\\n"\n\n      << "\\n// Hill shade: Hill shade two DEMs, output to a geotiff.\\n"\n      << appName << " --color 255 255 255 --azimuth 270 --elevation 45 --exaggeration 2.0 --op  hillshade N37W123.hgt N38W123.hgt outputs/hillshade.tif\\n"\n\n      << "\\n// Two color multi view with cut box.  First image is old, second image is new:\\n"\n      << appName << " --cut-bbox-ll 28.092885092033352 -80.664539599998633 28.109128691071547 -80.626914963229325 --op 2cmv oldMLB.tif newMLB.tif outputs/2cmv-test1.tif\\n"\n\n      << "\\n// Ortho about point, 512x512, with histogram stretch, and 3,2,1 band order:\\n"\n      << appName << " --op ortho -b 3,2,1 --histogram-op auto-minmax --cut-center-llwh -42.819784401784275 147.265811350983 512 512 5V090205M0001912264B220000100072M_001508507.ntf orth.tif\\n"\n\n      << "\\n// Chip, in image space, about point, 512x512, with histogram stretch, and 3,2,1 band order:\\n"\n      << appName << " --op chip -b 3,2,1 --histogram-op auto-minmax --cut-center-llwh -42.819784401784275 147.265811350983 512 512 5V090205M0001912264B220000100072M_001508507.ntf chip.tif\\n"\n\n      << "\\n// Chip in image space, rotate \\"up is up\\"(-u option) about point, 512x512 with histogram stretch and 3,2,1 band order:\\n"\n      << appName << " --op chip -u -b 3,2,1 --histogram-op auto-minmax --cut-center-llwh -42.819784401784275 147.265811350983 512 512 5V090205M0001912264B220000100072M_001508507.ntf up-is-up-chip.tif\\n"\n\n      << "\\n// Chip in image space, rotate 39 degrees (-r option) about point, 1024x1024, scaled to eight bit:\\n"\n      << appName << " --op chip -r 39 --histogram-op auto-minmax --cut-center-llwh -42.883809539602893 147.331984112985765 1024 1024 --output-radiometry ossim_uint8 5V090205P0001912264B220000100282M_001508507.ntf outputs/r39.png\\n"\n\n      << "\\n// Above command where all options are in a keyword list:\\n"\n      << appName << " --options r39-options.kwl\\n"\n      << std::endl;\n}\n', 'message': 'Merge pull request #283 from ossimlabs/drb-20220828\n\nFix for ossimChipperUtil::addImgSources() not honoring -e (entry) option.', 'author_name': 'GitHub', 'lang': 'cpp'}