/** * See http://en.wikipedia.org/wiki/Latitude#Degree_length * * TODO: check if these calculatiosn (based on the oa2gm source code) are * correct. I guess not, because they both use 6371e3 for computing * new latitudinal and longitudinal points. This is not 100% correct. * We should probably follow WGS84 or IERS 2003 ellipsoids. */ void Circle::discretize( std::vector<Coordinate>& coords, double resolution ) const { // Each circle must have *at least* 360 points (or more if the specified resolution is // not satisfied when taking 360 points). const int min_nb_points = 360; int nbPoints = max((int)(2*pi*radius/resolution), min_nb_points); coords.clear(); coords.reserve(nbPoints); double deg_lat, deg_lon; double angle; Coordinate centerCoord = center.getCoordinate(); Latitude lat = centerCoord.getLatitude(); Longitude lon = centerCoord.getLongitude(); // Compute arcdegree of latitude respectively longitude difference of the center. double arcdegree_lat = lat.getArcDegree(); double arcdegree_lon = lon.getArcDegree(lat); for (int i = 0; i < nbPoints; ++i) { angle = 2*pi*i/nbPoints; deg_lon = lon.getAngle() + getRadiusM()*cos(angle)/arcdegree_lon; deg_lat = lat.getAngle() + getRadiusM()*sin(angle)/arcdegree_lat; coords.push_back( Coordinate( deg_lat, deg_lon ) ); } }
/** * Filters a list of points for points that are of the selected * Range or in the given range. The filtered list will appear in * the navtools point list display. * @internal * @history 2009-01-08 Jeannie Walldren - Modified to remove * new filter points from the existing * filtered list. Previously, a new * filtered list was created from the * entire control net each time. * @history 2010-06-03 Jeannie Walldren - Removed "std::" * since "using namespace std" * */ void QnetPointRangeFilter::filter() { // Make sure there is a control net loaded if (controlNet() == NULL) { QMessageBox::information((QWidget *)parent(), "Error", "No points to filter"); return; } // Make sure all the values we need have been entered by the user if ((m_minlat->text() == "") || (m_maxlat->text() == "") || (m_minlon->text() == "") || (m_maxlon->text() == "")) { QMessageBox::information((QWidget *)parent(), "Error", "All lat/lon range values must be entered"); return; } else { // Get the user entered values for the range double minlat = m_minlat->text().toDouble(); double maxlat = m_maxlat->text().toDouble(); double minlon = m_minlon->text().toDouble(); double maxlon = m_maxlon->text().toDouble(); // Make sure the lat values are in order if (minlat > maxlat) { QString msg = "The minimum latitude value must be less than the maximum latitude value"; QMessageBox::information((QWidget *)parent(), "Error", msg); return; } // Make sure the lon values are in order else if (minlon > maxlon) { QString msg = "The minimum longitude value must be less than the maximum longitude value"; QMessageBox::information((QWidget *)parent(), "Error", msg); return; } // Loop through each value of the filtered points list // checking to see if each point falls within the rangee // Loop in reverse order since removal list of elements affects index number for (int i = filteredPoints().size() - 1; i >= 0; i--) { // Get the current control point ControlPoint &cp = *(*controlNet())[filteredPoints()[i]]; Latitude lat = cp.GetBestSurfacePoint().GetLatitude(); Longitude lon = cp.GetBestSurfacePoint().GetLongitude(); if (lat.inRange(Latitude(minlat,Angle::Degrees),Latitude(maxlat,Angle::Degrees)) && lon.inRange(Longitude(minlon,Angle::Degrees),Longitude(maxlon,Angle::Degrees))) { continue; } else { filteredPoints().removeAt(i); } } } // Tell the navtool that a list has been filtered and it needs to update emit filteredListModified(); return; }
static std::string latlon(const Latitude latitude, const Longitude longitude) { if( latitude.is_valid() && longitude.is_valid() ) { return latlon_abs_normalized(latitude.normalized(), "SN") + " " + latlon_abs_normalized(longitude.normalized(), "WE"); } else if( latitude.is_not_available() && longitude.is_not_available() ) { return "not available"; } else { return "invalid"; } }
/** * Checks if this latitude value is within the given range. Defines the * range as the change from the minimum latitude to the maximum latitude (an * angle), and returns whether the change from the minimum latitude to this * latitude is less than or equal to the maximum change allowed (the range). * * @param min The beginning of the valid latitude range * @param max The end of the valid latitude range * * @return Whether the latitude is in the given range */ bool Latitude::inRange(Latitude min, Latitude max) const { // Validity check on the range if (min > max) { IString msg = "Minimum latitude [" + IString(min.degrees()) + "] degrees is greater than maximum latitude [" + IString(max.degrees()) + "] degrees"; throw IException(IException::User, msg, _FILEINFO_); } // Provide a little wriggle room for precision problems Angle epsilon(DBL_EPSILON, Angle::Degrees); Latitude adjustedMin = min - epsilon; Latitude adjustedMax = max + epsilon; // Is this latitude between the min and the max return *this >= adjustedMin && *this <= adjustedMax; }
// Compute the radius at the lat/lon Distance GetRadius(QString filename, Latitude lat, Longitude lon) { Cube cube(filename, "r"); Sensor sensor(cube); sensor.SetGround(SurfacePoint(lat, lon, sensor.LocalRadius(lat, lon))); Distance radius = sensor.LocalRadius(); if(!radius.isValid()) { QString msg = "Could not determine radius from DEM at lat/lon ["; msg += toString(lat.degrees()) + "," + toString(lon.degrees()) + "]"; throw IException(IException::Unknown, msg, _FILEINFO_); } return radius; }
/** * Returns whether the lat/lon position was set successfully in the camera * model or projection. * * @param lat The universal latitude or ring radius for ring planes * @param lon The universal longitude or ring longitude (azimuth) for ring planes * * @return Returns true if the lat/lon position was set successfully, and * false if it was not */ bool UniversalGroundMap::SetGround(Latitude lat, Longitude lon) { if(p_camera != NULL) { if(p_camera->SetGround(lat, lon)) { // This should work for rings (radius,azimuth) return p_camera->InCube(); } else { return false; } } else { double universalLat = lat.degrees(); double universalLon = lon.degrees(); return p_projection->SetUniversalGround(universalLat, universalLon); // This should work for rings (radius,azimuth) } }
void Parser::handleLine(const std::string& line) { smatch matches; if ( regex_match(line, matches, regexMap.find(REGEX_COMMENT)->second) ) { // do nothing. return; } if ( regex_match(line, matches, regexMap.find(REGEX_AC)->second) ) { airspaces.push_back(new Airspace); setCurrentDirection('+'); currentArcCenter.reset(); // Assign the parsed class to the current airspace. string matched_text( matches[1].first, matches[1].second ); getCurrentAirspace()->setClass( parseAirspaceClass(matched_text) ); return; } if ( regex_match(line, matches, regexMap.find(REGEX_AN)->second) ) { string airspace_name( matches[1].first, matches[1].second ); getCurrentAirspace()->setName(airspace_name); return; } if ( regex_match(line, matches, regexMap.find(REGEX_AH)->second) ) { string airspace_ceiling(matches[1].first, matches[1].second); getCurrentAirspace()->setCeilingString(airspace_ceiling); getCurrentAirspace()->setCeiling( parseAltitude(airspace_ceiling) ); return; } if ( regex_match(line, matches, regexMap.find(REGEX_AL)->second) ) { string airspace_floor(matches[1].first, matches[1].second); getCurrentAirspace()->setFloorString(airspace_floor); getCurrentAirspace()->setFloor( parseAltitude(airspace_floor) ); return; } if ( regex_match(line, matches, regexMap.find(REGEX_AT)->second) ) { string airspace_coordinate(matches[1].first, matches[1].second); getCurrentAirspace()->add( Label(getCurrentAirspace()->getName(), parseCoordinate(airspace_coordinate)) ); return; } if ( regex_match(line, matches, regexMap.find(REGEX_VX)->second) ) { string matched_text(matches[1].first, matches[1].second); setCurrentArcCenter(parseCoordinate(matched_text)); return; } if ( regex_match(line, matches, regexMap.find(REGEX_VD)->second) ) { string direction_string; for (unsigned int i = 1; i < matches.size(); ++i) { direction_string.assign(matches[i].first, matches[i].second); } setCurrentDirection(direction_string[0]); return; } if ( regex_match(line, matches, regexMap.find(REGEX_DP)->second) ) { string point_coordinate( matches[1].first, matches[1].second ); getCurrentAirspace()->add( new Point(parseCoordinate(point_coordinate)) ); return; } if ( regex_match(line, matches, regexMap.find(REGEX_DA)->second) ) { // Read the matched values and create our Arc. string radiusNM( matches[1].first, matches[1].second ); string angleStart( matches[2].first, matches[2].second ); string angleEnd( matches[3].first, matches[3].second ); Arc arc( *(getCurrentArcCenter()), atof(radiusNM.c_str()), atof(angleStart.c_str()), atof(angleEnd.c_str()), getCurrentDirection()); getCurrentAirspace()->add( &arc ); return; } if ( regex_match(line, matches, regexMap.find(REGEX_DB)->second) ) { // We have a DB-record, now check if the coords are specified correctly... string dbcoords( matches[1].first, matches[1].second ); if ( regex_match(dbcoords, matches, regexMap.find(REGEX_DB_COORDS)->second) ) { // Fetch the start and end coordinate for this arc. string coord1( matches[1].first, matches[1].second ); string coord2( matches[2].first, matches[2].second ); Coordinate c1 = parseCoordinate(coord1); Coordinate c2 = parseCoordinate(coord2); std::shared_ptr<Coordinate> currentArcCenter = getCurrentArcCenter(); // Retrieve latitude and longitude of the arc-center. Latitude lat = currentArcCenter->getLatitude(); Longitude lon = currentArcCenter->getLongitude(); // Compute arcdegree of latitude respectively longitude, based on the center's coordinates. double arcdegree_lat = lat.getArcDegree(); double arcdegree_lon = lon.getArcDegree(lat); // Compute start and end angle (in standard coordinate frame!) // Note that we have to take into account the arcdegrees here!!! double dLat1 = ( c1.getLatitude().getAngle() - currentArcCenter->getLatitude().getAngle() )*arcdegree_lat; double dLon1 = ( c1.getLongitude().getAngle() - currentArcCenter->getLongitude().getAngle() )*arcdegree_lon; double dLat2 = ( c2.getLatitude().getAngle() - currentArcCenter->getLatitude().getAngle() )*arcdegree_lat; double dLon2 = ( c2.getLongitude().getAngle() - currentArcCenter->getLongitude().getAngle() )*arcdegree_lon; double startAngle = 180.0*atan2(dLat1, dLon1)/pi; double endAngle = 180.0*atan2(dLat2, dLon2)/pi; // Convert start and end angle to airspace coordinate frame. startAngle = 90 - startAngle; endAngle = 90 - endAngle; // Use maximum of the two radii (for safety reasons). //double radius = max( c1.getDistance(getCurrentCoordinate()), c2.getDistance(getCurrentCoordinate()) ); // Use minimum of the two radii. //double radius = min( c1.getDistance(getCurrentCoordinate()), c2.getDistance(getCurrentCoordinate()) ); // Use average of the two radii. double radius = ( c1.getDistance(*currentArcCenter) + c2.getDistance(*currentArcCenter) )*0.5; // Add the arc points to this space's Polygon. getCurrentAirspace()->add( new Arc(*currentArcCenter, radius/1852.0, startAngle, endAngle, getCurrentDirection()) ); } else { cout << "\nERROR: invalid coordinate string specification in DB-record: " << line << endl; exit(1); } } if ( regex_match(line, matches, regexMap.find(REGEX_DC)->second) ) { // Get circle radius (in Nautical Miles) from what we've just read. string radiusNM; radiusNM.assign(matches[1].first, matches[1].second); // Add circle to this Airspace. Point center(*(getCurrentArcCenter())); getCurrentAirspace()->add(new Circle(center, atof(radiusNM.c_str()))); } }
/** * Find the lat/lon range of the image. This will use the image footprint, * camera, or projection in order to find a good result. * * @param Cube* This is required for estimation. You can pass in NULL (it will * disable estimation). * @param minLat This is an output: minimum latitude * @param maxLat This is an output: maximum latitude * @param minLon This is an output: minimum longitude * @param maxLon This is an output: maximum longitude * @param allowEstimation If this is true then extra efforts will be made to * guess the ground range of the input. This can still fail. * @return True if a ground range was found, false if no ground range could * be determined. Some lat/lon results may still be populated; their * values are undefined. */ bool UniversalGroundMap::GroundRange(Cube *cube, Latitude &minLat, Latitude &maxLat, Longitude &minLon, Longitude &maxLon, bool allowEstimation) { // Do we need a RingRange method? // For now just return false if (HasCamera()) if (p_camera->target()->shape()->name() == "Plane") return false; if (HasProjection()) if (p_projection->projectionType() == Projection::RingPlane) return false; minLat = Latitude(); maxLat = Latitude(); minLon = Longitude(); maxLon = Longitude(); // If we have a footprint, use it try { if (cube) { ImagePolygon poly; cube->read(poly); geos::geom::MultiPolygon *footprint = PolygonTools::MakeMultiPolygon( poly.Polys()->clone()); geos::geom::Geometry *envelope = footprint->getEnvelope(); geos::geom::CoordinateSequence *coords = envelope->getCoordinates(); for (unsigned int i = 0; i < coords->getSize(); i++) { const geos::geom::Coordinate &coord = coords->getAt(i); Latitude coordLat(coord.y, Angle::Degrees); Longitude coordLon(coord.x, Angle::Degrees); if (!minLat.isValid() || minLat > coordLat) minLat = coordLat; if (!maxLat.isValid() || maxLat < coordLat) maxLat = coordLat; if (!minLon.isValid() || minLon > coordLon) minLon = coordLon; if (!maxLon.isValid() || maxLon < coordLon) maxLon = coordLon; } delete coords; coords = NULL; delete envelope; envelope = NULL; delete footprint; footprint = NULL; } } catch (IException &) { } if (!minLat.isValid() || !maxLat.isValid() || !minLon.isValid() || !maxLon.isValid()) { if (HasCamera()) { // Footprint failed, ask the camera PvlGroup mappingGrp("Mapping"); mappingGrp += PvlKeyword("LatitudeType", "Planetocentric"); mappingGrp += PvlKeyword("LongitudeDomain", "360"); mappingGrp += PvlKeyword("LongitudeDirection", "PositiveEast"); Pvl mappingPvl; mappingPvl += mappingGrp; double minLatDouble; double maxLatDouble; double minLonDouble; double maxLonDouble; p_camera->GroundRange( minLatDouble, maxLatDouble, minLonDouble, maxLonDouble, mappingPvl); minLat = Latitude(minLatDouble, Angle::Degrees); maxLat = Latitude(maxLatDouble, Angle::Degrees); minLon = Longitude(minLonDouble, Angle::Degrees); maxLon = Longitude(maxLonDouble, Angle::Degrees); } else if (HasProjection()) { // Footprint failed, look in the mapping group PvlGroup mappingGrp = p_projection->Mapping(); if (mappingGrp.hasKeyword("MinimumLatitude") && mappingGrp.hasKeyword("MaximumLatitude") && mappingGrp.hasKeyword("MinimumLongitude") && mappingGrp.hasKeyword("MaximumLongitude")) { minLat = Latitude(mappingGrp["MinimumLatitude"], mappingGrp, Angle::Degrees); maxLat = Latitude(mappingGrp["MaximumLatitude"], mappingGrp, Angle::Degrees); minLon = Longitude(mappingGrp["MinimumLongitude"], mappingGrp, Angle::Degrees); maxLon = Longitude(mappingGrp["MaximumLongitude"], mappingGrp, Angle::Degrees); } else if (allowEstimation && cube) { // Footprint and mapping failed... no lat/lon range of any kind is // available. Let's test points in the image to try to make our own // extent. QList<QPointF> imagePoints; // Reset to TProjection TProjection *tproj = (TProjection *) p_projection; /* * This is where we're testing: * * |---------------| * |***************| * |** * **| * |* * * * *| * |* * * * *| * |***************| * |* * * * *| * |* * * * *| * |** * **| * |***************| * |---------------| * * We'll test at the edges, a plus (+) and an (X) to help DEMs work. */ int sampleCount = cube->sampleCount(); int lineCount = cube->lineCount(); int stepsPerLength = 20; //number of steps per length double aspectRatio = (double)lineCount / (double)sampleCount; double xStepSize = sampleCount / stepsPerLength; double yStepSize = xStepSize * aspectRatio; if (lineCount > sampleCount) { aspectRatio = (double)sampleCount / (double)lineCount; yStepSize = lineCount / stepsPerLength; xStepSize = yStepSize * aspectRatio; } double yWalked = 0.5; //3 vertical lines for (int i = 0; i < 3; i++) { double xValue = 0.5 + ( i * (sampleCount / 2) ); while (yWalked <= lineCount) { imagePoints.append( QPointF(xValue, yWalked) ); yWalked += yStepSize; } yWalked = 0.5; } double xWalked = 0.5; //3 horizontal lines for (int i = 0; i < 3; i++) { double yValue = 0.5 + ( i * (lineCount / 2) ); while (xWalked <= sampleCount) { imagePoints.append( QPointF(xWalked, yValue) ); xWalked += xStepSize; } xWalked = 0.5; } double xDiagonalWalked = 0.5; double yDiagonalWalked = 0.5; xStepSize = sampleCount / stepsPerLength; yStepSize = lineCount / stepsPerLength; //Top-Down Diagonal while ( (xDiagonalWalked <= sampleCount) && (yDiagonalWalked <= lineCount) ) { imagePoints.append( QPointF(xDiagonalWalked, yDiagonalWalked) ); xDiagonalWalked += xStepSize; yDiagonalWalked += yStepSize; } xDiagonalWalked = 0.5; //Bottom-Up Diagonal while ( (xDiagonalWalked <= sampleCount) && (yDiagonalWalked >= 0) ) { imagePoints.append( QPointF(xDiagonalWalked, yDiagonalWalked) ); xDiagonalWalked += xStepSize; yDiagonalWalked -= yStepSize; } foreach (QPointF imagePoint, imagePoints) { if (tproj->SetWorld(imagePoint.x(), imagePoint.y())) { Latitude latResult(tproj->UniversalLatitude(), Angle::Degrees); Longitude lonResult(tproj->UniversalLongitude(), Angle::Degrees); if (minLat.isValid()) minLat = qMin(minLat, latResult); else minLat = latResult; if (maxLat.isValid()) maxLat = qMax(maxLat, latResult); else maxLat = latResult; if (minLon.isValid()) minLon = qMin(minLon, lonResult); else minLon = lonResult; if (maxLon.isValid()) maxLon = qMax(maxLon, lonResult); else maxLon = lonResult; } } } } }
/** Compute undistorted focal plane coordinate from ground position * * @param lat Planetocentric latitude in degrees * @param lon Planetocentric longitude in degrees * * @return @b bool Indicates whether the conversion was successful * * @internal * @history 2007-04-18 Tracie Sucharski - Added check for reasonable * match when attempting to find closest * lat/lon in map arrays. * @history 2007-09-14 Tracie Sucharski - Added check for longitude * outside min/max bounds. Don't know why * this wasn't put in before (lat check was * in), was it oversight, or did I take it out * for some reason??? * @history 2007-12-14 Tracie Sucharski - Remove resolution test, too * image dependent and the resolution for vims is * incorrect due to the instrument having * rectangular pixels. * @history 2008-01-02 Tracie Sucharski - Check validity of resulting * sample and line against edge of starting * ending pixels (0.5/Parent+0.5) instead of * center of pixels. * @history 2012-12-03 Tracie Sucharski - Check for valid minLat/maxLat, minLon/maxLon. If * none are valid, this means the latMap and lonMap have no valid * data, therefore we cannot back project, so return false. * */ bool VimsGroundMap::SetGround(const Latitude &lat, const Longitude &lon) { QVector3D xyz; if (p_camera->target()->shape()->name() == "Plane") { double radius = lat.degrees(); if(radius <= 0.0) return false; double xCheck = radius * 0.001 * cos(lon.radians()); double yCheck = radius * 0.001 * sin(lon.radians()); xyz.setX(xCheck); xyz.setY(yCheck); xyz.setZ(0.); } else { // Convert lat/lon to x/y/z Distance radius = p_camera->LocalRadius(lat, lon); SpiceDouble pB[3]; latrec_c(radius.kilometers(), lon.radians(), lat.radians(), pB); xyz.setX(pB[0]); xyz.setY(pB[1]); xyz.setZ(pB[2]); } double minDist = DBL_MAX; int minSamp = -1; int minLine = -1; // Find closest points ??? what tolerance ??? for (int line = 0; line < p_camera->ParentLines(); line++) { for (int samp = 0; samp < p_camera->ParentSamples(); samp++) { if (p_xyzMap[line][samp].isNull()) continue; // Subtract map from coordinate then get length QVector3D deltaXyz = xyz - p_xyzMap[line][samp]; if (deltaXyz.length() < minDist) { minDist = deltaXyz.length(); minSamp = samp; minLine = line; } } } //----------------------------------------------------------------- // If dist is less than some ??? tolerance ??? this is the // closest point. Use this point and surrounding 8 pts as // control pts. //---------------------------------------------------------------- if (minDist >= DBL_MAX) return false; //------------------------------------------------------------- // Set-up for LU decomposition (least2 fit). // Assume we will have 9 control points, this may not be true // and will need to be adjusted before the final solution. //------------------------------------------------------------- BasisFunction sampXyzBasis("Sample", 4, 4); BasisFunction lineXyzBasis("Line", 4, 4); LeastSquares sampXyzLsq(sampXyzBasis); LeastSquares lineXyzLsq(lineXyzBasis); vector<double> knownXyz(4); // Solve using x/y/z for (int line = minLine - 1; line < minLine + 2; line++) { if (line < 0 || line > p_camera->ParentLines() - 1) continue; for (int samp = minSamp - 1; samp < minSamp + 2; samp++) { // Check for edges if (samp < 0 || samp > p_camera->ParentSamples() - 1) continue; if (p_xyzMap[line][samp].isNull()) continue; knownXyz[0] = p_xyzMap[line][samp].x(); knownXyz[1] = p_xyzMap[line][samp].y(); knownXyz[2] = p_xyzMap[line][samp].z(); knownXyz[3] = 1; sampXyzLsq.AddKnown(knownXyz, samp + 1); lineXyzLsq.AddKnown(knownXyz, line + 1); } } if (sampXyzLsq.Knowns() < 4) return false; sampXyzLsq.Solve(); lineXyzLsq.Solve(); // Solve for sample, line position corresponding to input lat, lon knownXyz[0] = xyz.x(); knownXyz[1] = xyz.y(); knownXyz[2] = xyz.z(); knownXyz[3] = 1; double inSamp = sampXyzLsq.Evaluate(knownXyz); double inLine = lineXyzLsq.Evaluate(knownXyz); if (inSamp < 0.5 || inSamp > p_camera->ParentSamples() + 0.5 || inLine < 0.5 || inLine > p_camera->ParentLines() + 0.5) { return false; } p_camera->IgnoreProjection(true); p_camera->SetImage(inSamp, inLine); p_camera->IgnoreProjection(false); if (!p_camera->HasSurfaceIntersection()) return false; p_focalPlaneX = inSamp; p_focalPlaneY = inLine; return true; }
void IsisMain() { UserInterface &ui = Application::GetUserInterface(); FileList addList(ui.GetFileName("ADDLIST")); bool log = false; FileName logFile; if (ui.WasEntered("LOG")) { log = true; logFile = ui.GetFileName("LOG"); } Pvl results; results.setName("cnetadd_Results"); PvlKeyword added("FilesAdded"); PvlKeyword omitted("FilesOmitted"); PvlKeyword pointsModified("PointsModified"); bool checkMeasureValidity = ui.WasEntered("DEFFILE"); ControlNetValidMeasure validator; if (checkMeasureValidity) { Pvl deffile(ui.GetFileName("DEFFILE")); validator = ControlNetValidMeasure(deffile); } SerialNumberList *fromSerials = ui.WasEntered("FROMLIST") ? new SerialNumberList(ui.GetFileName("FROMLIST")) : new SerialNumberList(); ControlNet inNet = ControlNet(ui.GetFileName("CNET")); inNet.SetUserName(Application::UserName()); inNet.SetModifiedDate(iTime::CurrentLocalTime()); //This should be done in ControlNet's Write fn QString retrievalOpt = ui.GetString("RETRIEVAL"); PvlKeyword duplicates("DupSerialNumbers"); if (retrievalOpt == "REFERENCE") { FileList list1(ui.GetFileName("FROMLIST")); SerialNumberList addSerials(ui.GetFileName("ADDLIST")); //Check for duplicate files in the lists by serial number for (int i = 0; i < addSerials.Size(); i++) { // Check for duplicate SNs accross the lists if (fromSerials->HasSerialNumber(addSerials.SerialNumber(i))) { duplicates.addValue(addSerials.FileName(i)); } // Check for duplicate SNs within the addlist for (int j = i + 1; j < addSerials.Size(); j++) { if (addSerials.SerialNumber(i) == addSerials.SerialNumber(j)) { QString msg = "Add list files [" + addSerials.FileName(i) + "] and ["; msg += addSerials.FileName(j) + "] share the same serial number."; throw IException(IException::User, msg, _FILEINFO_); } } } // Get the lat/long coords from the existing reference measure setControlPointLatLon(*fromSerials, inNet); } else { for (int cp = 0; cp < inNet.GetNumPoints(); cp++) { // Get the surface point from the current control point ControlPoint *point = inNet.GetPoint(cp); SurfacePoint surfacePoint = point->GetBestSurfacePoint(); if (!surfacePoint.Valid()) { QString msg = "Unable to retreive lat/lon from Control Point ["; msg += point->GetId() + "]. RETREIVAL=POINT cannot be used unless "; msg += "all Control Points have Latitude/Longitude keywords."; throw IException(IException::User, msg, _FILEINFO_); } g_surfacePoints[point->GetId()] = surfacePoint; } } FileName outNetFile(ui.GetFileName("ONET")); Progress progress; progress.SetText("Adding Images"); progress.SetMaximumSteps(addList.size()); progress.CheckStatus(); STRtree coordTree; QList<ControlPoint *> pointList; bool usePolygon = ui.GetBoolean("POLYGON"); if (usePolygon) { for (int cp = 0; cp < inNet.GetNumPoints(); cp++) { ControlPoint *point = inNet.GetPoint(cp); SurfacePoint surfacePoint = g_surfacePoints[point->GetId()]; Longitude lon = surfacePoint.GetLongitude(); Latitude lat = surfacePoint.GetLatitude(); Coordinate *coord = new Coordinate(lon.degrees(), lat.degrees()); Envelope *envelope = new Envelope(*coord); coordTree.insert(envelope, point); } } else { for (int cp = 0; cp < inNet.GetNumPoints(); cp++) { pointList.append(inNet.GetPoint(cp)); } } // Loop through all the images for (int img = 0; img < addList.size(); img++) { Cube cube; cube.open(addList[img].toString()); Pvl *cubepvl = cube.label(); QString sn = SerialNumber::Compose(*cubepvl); Camera *cam = cube.camera(); // Loop through all the control points QList<ControlPoint *> validPoints = usePolygon ? getValidPoints(cube, coordTree) : pointList; bool imageAdded = false; for (int cp = 0; cp < validPoints.size(); cp++) { ControlPoint *point = validPoints[cp]; // If the point is locked and Apriori source is "AverageOfMeasures" // then do not add the measures. if (point->IsEditLocked() && point->GetAprioriSurfacePointSource() == ControlPoint::SurfacePointSource::AverageOfMeasures) { continue; } if (point->HasSerialNumber(sn)) continue; // Only use the surface point's latitude and longitude, rely on the DEM // for computing the radius. We do this because otherwise we will receive // inconsistent results from successive runs of this program if the // different DEMs are used, or the point X, Y, Z was generated from the // ellipsoid. SurfacePoint surfacePoint = g_surfacePoints[point->GetId()]; if (cam->SetGround( surfacePoint.GetLatitude(), surfacePoint.GetLongitude())) { // Make sure the samp & line are inside the image if (cam->InCube()) { ControlMeasure *newCm = new ControlMeasure(); newCm->SetCoordinate(cam->Sample(), cam->Line(), ControlMeasure::Candidate); newCm->SetAprioriSample(cam->Sample()); newCm->SetAprioriLine(cam->Line()); newCm->SetCubeSerialNumber(sn); newCm->SetDateTime(); newCm->SetChooserName("Application cnetadd"); // Check the measure for DEFFILE validity if (checkMeasureValidity) { if (!validator.ValidEmissionAngle(cam->EmissionAngle())) { //TODO: log that it was Emission Angle that failed the check newCm->SetIgnored(true); } else if (!validator.ValidIncidenceAngle(cam->IncidenceAngle())) { //TODO: log that it was Incidence Angle that failed the check newCm->SetIgnored(true); } else if (!validator.ValidResolution(cam->resolution())) { //TODO: log that it was Resolution that failed the check newCm->SetIgnored(true); } else if (!validator.PixelsFromEdge((int)cam->Sample(), (int)cam->Line(), &cube)) { //TODO: log that it was Pixels from Edge that failed the check newCm->SetIgnored(true); } else { Portal portal(1, 1, cube.pixelType()); portal.SetPosition(cam->Sample(), cam->Line(), 1); cube.read(portal); if (!validator.ValidDnValue(portal[0])) { //TODO: log that it was DN that failed the check newCm->SetIgnored(true); } } } point->Add(newCm); // Point takes ownership // Record the modified Point and Measure g_modifications[point->GetId()].insert(newCm->GetCubeSerialNumber()); newCm = NULL; // Do not delete because the point has ownership if (retrievalOpt == "POINT" && point->GetNumMeasures() == 1) point->SetIgnored(false); imageAdded = true; } } } cubepvl = NULL; cam = NULL; if (log) { PvlKeyword &logKeyword = (imageAdded) ? added : omitted; logKeyword.addValue(addList[img].baseName()); } progress.CheckStatus(); } if (log) { // Add the list of modified points to the output log file QList<QString> modifiedPointsList = g_modifications.keys(); for (int i = 0; i < modifiedPointsList.size(); i++) pointsModified += modifiedPointsList[i]; results.addKeyword(added); results.addKeyword(omitted); results.addKeyword(pointsModified); if (duplicates.size() > 0) { results.addKeyword(duplicates); } results.write(logFile.expanded()); } // List the modified points if (ui.WasEntered("MODIFIEDPOINTS")) { FileName pointList(ui.GetFileName("MODIFIEDPOINTS")); // Set up the output file for writing std::ofstream out_stream; out_stream.open(pointList.expanded().toAscii().data(), std::ios::out); out_stream.seekp(0, std::ios::beg); //Start writing from beginning of file QList<QString> modifiedPointsList = g_modifications.keys(); for (int i = 0; i < modifiedPointsList.size(); i++) out_stream << modifiedPointsList[i].toStdString() << std::endl; out_stream.close(); } // Modify the inNet to only have modified points/measures if (ui.GetString("EXTRACT") == "MODIFIED") { for (int cp = inNet.GetNumPoints() - 1; cp >= 0; cp--) { ControlPoint *point = inNet.GetPoint(cp); // If the point was not modified, delete // Even get rid of edit locked points in this case if (!g_modifications.contains(point->GetId())) { point->SetEditLock(false); inNet.DeletePoint(cp); } // Else, remove the unwanted measures from the modified point else { for (int cm = point->GetNumMeasures() - 1; cm >= 0; cm--) { ControlMeasure *measure = point->GetMeasure(cm); // Even get rid of edit locked measures in this case if (point->GetRefMeasure() != measure && !g_modifications[point->GetId()].contains( measure->GetCubeSerialNumber())) { measure->SetEditLock(false); point->Delete(cm); } } } } } // Generate the TOLIST if wanted if (ui.WasEntered("TOLIST")) { SerialNumberList toList; SerialNumberList addSerials(ui.GetFileName("ADDLIST")); const QList<QString> snList = inNet.GetCubeSerials(); for (int i = 0; i < snList.size(); i++) { QString sn = snList[i]; if (addSerials.HasSerialNumber(sn)) toList.Add(addSerials.FileName(sn)); else if (fromSerials->HasSerialNumber(sn)) toList.Add(fromSerials->FileName(sn)); } IString name(ui.GetFileName("TOLIST")); std::fstream out_stream; out_stream.open(name.c_str(), std::ios::out); out_stream.seekp(0, std::ios::beg); //Start writing from beginning of file for (int f = 0; f < (int) toList.Size(); f++) out_stream << toList.FileName(f) << std::endl; out_stream.close(); } inNet.Write(outNetFile.expanded()); delete fromSerials; }