Isis 3 Programmer Reference
GridGraphicsItem.cpp
1#include "GridGraphicsItem.h"
2
3#include <cmath>
4#include <float.h>
5#include <iostream>
6
7#include <QDebug>
8#include <QGraphicsScene>
9#include <QPen>
10
11#include "Angle.h"
12#include "Distance.h"
13#include "GroundGrid.h"
14#include "IException.h"
15#include "Latitude.h"
16#include "Longitude.h"
17#include "MosaicGraphicsView.h"
18#include "MosaicSceneWidget.h"
19#include "Projection.h"
20#include "TProjection.h"
21#include "UniversalGroundMap.h"
22
23using namespace std;
24
25namespace Isis {
26 GridGraphicsItem::GridGraphicsItem(Latitude baseLat, Longitude baseLon,
27 Angle latInc, Angle lonInc, MosaicSceneWidget *projectionSrc,
28 int density, Latitude latMin, Latitude latMax,
29 Longitude lonMin, Longitude lonMax) {
30 setZValue(DBL_MAX);
31
32
33 if (latInc > Angle(0.0, Angle::Degrees) && lonInc > Angle(0.0, Angle::Degrees)) {
34 // Walk the grid, creating a QGraphicsLineItem for each line segment.
35 Projection *proj = projectionSrc->getProjection();
36 Projection::ProjectionType pType = proj->projectionType();
37
38 if (proj && pType == Projection::Triaxial && lonMin < lonMax && latMin < latMax) {
39 TProjection *tproj = (TProjection *) proj;
40 PvlGroup mappingGroup(tproj->Mapping());
41
42 Latitude minLat;
43 Latitude maxLat;
44 Latitude startLat;
45 Latitude endLat;
46
47
48 if (tproj->IsPositiveWest()) {
49 // GridGraphicsItem is written assuming positive east
50 // for all angles. On positive West, lons come in swapped so we
51 // need to account for this.
52 Longitude temp = lonMin;
53 lonMin = lonMax;
54 lonMax = temp;
55 }
56 if (mappingGroup["LatitudeType"][0] == "Planetographic") {
57
58 Distance equaRad(tproj->EquatorialRadius(), Distance::Meters);
59 Distance polRad(tproj->PolarRadius(), Distance::Meters);
60
61 minLat = Latitude(latMin.planetographic(Angle::Degrees), mappingGroup,
63 maxLat = Latitude(latMax.planetographic(Angle::Degrees), mappingGroup,
65 baseLat = Latitude(baseLat.degrees(), equaRad, polRad,
67
68 // Make sure our lat increment is non-zero
69 if (!qFuzzyCompare(latInc.radians(), 0.0)) {
70 startLat = baseLat;
71
72 // We need startLat to start above min, and be as close to min as possible
73 try {
74 while (startLat < minLat) {
75 startLat = startLat.add(latInc, mappingGroup);
76 }
77 }
78 catch (IException &) {
79 }
80
81 try {
82 while (startLat.add(latInc * -1, mappingGroup) >= minLat) {
83 startLat = startLat.add(latInc * -1, mappingGroup);
84 }
85 }
86 catch (IException &) {
87 // Do nothing if we hit up against a pole
88 }
89 }
90
91 endLat = baseLat;
92
93 // We need endLat to start below max, and be as close to max as possible
94 try {
95 while (endLat > maxLat) {
96 endLat = endLat.add(latInc * -1, mappingGroup);
97 }
98 }
99 catch (IException &) {
100 }
101
102
103 try {
104 while (endLat.add(latInc, mappingGroup) <= maxLat) {
105 endLat = endLat.add(latInc, mappingGroup);
106 }
107 }
108 catch (IException &) {
109 // Do nothing if we hit up against a pole
110 }
111 }
112 else {
113 minLat = Latitude(latMin.degrees(), mappingGroup,
115 maxLat = Latitude(latMax.degrees(), mappingGroup,
117
118
119 // Make sure our lat increment is non-zero
120 if (!qFuzzyCompare(latInc.radians(), 0.0)) {
121 startLat = Latitude(
122 baseLat - Angle(floor((baseLat - minLat) / latInc) * latInc), mappingGroup);
123
124 if (qFuzzyCompare(startLat.degrees(), -90.0))
125 startLat = Latitude(-90.0, mappingGroup, Angle::Degrees);
126 }
127
128 endLat = Latitude(
129 (long)((maxLat - startLat) / latInc) * latInc + startLat,
130 mappingGroup);
131 if (qFuzzyCompare(endLat.degrees(), 90.0))
132 endLat = Latitude(90.0, mappingGroup, Angle::Degrees);
133 }
134
135 Longitude minLon(lonMin.degrees(), mappingGroup,
137 Longitude maxLon(lonMax.degrees(), mappingGroup,
139
140 Longitude startLon;
141 // Make sure our lon increment is non-zero
142 if (!qFuzzyCompare(lonInc.radians(), 0.0)) {
143 startLon = Longitude(
144 baseLon - Angle(floor((baseLon - minLon) / lonInc) * lonInc));
145 }
146
147 Longitude endLon =
148 (long)((maxLon - startLon) / lonInc) * lonInc + startLon;
149
150 if (qFuzzyCompare( (endLon + lonInc).radians(), maxLon.radians() )) {
151 endLon = maxLon;
152 }
153
154 // Make sure our increments will move our lat/lon values... prevent infinite loops
155 if (!qFuzzyCompare( (startLat + latInc).radians(), startLat.radians() ) &&
156 !qFuzzyCompare( (startLon + lonInc).radians(), startLon.radians() )) {
157
158 int numCurvedLines = (int)ceil(((maxLat - minLat) / latInc) + 1);
159 numCurvedLines += (int)ceil(((maxLon - minLon) / lonInc) + 1);
160
161 int curvedLineDensity = density / numCurvedLines + 1;
162 Angle latRes((maxLon - minLon) / (double)curvedLineDensity);
163 Angle lonRes((maxLat - minLat) / (double)curvedLineDensity);
164
165 if (mappingGroup["LatitudeType"][0] == "Planetographic") {
166 lonRes = Angle(
167 (maxLat.planetographic() - minLat.planetographic()) / (double)curvedLineDensity,
169 }
170
171 if (latRes <= Angle(0, Angle::Degrees))
172 latRes = Angle(1E-10, Angle::Degrees);
173
174 if (lonRes <= Angle(0, Angle::Degrees))
175 lonRes = Angle(1E-10, Angle::Degrees);
176
177 bool firstIteration = true;
178 bool atMaxLat = false;
179 bool atMaxLon = false;
180
181 // We're looping like this to guarantee we hit the correct end position in
182 // the loop despite double math.
183
184
185 Latitude lat = minLat;
186 while(!atMaxLat) {
187 double previousX = 0;
188 double previousY = 0;
189 bool havePrevious = false;
190
191 for(Longitude lon = minLon; lon != maxLon + latRes; lon += latRes) {
192
193 if (lon > maxLon && !atMaxLon) {
194 lon = maxLon;
195 atMaxLon = true;
196 }
197
198 double x = 0;
199 double y = 0;
200
201 bool valid;
202
203 // Set ground according to lon direction to get correct X,Y values
204 // when drawing lines.
205 if (tproj->IsPositiveWest()) {
206 valid = tproj->SetGround(lat.degrees(), lon.positiveWest(Angle::Degrees));
207 }
208 else {
209 valid = tproj->SetGround(lat.degrees(), lon.positiveEast(Angle::Degrees));
210 }
211
212 if (valid) {
213 x = tproj->XCoord();
214 y = -1 * tproj->YCoord();
215
216 if(havePrevious) {
217 if(previousX != x || previousY != y) {
218 QGraphicsLineItem* latLine =
219 new QGraphicsLineItem(QLineF(previousX, previousY, x, y), this);
220 // Ensure the line is cosmetic
221 // (i.e. the line width is always 1 pixel wide on screen)
222 QPen pen;
223 pen.setCosmetic(true);
224 latLine->setPen(pen);
225 }
226 }
227 }
228
229 havePrevious = valid;
230 previousX = x;
231 previousY = y;
232 }
233
234 // if (firstIteration) {
235 // if (startLat.planetographic(Angle::Degrees) - latInc.degrees() < -90.0)
236 // lat = Latitude(-90.0, mappingGroup, Angle::Degrees);
237 // else {
238 // lat = Latitude(startLat.planetographic() - latInc.radians(), mappingGroup,
239 // Angle::Radians);
240 // }
241 // }
242
243 firstIteration = false;
244 atMaxLon = false;
245
246 Latitude nextLat;
247
248 try {
249 nextLat = lat.add(latInc, mappingGroup);
250 }
251 catch (IException &) {
252 nextLat = maxLat;
253 }
254
255 if (lat == minLat && minLat != startLat) {
256 // If our increment doesn't intersect the lat range, set ourselves to max.
257 if (startLat < minLat || startLat > maxLat) {
258 nextLat = maxLat;
259 }
260 else {
261 // Our increment lands inside the range, go to start and begin incrementing towards
262 // end.
263 nextLat = startLat;
264 }
265 }
266 else if (lat >= maxLat) {
267 atMaxLat = true;
268 }
269 else if (nextLat > endLat) {
270 nextLat = maxLat;
271 }
272
273 lat = nextLat;
274 }
275
276 firstIteration = true;
277 atMaxLat = false;
278 atMaxLon = false;
279
280 // Create the longitude grid lines
281 for (Longitude lon = minLon; lon != maxLon + lonInc; lon += lonInc) {
282 if (lon > endLon && lon < maxLon) {
283 lon = endLon;
284 }
285
286 if (lon > maxLon && !atMaxLon) {
287 lon = maxLon;
288 atMaxLon = true;
289 }
290
291 double previousX = 0;
292 double previousY = 0;
293 bool havePrevious = false;
294
295 Latitude lat = minLat;
296 while (!atMaxLat) {
297 double x = 0;
298 double y = 0;
299
300 // Set ground according to lon direction to get correct X,Y values
301 // when drawing lines.
302 bool valid;
303
304 if (tproj->IsPositiveWest()) {
305 double glon = tproj->Has180Domain() ? -1*lon.positiveEast(Angle::Degrees): lon.positiveWest(Angle::Degrees);
306 valid = tproj->SetGround(lat.degrees(), glon);
307 }
308 else {
309 valid = tproj->SetGround(lat.degrees(), lon.positiveEast(Angle::Degrees));
310 }
311
312 if (valid) {
313 x = tproj->XCoord();
314 y = -1 * tproj->YCoord();
315
316 if(havePrevious) {
317 if(previousX != x || previousY != y) {
318 QGraphicsLineItem* lonLine =
319 new QGraphicsLineItem(QLineF(previousX, previousY, x, y), this);
320 // Ensure the line is cosmetic
321 // (i.e. the line width is always 1 pixel wide on screen)
322 QPen pen;
323 pen.setCosmetic(true);
324 lonLine->setPen(pen);
325 }
326 }
327 }
328
329 havePrevious = valid;
330 previousX = x;
331 previousY = y;
332
333 if (lat >= maxLat) {
334 atMaxLat = true;
335 }
336 else {
337 lat = lat.add(lonRes, mappingGroup);
338 }
339 }
340
341 if (firstIteration)
342 lon = startLon - lonInc;
343
344 firstIteration = false;
345 atMaxLat = false;
346 }
347 }
348 }
349 }
350
351 setRect(calcRect());
352 }
353
354
355 GridGraphicsItem::~GridGraphicsItem() {
356 }
357
358
359 void GridGraphicsItem::paint(QPainter *painter,
360 const QStyleOptionGraphicsItem *style, QWidget * widget) {
361 }
362
363
364 QRectF GridGraphicsItem::boundingRect() const {
365 return m_boundingRect;
366 }
367
368
369 QRectF GridGraphicsItem::rect() const {
370 return m_boundingRect;
371 }
372
373
374 QRectF GridGraphicsItem::calcRect() const {
375 QRectF sceneRect;
376
377 foreach (QGraphicsItem *child, childItems()) {
378 sceneRect = sceneRect.united(child->boundingRect());
379 }
380
381 return sceneRect;
382 }
383
384
385 void GridGraphicsItem::setRect(QRectF newBoundingRect) {
386 if (m_boundingRect != newBoundingRect) {
387 prepareGeometryChange();
388 m_boundingRect = newBoundingRect;
389 }
390 }
391}
392
@ Degrees
Degrees are generally considered more human readable, 0-360 is one circle, however most math does not...
Definition Angle.h:56
@ Radians
Radians are generally used in mathematical equations, 0-2*PI is one circle, however these are more di...
Definition Angle.h:63
@ Meters
The distance is being specified in meters.
Definition Distance.h:43
@ Planetocentric
This is the universal (and default) latitude coordinate system.
Definition Latitude.h:91
ProjectionType
This enum defines the subclasses of Projection supported in Isis.
Definition Projection.h:166
@ Triaxial
These projections are used to map triaxial and irregular-shaped bodies.
Definition Projection.h:166
const double E
Sets some basic constants for use in ISIS programming.
Definition Constants.h:39
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
Namespace for the standard library.