/** * Get Groups by translating from correct Translation table * * @param label A pvl formatted label to be used to generate the serial number */ PvlGroup ObservationNumber::FindObservationTranslation(Pvl &label) { Pvl outLabel; static PvlGroup dataDir(Preference::Preferences().findGroup("DataDirectory")); // Get the mission name static QString missionTransFile = (QString) dataDir["base"] + "/translations/MissionName2DataDir.trn"; static PvlTranslationManager missionXlater(missionTransFile); missionXlater.SetLabel(label); QString mission = missionXlater.Translate("MissionName"); // Get the instrument name static QString instTransFile = (QString) dataDir["base"] + "/translations/Instruments.trn"; static PvlTranslationManager instrumentXlater(instTransFile); instrumentXlater.SetLabel(label); QString instrument = instrumentXlater.Translate("InstrumentName"); // We want to use this instrument's translation manager. It's much faster for // ObservationNumberList if we keep the translation manager in memory, so re-reading // from the disk is not necessary every time. To do this, we'll use a map to store // the translation managers and observation number keys with a string identifier to find them. // This identifier needs to have the mission name and the instrument name. static std::map<QString, std::pair<PvlTranslationManager, PvlKeyword> > missionTranslators; QString key = mission + "_" + instrument; std::map<QString, std::pair<PvlTranslationManager, PvlKeyword> >::iterator translationIterator = missionTranslators.find(key); if(translationIterator == missionTranslators.end()) { // Get the file FileName snFile((QString) dataDir[mission] + "/translations/" + instrument + "SerialNumber????.trn"); snFile = snFile.highestVersion(); // Delets the extra Pvl translation(snFile.expanded()); PvlKeyword observationKeys; if(translation.hasKeyword("ObservationKeys")) { observationKeys = translation["ObservationKeys"]; } // use the translation file to generate keywords missionTranslators.insert( std::pair<QString, std::pair<PvlTranslationManager, PvlKeyword> > (key, std::pair<PvlTranslationManager, PvlKeyword>(PvlTranslationManager(snFile.expanded()), observationKeys)) ); translationIterator = missionTranslators.find(key); } translationIterator->second.first.SetLabel(label); translationIterator->second.first.Auto(outLabel); PvlGroup snGroup = outLabel.findGroup("SerialNumberKeywords"); // Delets the extra if(!translationIterator->second.second.name().isEmpty()) { snGroup += translationIterator->second.second; } else { snGroup += PvlKeyword("ObservationKeys", toString(snGroup.keywords())); } return snGroup; }
void IsisMain() { UserInterface &ui = Application::GetUserInterface(); SerialNumberList serialNumbers(true); FileList images(ui.GetFileName("FROMLIST")); // list of sns/filenames sorted by serial number vector< pair<QString, QString> > sortedList; // We want to sort the input data by serial number so that the same // results are produced every time this program is run with the same // images. This is a modified insertion sort. for(int image = 0; image < images.size(); image++) { unsigned int insertPos = 0; QString sn = SerialNumber::Compose(images[image].toString()); for(insertPos = 0; insertPos < sortedList.size(); insertPos++) { if(sn.compare(sortedList[insertPos].first) < 0) break; } pair<QString, QString> newPair = pair<QString, QString>(sn, images[image].toString()); sortedList.insert(sortedList.begin() + insertPos, newPair); } // Add the serial numbers in sorted order now for(unsigned int i = 0; i < sortedList.size(); i++) { serialNumbers.Add(sortedList[i].second); } // Now we want the ImageOverlapSet to calculate our overlaps ImageOverlapSet overlaps(true); // Use multi-threading to create the overlaps overlaps.FindImageOverlaps(serialNumbers, FileName(ui.GetFileName( "OVERLAPLIST")).expanded()); // This will only occur when "CONTINUE" was true, so we can assume "ERRORS" was // an entered parameter. if(overlaps.Errors().size() != 0 && ui.WasEntered("ERRORS")) { Pvl outFile; bool filenamesOnly = !ui.GetBoolean("DETAILED"); vector<PvlGroup> errorList = overlaps.Errors(); for(unsigned int err = 0; err < errorList.size(); err++) { if(!filenamesOnly) { outFile += errorList[err]; } else if(errorList[err].hasKeyword("FileNames")) { PvlGroup origError = errorList[err]; PvlGroup err("ImageOverlapError"); for(int keyword = 0; keyword < origError.keywords(); keyword++) { if(origError[keyword].name() == "FileNames") { err += origError[keyword]; } } outFile += err; } } outFile.write(FileName(ui.GetFileName("ERRORS")).expanded()); } PvlGroup results("Results"); results += PvlKeyword("ErrorCount", toString((BigInt)overlaps.Errors().size())); Application::Log(results); }
void IsisMain() { // We will be warping a cube ProcessRubberSheet p; // Get the map projection file provided by the user UserInterface &ui = Application::GetUserInterface(); Pvl userPvl(ui.GetFileName("MAP")); PvlGroup &userMappingGrp = userPvl.findGroup("Mapping", Pvl::Traverse); // Open the input cube and get the projection Cube *icube = p.SetInputCube("FROM"); // Get the mapping group PvlGroup fromMappingGrp = icube->group("Mapping"); TProjection *inproj = (TProjection *) icube->projection(); PvlGroup outMappingGrp = fromMappingGrp; // If the default range is FROM, then wipe out any range data in user mapping file if(ui.GetString("DEFAULTRANGE").compare("FROM") == 0 && !ui.GetBoolean("MATCHMAP")) { if(userMappingGrp.hasKeyword("MinimumLatitude")) { userMappingGrp.deleteKeyword("MinimumLatitude"); } if(userMappingGrp.hasKeyword("MaximumLatitude")) { userMappingGrp.deleteKeyword("MaximumLatitude"); } if(userMappingGrp.hasKeyword("MinimumLongitude")) { userMappingGrp.deleteKeyword("MinimumLongitude"); } if(userMappingGrp.hasKeyword("MaximumLongitude")) { userMappingGrp.deleteKeyword("MaximumLongitude"); } } // Deal with user overrides entered in the GUI. Do this by changing the user's mapping group, which // will then overlay anything in the output mapping group. if(ui.WasEntered("MINLAT") && !ui.GetBoolean("MATCHMAP")) { userMappingGrp.addKeyword(PvlKeyword("MinimumLatitude", toString(ui.GetDouble("MINLAT"))), Pvl::Replace); } if(ui.WasEntered("MAXLAT") && !ui.GetBoolean("MATCHMAP")) { userMappingGrp.addKeyword(PvlKeyword("MaximumLatitude", toString(ui.GetDouble("MAXLAT"))), Pvl::Replace); } if(ui.WasEntered("MINLON") && !ui.GetBoolean("MATCHMAP")) { userMappingGrp.addKeyword(PvlKeyword("MinimumLongitude", toString(ui.GetDouble("MINLON"))), Pvl::Replace); } if(ui.WasEntered("MAXLON") && !ui.GetBoolean("MATCHMAP")) { userMappingGrp.addKeyword(PvlKeyword("MaximumLongitude", toString(ui.GetDouble("MAXLON"))), Pvl::Replace); } /** * If the user is changing from positive east to positive west, or vice-versa, the output minimum is really * the input maximum. However, the user mapping group must be left unaffected (an input minimum must be the * output minimum). To accomplish this, we swap the minimums/maximums in the output group ahead of time. This * causes the minimums and maximums to correlate to the output minimums and maximums. That way when we copy * the user mapping group into the output group a mimimum overrides a minimum and a maximum overrides a maximum. */ bool sameDirection = true; if(userMappingGrp.hasKeyword("LongitudeDirection")) { if(((QString)userMappingGrp["LongitudeDirection"]).compare(fromMappingGrp["LongitudeDirection"]) != 0) { sameDirection = false; } } // Since the out mapping group came from the from mapping group, which came from a valid cube, // we can assume both min/max lon exists if min longitude exists. if(!sameDirection && outMappingGrp.hasKeyword("MinimumLongitude")) { double minLon = outMappingGrp["MinimumLongitude"]; double maxLon = outMappingGrp["MaximumLongitude"]; outMappingGrp["MaximumLongitude"] = toString(minLon); outMappingGrp["MinimumLongitude"] = toString(maxLon); } if(ui.GetString("PIXRES").compare("FROM") == 0 && !ui.GetBoolean("MATCHMAP")) { // Resolution will be in fromMappingGrp and outMappingGrp at this time // delete from user mapping grp if(userMappingGrp.hasKeyword("Scale")) { userMappingGrp.deleteKeyword("Scale"); } if(userMappingGrp.hasKeyword("PixelResolution")) { userMappingGrp.deleteKeyword("PixelResolution"); } } else if(ui.GetString("PIXRES").compare("MAP") == 0 || ui.GetBoolean("MATCHMAP")) { // Resolution will be in userMappingGrp - delete all others if(outMappingGrp.hasKeyword("Scale")) { outMappingGrp.deleteKeyword("Scale"); } if(outMappingGrp.hasKeyword("PixelResolution")) { outMappingGrp.deleteKeyword("PixelResolution"); } if(fromMappingGrp.hasKeyword("Scale")); { fromMappingGrp.deleteKeyword("Scale"); } if(fromMappingGrp.hasKeyword("PixelResolution")) { fromMappingGrp.deleteKeyword("PixelResolution"); } } else if(ui.GetString("PIXRES").compare("MPP") == 0) { // Resolution specified - delete all and add to outMappingGrp if(outMappingGrp.hasKeyword("Scale")) { outMappingGrp.deleteKeyword("Scale"); } if(outMappingGrp.hasKeyword("PixelResolution")) { outMappingGrp.deleteKeyword("PixelResolution"); } if(fromMappingGrp.hasKeyword("Scale")) { fromMappingGrp.deleteKeyword("Scale"); } if(fromMappingGrp.hasKeyword("PixelResolution")) { fromMappingGrp.deleteKeyword("PixelResolution"); } if(userMappingGrp.hasKeyword("Scale")) { userMappingGrp.deleteKeyword("Scale"); } if(userMappingGrp.hasKeyword("PixelResolution")) { userMappingGrp.deleteKeyword("PixelResolution"); } outMappingGrp.addKeyword(PvlKeyword("PixelResolution", toString(ui.GetDouble("RESOLUTION")), "meters/pixel"), Pvl::Replace); } else if(ui.GetString("PIXRES").compare("PPD") == 0) { // Resolution specified - delete all and add to outMappingGrp if(outMappingGrp.hasKeyword("Scale")) { outMappingGrp.deleteKeyword("Scale"); } if(outMappingGrp.hasKeyword("PixelResolution")) { outMappingGrp.deleteKeyword("PixelResolution"); } if(fromMappingGrp.hasKeyword("Scale")) { fromMappingGrp.deleteKeyword("Scale"); } if(fromMappingGrp.hasKeyword("PixelResolution")) { fromMappingGrp.deleteKeyword("PixelResolution"); } if(userMappingGrp.hasKeyword("Scale")) { userMappingGrp.deleteKeyword("Scale"); } if(userMappingGrp.hasKeyword("PixelResolution")) { userMappingGrp.deleteKeyword("PixelResolution"); } outMappingGrp.addKeyword(PvlKeyword("Scale", toString(ui.GetDouble("RESOLUTION")), "pixels/degree"), Pvl::Replace); } // Rotation will NOT Propagate if(outMappingGrp.hasKeyword("Rotation")) { outMappingGrp.deleteKeyword("Rotation"); } /** * The user specified map template file overrides what ever is in the * cube's mapping group. */ for(int keyword = 0; keyword < userMappingGrp.keywords(); keyword ++) { outMappingGrp.addKeyword(userMappingGrp[keyword], Pvl::Replace); } /** * Now, we have to deal with unit conversions. We convert only if the following are true: * 1) We used values from the input cube * 2) The values are longitudes or latitudes * 3) The map file or user-specified information uses a different measurement system than * the input cube for said values. * * The data is corrected for: * 1) Positive east/positive west * 2) Longitude domain * 3) planetographic/planetocentric. */ // First, the longitude direction if(!sameDirection) { PvlGroup longitudes = inproj->MappingLongitudes(); for(int index = 0; index < longitudes.keywords(); index ++) { if(!userMappingGrp.hasKeyword(longitudes[index].name())) { // use the from domain because that's where our values are coming from if(((QString)userMappingGrp["LongitudeDirection"]).compare("PositiveEast") == 0) { outMappingGrp[longitudes[index].name()] = toString( TProjection::ToPositiveEast(outMappingGrp[longitudes[index].name()], outMappingGrp["LongitudeDomain"])); } else { outMappingGrp[longitudes[index].name()] = toString( TProjection::ToPositiveWest(outMappingGrp[longitudes[index].name()], outMappingGrp["LongitudeDomain"])); } } } } // Second, longitude domain if(userMappingGrp.hasKeyword("LongitudeDomain")) { // user set a new domain? if((int)userMappingGrp["LongitudeDomain"] != (int)fromMappingGrp["LongitudeDomain"]) { // new domain different? PvlGroup longitudes = inproj->MappingLongitudes(); for(int index = 0; index < longitudes.keywords(); index ++) { if(!userMappingGrp.hasKeyword(longitudes[index].name())) { if((int)userMappingGrp["LongitudeDomain"] == 180) { outMappingGrp[longitudes[index].name()] = toString( TProjection::To180Domain(outMappingGrp[longitudes[index].name()])); } else { outMappingGrp[longitudes[index].name()] = toString( TProjection::To360Domain(outMappingGrp[longitudes[index].name()])); } } } } } // Third, planetographic/planetocentric if(userMappingGrp.hasKeyword("LatitudeType")) { // user set a new domain? if(((QString)userMappingGrp["LatitudeType"]).compare(fromMappingGrp["LatitudeType"]) != 0) { // new lat type different? PvlGroup latitudes = inproj->MappingLatitudes(); for(int index = 0; index < latitudes.keywords(); index ++) { if(!userMappingGrp.hasKeyword(latitudes[index].name())) { if(((QString)userMappingGrp["LatitudeType"]).compare("Planetographic") == 0) { outMappingGrp[latitudes[index].name()] = toString(TProjection::ToPlanetographic( (double)fromMappingGrp[latitudes[index].name()], (double)fromMappingGrp["EquatorialRadius"], (double)fromMappingGrp["PolarRadius"])); } else { outMappingGrp[latitudes[index].name()] = toString(TProjection::ToPlanetocentric( (double)fromMappingGrp[latitudes[index].name()], (double)fromMappingGrp["EquatorialRadius"], (double)fromMappingGrp["PolarRadius"])); } } } } } // Try a couple equivalent longitudes to fix the ordering of min,max for border cases if ((double)outMappingGrp["MinimumLongitude"] >= (double)outMappingGrp["MaximumLongitude"]) { if ((QString)outMappingGrp["MinimumLongitude"] == "180.0" && (int)userMappingGrp["LongitudeDomain"] == 180) outMappingGrp["MinimumLongitude"] = "-180"; if ((QString)outMappingGrp["MaximumLongitude"] == "-180.0" && (int)userMappingGrp["LongitudeDomain"] == 180) outMappingGrp["MaximumLongitude"] = "180"; if ((QString)outMappingGrp["MinimumLongitude"] == "360.0" && (int)userMappingGrp["LongitudeDomain"] == 360) outMappingGrp["MinimumLongitude"] = "0"; if ((QString)outMappingGrp["MaximumLongitude"] == "0.0" && (int)userMappingGrp["LongitudeDomain"] == 360) outMappingGrp["MaximumLongitude"] = "360"; } // If MinLon/MaxLon out of order, we weren't able to calculate the correct values if((double)outMappingGrp["MinimumLongitude"] >= (double)outMappingGrp["MaximumLongitude"]) { if(!ui.WasEntered("MINLON") || !ui.WasEntered("MAXLON")) { QString msg = "Unable to determine the correct [MinimumLongitude,MaximumLongitude]."; msg += " Please specify these values in the [MINLON,MAXLON] parameters"; throw IException(IException::Unknown, msg, _FILEINFO_); } } int samples, lines; Pvl mapData; // Copy to preserve cube labels so we can match cube size if(userPvl.hasObject("IsisCube")) { mapData = userPvl; mapData.findObject("IsisCube").deleteGroup("Mapping"); mapData.findObject("IsisCube").addGroup(outMappingGrp); } else { mapData.addGroup(outMappingGrp); } // *NOTE: The UpperLeftX,UpperLeftY keywords will not be used in the CreateForCube // method, and they will instead be recalculated. This is correct. TProjection *outproj = (TProjection *) ProjectionFactory::CreateForCube(mapData, samples, lines, ui.GetBoolean("MATCHMAP")); // Set up the transform object which will simply map // output line/samps -> output lat/lons -> input line/samps Transform *transform = new map2map(icube->sampleCount(), icube->lineCount(), (TProjection *) icube->projection(), samples, lines, outproj, ui.GetBoolean("TRIM")); // Allocate the output cube and add the mapping labels Cube *ocube = p.SetOutputCube("TO", transform->OutputSamples(), transform->OutputLines(), icube->bandCount()); PvlGroup cleanOutGrp = outproj->Mapping(); // ProjectionFactory::CreateForCube updated mapData to have the correct // upperleftcornerx, upperleftcornery, scale and resolution. Use these // updated numbers. cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["UpperLeftCornerX"], Pvl::Replace); cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["UpperLeftCornerY"], Pvl::Replace); cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["Scale"], Pvl::Replace); cleanOutGrp.addKeyword(mapData.findGroup("Mapping", Pvl::Traverse)["PixelResolution"], Pvl::Replace); ocube->putGroup(cleanOutGrp); // Set up the interpolator Interpolator *interp; if(ui.GetString("INTERP") == "NEARESTNEIGHBOR") { interp = new Interpolator(Interpolator::NearestNeighborType); } else if(ui.GetString("INTERP") == "BILINEAR") { interp = new Interpolator(Interpolator::BiLinearType); } else if(ui.GetString("INTERP") == "CUBICCONVOLUTION") { interp = new Interpolator(Interpolator::CubicConvolutionType); } else { QString msg = "Unknow value for INTERP [" + ui.GetString("INTERP") + "]"; throw IException(IException::Programmer, msg, _FILEINFO_); } // Warp the cube p.StartProcess(*transform, *interp); p.EndProcess(); Application::Log(cleanOutGrp); // Cleanup delete transform; delete interp; }
/** * GetPointInfo builds the PvlGroup containing all the important * information derived from the Camera. * * @return PvlGroup* Data taken directly from the Camera and * drived from Camera information. Ownership passed. */ PvlGroup *CameraPointInfo::GetPointInfo(bool passed, bool allowOutside, bool allowErrors) { PvlGroup *gp = new PvlGroup("GroundPoint"); { gp->addKeyword(PvlKeyword("Filename")); gp->addKeyword(PvlKeyword("Sample")); gp->addKeyword(PvlKeyword("Line")); gp->addKeyword(PvlKeyword("PixelValue")); gp->addKeyword(PvlKeyword("RightAscension")); gp->addKeyword(PvlKeyword("Declination")); gp->addKeyword(PvlKeyword("PlanetocentricLatitude")); gp->addKeyword(PvlKeyword("PlanetographicLatitude")); gp->addKeyword(PvlKeyword("PositiveEast360Longitude")); gp->addKeyword(PvlKeyword("PositiveEast180Longitude")); gp->addKeyword(PvlKeyword("PositiveWest360Longitude")); gp->addKeyword(PvlKeyword("PositiveWest180Longitude")); gp->addKeyword(PvlKeyword("BodyFixedCoordinate")); gp->addKeyword(PvlKeyword("LocalRadius")); gp->addKeyword(PvlKeyword("SampleResolution")); gp->addKeyword(PvlKeyword("LineResolution")); gp->addKeyword(PvlKeyword("SpacecraftPosition")); gp->addKeyword(PvlKeyword("SpacecraftAzimuth")); gp->addKeyword(PvlKeyword("SlantDistance")); gp->addKeyword(PvlKeyword("TargetCenterDistance")); gp->addKeyword(PvlKeyword("SubSpacecraftLatitude")); gp->addKeyword(PvlKeyword("SubSpacecraftLongitude")); gp->addKeyword(PvlKeyword("SpacecraftAltitude")); gp->addKeyword(PvlKeyword("OffNadirAngle")); gp->addKeyword(PvlKeyword("SubSpacecraftGroundAzimuth")); gp->addKeyword(PvlKeyword("SunPosition")); gp->addKeyword(PvlKeyword("SubSolarAzimuth")); gp->addKeyword(PvlKeyword("SolarDistance")); gp->addKeyword(PvlKeyword("SubSolarLatitude")); gp->addKeyword(PvlKeyword("SubSolarLongitude")); gp->addKeyword(PvlKeyword("SubSolarGroundAzimuth")); gp->addKeyword(PvlKeyword("Phase")); gp->addKeyword(PvlKeyword("Incidence")); gp->addKeyword(PvlKeyword("Emission")); gp->addKeyword(PvlKeyword("NorthAzimuth")); gp->addKeyword(PvlKeyword("EphemerisTime")); gp->addKeyword(PvlKeyword("UTC")); gp->addKeyword(PvlKeyword("LocalSolarTime")); gp->addKeyword(PvlKeyword("SolarLongitude")); if (allowErrors) gp->addKeyword(PvlKeyword("Error")); } bool noErrors = passed; QString error = ""; if (!m_camera->HasSurfaceIntersection()) { error = "Requested position does not project in camera model; no surface intersection"; noErrors = false; if (!allowErrors) throw IException(IException::Unknown, error, _FILEINFO_); } if (!m_camera->InCube() && !allowOutside) { error = "Requested position does not project in camera model; not inside cube"; noErrors = false; if (!allowErrors) throw IException(IException::Unknown, error, _FILEINFO_); } if (!noErrors) { for (int i = 0; i < gp->keywords(); i++) { QString name = (*gp)[i].name(); // These three keywords have 3 values, so they must have 3 N/As if (name == "BodyFixedCoordinate" || name == "SpacecraftPosition" || name == "SunPosition") { (*gp)[i].addValue("N/A"); (*gp)[i].addValue("N/A"); (*gp)[i].addValue("N/A"); } else { (*gp)[i].setValue("N/A"); } } // Set all keywords that still have valid information gp->findKeyword("Error").setValue(error); gp->findKeyword("FileName").setValue(m_currentCube->fileName()); gp->findKeyword("Sample").setValue(toString(m_camera->Sample())); gp->findKeyword("Line").setValue(toString(m_camera->Line())); gp->findKeyword("EphemerisTime").setValue( toString(m_camera->time().Et()), "seconds"); gp->findKeyword("EphemerisTime").addComment("Time"); QString utc = m_camera->time().UTC(); gp->findKeyword("UTC").setValue(utc); gp->findKeyword("SpacecraftPosition").addComment("Spacecraft Information"); gp->findKeyword("SunPosition").addComment("Sun Information"); gp->findKeyword("Phase").addComment("Illumination and Other"); } else { Brick b(3, 3, 1, m_currentCube->pixelType()); int intSamp = (int)(m_camera->Sample() + 0.5); int intLine = (int)(m_camera->Line() + 0.5); b.SetBasePosition(intSamp, intLine, 1); m_currentCube->read(b); double pB[3], spB[3], sB[3]; QString utc; double ssplat, ssplon, sslat, sslon, pwlon, oglat; { gp->findKeyword("FileName").setValue(m_currentCube->fileName()); gp->findKeyword("Sample").setValue(toString(m_camera->Sample())); gp->findKeyword("Line").setValue(toString(m_camera->Line())); gp->findKeyword("PixelValue").setValue(PixelToString(b[0])); gp->findKeyword("RightAscension").setValue(toString( m_camera->RightAscension())); gp->findKeyword("Declination").setValue(toString( m_camera->Declination())); gp->findKeyword("PlanetocentricLatitude").setValue(toString( m_camera->UniversalLatitude())); // Convert lat to planetographic Distance radii[3]; m_camera->radii(radii); oglat = TProjection::ToPlanetographic(m_camera->UniversalLatitude(), radii[0].kilometers(), radii[2].kilometers()); gp->findKeyword("PlanetographicLatitude").setValue(toString(oglat)); gp->findKeyword("PositiveEast360Longitude").setValue(toString( m_camera->UniversalLongitude())); //Convert lon to -180 - 180 range gp->findKeyword("PositiveEast180Longitude").setValue(toString( TProjection::To180Domain(m_camera->UniversalLongitude()))); //Convert lon to positive west pwlon = TProjection::ToPositiveWest( m_camera->UniversalLongitude(), 360); gp->findKeyword("PositiveWest360Longitude").setValue(toString(pwlon)); //Convert pwlon to -180 - 180 range gp->findKeyword("PositiveWest180Longitude").setValue(toString( TProjection::To180Domain(pwlon))); m_camera->Coordinate(pB); gp->findKeyword("BodyFixedCoordinate").addValue(toString(pB[0]), "km"); gp->findKeyword("BodyFixedCoordinate").addValue(toString(pB[1]), "km"); gp->findKeyword("BodyFixedCoordinate").addValue(toString(pB[2]), "km"); gp->findKeyword("LocalRadius").setValue(toString( m_camera->LocalRadius().meters()), "meters"); gp->findKeyword("SampleResolution").setValue(toString( m_camera->SampleResolution()), "meters/pixel"); gp->findKeyword("LineResolution").setValue(toString( m_camera->LineResolution()), "meters/pixel"); //body fixed m_camera->instrumentPosition(spB); gp->findKeyword("SpacecraftPosition").addValue(toString(spB[0]), "km"); gp->findKeyword("SpacecraftPosition").addValue(toString(spB[1]), "km"); gp->findKeyword("SpacecraftPosition").addValue(toString(spB[2]), "km"); gp->findKeyword("SpacecraftPosition").addComment("Spacecraft Information"); gp->findKeyword("SpacecraftAzimuth").setValue(toString( m_camera->SpacecraftAzimuth())); gp->findKeyword("SlantDistance").setValue(toString( m_camera->SlantDistance()), "km"); gp->findKeyword("TargetCenterDistance").setValue(toString( m_camera->targetCenterDistance()), "km"); m_camera->subSpacecraftPoint(ssplat, ssplon); gp->findKeyword("SubSpacecraftLatitude").setValue(toString(ssplat)); gp->findKeyword("SubSpacecraftLongitude").setValue(toString(ssplon)); gp->findKeyword("SpacecraftAltitude").setValue(toString( m_camera->SpacecraftAltitude()), "km"); gp->findKeyword("OffNadirAngle").setValue(toString( m_camera->OffNadirAngle())); double subspcgrdaz; subspcgrdaz = m_camera->GroundAzimuth(m_camera->UniversalLatitude(), m_camera->UniversalLongitude(), ssplat, ssplon); gp->findKeyword("SubSpacecraftGroundAzimuth").setValue(toString(subspcgrdaz)); m_camera->sunPosition(sB); gp->findKeyword("SunPosition").addValue(toString(sB[0]), "km"); gp->findKeyword("SunPosition").addValue(toString(sB[1]), "km"); gp->findKeyword("SunPosition").addValue(toString(sB[2]), "km"); gp->findKeyword("SunPosition").addComment("Sun Information"); gp->findKeyword("SubSolarAzimuth").setValue(toString(m_camera->SunAzimuth())); gp->findKeyword("SolarDistance").setValue(toString( m_camera->SolarDistance()), "AU"); m_camera->subSolarPoint(sslat, sslon); gp->findKeyword("SubSolarLatitude").setValue(toString(sslat)); gp->findKeyword("SubSolarLongitude").setValue(toString(sslon)); double subsolgrdaz; subsolgrdaz = m_camera->GroundAzimuth(m_camera->UniversalLatitude(), m_camera->UniversalLongitude(), sslat, sslon); gp->findKeyword("SubSolarGroundAzimuth").setValue(toString(subsolgrdaz)); gp->findKeyword("Phase").setValue(toString(m_camera->PhaseAngle())); gp->findKeyword("Phase").addComment("Illumination and Other"); gp->findKeyword("Incidence").setValue(toString( m_camera->IncidenceAngle())); gp->findKeyword("Emission").setValue(toString( m_camera->EmissionAngle())); gp->findKeyword("NorthAzimuth").setValue(toString( m_camera->NorthAzimuth())); gp->findKeyword("EphemerisTime").setValue(toString( m_camera->time().Et()), "seconds"); gp->findKeyword("EphemerisTime").addComment("Time"); utc = m_camera->time().UTC(); gp->findKeyword("UTC").setValue(utc); gp->findKeyword("LocalSolarTime").setValue(toString( m_camera->LocalSolarTime()), "hour"); gp->findKeyword("SolarLongitude").setValue(toString( m_camera->solarLongitude().degrees())); if (allowErrors) gp->findKeyword("Error").setValue("N/A"); } } return gp; }