Fixed SRTM positioning (each tile overlaps the next by one sample) and added config...
[tecorrec.git] / geo / tcSrtmData.cpp
blob20fd0250a613046af1a08409e27c90d646411e03
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 tcSrtmData.cpp
22 * @brief Block of raw SRTM elevation data.
25 #include "tcSrtmData.h"
26 #include <SplineDefinitions.h>
28 #include <QFile>
29 #include <QDataStream>
31 #include <cmath>
34 * Constructors + destructor
37 /// Load SRTM data.
38 tcSrtmData::tcSrtmData(int lon, int lat, QFile& file)
39 : m_lon(lon)
40 , m_lat(lat)
41 , m_lines(0)
42 , m_samples(0)
43 , m_data(0)
45 qint64 elements = file.size() / sizeof(uint16_t);
46 double side = sqrt(elements);
47 m_lines = m_samples = (int)side;
48 Q_ASSERT(m_lines*m_samples == elements);
49 m_data = new uint16_t[2*m_lines*m_samples];
50 Q_ASSERT(0 != m_data);
52 QDataStream in(&file);
53 in.setByteOrder(QDataStream::BigEndian);
54 for (int i = 0; i < m_lines; ++i)
56 for (int j = 0; j < m_samples; ++j)
58 Q_ASSERT(!in.atEnd());
59 uint16_t sample;
60 in >> sample;
61 m_data[indexData(0, i, j)] = sample;
62 m_data[indexData(1, i, j)] = sample;
65 /// @todo Handle larger files!
66 Q_ASSERT(in.atEnd());
68 fillVoidsBicubic();
71 /// Destructor.
72 tcSrtmData::~tcSrtmData()
74 delete [] m_data;
78 * Accessors
81 /// Get the longitude in degrees.
82 int tcSrtmData::lon() const
84 return m_lon;
87 /// Get the latitude in degrees.
88 int tcSrtmData::lat() const
90 return m_lat;
93 /// Get the number of scan lines.
94 int tcSrtmData::lines() const
96 return m_lines;
99 /// Get the number of samples in each scan line.
100 int tcSrtmData::samples() const
102 return m_samples;
105 /// Find the geographical angles between samples.
106 tcGeo tcSrtmData::sampleResolution() const
108 return tcGeo(M_PI/180 / (m_samples - 1),
109 M_PI/180 / (m_lines - 1));
112 /// Get the geographical coordinate of a sample.
113 tcGeo tcSrtmData::sampleCoordinate(int line, int sample) const
115 return tcGeo(M_PI/180*((double) m_lon + (double)sample/(m_samples-1)),
116 M_PI/180*((double)(m_lat+1) - (double)line /(m_lines -1)));
119 /// Does data exist for a sample?
120 bool tcSrtmData::sampleExists(int line, int sample) const
122 Q_ASSERT(line >= 0 && line < m_lines);
123 Q_ASSERT(sample >= 0 && sample < m_samples);
124 return 0x0000 == (0x8000 & m_data[indexData(0, line, sample)]);
127 /// Get the altitude at a sample (interpolating gaps).
128 uint16_t tcSrtmData::sampleAltitude(int line, int sample, bool corrected, bool* exists) const
130 Q_ASSERT(line >= 0 && line < m_lines);
131 Q_ASSERT(sample >= 0 && sample < m_samples);
132 if (0 != exists)
134 *exists = (0x0000 == (0x8000 & m_data[indexData(corrected ? 1 : 0, line, sample)]));
136 return 0x7FFF & m_data[indexData(corrected ? 1 : 0, line, sample)];
139 /// Get the altitude at a coordinate.
140 float tcSrtmData::sampleAltitude(const tcGeo& coord, bool corrected, bool* exists) const
142 double dlon = coord.lon()*180.0/M_PI - m_lon;
143 double dlat = coord.lat()*180.0/M_PI - m_lat;
144 Q_ASSERT(dlon >= 0.0 && dlon < 1.0);
145 Q_ASSERT(dlat >= 0.0 && dlat < 1.0);
146 int line = (int)floor(dlat*(m_lines-1));
147 float dy = dlat*(m_lines-1) - line;
148 line = m_lines - 1 - line;
149 int sample = (int)floor(dlon*(m_samples-1));
150 float dx = dlon*(m_samples-1) - sample;
151 Q_ASSERT(line > 0 && line < m_lines);
152 Q_ASSERT(sample >= 0 && sample < m_samples-1);
153 Q_ASSERT(dx >= 0.0f && dx <= 1.0f);
154 Q_ASSERT(dy >= 0.0f && dy <= 1.0f);
155 if (0 != exists)
157 *exists = (0x0000 == (0x8000 & m_data[indexData(corrected ? 1 : 0, line, sample)]));
159 uint16_t x00 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line, sample )];
160 uint16_t x10 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line, sample+1)];
161 uint16_t x01 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line-1, sample )];
162 uint16_t x11 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line-1, sample+1)];
163 float mdx = 1.0f-dx;
164 float mdy = 1.0f-dy;
165 return ((float)x00*(mdx + mdy) + (float)x01*(mdx + dy) + (float)x10*(dx + mdy) + (float)x11*(dx + dy)) / 4.0;
169 * Mutators
172 /// Fill voids using bilinear interpolation.
173 void tcSrtmData::fillVoidsBilinear()
175 for (int line = 0; line < m_lines; ++line)
177 int lastNonVoid = -1;
178 uint16_t lastNonVoidValue;
179 for (int sample = 0; sample < m_samples; ++sample)
181 int index = indexData(0, line, sample);
182 uint16_t value = m_data[index];
183 bool isVoid = (0 != (value & 0x8000));
184 if (!isVoid)
186 if (lastNonVoid != -1)
188 int gap = sample - lastNonVoid;
189 float startAlt = lastNonVoidValue;
190 float dAlt = (float)(value - lastNonVoidValue) / gap;
191 // Lovely, between lastNonVoid and sample is a void
192 for (int i = lastNonVoid+1; i < sample; ++i)
194 int iIndex = indexData(0, line, i);
195 uint16_t alt = startAlt + dAlt * (i - lastNonVoid);
196 alt |= 0x8000;
197 m_data[iIndex] = alt;
198 // Keep track of the gap
199 m_data[indexData(1, line, i)] = gap;
202 lastNonVoid = sample;
203 lastNonVoidValue = value;
205 else
207 // Clear any previous data
208 m_data[index] = 0x8000;
212 for (int sample = 0; sample < m_samples; ++sample)
214 int lastNonVoid = -1;
215 uint16_t lastNonVoidValue;
216 for (int line = 0; line < m_lines; ++line)
218 int index = indexData(0, line, sample);
219 uint16_t value = m_data[index];
220 bool isVoid = (0 != (value & 0x8000));
221 if (!isVoid)
223 if (lastNonVoid != -1)
225 int gap = line - lastNonVoid;
226 float startAlt = lastNonVoidValue;
227 float dAlt = (float)(value - lastNonVoidValue) / gap;
228 // Lovely, between lastNonVoid and line is a void
229 for (int i = lastNonVoid+1; i < line; ++i)
231 // Average with previous value if non zero, otherwise overwrite
232 int iIndex = indexData(0, i, sample);
233 int iIndexC = indexData(1, i, sample);
234 uint16_t alt = startAlt + dAlt * (i - lastNonVoid);
235 uint16_t prevAlt = 0x7fff & m_data[iIndex];
236 if (prevAlt != 0)
238 int otherGap = m_data[iIndexC];
239 // Prefer the value of the smaller gap
240 alt = (float)((int)alt * otherGap + (int)prevAlt * gap) / (int)(gap+otherGap);
242 alt |= 0x8000;
243 m_data[iIndex] = alt;
244 m_data[iIndexC] = 0x8000; //alt;
247 else
249 for (int i = lastNonVoid+1; i < line; ++i)
251 int iIndex = indexData(0, i, sample);
252 int iIndexC = indexData(1, i, sample);
253 // The corrected value may contain a gap, which isn't needed anymore
254 m_data[iIndexC] = 0x8000; //m_data[iIndex];
257 lastNonVoid = line;
258 lastNonVoidValue = value;
264 /// Fill voids using bicubic interpolation.
265 void tcSrtmData::fillVoidsBicubic()
267 for (int line = 0; line < m_lines; ++line)
269 int lastNonVoid = -1;
270 uint16_t lastNonVoidValue;
271 for (int sample = 0; sample < m_samples; ++sample)
273 int index = indexData(0, line, sample);
274 uint16_t value = m_data[index];
275 bool isVoid = (0 != (value & 0x8000));
276 if (!isVoid)
278 if (lastNonVoid != -1)
280 int gap = sample - lastNonVoid;
281 // Obtain some control points for the splines
282 // use the two samples either side of the gap as control points 1 and 2
283 // use the ones before and after these samples, extended to the size of
284 // the gap as control points 0 and 3
285 float startAlt = lastNonVoidValue;
286 float beforeStartAlt = startAlt;
287 if (lastNonVoid > 0)
289 uint16_t beforeStart = m_data[indexData(0, line, lastNonVoid-1)];
290 if (0 == (beforeStart & 0x8000))
292 beforeStartAlt = startAlt + ((float)beforeStart - startAlt) * gap;
295 float endAlt = value;
296 float afterEndAlt = endAlt;
297 if (sample < m_samples-1)
299 uint16_t afterEnd = m_data[indexData(0, line, sample+1)];
300 if (0 == (afterEnd & 0x8000))
302 afterEndAlt = endAlt + ((float)afterEnd - endAlt) * gap;
305 // Lovely, between lastNonVoid and sample is a void
306 for (int i = lastNonVoid+1; i < sample; ++i)
308 int iIndex = indexData(0, line, i);
309 float u = (float)(i-lastNonVoid) / gap;
310 float u2 = u*u;
311 float u3 = u2*u;
312 uint16_t alt = maths::catmullRomSpline.Spline(u, u2, u3, beforeStartAlt, startAlt, endAlt, afterEndAlt);
313 alt |= 0x8000;
314 m_data[iIndex] = alt;
315 // Keep track of the gap
316 m_data[indexData(1, line, i)] = gap;
319 lastNonVoid = sample;
320 lastNonVoidValue = value;
322 else
324 // Clear any previous data
325 m_data[index] = 0x8000;
329 for (int sample = 0; sample < m_samples; ++sample)
331 int lastNonVoid = -1;
332 uint16_t lastNonVoidValue;
333 for (int line = 0; line < m_lines; ++line)
335 int index = indexData(0, line, sample);
336 uint16_t value = m_data[index];
337 bool isVoid = (0 != (value & 0x8000));
338 if (!isVoid)
340 if (lastNonVoid != -1)
342 int gap = line - lastNonVoid;
343 // Obtain some control points for the splines
344 // use the two samples either side of the gap as control points 1 and 2
345 // use the ones before and after these samples, extended to the size of
346 // the gap as control points 0 and 3
347 float startAlt = lastNonVoidValue;
348 float beforeStartAlt = startAlt;
349 if (lastNonVoid > 0)
351 uint16_t beforeStart = m_data[indexData(0, lastNonVoid-1, sample)];
352 if (0 == (beforeStart & 0x8000))
354 beforeStartAlt = startAlt + ((float)beforeStart - startAlt) * gap;
357 float endAlt = value;
358 float afterEndAlt = endAlt;
359 if (line < m_lines-1)
361 uint16_t afterEnd = m_data[indexData(0, line+1, sample)];
362 if (0 == (afterEnd & 0x8000))
364 afterEndAlt = endAlt + ((float)afterEnd - endAlt) * gap;
367 // Lovely, between lastNonVoid and line is a void
368 for (int i = lastNonVoid+1; i < line; ++i)
370 // Average with previous value if non zero, otherwise overwrite
371 int iIndex = indexData(0, i, sample);
372 int iIndexC = indexData(1, i, sample);
373 float u = (float)(i-lastNonVoid) / gap;
374 float u2 = u*u;
375 float u3 = u2*u;
376 uint16_t alt = maths::catmullRomSpline.Spline(u, u2, u3, beforeStartAlt, startAlt, endAlt, afterEndAlt);
377 uint16_t prevAlt = 0x7fff & m_data[iIndex];
378 if (prevAlt != 0)
380 int otherGap = m_data[iIndexC];
381 // Prefer the value of the smaller gap
382 alt = (float)((int)alt * otherGap + (int)prevAlt * gap) / (int)(gap+otherGap);
384 alt |= 0x8000;
385 m_data[iIndex] = alt;
386 m_data[iIndexC] = 0x8000; //alt;
389 else
391 for (int i = lastNonVoid+1; i < line; ++i)
393 int iIndex = indexData(0, i, sample);
394 int iIndexC = indexData(1, i, sample);
395 // The corrected value may contain a gap, which isn't needed anymore
396 m_data[iIndexC] = 0x8000; //m_data[iIndex];
399 lastNonVoid = line;
400 lastNonVoidValue = value;
407 * Private functions
410 /// Index into the data array.
411 unsigned int tcSrtmData::indexData(int corrected, int line, int sample) const
413 return (corrected*m_lines + line)*m_samples + sample;