tcElevationOptimization: fix typo s/write/read/
[tecorrec.git] / geo / tcSrtmData.cpp
blob4098573ebcf0c757b25cf95df0c46e811c957b93
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 "tcElevationData.h"
27 #include <SplineDefinitions.h>
29 #include <QFile>
30 #include <QDataStream>
32 #include <cmath>
35 * Constructors + destructor
38 /// Load SRTM data.
39 tcSrtmData::tcSrtmData(int lon, int lat, QFile& file, QObject* progressObj, const char* progressMember)
40 : m_lon(lon)
41 , m_lat(lat)
42 , m_lines(0)
43 , m_samples(0)
44 , m_data(0)
46 connect(this, SIGNAL(progressing(const QString&)), progressObj, progressMember);
48 qint64 elements = file.size() / sizeof(uint16_t);
49 double side = sqrt(elements);
50 m_lines = m_samples = (int)side;
51 Q_ASSERT(m_lines*m_samples == elements);
52 m_data = new uint16_t[2*m_lines*m_samples];
53 Q_ASSERT(0 != m_data);
55 QDataStream in(&file);
56 in.setByteOrder(QDataStream::BigEndian);
57 int lastPercent = -1;
58 bool alreadyFilled = false;
59 for (int i = 0; i < m_lines; ++i)
61 int percent = 20*i/(m_lines-1);
62 if (percent != lastPercent)
64 lastPercent = percent;
65 progressing(tr("Loading SRTM (%1,%2): %3%").arg(lon).arg(lat).arg(percent*5));
67 for (int j = 0; j < m_samples; ++j)
69 Q_ASSERT(!in.atEnd());
70 uint16_t sample;
71 in >> sample;
72 if (sample & 0x8000 && sample != 0x8000)
74 alreadyFilled = true;
76 m_data[indexData(0, i, j)] = sample;
77 m_data[indexData(1, i, j)] = sample;
80 /// @todo Handle larger files!
81 Q_ASSERT(in.atEnd());
83 if (!alreadyFilled)
85 progressing(tr("Filling SRTM voids (%1,%2)").arg(lon).arg(lat));
86 fillVoidsBicubic();
89 progressing(tr("SRTM (%1,%2): Complete").arg(lon).arg(lat));
92 /// Destructor.
93 tcSrtmData::~tcSrtmData()
95 delete [] m_data;
99 * Accessors
102 /// Get the longitude in degrees.
103 int tcSrtmData::lon() const
105 return m_lon;
108 /// Get the latitude in degrees.
109 int tcSrtmData::lat() const
111 return m_lat;
114 /// Get the number of scan lines.
115 int tcSrtmData::lines() const
117 return m_lines;
120 /// Get the number of samples in each scan line.
121 int tcSrtmData::samples() const
123 return m_samples;
126 /// Find the geographical angles between samples.
127 tcGeo tcSrtmData::sampleResolution() const
129 return tcGeo(M_PI/180 / (m_samples - 1),
130 M_PI/180 / (m_lines - 1));
133 /// Get the geographical coordinate of a sample.
134 tcGeo tcSrtmData::sampleCoordinate(int line, int sample) const
136 return tcGeo(M_PI/180*((double) m_lon + (double)sample/(m_samples-1)),
137 M_PI/180*((double)(m_lat+1) - (double)line /(m_lines -1)));
140 /// Does data exist for a sample?
141 bool tcSrtmData::sampleExists(int line, int sample) const
143 Q_ASSERT(line >= 0 && line < m_lines);
144 Q_ASSERT(sample >= 0 && sample < m_samples);
145 return 0x0000 == (0x8000 & m_data[indexData(0, line, sample)]);
148 /// Get the altitude at a sample (interpolating gaps).
149 uint16_t tcSrtmData::sampleAltitude(int line, int sample, bool corrected, bool* exists) const
151 Q_ASSERT(line >= 0 && line < m_lines);
152 Q_ASSERT(sample >= 0 && sample < m_samples);
153 if (0 != exists)
155 *exists = (0x0000 == (0x8000 & m_data[indexData(corrected ? 1 : 0, line, sample)]));
157 return 0x7FFF & m_data[indexData(corrected ? 1 : 0, line, sample)];
160 /// Get the altitude at a coordinate.
161 float tcSrtmData::sampleAltitude(const tcGeo& coord, bool corrected, bool* exists) const
163 double dlon = coord.lon()*180.0/M_PI - m_lon;
164 double dlat = coord.lat()*180.0/M_PI - m_lat;
165 Q_ASSERT(dlon >= 0.0 && dlon < 1.0);
166 Q_ASSERT(dlat >= 0.0 && dlat < 1.0);
167 int line = (int)floor(dlat*(m_lines-1));
168 float dy = dlat*(m_lines-1) - line;
169 line = m_lines - 1 - line;
170 int sample = (int)floor(dlon*(m_samples-1));
171 float dx = dlon*(m_samples-1) - sample;
172 Q_ASSERT(line > 0 && line < m_lines);
173 Q_ASSERT(sample >= 0 && sample < m_samples-1);
174 Q_ASSERT(dx >= 0.0f && dx <= 1.0f);
175 Q_ASSERT(dy >= 0.0f && dy <= 1.0f);
176 if (0 != exists)
178 *exists = (0x0000 == (0x8000 & m_data[indexData(corrected ? 1 : 0, line, sample )]))
179 && (0x0000 == (0x8000 & m_data[indexData(corrected ? 1 : 0, line, sample+1)]))
180 && (0x0000 == (0x8000 & m_data[indexData(corrected ? 1 : 0, line-1, sample )]))
181 && (0x0000 == (0x8000 & m_data[indexData(corrected ? 1 : 0, line-1, sample+1)]));
183 uint16_t x00 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line, sample )];
184 uint16_t x10 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line, sample+1)];
185 uint16_t x01 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line-1, sample )];
186 uint16_t x11 = 0x7FFF & m_data[indexData(corrected ? 1 : 0, line-1, sample+1)];
187 float mdx = 1.0f-dx;
188 float mdy = 1.0f-dy;
189 return ((float)x00*mdx*mdy + (float)x01*mdx*dy + (float)x10*dx*mdy + (float)x11*dx*dy);
193 * Mutators
196 /// Correct the altitude at a sample.
197 void tcSrtmData::setSampleAltitude(int line, int sample, uint16_t altitude)
199 Q_ASSERT(line >= 0 && line < m_lines);
200 Q_ASSERT(sample >= 0 && sample < m_samples);
201 m_data[indexData(1, line, sample)] = altitude;
204 /// Update any samples affected by the elevation field.
205 void tcSrtmData::updateFromElevationData(const tcElevationData* elevation)
207 tcGeo swCorner = elevation->swCorner();
208 tcGeo neCorner = elevation->neCorner();
209 int minl = qMax(0, (int)floor(((double)m_lat+1 - neCorner.lat()*180.0/M_PI)*(m_lines-1)));
210 int maxl = qMin(m_lines, (int)ceil (((double)m_lat+1 - swCorner.lat()*180.0/M_PI)*(m_lines-1)));
211 int mins = qMax(0, (int)floor((swCorner.lon()*180.0/M_PI - m_lon)*(m_samples-1)));
212 int maxs = qMin(m_samples, (int)ceil ((neCorner.lon()*180.0/M_PI - m_lon)*(m_samples-1)));
213 // Go through all elevation samples to update
214 for (int line = minl; line < maxl; ++line)
216 for (int sample = mins; sample < maxs; ++sample)
218 tcGeo corner1(((double) m_lon + (-0.5 + sample)/(m_samples-1)) * M_PI/180,
219 ((double)(m_lat+1) - (-0.5 + line )/(m_lines -1)) * M_PI/180);
220 tcGeo corner2(((double) m_lon + ( 0.5 + sample)/(m_samples-1)) * M_PI/180,
221 ((double)(m_lat+1) - ( 0.5 + line )/(m_lines -1)) * M_PI/180);
222 bool inRange = false;
223 float elev = elevation->meanElevation(corner1, corner2, &inRange);
224 if (inRange)
226 m_data[indexData(1, line, sample)] = (0x8000 & m_data[indexData(1, line, sample)]) | (uint16_t)floor(elev+0.5f);
232 /// Fill voids using bilinear interpolation.
233 void tcSrtmData::fillVoidsBilinear()
235 for (int line = 0; line < m_lines; ++line)
237 int lastNonVoid = -1;
238 uint16_t lastNonVoidValue;
239 for (int sample = 0; sample < m_samples; ++sample)
241 int index = indexData(0, line, sample);
242 uint16_t value = m_data[index];
243 bool isVoid = (0 != (value & 0x8000));
244 if (!isVoid)
246 if (lastNonVoid != -1)
248 int gap = sample - lastNonVoid;
249 float startAlt = lastNonVoidValue;
250 float dAlt = (float)(value - lastNonVoidValue) / gap;
251 // Lovely, between lastNonVoid and sample is a void
252 for (int i = lastNonVoid+1; i < sample; ++i)
254 int iIndex = indexData(0, line, i);
255 uint16_t alt = startAlt + dAlt * (i - lastNonVoid);
256 alt |= 0x8000;
257 m_data[iIndex] = alt;
258 // Keep track of the gap
259 m_data[indexData(1, line, i)] = gap;
262 lastNonVoid = sample;
263 lastNonVoidValue = value;
265 else
267 // Clear any previous data
268 m_data[index] = 0x8000;
272 for (int sample = 0; sample < m_samples; ++sample)
274 int lastNonVoid = -1;
275 uint16_t lastNonVoidValue;
276 for (int line = 0; line < m_lines; ++line)
278 int index = indexData(0, line, sample);
279 uint16_t value = m_data[index];
280 bool isVoid = (0 != (value & 0x8000));
281 if (!isVoid)
283 if (lastNonVoid != -1)
285 int gap = line - lastNonVoid;
286 float startAlt = lastNonVoidValue;
287 float dAlt = (float)(value - lastNonVoidValue) / gap;
288 // Lovely, between lastNonVoid and line is a void
289 for (int i = lastNonVoid+1; i < line; ++i)
291 // Average with previous value if non zero, otherwise overwrite
292 int iIndex = indexData(0, i, sample);
293 int iIndexC = indexData(1, i, sample);
294 uint16_t alt = startAlt + dAlt * (i - lastNonVoid);
295 uint16_t prevAlt = 0x7fff & m_data[iIndex];
296 if (prevAlt != 0)
298 int otherGap = m_data[iIndexC];
299 // Prefer the value of the smaller gap
300 alt = (float)((int)alt * otherGap + (int)prevAlt * gap) / (int)(gap+otherGap);
302 alt |= 0x8000;
303 m_data[iIndex] = alt;
304 m_data[iIndexC] = alt;
307 else
309 for (int i = lastNonVoid+1; i < line; ++i)
311 int iIndex = indexData(0, i, sample);
312 int iIndexC = indexData(1, i, sample);
313 // The corrected value may contain a gap, which isn't needed anymore
314 m_data[iIndexC] = m_data[iIndex];
317 lastNonVoid = line;
318 lastNonVoidValue = value;
324 /// Fill voids using bicubic interpolation.
325 void tcSrtmData::fillVoidsBicubic()
327 for (int line = 0; line < m_lines; ++line)
329 int lastNonVoid = -1;
330 uint16_t lastNonVoidValue;
331 for (int sample = 0; sample < m_samples; ++sample)
333 int index = indexData(0, line, sample);
334 uint16_t value = m_data[index];
335 bool isVoid = (0 != (value & 0x8000));
336 if (!isVoid)
338 if (lastNonVoid != -1)
340 int gap = sample - lastNonVoid;
341 // Obtain some control points for the splines
342 // use the two samples either side of the gap as control points 1 and 2
343 // use the ones before and after these samples, extended to the size of
344 // the gap as control points 0 and 3
345 float startAlt = lastNonVoidValue;
346 float beforeStartAlt = startAlt;
347 if (lastNonVoid > 0)
349 uint16_t beforeStart = m_data[indexData(0, line, lastNonVoid-1)];
350 if (0 == (beforeStart & 0x8000))
352 beforeStartAlt = startAlt + ((float)beforeStart - startAlt) * gap;
355 float endAlt = value;
356 float afterEndAlt = endAlt;
357 if (sample < m_samples-1)
359 uint16_t afterEnd = m_data[indexData(0, line, sample+1)];
360 if (0 == (afterEnd & 0x8000))
362 afterEndAlt = endAlt + ((float)afterEnd - endAlt) * gap;
365 // Lovely, between lastNonVoid and sample is a void
366 for (int i = lastNonVoid+1; i < sample; ++i)
368 int iIndex = indexData(0, line, i);
369 float u = (float)(i-lastNonVoid) / gap;
370 float u2 = u*u;
371 float u3 = u2*u;
372 uint16_t alt = maths::catmullRomSpline.Spline(u, u2, u3, beforeStartAlt, startAlt, endAlt, afterEndAlt);
373 alt |= 0x8000;
374 m_data[iIndex] = alt;
375 // Keep track of the gap
376 m_data[indexData(1, line, i)] = gap;
379 lastNonVoid = sample;
380 lastNonVoidValue = value;
382 else
384 // Clear any previous data
385 m_data[index] = 0x8000;
389 for (int sample = 0; sample < m_samples; ++sample)
391 int lastNonVoid = -1;
392 uint16_t lastNonVoidValue;
393 for (int line = 0; line < m_lines; ++line)
395 int index = indexData(0, line, sample);
396 uint16_t value = m_data[index];
397 bool isVoid = (0 != (value & 0x8000));
398 if (!isVoid)
400 if (lastNonVoid != -1)
402 int gap = line - lastNonVoid;
403 // Obtain some control points for the splines
404 // use the two samples either side of the gap as control points 1 and 2
405 // use the ones before and after these samples, extended to the size of
406 // the gap as control points 0 and 3
407 float startAlt = lastNonVoidValue;
408 float beforeStartAlt = startAlt;
409 if (lastNonVoid > 0)
411 uint16_t beforeStart = m_data[indexData(0, lastNonVoid-1, sample)];
412 if (0 == (beforeStart & 0x8000))
414 beforeStartAlt = startAlt + ((float)beforeStart - startAlt) * gap;
417 float endAlt = value;
418 float afterEndAlt = endAlt;
419 if (line < m_lines-1)
421 uint16_t afterEnd = m_data[indexData(0, line+1, sample)];
422 if (0 == (afterEnd & 0x8000))
424 afterEndAlt = endAlt + ((float)afterEnd - endAlt) * gap;
427 // Lovely, between lastNonVoid and line is a void
428 for (int i = lastNonVoid+1; i < line; ++i)
430 // Average with previous value if non zero, otherwise overwrite
431 int iIndex = indexData(0, i, sample);
432 int iIndexC = indexData(1, i, sample);
433 float u = (float)(i-lastNonVoid) / gap;
434 float u2 = u*u;
435 float u3 = u2*u;
436 uint16_t alt = maths::catmullRomSpline.Spline(u, u2, u3, beforeStartAlt, startAlt, endAlt, afterEndAlt);
437 uint16_t prevAlt = 0x7fff & m_data[iIndex];
438 if (prevAlt != 0)
440 int otherGap = m_data[iIndexC];
441 // Prefer the value of the smaller gap
442 alt = (float)((int)alt * otherGap + (int)prevAlt * gap) / (int)(gap+otherGap);
444 alt |= 0x8000;
445 m_data[iIndex] = alt;
446 m_data[iIndexC] = alt;
449 else
451 for (int i = lastNonVoid+1; i < line; ++i)
453 int iIndex = indexData(0, i, sample);
454 int iIndexC = indexData(1, i, sample);
455 // The corrected value may contain a gap, which isn't needed anymore
456 m_data[iIndexC] = m_data[iIndex];
459 lastNonVoid = line;
460 lastNonVoidValue = value;
467 * Private functions
470 /// Index into the data array.
471 unsigned int tcSrtmData::indexData(int corrected, int line, int sample) const
473 return (corrected*m_lines + line)*m_samples + sample;