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 ***************************************************************************/
21 * @file tcSrtmData.cpp
22 * @brief Block of raw SRTM elevation data.
25 #include "tcSrtmData.h"
26 #include <SplineDefinitions.h>
29 #include <QDataStream>
34 * Constructors + destructor
38 tcSrtmData::tcSrtmData(int lon
, int lat
, QFile
& file
, QObject
* progressObj
, const char* progressMember
)
45 connect(this, SIGNAL(progressing(const QString
&)), progressObj
, progressMember
);
47 qint64 elements
= file
.size() / sizeof(uint16_t);
48 double side
= sqrt(elements
);
49 m_lines
= m_samples
= (int)side
;
50 Q_ASSERT(m_lines
*m_samples
== elements
);
51 m_data
= new uint16_t[2*m_lines
*m_samples
];
52 Q_ASSERT(0 != m_data
);
54 QDataStream
in(&file
);
55 in
.setByteOrder(QDataStream::BigEndian
);
57 for (int i
= 0; i
< m_lines
; ++i
)
59 int percent
= 20*i
/(m_lines
-1);
60 if (percent
!= lastPercent
)
62 lastPercent
= percent
;
63 progressing(tr("Loading SRTM (%1,%2): %3%").arg(lon
).arg(lat
).arg(percent
*5));
65 for (int j
= 0; j
< m_samples
; ++j
)
67 Q_ASSERT(!in
.atEnd());
70 m_data
[indexData(0, i
, j
)] = sample
;
71 m_data
[indexData(1, i
, j
)] = sample
;
74 /// @todo Handle larger files!
77 progressing(tr("Filling SRTM voids (%1,%2)").arg(lon
).arg(lat
));
80 progressing(tr("SRTM (%1,%2): Complete").arg(lon
).arg(lat
));
84 tcSrtmData::~tcSrtmData()
93 /// Get the longitude in degrees.
94 int tcSrtmData::lon() const
99 /// Get the latitude in degrees.
100 int tcSrtmData::lat() const
105 /// Get the number of scan lines.
106 int tcSrtmData::lines() const
111 /// Get the number of samples in each scan line.
112 int tcSrtmData::samples() const
117 /// Find the geographical angles between samples.
118 tcGeo
tcSrtmData::sampleResolution() const
120 return tcGeo(M_PI
/180 / (m_samples
- 1),
121 M_PI
/180 / (m_lines
- 1));
124 /// Get the geographical coordinate of a sample.
125 tcGeo
tcSrtmData::sampleCoordinate(int line
, int sample
) const
127 return tcGeo(M_PI
/180*((double) m_lon
+ (double)sample
/(m_samples
-1)),
128 M_PI
/180*((double)(m_lat
+1) - (double)line
/(m_lines
-1)));
131 /// Does data exist for a sample?
132 bool tcSrtmData::sampleExists(int line
, int sample
) const
134 Q_ASSERT(line
>= 0 && line
< m_lines
);
135 Q_ASSERT(sample
>= 0 && sample
< m_samples
);
136 return 0x0000 == (0x8000 & m_data
[indexData(0, line
, sample
)]);
139 /// Get the altitude at a sample (interpolating gaps).
140 uint16_t tcSrtmData::sampleAltitude(int line
, int sample
, bool corrected
, bool* exists
) const
142 Q_ASSERT(line
>= 0 && line
< m_lines
);
143 Q_ASSERT(sample
>= 0 && sample
< m_samples
);
146 *exists
= (0x0000 == (0x8000 & m_data
[indexData(corrected
? 1 : 0, line
, sample
)]));
148 return 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
, sample
)];
151 /// Get the altitude at a coordinate.
152 float tcSrtmData::sampleAltitude(const tcGeo
& coord
, bool corrected
, bool* exists
) const
154 double dlon
= coord
.lon()*180.0/M_PI
- m_lon
;
155 double dlat
= coord
.lat()*180.0/M_PI
- m_lat
;
156 Q_ASSERT(dlon
>= 0.0 && dlon
< 1.0);
157 Q_ASSERT(dlat
>= 0.0 && dlat
< 1.0);
158 int line
= (int)floor(dlat
*(m_lines
-1));
159 float dy
= dlat
*(m_lines
-1) - line
;
160 line
= m_lines
- 1 - line
;
161 int sample
= (int)floor(dlon
*(m_samples
-1));
162 float dx
= dlon
*(m_samples
-1) - sample
;
163 Q_ASSERT(line
> 0 && line
< m_lines
);
164 Q_ASSERT(sample
>= 0 && sample
< m_samples
-1);
165 Q_ASSERT(dx
>= 0.0f
&& dx
<= 1.0f
);
166 Q_ASSERT(dy
>= 0.0f
&& dy
<= 1.0f
);
169 *exists
= (0x0000 == (0x8000 & m_data
[indexData(corrected
? 1 : 0, line
, sample
)]));
171 uint16_t x00
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
, sample
)];
172 uint16_t x10
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
, sample
+1)];
173 uint16_t x01
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
-1, sample
)];
174 uint16_t x11
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
-1, sample
+1)];
177 return ((float)x00
*mdx
*mdy
+ (float)x01
*mdx
*dy
+ (float)x10
*dx
*mdy
+ (float)x11
*dx
*dy
);
184 /// Fill voids using bilinear interpolation.
185 void tcSrtmData::fillVoidsBilinear()
187 for (int line
= 0; line
< m_lines
; ++line
)
189 int lastNonVoid
= -1;
190 uint16_t lastNonVoidValue
;
191 for (int sample
= 0; sample
< m_samples
; ++sample
)
193 int index
= indexData(0, line
, sample
);
194 uint16_t value
= m_data
[index
];
195 bool isVoid
= (0 != (value
& 0x8000));
198 if (lastNonVoid
!= -1)
200 int gap
= sample
- lastNonVoid
;
201 float startAlt
= lastNonVoidValue
;
202 float dAlt
= (float)(value
- lastNonVoidValue
) / gap
;
203 // Lovely, between lastNonVoid and sample is a void
204 for (int i
= lastNonVoid
+1; i
< sample
; ++i
)
206 int iIndex
= indexData(0, line
, i
);
207 uint16_t alt
= startAlt
+ dAlt
* (i
- lastNonVoid
);
209 m_data
[iIndex
] = alt
;
210 // Keep track of the gap
211 m_data
[indexData(1, line
, i
)] = gap
;
214 lastNonVoid
= sample
;
215 lastNonVoidValue
= value
;
219 // Clear any previous data
220 m_data
[index
] = 0x8000;
224 for (int sample
= 0; sample
< m_samples
; ++sample
)
226 int lastNonVoid
= -1;
227 uint16_t lastNonVoidValue
;
228 for (int line
= 0; line
< m_lines
; ++line
)
230 int index
= indexData(0, line
, sample
);
231 uint16_t value
= m_data
[index
];
232 bool isVoid
= (0 != (value
& 0x8000));
235 if (lastNonVoid
!= -1)
237 int gap
= line
- lastNonVoid
;
238 float startAlt
= lastNonVoidValue
;
239 float dAlt
= (float)(value
- lastNonVoidValue
) / gap
;
240 // Lovely, between lastNonVoid and line is a void
241 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
243 // Average with previous value if non zero, otherwise overwrite
244 int iIndex
= indexData(0, i
, sample
);
245 int iIndexC
= indexData(1, i
, sample
);
246 uint16_t alt
= startAlt
+ dAlt
* (i
- lastNonVoid
);
247 uint16_t prevAlt
= 0x7fff & m_data
[iIndex
];
250 int otherGap
= m_data
[iIndexC
];
251 // Prefer the value of the smaller gap
252 alt
= (float)((int)alt
* otherGap
+ (int)prevAlt
* gap
) / (int)(gap
+otherGap
);
255 m_data
[iIndex
] = alt
;
256 m_data
[iIndexC
] = 0x8000; //alt;
261 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
263 int iIndex
= indexData(0, i
, sample
);
264 int iIndexC
= indexData(1, i
, sample
);
265 // The corrected value may contain a gap, which isn't needed anymore
266 m_data
[iIndexC
] = 0x8000; //m_data[iIndex];
270 lastNonVoidValue
= value
;
276 /// Fill voids using bicubic interpolation.
277 void tcSrtmData::fillVoidsBicubic()
279 for (int line
= 0; line
< m_lines
; ++line
)
281 int lastNonVoid
= -1;
282 uint16_t lastNonVoidValue
;
283 for (int sample
= 0; sample
< m_samples
; ++sample
)
285 int index
= indexData(0, line
, sample
);
286 uint16_t value
= m_data
[index
];
287 bool isVoid
= (0 != (value
& 0x8000));
290 if (lastNonVoid
!= -1)
292 int gap
= sample
- lastNonVoid
;
293 // Obtain some control points for the splines
294 // use the two samples either side of the gap as control points 1 and 2
295 // use the ones before and after these samples, extended to the size of
296 // the gap as control points 0 and 3
297 float startAlt
= lastNonVoidValue
;
298 float beforeStartAlt
= startAlt
;
301 uint16_t beforeStart
= m_data
[indexData(0, line
, lastNonVoid
-1)];
302 if (0 == (beforeStart
& 0x8000))
304 beforeStartAlt
= startAlt
+ ((float)beforeStart
- startAlt
) * gap
;
307 float endAlt
= value
;
308 float afterEndAlt
= endAlt
;
309 if (sample
< m_samples
-1)
311 uint16_t afterEnd
= m_data
[indexData(0, line
, sample
+1)];
312 if (0 == (afterEnd
& 0x8000))
314 afterEndAlt
= endAlt
+ ((float)afterEnd
- endAlt
) * gap
;
317 // Lovely, between lastNonVoid and sample is a void
318 for (int i
= lastNonVoid
+1; i
< sample
; ++i
)
320 int iIndex
= indexData(0, line
, i
);
321 float u
= (float)(i
-lastNonVoid
) / gap
;
324 uint16_t alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
326 m_data
[iIndex
] = alt
;
327 // Keep track of the gap
328 m_data
[indexData(1, line
, i
)] = gap
;
331 lastNonVoid
= sample
;
332 lastNonVoidValue
= value
;
336 // Clear any previous data
337 m_data
[index
] = 0x8000;
341 for (int sample
= 0; sample
< m_samples
; ++sample
)
343 int lastNonVoid
= -1;
344 uint16_t lastNonVoidValue
;
345 for (int line
= 0; line
< m_lines
; ++line
)
347 int index
= indexData(0, line
, sample
);
348 uint16_t value
= m_data
[index
];
349 bool isVoid
= (0 != (value
& 0x8000));
352 if (lastNonVoid
!= -1)
354 int gap
= line
- lastNonVoid
;
355 // Obtain some control points for the splines
356 // use the two samples either side of the gap as control points 1 and 2
357 // use the ones before and after these samples, extended to the size of
358 // the gap as control points 0 and 3
359 float startAlt
= lastNonVoidValue
;
360 float beforeStartAlt
= startAlt
;
363 uint16_t beforeStart
= m_data
[indexData(0, lastNonVoid
-1, sample
)];
364 if (0 == (beforeStart
& 0x8000))
366 beforeStartAlt
= startAlt
+ ((float)beforeStart
- startAlt
) * gap
;
369 float endAlt
= value
;
370 float afterEndAlt
= endAlt
;
371 if (line
< m_lines
-1)
373 uint16_t afterEnd
= m_data
[indexData(0, line
+1, sample
)];
374 if (0 == (afterEnd
& 0x8000))
376 afterEndAlt
= endAlt
+ ((float)afterEnd
- endAlt
) * gap
;
379 // Lovely, between lastNonVoid and line is a void
380 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
382 // Average with previous value if non zero, otherwise overwrite
383 int iIndex
= indexData(0, i
, sample
);
384 int iIndexC
= indexData(1, i
, sample
);
385 float u
= (float)(i
-lastNonVoid
) / gap
;
388 uint16_t alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
389 uint16_t prevAlt
= 0x7fff & m_data
[iIndex
];
392 int otherGap
= m_data
[iIndexC
];
393 // Prefer the value of the smaller gap
394 alt
= (float)((int)alt
* otherGap
+ (int)prevAlt
* gap
) / (int)(gap
+otherGap
);
397 m_data
[iIndex
] = alt
;
398 m_data
[iIndexC
] = 0x8000; //alt;
403 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
405 int iIndex
= indexData(0, i
, sample
);
406 int iIndexC
= indexData(1, i
, sample
);
407 // The corrected value may contain a gap, which isn't needed anymore
408 m_data
[iIndexC
] = 0x8000; //m_data[iIndex];
412 lastNonVoidValue
= value
;
422 /// Index into the data array.
423 unsigned int tcSrtmData::indexData(int corrected
, int line
, int sample
) const
425 return (corrected
*m_lines
+ line
)*m_samples
+ sample
;