Isis 3 Programmer Reference
PixelFOV.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include "PixelFOV.h"
8 
9 #include <QDebug>
10 #include <QList>
11 #include <QPoint>
12 #include <QPointF>
13 #include <QScopedPointer>
14 
15 #include <geos/geom/CoordinateArraySequence.h>
16 #include <geos/geom/CoordinateSequence.h>
17 #include <geos/geom/LineString.h>
18 #include <geos/geom/MultiPolygon.h>
19 #include <geos/geom/Polygon.h>
20 
21 #include "Camera.h"
22 #include "CameraDistortionMap.h"
23 #include "PolygonTools.h"
24 
25 using namespace std;
26 
27 namespace Isis {
28 
33  PixelFOV::PixelFOV() {
34  }
35 
36 
41  PixelFOV::~PixelFOV() {
42  }
43 
44 
67  QList< QList<QPointF> > PixelFOV::latLonVertices(Camera &camera,
68  const double sample,
69  const double line,
70  const int numIfovs) const {
71 
72  // Output list
73  QList< QList<QPointF> > boundaryVertices;
74 
75  // Polygon pieces are sorted based on average longitude. lowerVertices contains pieces with
76  // average longitude less than 180. upperVertices contains the rest.
77  QList<QPointF> lowerVertices;
78  QList<QPointF> upperVertices;
79 
80 
81  if (numIfovs < 1) {
82  QString msg = "The number of instantaneous field of views must be a positive integer.";
83  throw IException(IException::Programmer, msg, _FILEINFO_);
84  }
85 
86  // If computing an instantaneous fov
87  if (numIfovs == 1) {
88 
89  camera.SetImage(line, sample);
90  boundaryVertices.append(instantaneousFov(camera));
91  return boundaryVertices;
92  }
93 
94  // If computing a full fov
95  else {
96 
97  // Collect the instantaneous fovs
98  double timeStep = 0.0;
99  try {
100  timeStep = camera.exposureDuration(line, sample)/(numIfovs - 1);
101  }
102  catch (IException &e) {
103  throw IException(e, IException::Unknown, "Unable to get FOV for full exposure.", _FILEINFO_);
104  }
105  for (int i = 0; i < numIfovs; i++) {
106  camera.SetImage(line, sample,
107  timeStep * i - camera.exposureDuration(line, sample)/2);
108  QList<QPointF> iFov = instantaneousFov(camera);
109 
110  // If the Ifov does not intersect the target move on
111  if (iFov.isEmpty()) {
112  break;
113  }
114 
115  // Determine if the Ifov needs to be split
116  bool crosses = false;
117  int numVerts = iFov.size();
118  for (int j = 0; j < numVerts - 1; j++) {
119  if (fabs(iFov[j].y() - iFov[j+1].y()) > 180.0) {
120  crosses = true;
121  }
122  }
123 
124  // If it needs to be split
125  if (crosses) {
126  QList< QList<QPointF> > splitIFov = splitIfov(iFov);
127 
128  // Sort the pieces based on average longitude.
129  for (int j = 0; j < splitIFov.size(); j++) {
130  double averageLong = 0;
131  int numSubVerts = splitIFov[j].size();
132  for (int k = 0; k < numSubVerts; k++) {
133  averageLong += splitIFov[j][k].y();
134  }
135  averageLong = averageLong / numSubVerts;
136  if (averageLong < 180) {
137  lowerVertices.append(splitIFov[j]);
138  }
139  else {
140  upperVertices.append(splitIFov[j]);
141  }
142  }
143  }
144 
145  // If it does not need to be split
146  else {
147  // Put the Ifov in the proper class
148  double averageLong = 0;
149  for (int j = 0; j < numVerts; j++) {
150  averageLong += iFov[j].y();
151  }
152  averageLong = averageLong / numVerts;
153  if (averageLong < 180) {
154  lowerVertices.append(iFov);
155  }
156  else {
157  upperVertices.append(iFov);
158  }
159  }
160  }
161  }
162 
163  // Compute convex hulls for the two sets of points and append to output list.
164  // If a set is empty then it is not output.
165  if (!lowerVertices.isEmpty()) {
166  boundaryVertices.append(envelope(lowerVertices));
167  }
168  if (!upperVertices.isEmpty()) {
169  boundaryVertices.append(envelope(upperVertices));
170  }
171 
172  return boundaryVertices;
173  }
174 
175 
188  QList<QPointF> PixelFOV::instantaneousFov(Camera &camera) const {
189 
190  QList<QPointF> vertices;
191 
192  QList<QPointF> offsets = camera.PixelIfovOffsets();
193  int numVertices = offsets.size();
194 
195  double saveLook[3];
196  double newLook[3];
197  double unitNewLook[3];
198  camera.LookDirection(saveLook);
199  double focalLength = camera.FocalLength();
200 
201  // For highly distorted instruments, take fpx, fpy (which are undistorted) convert to distorted,
202  // add offsets, undistort. Only need to worry about if distortion high on a pixel to pixel
203  // basis. If this is done, save samp/line and reset the camera (SetImage).
204  double scale = focalLength / saveLook[2];
205  for (int i = 0; i < numVertices; i++) {
206  double focalPlaneX = saveLook[0] * scale;
207  double focalPlaneY = saveLook[1] * scale;
208  focalPlaneX += offsets[i].x();
209  focalPlaneY += offsets[i].y();
210  newLook[0] = focalPlaneX;
211  newLook[1] = focalPlaneY;
212  newLook[2] = camera.DistortionMap()->UndistortedFocalPlaneZ();
213  vhat_c(newLook, unitNewLook);
214  if (camera.SetLookDirection(unitNewLook)) {
215  vertices.append(QPointF(camera.UniversalLatitude(), camera.UniversalLongitude()));
216  }
217  }
218  // Reset look direction back to center of pixel
219  camera.SetLookDirection(saveLook);
220  return vertices;
221  }
222 
223 
231  QList<QPointF> PixelFOV::envelope(QList<QPointF> vertices) const{
232 
233  //Put the vertices in a line string
234  QScopedPointer<geos::geom::CoordinateSequence> points(new geos::geom::CoordinateArraySequence());
235 
236  for (int i = 0; i < vertices.size(); i++) {
237  points->add(geos::geom::Coordinate(vertices[i].x(), vertices[i].y()));
238  }
239  QScopedPointer<geos::geom::LineString> pointString(Isis::globalFactory->createLineString(
240  points.take()));
241 
242  //Compute a convex hull for the line string
243  QScopedPointer<geos::geom::Geometry> boundingHull(pointString->convexHull());
244 
245  //Get the points
246  geos::geom::CoordinateSequence *boundingPoints = boundingHull->getCoordinates();
247 
248  QList<QPointF> boundingVertices;
249  for (unsigned int i = 0; i < boundingPoints->getSize(); i++) {
250  boundingVertices.append(QPointF(boundingPoints->getAt(i).x,boundingPoints->getAt(i).y));
251  }
252 
253  return boundingVertices;
254  }
255 
256 
268  QList< QList<QPointF> > PixelFOV::splitIfov(QList<QPointF> vertices) const{
269  // Create output list.
270  QList< QList<QPointF> > splitPoints;
271 
272  // Create a polygon to split.
273  QScopedPointer<geos::geom::CoordinateSequence> pts(new geos::geom::CoordinateArraySequence());
274  for (int i = 0; i < vertices.size(); i++) {
275  pts->add(geos::geom::Coordinate(vertices[i].y(), vertices[i].x()));
276  }
277  pts->add(geos::geom::Coordinate(vertices[0].y(), vertices[0].x()));
278  QScopedPointer<geos::geom::Polygon> originalPoly(Isis::globalFactory->createPolygon(
279  globalFactory->createLinearRing(pts.take()),
280  NULL));
281 
282  // Split the polygon
283  QScopedPointer<geos::geom::MultiPolygon> splitPolygons(
284  PolygonTools::SplitPolygonOn360(originalPoly.data()));
285 
286  // Extract the vertices coordinates.
287  QList< QList<QPointF> > splitVertices;
288  for (unsigned int i = 0; i < splitPolygons->getNumGeometries(); i++) {
289  QList<QPointF> subVertices;
290  // The following objects don't need to be deleted, as the splitPolygons object will
291  // delete them when it is deleted.
292  const geos::geom::Polygon *subPolygon =
293  dynamic_cast<const geos::geom::Polygon *>(splitPolygons->getGeometryN(i));
294  geos::geom::CoordinateSequence *subCoordinates = subPolygon->
295  getExteriorRing()->getCoordinates();
296  for (unsigned int j = 0; j < subCoordinates->getSize(); j++) {
297  subVertices.append(QPointF(subCoordinates->getAt(j).y,subCoordinates->getAt(j).x));
298  }
299 
300  // Put the vertices in the output list.
301  splitPoints.append(subVertices);
302 
303  }
304 
305  return splitPoints;
306  }
307 }
Isis::Camera::exposureDuration
virtual double exposureDuration() const
Return the exposure duration for the pixel that the camera is set to.
Definition: Camera.cpp:3063
QList
This is free and unencumbered software released into the public domain.
Definition: BoxcarCachingAlgorithm.h:13
Isis::Camera::SetImage
virtual bool SetImage(const double sample, const double line)
Sets the sample/line values of the image to get the lat/lon values.
Definition: Camera.cpp:154
Isis::Camera::DistortionMap
CameraDistortionMap * DistortionMap()
Returns a pointer to the CameraDistortionMap object.
Definition: Camera.cpp:2826
Isis::CameraDistortionMap::UndistortedFocalPlaneZ
double UndistortedFocalPlaneZ() const
Gets the z-value in the undistorted focal plane coordinate system.
Definition: CameraDistortionMap.cpp:260
Isis::Camera
Definition: Camera.h:236
Isis::Sensor::UniversalLongitude
virtual double UniversalLongitude() const
Returns the positive east, 0-360 domain longitude, in degrees, at the surface intersection point in t...
Definition: Sensor.cpp:233
Isis::Sensor::LookDirection
void LookDirection(double v[3]) const
Returns the look direction in the camera coordinate system.
Definition: Sensor.cpp:523
Isis::Camera::PixelIfovOffsets
virtual QList< QPointF > PixelIfovOffsets()
Returns the pixel ifov offsets from center of pixel, which defaults to the (pixel pitch * summing mod...
Definition: Camera.cpp:2755
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::Camera::FocalLength
double FocalLength() const
Returns the focal length.
Definition: Camera.cpp:2732
std
Namespace for the standard library.
Isis::Sensor::SetLookDirection
bool SetLookDirection(const double v[3])
Sets the look direction of the spacecraft.
Definition: Sensor.cpp:141
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16
Isis::Sensor::UniversalLatitude
virtual double UniversalLatitude() const
Returns the planetocentric latitude, in degrees, at the surface intersection point in the body fixed ...
Definition: Sensor.cpp:210