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 "tcElevationData.h"
27 #include <SplineDefinitions.h>
30 #include <QDataStream>
35 * Constructors + destructor
39 tcSrtmData::tcSrtmData(int lon
, int lat
, QFile
& file
, QObject
* progressObj
, const char* progressMember
)
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
);
58 for (int i
= 0; i
< m_lines
; ++i
)
60 int percent
= 20*i
/(m_lines
-1);
61 if (percent
!= lastPercent
)
63 lastPercent
= percent
;
64 progressing(tr("Loading SRTM (%1,%2): %3%").arg(lon
).arg(lat
).arg(percent
*5));
66 for (int j
= 0; j
< m_samples
; ++j
)
68 Q_ASSERT(!in
.atEnd());
71 m_data
[indexData(0, i
, j
)] = sample
;
72 m_data
[indexData(1, i
, j
)] = sample
;
75 /// @todo Handle larger files!
78 progressing(tr("Filling SRTM voids (%1,%2)").arg(lon
).arg(lat
));
81 progressing(tr("SRTM (%1,%2): Complete").arg(lon
).arg(lat
));
85 tcSrtmData::~tcSrtmData()
94 /// Get the longitude in degrees.
95 int tcSrtmData::lon() const
100 /// Get the latitude in degrees.
101 int tcSrtmData::lat() const
106 /// Get the number of scan lines.
107 int tcSrtmData::lines() const
112 /// Get the number of samples in each scan line.
113 int tcSrtmData::samples() const
118 /// Find the geographical angles between samples.
119 tcGeo
tcSrtmData::sampleResolution() const
121 return tcGeo(M_PI
/180 / (m_samples
- 1),
122 M_PI
/180 / (m_lines
- 1));
125 /// Get the geographical coordinate of a sample.
126 tcGeo
tcSrtmData::sampleCoordinate(int line
, int sample
) const
128 return tcGeo(M_PI
/180*((double) m_lon
+ (double)sample
/(m_samples
-1)),
129 M_PI
/180*((double)(m_lat
+1) - (double)line
/(m_lines
-1)));
132 /// Does data exist for a sample?
133 bool tcSrtmData::sampleExists(int line
, int sample
) const
135 Q_ASSERT(line
>= 0 && line
< m_lines
);
136 Q_ASSERT(sample
>= 0 && sample
< m_samples
);
137 return 0x0000 == (0x8000 & m_data
[indexData(0, line
, sample
)]);
140 /// Get the altitude at a sample (interpolating gaps).
141 uint16_t tcSrtmData::sampleAltitude(int line
, int sample
, bool corrected
, bool* exists
) const
143 Q_ASSERT(line
>= 0 && line
< m_lines
);
144 Q_ASSERT(sample
>= 0 && sample
< m_samples
);
147 *exists
= (0x0000 == (0x8000 & m_data
[indexData(corrected
? 1 : 0, line
, sample
)]));
149 return 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
, sample
)];
152 /// Get the altitude at a coordinate.
153 float tcSrtmData::sampleAltitude(const tcGeo
& coord
, bool corrected
, bool* exists
) const
155 double dlon
= coord
.lon()*180.0/M_PI
- m_lon
;
156 double dlat
= coord
.lat()*180.0/M_PI
- m_lat
;
157 Q_ASSERT(dlon
>= 0.0 && dlon
< 1.0);
158 Q_ASSERT(dlat
>= 0.0 && dlat
< 1.0);
159 int line
= (int)floor(dlat
*(m_lines
-1));
160 float dy
= dlat
*(m_lines
-1) - line
;
161 line
= m_lines
- 1 - line
;
162 int sample
= (int)floor(dlon
*(m_samples
-1));
163 float dx
= dlon
*(m_samples
-1) - sample
;
164 Q_ASSERT(line
> 0 && line
< m_lines
);
165 Q_ASSERT(sample
>= 0 && sample
< m_samples
-1);
166 Q_ASSERT(dx
>= 0.0f
&& dx
<= 1.0f
);
167 Q_ASSERT(dy
>= 0.0f
&& dy
<= 1.0f
);
170 *exists
= (0x0000 == (0x8000 & m_data
[indexData(corrected
? 1 : 0, line
, sample
)]))
171 && (0x0000 == (0x8000 & m_data
[indexData(corrected
? 1 : 0, line
, sample
+1)]))
172 && (0x0000 == (0x8000 & m_data
[indexData(corrected
? 1 : 0, line
-1, sample
)]))
173 && (0x0000 == (0x8000 & m_data
[indexData(corrected
? 1 : 0, line
-1, sample
+1)]));
175 uint16_t x00
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
, sample
)];
176 uint16_t x10
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
, sample
+1)];
177 uint16_t x01
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
-1, sample
)];
178 uint16_t x11
= 0x7FFF & m_data
[indexData(corrected
? 1 : 0, line
-1, sample
+1)];
181 return ((float)x00
*mdx
*mdy
+ (float)x01
*mdx
*dy
+ (float)x10
*dx
*mdy
+ (float)x11
*dx
*dy
);
188 /// Correct the altitude at a sample.
189 void tcSrtmData::setSampleAltitude(int line
, int sample
, uint16_t altitude
)
191 Q_ASSERT(line
>= 0 && line
< m_lines
);
192 Q_ASSERT(sample
>= 0 && sample
< m_samples
);
193 m_data
[indexData(1, line
, sample
)] = altitude
;
196 /// Update any samples affected by the elevation field.
197 void tcSrtmData::updateFromElevationData(const tcElevationData
* elevation
)
199 tcGeo swCorner
= elevation
->swCorner();
200 tcGeo neCorner
= elevation
->neCorner();
201 int minl
= qMax(0, (int)floor(((double)m_lat
+1 - neCorner
.lat()*180.0/M_PI
)*(m_lines
-1)));
202 int maxl
= qMin(m_lines
, (int)ceil (((double)m_lat
+1 - swCorner
.lat()*180.0/M_PI
)*(m_lines
-1)));
203 int mins
= qMax(0, (int)floor((swCorner
.lon()*180.0/M_PI
- m_lon
)*(m_samples
-1)));
204 int maxs
= qMin(m_samples
, (int)ceil ((neCorner
.lon()*180.0/M_PI
- m_lon
)*(m_samples
-1)));
205 // Go through all elevation samples to update
206 for (int line
= minl
; line
< maxl
; ++line
)
208 for (int sample
= mins
; sample
< maxs
; ++sample
)
210 tcGeo
corner1(((double) m_lon
+ (-0.5 + sample
)/(m_samples
-1)) * M_PI
/180,
211 ((double)(m_lat
+1) - (-0.5 + line
)/(m_lines
-1)) * M_PI
/180);
212 tcGeo
corner2(((double) m_lon
+ ( 0.5 + sample
)/(m_samples
-1)) * M_PI
/180,
213 ((double)(m_lat
+1) - ( 0.5 + line
)/(m_lines
-1)) * M_PI
/180);
214 bool inRange
= false;
215 float elev
= elevation
->meanElevation(corner1
, corner2
, &inRange
);
218 m_data
[indexData(1, line
, sample
)] = (0x8000 & m_data
[indexData(1, line
, sample
)]) | (uint16_t)floor(elev
+0.5f
);
224 /// Fill voids using bilinear interpolation.
225 void tcSrtmData::fillVoidsBilinear()
227 for (int line
= 0; line
< m_lines
; ++line
)
229 int lastNonVoid
= -1;
230 uint16_t lastNonVoidValue
;
231 for (int sample
= 0; sample
< m_samples
; ++sample
)
233 int index
= indexData(0, line
, sample
);
234 uint16_t value
= m_data
[index
];
235 bool isVoid
= (0 != (value
& 0x8000));
238 if (lastNonVoid
!= -1)
240 int gap
= sample
- lastNonVoid
;
241 float startAlt
= lastNonVoidValue
;
242 float dAlt
= (float)(value
- lastNonVoidValue
) / gap
;
243 // Lovely, between lastNonVoid and sample is a void
244 for (int i
= lastNonVoid
+1; i
< sample
; ++i
)
246 int iIndex
= indexData(0, line
, i
);
247 uint16_t alt
= startAlt
+ dAlt
* (i
- lastNonVoid
);
249 m_data
[iIndex
] = alt
;
250 // Keep track of the gap
251 m_data
[indexData(1, line
, i
)] = gap
;
254 lastNonVoid
= sample
;
255 lastNonVoidValue
= value
;
259 // Clear any previous data
260 m_data
[index
] = 0x8000;
264 for (int sample
= 0; sample
< m_samples
; ++sample
)
266 int lastNonVoid
= -1;
267 uint16_t lastNonVoidValue
;
268 for (int line
= 0; line
< m_lines
; ++line
)
270 int index
= indexData(0, line
, sample
);
271 uint16_t value
= m_data
[index
];
272 bool isVoid
= (0 != (value
& 0x8000));
275 if (lastNonVoid
!= -1)
277 int gap
= line
- lastNonVoid
;
278 float startAlt
= lastNonVoidValue
;
279 float dAlt
= (float)(value
- lastNonVoidValue
) / gap
;
280 // Lovely, between lastNonVoid and line is a void
281 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
283 // Average with previous value if non zero, otherwise overwrite
284 int iIndex
= indexData(0, i
, sample
);
285 int iIndexC
= indexData(1, i
, sample
);
286 uint16_t alt
= startAlt
+ dAlt
* (i
- lastNonVoid
);
287 uint16_t prevAlt
= 0x7fff & m_data
[iIndex
];
290 int otherGap
= m_data
[iIndexC
];
291 // Prefer the value of the smaller gap
292 alt
= (float)((int)alt
* otherGap
+ (int)prevAlt
* gap
) / (int)(gap
+otherGap
);
295 m_data
[iIndex
] = alt
;
296 m_data
[iIndexC
] = alt
;
301 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
303 int iIndex
= indexData(0, i
, sample
);
304 int iIndexC
= indexData(1, i
, sample
);
305 // The corrected value may contain a gap, which isn't needed anymore
306 m_data
[iIndexC
] = m_data
[iIndex
];
310 lastNonVoidValue
= value
;
316 /// Fill voids using bicubic interpolation.
317 void tcSrtmData::fillVoidsBicubic()
319 for (int line
= 0; line
< m_lines
; ++line
)
321 int lastNonVoid
= -1;
322 uint16_t lastNonVoidValue
;
323 for (int sample
= 0; sample
< m_samples
; ++sample
)
325 int index
= indexData(0, line
, sample
);
326 uint16_t value
= m_data
[index
];
327 bool isVoid
= (0 != (value
& 0x8000));
330 if (lastNonVoid
!= -1)
332 int gap
= sample
- lastNonVoid
;
333 // Obtain some control points for the splines
334 // use the two samples either side of the gap as control points 1 and 2
335 // use the ones before and after these samples, extended to the size of
336 // the gap as control points 0 and 3
337 float startAlt
= lastNonVoidValue
;
338 float beforeStartAlt
= startAlt
;
341 uint16_t beforeStart
= m_data
[indexData(0, line
, lastNonVoid
-1)];
342 if (0 == (beforeStart
& 0x8000))
344 beforeStartAlt
= startAlt
+ ((float)beforeStart
- startAlt
) * gap
;
347 float endAlt
= value
;
348 float afterEndAlt
= endAlt
;
349 if (sample
< m_samples
-1)
351 uint16_t afterEnd
= m_data
[indexData(0, line
, sample
+1)];
352 if (0 == (afterEnd
& 0x8000))
354 afterEndAlt
= endAlt
+ ((float)afterEnd
- endAlt
) * gap
;
357 // Lovely, between lastNonVoid and sample is a void
358 for (int i
= lastNonVoid
+1; i
< sample
; ++i
)
360 int iIndex
= indexData(0, line
, i
);
361 float u
= (float)(i
-lastNonVoid
) / gap
;
364 uint16_t alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
366 m_data
[iIndex
] = alt
;
367 // Keep track of the gap
368 m_data
[indexData(1, line
, i
)] = gap
;
371 lastNonVoid
= sample
;
372 lastNonVoidValue
= value
;
376 // Clear any previous data
377 m_data
[index
] = 0x8000;
381 for (int sample
= 0; sample
< m_samples
; ++sample
)
383 int lastNonVoid
= -1;
384 uint16_t lastNonVoidValue
;
385 for (int line
= 0; line
< m_lines
; ++line
)
387 int index
= indexData(0, line
, sample
);
388 uint16_t value
= m_data
[index
];
389 bool isVoid
= (0 != (value
& 0x8000));
392 if (lastNonVoid
!= -1)
394 int gap
= line
- lastNonVoid
;
395 // Obtain some control points for the splines
396 // use the two samples either side of the gap as control points 1 and 2
397 // use the ones before and after these samples, extended to the size of
398 // the gap as control points 0 and 3
399 float startAlt
= lastNonVoidValue
;
400 float beforeStartAlt
= startAlt
;
403 uint16_t beforeStart
= m_data
[indexData(0, lastNonVoid
-1, sample
)];
404 if (0 == (beforeStart
& 0x8000))
406 beforeStartAlt
= startAlt
+ ((float)beforeStart
- startAlt
) * gap
;
409 float endAlt
= value
;
410 float afterEndAlt
= endAlt
;
411 if (line
< m_lines
-1)
413 uint16_t afterEnd
= m_data
[indexData(0, line
+1, sample
)];
414 if (0 == (afterEnd
& 0x8000))
416 afterEndAlt
= endAlt
+ ((float)afterEnd
- endAlt
) * gap
;
419 // Lovely, between lastNonVoid and line is a void
420 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
422 // Average with previous value if non zero, otherwise overwrite
423 int iIndex
= indexData(0, i
, sample
);
424 int iIndexC
= indexData(1, i
, sample
);
425 float u
= (float)(i
-lastNonVoid
) / gap
;
428 uint16_t alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
429 uint16_t prevAlt
= 0x7fff & m_data
[iIndex
];
432 int otherGap
= m_data
[iIndexC
];
433 // Prefer the value of the smaller gap
434 alt
= (float)((int)alt
* otherGap
+ (int)prevAlt
* gap
) / (int)(gap
+otherGap
);
437 m_data
[iIndex
] = alt
;
438 m_data
[iIndexC
] = alt
;
443 for (int i
= lastNonVoid
+1; i
< line
; ++i
)
445 int iIndex
= indexData(0, i
, sample
);
446 int iIndexC
= indexData(1, i
, sample
);
447 // The corrected value may contain a gap, which isn't needed anymore
448 m_data
[iIndexC
] = m_data
[iIndex
];
452 lastNonVoidValue
= value
;
462 /// Index into the data array.
463 unsigned int tcSrtmData::indexData(int corrected
, int line
, int sample
) const
465 return (corrected
*m_lines
+ line
)*m_samples
+ sample
;