Colour map widget gets band names and descriptions from the imagery data object,...
[tecorrec.git] / geo / tcGlobe.cpp
blobb8d612077f079f81c35ffdaf399cac753635d69c
1 /***************************************************************************
2 * This file is part of Tecorrec. *
3 * Copyright 2008 James Hogan <james@albanarts.com> *
4 * *
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. *
9 * *
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. *
14 * *
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 ***************************************************************************/
20 /**
21 * @file tcGlobe.cpp
22 * @brief Manages data for a globe.
25 #include "tcGlobe.h"
26 #include "tcGeoImageData.h"
27 #include "tcLandsatData.h"
28 #include "tcSrtmModel.h"
29 #include <glMatrix.h>
30 #include <glVector.h>
32 #include <GL/gl.h>
34 #include <cmath>
37 * Constructors + destructor
40 /// Primary constructor.
41 tcGlobe::tcGlobe(double meanRadius)
42 : m_meanRadius(meanRadius)
43 , m_elevation(new tcSrtmModel())
44 , m_imagery(0)
45 , m_elevationMode(RawElevation)
46 , m_elevationCorrection(0.0f)
47 , m_colourCoding(NoColourCoding)
48 , m_colourMapping()
50 for (int i = 0; i < 3; ++i)
52 m_colourMapping[i] = 2-i;
54 //addDataSet(new tcLandsatData("/home/james/cs/pro/data/LE71950281999206EDC01/"));
55 setImagery(new tcLandsatData("/home/james/cs/pro/data/LE71950282000081EDC00/"));
58 /// Destructor.
59 tcGlobe::~tcGlobe()
64 * Data sets
67 /// Set the imagery data.
68 void tcGlobe::setImagery(tcGeoImageData* data)
70 m_imagery = data;
73 /// Get the imagery data.
74 tcGeoImageData* tcGlobe::imagery() const
76 return m_imagery;
80 * Rendering
83 /// Draw a line of latitude.
84 void tcGlobe::drawLineOfLatitude(double latitude) const
86 double z = sin(latitude) * m_meanRadius;
87 double xy = cos(latitude) * m_meanRadius;
88 glBegin(GL_LINE_LOOP);
90 for (int lon = 0; lon < 360; ++lon)
92 glVertex3d(xy*sin(M_PI/180*lon), xy*cos(M_PI/180*lon), z);
95 glEnd();
98 /// Render a cell.
99 void tcGlobe::renderCell(tcObserver* const observer, const tcGeo& swCorner, const tcGeo& neCorner, int samples,
100 bool northEdge, bool eastEdge, bool southEdge, bool westEdge) const
102 // Sample at a sensible level
103 tcSrtmModel::RenderState state;
104 m_elevation->sampleAlign(swCorner, neCorner, &state, samples);
106 if (state.moreAvailableLon || state.moreAvailableLat)
108 // Find the square distances to each corner
109 GLmat4d modelview;
110 glGetv(GL_MODELVIEW_MATRIX, &modelview);
111 tcGeo geoCorners[4] = {
112 tcGeo(swCorner.lon(), swCorner.lat()),
113 tcGeo(swCorner.lon(), neCorner.lat()),
114 tcGeo(neCorner.lon(), neCorner.lat()),
115 tcGeo(neCorner.lon(), swCorner.lat())
117 GLvec3d cartCorners[4];
118 float toCorners[4];
119 double altitudeMean = m_meanRadius + altitudeAt((geoCorners[0] + geoCorners[1] + geoCorners[2] + geoCorners[3])/4);
120 for (int i = 0; i < 4; ++i)
122 cartCorners[i] = (GLvec3d)geoCorners[i] * altitudeMean;
123 toCorners[i] = (modelview*(cartCorners[i], 1.0)).slice<0,3>().sqr();
124 // Cull faces which are roughly backfacing
125 if ((modelview*(cartCorners[i], 0.0))[2] <= 0.0)
127 return;
131 // Decide whether to subdivide
132 float diagonal = ( (cartCorners[0]-cartCorners[2]).sqr()
133 + (cartCorners[3]-cartCorners[1]).sqr())/2*4;
134 // If it is disproportionately tall, only subdivide horizontally
135 bool tall = (cartCorners[1] - cartCorners[0]).sqr() > (cartCorners[3] - cartCorners[0]).sqr()*4.0
136 || (cartCorners[2] - cartCorners[3]).sqr() > (cartCorners[2] - cartCorners[1]).sqr()*4.0;
137 // If it is disproportionately wide, only subdivide vertically
138 bool wide = (cartCorners[3] - cartCorners[0]).sqr() > (cartCorners[1] - cartCorners[0]).sqr()*4.0
139 || (cartCorners[2] - cartCorners[1]).sqr() > (cartCorners[2] - cartCorners[3]).sqr()*4.0;
140 bool subdivide = true;
141 for (int i = 0; i < 4; ++i)
143 if (toCorners[i] > diagonal)
145 subdivide = false;
146 break;
150 if (subdivide)
152 if (tall && !wide)
154 // bottom
155 renderCell(observer, geoCorners[0], (geoCorners[3] + geoCorners[2]) * 0.5, samples,
156 false, eastEdge, southEdge, westEdge);
157 // top
158 renderCell(observer, (geoCorners[0] + geoCorners[1]) * 0.5, geoCorners[2], samples,
159 northEdge, eastEdge, false, westEdge);
161 else if (wide && !tall)
163 // left
164 renderCell(observer, geoCorners[0], (geoCorners[1] + geoCorners[2]) * 0.5, samples,
165 northEdge, false, southEdge, westEdge);
166 // right
167 renderCell(observer, (geoCorners[0] + geoCorners[3]) * 0.5, geoCorners[2], samples,
168 northEdge, eastEdge, southEdge, false);
170 else
172 // bottom left
173 renderCell(observer, geoCorners[0], (geoCorners[0] + geoCorners[2]) * 0.5, samples,
174 false, false, southEdge, westEdge);
175 // bottom right
176 renderCell(observer, (geoCorners[0] + geoCorners[3]) * 0.5, (geoCorners[3] + geoCorners[2]) * 0.5, samples,
177 false, eastEdge, southEdge, false);
178 // top left
179 renderCell(observer, (geoCorners[0] + geoCorners[1]) * 0.5, (geoCorners[1] + geoCorners[2]) * 0.5, samples,
180 northEdge, false, false, westEdge);
181 // top right
182 renderCell(observer, (geoCorners[0] + geoCorners[2]) * 0.5, geoCorners[2], samples,
183 northEdge, eastEdge, false, false);
185 return;
189 #if 0
190 if (subdivide)
192 glColor3f(0.0f, 0.0f, 1.0f);
194 else
196 glColor3f(1.0f, 0.5f, 0.0f);
199 glBegin(GL_LINE_LOOP);
200 for (int i = 0; i < 4; ++i)
202 glVertex3(cartCorners[i]);
204 glEnd();
205 #endif
207 #define EDGE_KERNEL_1 \
208 glColor4f(0.5f, 0.3f, 0.2f, 1.0f); \
209 glVertex3(dir * (m_meanRadius+alt)); \
210 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
211 glVertex3(dir * m_meanRadius);
212 #define EDGE_KERNEL_2 \
213 glColor4f(0.5f, 0.5f, 1.0f, 1.0f); \
214 glVertex3(dir * m_meanRadius); \
215 glColor4f(0.5f, 0.5f, 1.0f, 1.0f); \
216 glVertex3(dir * (m_meanRadius-(accurate ? 100.0 : 1000.0)));
217 #define EDGE_KERNEL_3 \
218 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
219 glVertex3(dir * (m_meanRadius-(accurate ? 100.0 : 1000.0))); \
220 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
221 glVertex3(dir * (m_meanRadius-5000.0));
222 #define EDGE_KERNEL_4 \
223 glColor4f(0.5f, 0.5f, 0.5f, 1.0f); \
224 glVertex3(dir * (m_meanRadius-5000.0)); \
225 glColor4f(1.0f, 0.0f, 0.0f, 0.5f); \
226 glVertex3(dir * (m_meanRadius-8000.0));
227 #define EDGE_KERNEL(STAGE, EDGE, SAMPLES, LON, LAT) \
228 if (EDGE##Edge) \
230 glBegin(GL_TRIANGLE_STRIP); \
231 for (int i = 0; i < SAMPLES; ++i) \
233 tcGeo coord; \
234 bool accurate = true; \
235 double alt = altitudeAt(state, (LON), (LAT), &coord, &accurate); \
236 GLvec3d dir = coord; \
237 EDGE_KERNEL_##STAGE \
239 glEnd(); \
241 #define EDGE(STAGE) \
242 EDGE_KERNEL(STAGE, north, state.samplesLon, i, state.samplesLat-1); \
243 EDGE_KERNEL(STAGE, east, state.samplesLat, state.samplesLon-1, i); \
244 EDGE_KERNEL(STAGE, south, state.samplesLon, i, 0); \
245 EDGE_KERNEL(STAGE, west, state.samplesLat, 0, i);
247 #if 0
248 // Render the solid rock walls
249 glDisable(GL_CULL_FACE);
250 EDGE(1)
251 EDGE(2)
252 EDGE(3)
253 EDGE(4)
254 glEnable(GL_CULL_FACE);
255 #endif
257 // Render this cell
258 /// @todo cache one edge of strip to save time on other
259 for (int i = 0; i < state.samplesLon-1; ++i)
261 glBegin(GL_TRIANGLE_STRIP);
263 for (int j = 0; j < state.samplesLat; ++j)
265 for (int k = 0; k < 2; ++k)
267 tcGeo coord;
268 bool accurate = true;
269 double alt = altitudeAt(state, i+k, j, &coord, &accurate);
271 // Get colour
272 if (0 != m_imagery)
274 tcGeoImageData::LocalCoord loc;
275 m_imagery->geoToLocal(coord, &loc);
276 m_imagery->texCoord(loc);
279 if (!accurate)
281 glColor4f(0.5f, 0.5f, 1.0f, 1.0f);
283 else
285 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
286 //glColor4f(1.0f, 1.0f-(float)alt/3278.0f, 0.0f, 1.0f);
288 // Colour code if applicable
289 if (m_colourCoding == ElevationSampleAlignment)
291 if (state.moreAvailableLon && state.moreAvailableLat)
293 glColor3f(1.0f, 0.0f, 1.0f);
295 else if (state.moreAvailableLon)
297 glColor3f(1.0f, 0.0f, 0.0f);
299 else if (state.moreAvailableLat)
301 glColor3f(0.0f, 0.0f, 1.0f);
303 else
305 glColor3f(0.0f, 1.0f, 0.0f);
308 GLvec3d dir = coord;
309 double rad = m_meanRadius + alt;
310 glVertex3(dir * rad);
314 glEnd();
318 /// Render from the POV of an observer.
319 void tcGlobe::render(tcObserver* const observer, bool adaptive, const tcGeo* extent)
321 /// @todo use a really simple fragment shader to cull backfacing lines
322 #if 1
323 // Lines of latitude
324 glColor3f(0.0f, 1.0f, 0.0f);
325 for (int lat = -75; lat <= 75; lat += 15)
327 if (lat != 0)
329 drawLineOfLatitude(M_PI/180*lat);
333 double tropic = (23.0 + 26.0/60 + 22.0/3600) * M_PI/180;
334 // Equator
335 glColor3f(1.0f, 0.0f, 0.0f);
336 drawLineOfLatitude(0.0);
337 // Tropics (Capricorn and Cancer)
338 glColor3f(1.0f, 0.0f, 1.0f);
339 drawLineOfLatitude(-tropic);
340 glColor3f(1.0f, 1.0f, 0.0f);
341 drawLineOfLatitude(+tropic);
342 // Arctic and Antarctic Circles
343 glColor3f(1.0f, 1.0f, 1.0f);
344 drawLineOfLatitude(+M_PI/2 - tropic);
345 drawLineOfLatitude(-M_PI/2 + tropic);
347 // Lines of longitude
348 for (int lon = 0; lon < 360; lon += 15)
350 double x = sin(M_PI/180*lon) * m_meanRadius;
351 double y = -cos(M_PI/180*lon) * m_meanRadius;
352 int minLat = 15;
353 int maxLat = 165;
354 if (lon % 180 == 0)
356 glLineWidth(2.0f);
357 glColor3f(1.0f, lon/180, 0.0f);
359 if (lon % 90 == 0)
361 minLat = 0;
362 maxLat = 180;
364 glBegin(GL_LINE_STRIP);
366 for (int lat = minLat; lat <= maxLat; ++lat)
368 double z = cos(M_PI/180*lat) * m_meanRadius;
369 double xy = sin(M_PI/180*lat);
370 glVertex3d(xy*x, xy*y, z);
373 glEnd();
374 if (lon % 180 == 0)
376 glLineWidth(1.0f);
377 glColor3f(0.0f, 1.0f, 0.0f);
380 #endif
382 // Draw data diagramatically
383 if (0 != m_imagery)
385 m_imagery->renderSchematic(m_meanRadius, observer);
388 if (0 == extent)
390 if (0 != m_imagery)
392 m_imagery->setupThumbnailRendering(m_colourMapping[0], m_colourMapping[1], m_colourMapping[2]);
394 // Go through cells
395 const int dlon = 30;
396 const int dlat = 30;
397 for (int lon = -180; lon < 180; lon += dlon)
399 for (int lat = -90; lat < 90; lat += dlat)
401 tcGeo sw(M_PI/180 * (lon), M_PI/180 * (lat));
402 tcGeo ne(M_PI/180 * (lon+dlon), M_PI/180 * (lat+dlat));
403 renderCell(observer, sw, ne, adaptive ? 5 : 0);
407 else
409 tcGeo sw = extent[0];
410 tcGeo ne = extent[1];
411 if (sw.lon() > ne.lon())
413 sw.setLon(ne.lon());
414 ne.setLon(extent[0].lon());
416 if (sw.lat() > ne.lat())
418 sw.setLat(ne.lat());
419 ne.setLat(extent[0].lat());
421 if (0 != m_imagery)
423 m_imagery->setupDetailedRendering(m_colourMapping[0], m_colourMapping[1], m_colourMapping[2], sw, ne);
425 /// @todo If it is really big, split it
426 renderCell(observer, sw, ne, adaptive ? 16 : 0, true, true, true, true);
428 if (0 != m_imagery)
430 m_imagery->finishRendering();
434 /// Set the elevation mode to render in.
435 void tcGlobe::setElevationMode(ElevationMode mode)
437 m_elevationMode = mode;
440 /// Set the level of correction to show.
441 void tcGlobe::setElevationCorrection(float correction)
443 m_elevationCorrection = correction;
446 /// Set the elevation data set name.
447 void tcGlobe::setElevationDataSet(const QString& name)
449 m_elevation->setDataSet(name);
452 /// Set colour coding method.
453 void tcGlobe::setColourCoding(ColourCoding colourCoding)
455 m_colourCoding = colourCoding;
458 /// Adjust the mapping between bands and colour channels.
459 void tcGlobe::setColourMapping(int outputChannel, int inputBand)
461 m_colourMapping[outputChannel] = inputBand;
465 * Accessors
468 /// Get the mean radius.
469 double tcGlobe::meanRadius() const
471 return m_meanRadius;
474 /// Get the altitude above sea level at a sample in a render state.
475 double tcGlobe::altitudeAt(const tcSrtmModel::RenderState& state, int x, int y, tcGeo* outCoord, bool* isAccurate) const
477 bool accurate = true;
478 double alt = 0.0;
479 switch (m_elevationMode)
481 case NoElevation:
483 // get accurate set
484 m_elevation->altitudeAt(state, x, y, outCoord, &accurate);
486 break;
487 case RawElevation:
489 alt = m_elevation->altitudeAt(state, x, y, outCoord, false, &accurate);
490 if (!accurate)
492 alt = 0.0;
495 break;
496 case CorrectedElevation:
498 // get accurate set
499 m_elevation->altitudeAt(state, x, y, outCoord, true, &accurate);
500 if (m_elevationCorrection <= 0.0f)
502 alt = m_elevation->altitudeAt(state, x, y, outCoord, false, 0);
504 else if (m_elevationCorrection >= 1.0f)
506 alt = m_elevation->altitudeAt(state, x, y, outCoord, true, 0);
508 else
510 double alt1 = m_elevation->altitudeAt(state, x, y, outCoord, false, 0);
511 double alt2 = m_elevation->altitudeAt(state, x, y, outCoord, true, 0);
512 alt = alt1*(1.0-m_elevationCorrection) + alt2*m_elevationCorrection;
515 break;
516 default:
517 Q_ASSERT(false);
518 break;
520 if (0 != isAccurate)
522 *isAccurate = accurate;
524 return alt;
527 /// Get the altitude above sea level at a coordinate.
528 double tcGlobe::altitudeAt(const tcGeo& coord, bool* isAccurate) const
530 bool accurate = true;
531 double alt = 0.0;
532 switch (m_elevationMode)
534 case NoElevation:
536 // get accurate set
537 m_elevation->altitudeAt(coord, true, &accurate);
539 break;
540 case RawElevation:
542 alt = m_elevation->altitudeAt(coord, false, &accurate);
543 if (!accurate)
545 alt = 0.0;
548 break;
549 case CorrectedElevation:
551 // get accurate set
552 m_elevation->altitudeAt(coord, true, &accurate);
553 if (m_elevationCorrection <= 0.0f)
555 alt = m_elevation->altitudeAt(coord, false, 0);
557 else if (m_elevationCorrection >= 1.0f)
559 alt = m_elevation->altitudeAt(coord, true, 0);
561 else
563 double alt1 = m_elevation->altitudeAt(coord, false, 0);
564 double alt2 = m_elevation->altitudeAt(coord, true, 0);
565 alt = alt1*(1.0-m_elevationCorrection) + alt2*m_elevationCorrection;
568 break;
569 default:
570 Q_ASSERT(false);
571 break;
573 if (0 != isAccurate)
575 *isAccurate = accurate;
577 return alt;
580 /// Get the radius at a coordinate.
581 double tcGlobe::radiusAt(const tcGeo& coord) const
583 return m_meanRadius + altitudeAt(coord);