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 "tcSrtmModel.h"
37 * Constructors + destructor
40 /// Primary constructor.
41 tcGlobe::tcGlobe(double meanRadius
)
42 : m_meanRadius(meanRadius
)
43 , m_elevation(new tcSrtmModel())
45 , m_elevationMode(CorrectedElevation
)
46 , m_elevationCorrection(0.0f
)
47 , m_colourCoding(NoColourCoding
)
50 for (int i
= 0; i
< 3; ++i
)
52 m_colourMapping
[i
] = 2-i
;
54 //setImagery(new tcLandsatData("/home/james/cs/pro/data/LE71950281999206EDC01/", m_elevation));
55 setImagery(new tcLandsatData("/home/james/cs/pro/data/LE71950282000081EDC00/", m_elevation
));
67 /// Set the imagery data.
68 void tcGlobe::setImagery(tcGeoImageData
* data
)
73 /// Get the imagery data.
74 tcGeoImageData
* tcGlobe::imagery() const
79 /// Get the elevation model.
80 tcSrtmModel
* tcGlobe::dem() const
89 /// Draw a line of latitude.
90 void tcGlobe::drawLineOfLatitude(double latitude
) const
92 double z
= sin(latitude
) * m_meanRadius
;
93 double xy
= cos(latitude
) * m_meanRadius
;
94 glBegin(GL_LINE_LOOP
);
96 for (int lon
= 0; lon
< 360; ++lon
)
98 glVertex3d(xy
*sin(M_PI
/180*lon
), xy
*cos(M_PI
/180*lon
), z
);
105 void tcGlobe::renderCell(tcObserver
* const observer
, const tcGeo
& swCorner
, const tcGeo
& neCorner
, int samples
,
106 bool northEdge
, bool eastEdge
, bool southEdge
, bool westEdge
) const
108 // Sample at a sensible level
109 tcSrtmModel::RenderState state
;
110 m_elevation
->sampleAlign(swCorner
, neCorner
, &state
, samples
);
112 if (state
.moreAvailableLon
|| state
.moreAvailableLat
)
114 // Find the square distances to each corner
116 glGetv(GL_MODELVIEW_MATRIX
, &modelview
);
117 tcGeo geoCorners
[4] = {
118 tcGeo(swCorner
.lon(), swCorner
.lat()),
119 tcGeo(swCorner
.lon(), neCorner
.lat()),
120 tcGeo(neCorner
.lon(), neCorner
.lat()),
121 tcGeo(neCorner
.lon(), swCorner
.lat())
123 GLvec3d cartCorners
[4];
125 double altitudeMean
= m_meanRadius
+ altitudeAt((geoCorners
[0] + geoCorners
[1] + geoCorners
[2] + geoCorners
[3])/4);
126 for (int i
= 0; i
< 4; ++i
)
128 cartCorners
[i
] = (GLvec3d
)geoCorners
[i
] * altitudeMean
;
129 toCorners
[i
] = (modelview
*(cartCorners
[i
], 1.0)).slice
<0,3>().sqr();
130 // Cull faces which are roughly backfacing
131 if ((modelview
*(cartCorners
[i
], 0.0))[2] <= 0.0)
137 // Decide whether to subdivide
138 float diagonal
= ( (cartCorners
[0]-cartCorners
[2]).sqr()
139 + (cartCorners
[3]-cartCorners
[1]).sqr())/2*4;
140 // If it is disproportionately tall, only subdivide horizontally
141 bool tall
= (cartCorners
[1] - cartCorners
[0]).sqr() > (cartCorners
[3] - cartCorners
[0]).sqr()*4.0
142 || (cartCorners
[2] - cartCorners
[3]).sqr() > (cartCorners
[2] - cartCorners
[1]).sqr()*4.0;
143 // If it is disproportionately wide, only subdivide vertically
144 bool wide
= (cartCorners
[3] - cartCorners
[0]).sqr() > (cartCorners
[1] - cartCorners
[0]).sqr()*4.0
145 || (cartCorners
[2] - cartCorners
[1]).sqr() > (cartCorners
[2] - cartCorners
[3]).sqr()*4.0;
146 bool subdivide
= true;
147 for (int i
= 0; i
< 4; ++i
)
149 if (toCorners
[i
] > diagonal
)
161 renderCell(observer
, geoCorners
[0], (geoCorners
[3] + geoCorners
[2]) * 0.5, samples
,
162 false, eastEdge
, southEdge
, westEdge
);
164 renderCell(observer
, (geoCorners
[0] + geoCorners
[1]) * 0.5, geoCorners
[2], samples
,
165 northEdge
, eastEdge
, false, westEdge
);
167 else if (wide
&& !tall
)
170 renderCell(observer
, geoCorners
[0], (geoCorners
[1] + geoCorners
[2]) * 0.5, samples
,
171 northEdge
, false, southEdge
, westEdge
);
173 renderCell(observer
, (geoCorners
[0] + geoCorners
[3]) * 0.5, geoCorners
[2], samples
,
174 northEdge
, eastEdge
, southEdge
, false);
179 renderCell(observer
, geoCorners
[0], (geoCorners
[0] + geoCorners
[2]) * 0.5, samples
,
180 false, false, southEdge
, westEdge
);
182 renderCell(observer
, (geoCorners
[0] + geoCorners
[3]) * 0.5, (geoCorners
[3] + geoCorners
[2]) * 0.5, samples
,
183 false, eastEdge
, southEdge
, false);
185 renderCell(observer
, (geoCorners
[0] + geoCorners
[1]) * 0.5, (geoCorners
[1] + geoCorners
[2]) * 0.5, samples
,
186 northEdge
, false, false, westEdge
);
188 renderCell(observer
, (geoCorners
[0] + geoCorners
[2]) * 0.5, geoCorners
[2], samples
,
189 northEdge
, eastEdge
, false, false);
198 glColor3f(0.0f
, 0.0f
, 1.0f
);
202 glColor3f(1.0f
, 0.5f
, 0.0f
);
205 glBegin(GL_LINE_LOOP
);
206 for (int i
= 0; i
< 4; ++i
)
208 glVertex3(cartCorners
[i
]);
213 #define EDGE_KERNEL_1 \
214 glColor4f(0.5f, 0.3f, 0.2f, 1.0f); \
215 glVertex3(dir * (m_meanRadius+alt)); \
216 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
217 glVertex3(dir * m_meanRadius);
218 #define EDGE_KERNEL_2 \
219 glColor4f(0.5f, 0.5f, 1.0f, 1.0f); \
220 glVertex3(dir * m_meanRadius); \
221 glColor4f(0.5f, 0.5f, 1.0f, 1.0f); \
222 glVertex3(dir * (m_meanRadius-(accurate ? 100.0 : 1000.0)));
223 #define EDGE_KERNEL_3 \
224 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
225 glVertex3(dir * (m_meanRadius-(accurate ? 100.0 : 1000.0))); \
226 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
227 glVertex3(dir * (m_meanRadius-5000.0));
228 #define EDGE_KERNEL_4 \
229 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
230 glVertex3(dir * (m_meanRadius-5000.0)); \
231 glColor4f(1.0f, 0.0f, 0.0f, 0.5f); \
232 glVertex3(dir * (m_meanRadius-8000.0));
233 #define EDGE_KERNEL(STAGE, EDGE, SAMPLES, LON, LAT) \
236 glBegin(GL_TRIANGLE_STRIP); \
237 for (int i = 0; i < SAMPLES; ++i) \
240 bool accurate = true; \
241 double alt = altitudeAt(state, (LON), (LAT), &coord, &accurate); \
242 GLvec3d dir = coord; \
243 EDGE_KERNEL_##STAGE \
247 #define EDGE(STAGE) \
248 EDGE_KERNEL(STAGE, north, state.samplesLon, i, state.samplesLat-1); \
249 EDGE_KERNEL(STAGE, east, state.samplesLat, state.samplesLon-1, i); \
250 EDGE_KERNEL(STAGE, south, state.samplesLon, i, 0); \
251 EDGE_KERNEL(STAGE, west, state.samplesLat, 0, i);
254 // Render the solid rock walls
255 glDisable(GL_CULL_FACE
);
260 glEnable(GL_CULL_FACE
);
264 /// @todo cache one edge of strip to save time on other
265 for (int i
= 0; i
< state
.samplesLon
-1; ++i
)
267 glBegin(GL_TRIANGLE_STRIP
);
269 for (int j
= 0; j
< state
.samplesLat
; ++j
)
271 for (int k
= 0; k
< 2; ++k
)
274 bool accurate
= true;
275 double alt
= altitudeAt(state
, i
+k
, j
, &coord
, &accurate
);
280 tcGeoImageData::LocalCoord loc
;
281 m_imagery
->geoToLocal(coord
, &loc
);
282 m_imagery
->texCoord(loc
);
287 glColor4f(0.5f
, 0.5f
, 1.0f
, 1.0f
);
291 glColor4f(1.0f
, 1.0f
, 1.0f
, 1.0f
);
292 //glColor4f(1.0f, 1.0f-(float)alt/3278.0f, 0.0f, 1.0f);
294 // Colour code if applicable
295 if (m_colourCoding
== ElevationSampleAlignment
)
297 if (state
.moreAvailableLon
&& state
.moreAvailableLat
)
299 glColor3f(1.0f
, 0.0f
, 1.0f
);
301 else if (state
.moreAvailableLon
)
303 glColor3f(1.0f
, 0.0f
, 0.0f
);
305 else if (state
.moreAvailableLat
)
307 glColor3f(0.0f
, 0.0f
, 1.0f
);
311 glColor3f(0.0f
, 1.0f
, 0.0f
);
315 double rad
= m_meanRadius
+ alt
;
316 glVertex3(dir
* rad
);
324 /// Render from the POV of an observer.
325 void tcGlobe::render(tcObserver
* const observer
, bool adaptive
, const tcGeo
* extent
)
327 /// @todo use a really simple fragment shader to cull backfacing lines
330 glColor3f(0.0f
, 1.0f
, 0.0f
);
331 for (int lat
= -75; lat
<= 75; lat
+= 15)
335 drawLineOfLatitude(M_PI
/180*lat
);
339 double tropic
= (23.0 + 26.0/60 + 22.0/3600) * M_PI
/180;
341 glColor3f(1.0f
, 0.0f
, 0.0f
);
342 drawLineOfLatitude(0.0);
343 // Tropics (Capricorn and Cancer)
344 glColor3f(1.0f
, 0.0f
, 1.0f
);
345 drawLineOfLatitude(-tropic
);
346 glColor3f(1.0f
, 1.0f
, 0.0f
);
347 drawLineOfLatitude(+tropic
);
348 // Arctic and Antarctic Circles
349 glColor3f(1.0f
, 1.0f
, 1.0f
);
350 drawLineOfLatitude(+M_PI
/2 - tropic
);
351 drawLineOfLatitude(-M_PI
/2 + tropic
);
353 // Lines of longitude
354 for (int lon
= 0; lon
< 360; lon
+= 15)
356 double x
= sin(M_PI
/180*lon
) * m_meanRadius
;
357 double y
= -cos(M_PI
/180*lon
) * m_meanRadius
;
363 glColor3f(1.0f
, lon
/180, 0.0f
);
370 glBegin(GL_LINE_STRIP
);
372 for (int lat
= minLat
; lat
<= maxLat
; ++lat
)
374 double z
= cos(M_PI
/180*lat
) * m_meanRadius
;
375 double xy
= sin(M_PI
/180*lat
);
376 glVertex3d(xy
*x
, xy
*y
, z
);
383 glColor3f(0.0f
, 1.0f
, 0.0f
);
388 // Draw data diagramatically
391 m_imagery
->renderSchematic(m_meanRadius
, observer
);
398 m_imagery
->setupThumbnailRendering(m_colourMapping
[0], m_colourMapping
[1], m_colourMapping
[2]);
403 for (int lon
= -180; lon
< 180; lon
+= dlon
)
405 for (int lat
= -90; lat
< 90; lat
+= dlat
)
407 tcGeo
sw(M_PI
/180 * (lon
), M_PI
/180 * (lat
));
408 tcGeo
ne(M_PI
/180 * (lon
+dlon
), M_PI
/180 * (lat
+dlat
));
409 renderCell(observer
, sw
, ne
, adaptive
? 5 : 0);
415 tcGeo sw
= extent
[0];
416 tcGeo ne
= extent
[1];
417 if (sw
.lon() > ne
.lon())
420 ne
.setLon(extent
[0].lon());
422 if (sw
.lat() > ne
.lat())
425 ne
.setLat(extent
[0].lat());
429 m_imagery
->setupDetailedRendering(m_colourMapping
[0], m_colourMapping
[1], m_colourMapping
[2], sw
, ne
);
431 /// @todo If it is really big, split it
432 renderCell(observer
, sw
, ne
, adaptive
? 16 : 0, true, true, true, true);
436 m_imagery
->finishRendering();
440 /// Set the elevation mode to render in.
441 void tcGlobe::setElevationMode(ElevationMode mode
)
443 m_elevationMode
= mode
;
446 /// Set the level of correction to show.
447 void tcGlobe::setElevationCorrection(float correction
)
449 m_elevationCorrection
= correction
;
452 /// Set the elevation data set name.
453 void tcGlobe::setElevationDataSet(const QString
& name
)
455 m_elevation
->setDataSet(name
);
458 /// Set colour coding method.
459 void tcGlobe::setColourCoding(ColourCoding colourCoding
)
461 m_colourCoding
= colourCoding
;
464 /// Adjust the mapping between bands and colour channels.
465 void tcGlobe::setColourMapping(int outputChannel
, int inputBand
)
467 m_colourMapping
[outputChannel
] = inputBand
;
474 /// Get the mean radius.
475 double tcGlobe::meanRadius() const
480 /// Get the altitude above sea level at a sample in a render state.
481 double tcGlobe::altitudeAt(const tcSrtmModel::RenderState
& state
, int x
, int y
, tcGeo
* outCoord
, bool* isAccurate
) const
483 bool accurate
= true;
485 switch (m_elevationMode
)
490 m_elevation
->altitudeAt(state
, x
, y
, outCoord
, &accurate
);
495 alt
= m_elevation
->altitudeAt(state
, x
, y
, outCoord
, false, &accurate
);
502 case CorrectedElevation
:
505 m_elevation
->altitudeAt(state
, x
, y
, outCoord
, true, &accurate
);
506 if (m_elevationCorrection
<= 0.0f
)
508 alt
= m_elevation
->altitudeAt(state
, x
, y
, outCoord
, false, 0);
510 else if (m_elevationCorrection
>= 1.0f
)
512 alt
= m_elevation
->altitudeAt(state
, x
, y
, outCoord
, true, 0);
516 double alt1
= m_elevation
->altitudeAt(state
, x
, y
, outCoord
, false, 0);
517 double alt2
= m_elevation
->altitudeAt(state
, x
, y
, outCoord
, true, 0);
518 alt
= alt1
*(1.0-m_elevationCorrection
) + alt2
*m_elevationCorrection
;
528 *isAccurate
= accurate
;
533 /// Get the altitude above sea level at a coordinate.
534 double tcGlobe::altitudeAt(const tcGeo
& coord
, bool* isAccurate
) const
536 bool accurate
= true;
538 switch (m_elevationMode
)
543 m_elevation
->altitudeAt(coord
, true, &accurate
);
548 alt
= m_elevation
->altitudeAt(coord
, false, &accurate
);
555 case CorrectedElevation
:
558 m_elevation
->altitudeAt(coord
, true, &accurate
);
559 if (m_elevationCorrection
<= 0.0f
)
561 alt
= m_elevation
->altitudeAt(coord
, false, 0);
563 else if (m_elevationCorrection
>= 1.0f
)
565 alt
= m_elevation
->altitudeAt(coord
, true, 0);
569 double alt1
= m_elevation
->altitudeAt(coord
, false, 0);
570 double alt2
= m_elevation
->altitudeAt(coord
, true, 0);
571 alt
= alt1
*(1.0-m_elevationCorrection
) + alt2
*m_elevationCorrection
;
581 *isAccurate
= accurate
;
586 /// Get the radius at a coordinate.
587 double tcGlobe::radiusAt(const tcGeo
& coord
) const
589 return m_meanRadius
+ altitudeAt(coord
);
592 /// Get the texture coordinate of the effective texture at a geographical coordinate.
593 maths::Vector
<2,double> tcGlobe::textureCoordOfGeo(const tcGeo
& coord
) const
595 const tcGeoImageData::LocalCoord
& min
= m_imagery
->minEffectiveLocal();
596 const tcGeoImageData::LocalCoord
& range
= m_imagery
->rangeEffectiveLocal();
597 tcGeoImageData::LocalCoord localCoord
;
598 m_imagery
->geoToLocal(coord
, &localCoord
);
599 return maths::Vector
<2,double>((localCoord
.x
-min
.x
) / range
.x
,
600 (localCoord
.y
-min
.y
) / range
.y
);