1 /***************************************************************************
2 * This file is part of Tecorrec. *
3 * Copyright 2008 James Hogan <james@albanarts.com> *
5 * Tecorrec is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 2 of the License, or *
8 * (at your option) any later version. *
10 * Tecorrec is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with Tecorrec. If not, write to the Free Software Foundation, *
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
22 * @brief Manages data for a globe.
26 #include "tcGeoImageData.h"
27 #include "tcLandsatData.h"
28 #include "tcProcessingData.h"
29 #include "tcCustomSunDirection.h"
30 #include "tcSrtmModel.h"
39 * Constructors + destructor
42 /// Primary constructor.
43 tcGlobe::tcGlobe(double meanRadius
)
44 : m_meanRadius(meanRadius
)
45 , m_elevation(new tcSrtmModel
[2])
48 , m_elevationInterpolation(1.0f
)
49 , m_colourCoding(NoColourCoding
)
52 for (int i
= 0; i
< 2; ++i
)
54 m_elevationMode
[i
] = CorrectedElevation
;
56 for (int i
= 0; i
< 3; ++i
)
58 m_colourMapping
[i
][0] = 0;
59 m_colourMapping
[i
][1] = 2-i
;
61 for (int i
= 3; i
< 6; ++i
)
63 m_colourMapping
[i
][0] = -1;
64 m_colourMapping
[i
][1] = -1;
66 QList
<tcShadowClassifyingData
*> datasets
;
67 // this one is a bit rubbish, the sun is too high
68 //datasets << new tcLandsatData("/home/james/cs/pro/data/LE71950281999206EDC01/", m_elevation);
70 // high azimuth, low elevation
71 datasets
<< new tcLandsatData("/home/james/cs/pro/data/LE71950282001307EDC00/", m_elevation
);
72 datasets
<< new tcLandsatData("/home/james/cs/pro/data/LE71950282002006EDC00/", m_elevation
);
73 //datasets << new tcLandsatData("/home/james/cs/pro/data/LE71950282002310EDC00/", m_elevation);
75 datasets
<< new tcLandsatData("/home/james/cs/pro/data/LE71950282000081EDC00/", m_elevation
);
76 //datasets << new tcLandsatData("/home/james/cs/pro/data/etp195r28_5t19900910/", m_elevation);
79 #define NEW_DATASET(AZ, EL, TILT) \
80 datasets << new tcCustomSunDirection(m_elevation+1, tcGeo((180.0-AZ)*M_PI/180, (EL)*M_PI/180), \
81 tcGeo(7.0*M_PI/180, (TILT)*M_PI/180), true)
82 // Relative to our region of interest
83 #define NEW_DATASET_REL(AZ, EL) \
84 datasets << new tcCustomSunDirection(m_elevation+1, tcGeo((180.0-AZ)*M_PI/180, (EL)*M_PI/180), \
85 tcGeo(7.0*M_PI/180, 46.0*M_PI/180))
87 // Mustn't be co-planar
88 for (int i
= -10; i
<= 10; i
+= 20)
90 for (int j
= 30; j
<= 150; j
+= 10)
92 //NEW_DATASET((double)90, (double)j, (double)i);
96 foreach (tcShadowClassifyingData
* dataset
, datasets
)
100 addImagery(new tcProcessingData(datasets
, m_elevation
));
106 foreach (tcGeoImageData
* data
, m_imagery
)
110 delete [] m_elevation
;
117 /// Add some imagery data.
118 void tcGlobe::addImagery(tcGeoImageData
* data
)
120 m_imagery
.push_back(data
);
123 /// Get the imagery data.
124 const QList
<tcGeoImageData
*>& tcGlobe::imagery() const
129 /// Get the elevation model.
130 tcSrtmModel
* tcGlobe::dem() const
139 /// Draw a line of latitude.
140 void tcGlobe::drawLineOfLatitude(double latitude
) const
142 double z
= sin(latitude
) * m_meanRadius
;
143 double xy
= cos(latitude
) * m_meanRadius
;
144 glBegin(GL_LINE_LOOP
);
146 for (int lon
= 0; lon
< 360; ++lon
)
148 glVertex3d(xy
*sin(M_PI
/180*lon
), xy
*cos(M_PI
/180*lon
), z
);
155 void tcGlobe::renderCell(tcObserver
* const observer
, const tcGeo
& swCorner
, const tcGeo
& neCorner
, int samples
, bool normals
,
156 bool northEdge
, bool eastEdge
, bool southEdge
, bool westEdge
) const
158 // Sample at a sensible level
159 tcSrtmModel::RenderState state
;
160 m_elevation
->sampleAlign(swCorner
, neCorner
, &state
, samples
);
162 if (state
.moreAvailableLon
|| state
.moreAvailableLat
)
164 // Find the square distances to each corner
166 glGetv(GL_MODELVIEW_MATRIX
, &modelview
);
167 tcGeo geoCorners
[4] = {
168 tcGeo(swCorner
.lon(), swCorner
.lat()),
169 tcGeo(swCorner
.lon(), neCorner
.lat()),
170 tcGeo(neCorner
.lon(), neCorner
.lat()),
171 tcGeo(neCorner
.lon(), swCorner
.lat())
173 GLvec3d cartCorners
[4];
175 double altitudeMean
= m_meanRadius
+ altitudeAt((geoCorners
[0] + geoCorners
[1] + geoCorners
[2] + geoCorners
[3])/4);
176 for (int i
= 0; i
< 4; ++i
)
178 cartCorners
[i
] = (GLvec3d
)geoCorners
[i
] * altitudeMean
;
179 toCorners
[i
] = (modelview
*(cartCorners
[i
], 1.0)).slice
<0,3>().sqr();
180 // Cull faces which are roughly backfacing
183 if ((modelview
*(cartCorners
[i
], 0.0))[2] <= 0.0)
190 // Decide whether to subdivide
191 float diagonal
= ( (cartCorners
[0]-cartCorners
[2]).sqr()
192 + (cartCorners
[3]-cartCorners
[1]).sqr())/2*4;
193 // If it is disproportionately tall, only subdivide horizontally
194 bool tall
= (cartCorners
[1] - cartCorners
[0]).sqr() > (cartCorners
[3] - cartCorners
[0]).sqr()*4.0
195 || (cartCorners
[2] - cartCorners
[3]).sqr() > (cartCorners
[2] - cartCorners
[1]).sqr()*4.0;
196 // If it is disproportionately wide, only subdivide vertically
197 bool wide
= (cartCorners
[3] - cartCorners
[0]).sqr() > (cartCorners
[1] - cartCorners
[0]).sqr()*4.0
198 || (cartCorners
[2] - cartCorners
[1]).sqr() > (cartCorners
[2] - cartCorners
[3]).sqr()*4.0;
199 bool subdivide
= true;
200 for (int i
= 0; i
< 4; ++i
)
202 if (toCorners
[i
] > diagonal
)
214 renderCell(observer
, geoCorners
[0], (geoCorners
[3] + geoCorners
[2]) * 0.5, samples
, normals
,
215 false, eastEdge
, southEdge
, westEdge
);
217 renderCell(observer
, (geoCorners
[0] + geoCorners
[1]) * 0.5, geoCorners
[2], samples
, normals
,
218 northEdge
, eastEdge
, false, westEdge
);
220 else if (wide
&& !tall
)
223 renderCell(observer
, geoCorners
[0], (geoCorners
[1] + geoCorners
[2]) * 0.5, samples
, normals
,
224 northEdge
, false, southEdge
, westEdge
);
226 renderCell(observer
, (geoCorners
[0] + geoCorners
[3]) * 0.5, geoCorners
[2], samples
, normals
,
227 northEdge
, eastEdge
, southEdge
, false);
232 renderCell(observer
, geoCorners
[0], (geoCorners
[0] + geoCorners
[2]) * 0.5, samples
, normals
,
233 false, false, southEdge
, westEdge
);
235 renderCell(observer
, (geoCorners
[0] + geoCorners
[3]) * 0.5, (geoCorners
[3] + geoCorners
[2]) * 0.5, samples
, normals
,
236 false, eastEdge
, southEdge
, false);
238 renderCell(observer
, (geoCorners
[0] + geoCorners
[1]) * 0.5, (geoCorners
[1] + geoCorners
[2]) * 0.5, samples
, normals
,
239 northEdge
, false, false, westEdge
);
241 renderCell(observer
, (geoCorners
[0] + geoCorners
[2]) * 0.5, geoCorners
[2], samples
, normals
,
242 northEdge
, eastEdge
, false, false);
251 glColor3f(0.0f
, 0.0f
, 1.0f
);
255 glColor3f(1.0f
, 0.5f
, 0.0f
);
258 glBegin(GL_LINE_LOOP
);
259 for (int i
= 0; i
< 4; ++i
)
261 glVertex3(cartCorners
[i
]);
266 #define EDGE_KERNEL_1 \
267 glColor4f(0.5f, 0.3f, 0.2f, 1.0f); \
268 glVertex3(dir * (m_meanRadius+alt)); \
269 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
270 glVertex3(dir * m_meanRadius);
271 #define EDGE_KERNEL_2 \
272 glColor4f(0.5f, 0.5f, 1.0f, 1.0f); \
273 glVertex3(dir * m_meanRadius); \
274 glColor4f(0.5f, 0.5f, 1.0f, 1.0f); \
275 glVertex3(dir * (m_meanRadius-(accurate ? 100.0 : 1000.0)));
276 #define EDGE_KERNEL_3 \
277 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
278 glVertex3(dir * (m_meanRadius-(accurate ? 100.0 : 1000.0))); \
279 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
280 glVertex3(dir * (m_meanRadius-5000.0));
281 #define EDGE_KERNEL_4 \
282 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
283 glVertex3(dir * (m_meanRadius-5000.0)); \
284 glColor4f(1.0f, 0.0f, 0.0f, 0.5f); \
285 glVertex3(dir * (m_meanRadius-8000.0));
286 #define EDGE_KERNEL(STAGE, EDGE, SAMPLES, LON, LAT) \
289 glBegin(GL_TRIANGLE_STRIP); \
290 for (int i = 0; i < SAMPLES; ++i) \
293 bool accurate = true; \
294 double alt = altitudeAt(state, (LON), (LAT), &coord, &accurate); \
295 GLvec3d dir = coord; \
296 EDGE_KERNEL_##STAGE \
300 #define EDGE(STAGE) \
301 EDGE_KERNEL(STAGE, north, state.samplesLon, i, state.samplesLat-1); \
302 EDGE_KERNEL(STAGE, east, state.samplesLat, state.samplesLon-1, i); \
303 EDGE_KERNEL(STAGE, south, state.samplesLon, i, 0); \
304 EDGE_KERNEL(STAGE, west, state.samplesLat, 0, i);
309 float normColours
[3][2][3] = {
310 { { 1.0f
, 1.0f
, 1.0f
}, { 1.0f
, 0.0f
, 0.0f
} },
311 { { 1.0f
, 1.0f
, 1.0f
}, { 0.0f
, 1.0f
, 0.0f
} },
312 { { 1.0f
, 1.0f
, 1.0f
}, { 0.0f
, 0.0f
, 1.0f
} }
315 for (int i
= 0; i
< state
.samplesLon
; ++i
)
317 for (int j
= 0; j
< state
.samplesLat
; ++j
)
320 bool accurate
= true;
321 double alt
= altitudeAt(state
, i
, j
, &coord
, &accurate
);
322 for (int n
= 0; n
< 3; ++n
)
324 GLvec3f norm
= normalAt(n
, coord
);
329 double rad
= m_meanRadius
+ alt
;
330 glColor3fv(normColours
[n
][0]);
331 glVertex3(dir
* rad
);
332 glColor3fv(normColours
[n
][1]);
333 glVertex3(dir
* rad
+ norm
*50.0f
);
343 // Render the solid rock walls
344 glDisable(GL_CULL_FACE
);
349 glEnable(GL_CULL_FACE
);
353 /// @todo cache one edge of strip to save time on other
354 for (int i
= 0; i
< state
.samplesLon
-1; ++i
)
356 glBegin(GL_TRIANGLE_STRIP
);
358 for (int j
= 0; j
< state
.samplesLat
; ++j
)
360 for (int k
= 0; k
< 2; ++k
)
363 bool accurate
= true;
364 double alt
= altitudeAt(state
, i
+k
, j
, &coord
, &accurate
);
367 foreach (tcGeoImageData
* imagery
, m_imagery
)
369 imagery
->texCoord(coord
);
374 glColor4f(0.5f
, 0.5f
, 1.0f
, 1.0f
);
378 glColor4f(1.0f
, 1.0f
, 1.0f
, 1.0f
);
379 //glColor4f(1.0f, 1.0f-(float)alt/3278.0f, 0.0f, 1.0f);
381 // Colour code if applicable
382 if (m_colourCoding
== ElevationSampleAlignment
)
384 if (state
.moreAvailableLon
&& state
.moreAvailableLat
)
386 glColor3f(1.0f
, 0.0f
, 1.0f
);
388 else if (state
.moreAvailableLon
)
390 glColor3f(1.0f
, 0.0f
, 0.0f
);
392 else if (state
.moreAvailableLat
)
394 glColor3f(0.0f
, 0.0f
, 1.0f
);
398 glColor3f(0.0f
, 1.0f
, 0.0f
);
402 double rad
= m_meanRadius
+ alt
;
403 glVertex3(dir
* rad
);
412 /// Render from the POV of an observer.
413 void tcGlobe::render(tcObserver
* const observer
, bool adaptive
, const tcGeo
* extent
)
415 /// @todo use a really simple fragment shader to cull backfacing lines
418 glColor3f(0.0f
, 1.0f
, 0.0f
);
419 for (int lat
= -75; lat
<= 75; lat
+= 15)
423 drawLineOfLatitude(M_PI
/180*lat
);
427 double tropic
= (23.0 + 26.0/60 + 22.0/3600) * M_PI
/180;
429 glColor3f(1.0f
, 0.0f
, 0.0f
);
430 drawLineOfLatitude(0.0);
431 // Tropics (Capricorn and Cancer)
432 glColor3f(1.0f
, 0.0f
, 1.0f
);
433 drawLineOfLatitude(-tropic
);
434 glColor3f(1.0f
, 1.0f
, 0.0f
);
435 drawLineOfLatitude(+tropic
);
436 // Arctic and Antarctic Circles
437 glColor3f(1.0f
, 1.0f
, 1.0f
);
438 drawLineOfLatitude(+M_PI
/2 - tropic
);
439 drawLineOfLatitude(-M_PI
/2 + tropic
);
441 // Lines of longitude
442 for (int lon
= 0; lon
< 360; lon
+= 15)
444 double x
= sin(M_PI
/180*lon
) * m_meanRadius
;
445 double y
= -cos(M_PI
/180*lon
) * m_meanRadius
;
451 glColor3f(1.0f
, lon
/180, 0.0f
);
458 glBegin(GL_LINE_STRIP
);
460 for (int lat
= minLat
; lat
<= maxLat
; ++lat
)
462 double z
= cos(M_PI
/180*lat
) * m_meanRadius
;
463 double xy
= sin(M_PI
/180*lat
);
464 glVertex3d(xy
*x
, xy
*y
, z
);
471 glColor3f(0.0f
, 1.0f
, 0.0f
);
476 // Draw data diagramatically
477 foreach (tcGeoImageData
* imagery
, m_imagery
)
479 imagery
->renderSchematic(m_meanRadius
, observer
);
484 int colourMapping
[6];
485 for (int band
= 0; band
< m_imagery
.size(); ++band
)
487 for (int i
= 0; i
< 6; ++i
)
489 colourMapping
[i
] = (m_colourMapping
[i
][0] == band
? m_colourMapping
[i
][1] : -1);
491 tcGeoImageData
* imagery
= m_imagery
[band
];
492 imagery
->setupThumbnailRendering(6, colourMapping
);
497 for (int lon
= -180; lon
< 180; lon
+= dlon
)
499 for (int lat
= -90; lat
< 90; lat
+= dlat
)
501 tcGeo
sw(M_PI
/180 * (lon
), M_PI
/180 * (lat
));
502 tcGeo
ne(M_PI
/180 * (lon
+dlon
), M_PI
/180 * (lat
+dlat
));
503 renderCell(observer
, sw
, ne
, adaptive
? 5 : 0, false);
506 foreach (tcGeoImageData
* imagery
, m_imagery
)
508 imagery
->setupNormalRendering();
510 for (int lon
= -180; lon
< 180; lon
+= dlon
)
512 for (int lat
= -90; lat
< 90; lat
+= dlat
)
514 tcGeo
sw(M_PI
/180 * (lon
), M_PI
/180 * (lat
));
515 tcGeo
ne(M_PI
/180 * (lon
+dlon
), M_PI
/180 * (lat
+dlat
));
516 renderCell(observer
, sw
, ne
, adaptive
? 5 : 0, true);
522 tcGeo sw
= extent
[0];
523 tcGeo ne
= extent
[1];
524 if (sw
.lon() > ne
.lon())
527 ne
.setLon(extent
[0].lon());
529 if (sw
.lat() > ne
.lat())
532 ne
.setLat(extent
[0].lat());
534 int colourMapping
[6];
535 for (int band
= 0; band
< m_imagery
.size(); ++band
)
537 for (int i
= 0; i
< 6; ++i
)
539 colourMapping
[i
] = (m_colourMapping
[i
][0] == band
? m_colourMapping
[i
][1] : -1);
541 tcGeoImageData
* imagery
= m_imagery
[band
];
542 imagery
->setupDetailedRendering(6, colourMapping
, sw
, ne
);
544 /// @todo If it is really big, split it
545 renderCell(observer
, sw
, ne
, adaptive
? 16 : 0, false, true, true, true, true);
546 foreach (tcGeoImageData
* imagery
, m_imagery
)
548 imagery
->setupNormalRendering();
550 renderCell(observer
, sw
, ne
, adaptive
? 16 : 0, true, true, true, true, true);
552 foreach (tcGeoImageData
* imagery
, m_imagery
)
554 imagery
->finishRendering();
558 /// Set the elevation mode to render in.
559 void tcGlobe::setElevationMode(int dem
, ElevationMode mode
)
561 Q_ASSERT(dem
>= 0 && dem
< 2);
562 m_elevationMode
[dem
] = mode
;
565 /// Set the level of interpolation.
566 void tcGlobe::setElevationInterpolation(float interpolation
)
568 m_elevationInterpolation
= interpolation
;
571 /// Set the elevation data set name.
572 void tcGlobe::setElevationDataSet(int dem
, const QString
& name
)
574 Q_ASSERT(dem
>= 0 && dem
< 2);
575 m_elevation
[dem
].setDataSet(name
);
578 /// Set colour coding method.
579 void tcGlobe::setColourCoding(ColourCoding colourCoding
)
581 m_colourCoding
= colourCoding
;
584 /// Adjust the mapping between bands and colour channels.
585 void tcGlobe::setColourMapping(int outputChannel
, int inputBand
, int inputGroup
)
587 if (outputChannel
< 6)
589 m_colourMapping
[outputChannel
][0] = inputGroup
;
590 m_colourMapping
[outputChannel
][1] = inputBand
;
598 /// Get the mean radius.
599 double tcGlobe::meanRadius() const
604 /// Get the altitude above sea level at a sample in a render state.
605 double tcGlobe::altitudeAt(const tcSrtmModel::RenderState
& state
, int x
, int y
, tcGeo
* outCoord
, bool* isAccurate
) const
616 m_elevationInterpolation
< 1.0f
,
617 m_elevationInterpolation
> 0.0f
619 for (int i
= 0; i
< 2; ++i
)
621 switch (m_elevationMode
[i
])
627 alt
[i
] = m_elevation
[i
].altitudeAt(state
, x
, y
, outCoord
, false, &accurate
[i
]);
634 case CorrectedElevation
:
637 alt
[i
] = m_elevation
[i
].altitudeAt(state
, x
, y
, outCoord
, true, &accurate
[i
]);
645 double result
= alt
[0]*(1.0-m_elevationInterpolation
) + alt
[1]*m_elevationInterpolation
;
648 *isAccurate
= accurate
[0] && accurate
[1];
653 /// Get the altitude above sea level at a coordinate.
654 double tcGlobe::altitudeAt(const tcGeo
& coord
, bool* isAccurate
) const
665 m_elevationInterpolation
< 1.0f
,
666 m_elevationInterpolation
> 0.0f
668 for (int i
= 0; i
< 2; ++i
)
670 switch (m_elevationMode
[i
])
676 alt
[i
] = m_elevation
[i
].altitudeAt(coord
, false, &accurate
[i
]);
683 case CorrectedElevation
:
686 alt
[i
] = m_elevation
[i
].altitudeAt(coord
, true, &accurate
[i
]);
694 double result
= alt
[0]*(1.0-m_elevationInterpolation
) + alt
[1]*m_elevationInterpolation
;
697 *isAccurate
= accurate
[0] && accurate
[1];
702 /// Get the radius at a coordinate.
703 double tcGlobe::radiusAt(const tcGeo
& coord
) const
705 return m_meanRadius
+ altitudeAt(coord
);
708 /// Get the texture coordinate of the effective texture at a geographical coordinate.
709 maths::Vector
<2,double> tcGlobe::textureCoordOfGeo(const tcGeo
& coord
) const
711 /// @todo Reimplement tcGlobe::textureCoordOfGeo with multiple imagery
712 Q_ASSERT(0 && "Reimplement tcGlobe::TextureCoordOfGeo with multiple imagery");
713 return m_imagery
[0]->geoToEffectiveTex() * coord
;
716 /// Get the current normal at a coordinate.
717 maths::Vector
<3,float> tcGlobe::normalAt(int norm
, const tcGeo
& coord
) const
719 Q_ASSERT(norm
>= 0 && norm
< 3);
720 if (m_colourMapping
[3+norm
][0] >= 0)
722 return m_imagery
[m_colourMapping
[3+norm
][0]]->normalAt(norm
, coord
);
726 return maths::Vector
<3,float>(0.0f
);