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 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());
72 if (sample
& 0x8000 && sample
!= 0x8000)
76 m_data
[indexData(0, i
, j
)] = sample
;
77 m_data
[indexData(1, i
, j
)] = sample
;
80 /// @todo Handle larger files!
85 progressing(tr("Filling SRTM voids (%1,%2)").arg(lon
).arg(lat
));
89 progressing(tr("SRTM (%1,%2): Complete").arg(lon
).arg(lat
));
93 tcSrtmData::~tcSrtmData()
102 /// Get the longitude in degrees.
103 int tcSrtmData::lon() const
108 /// Get the latitude in degrees.
109 int tcSrtmData::lat() const
114 /// Get the number of scan lines.
115 int tcSrtmData::lines() const
120 /// Get the number of samples in each scan line.
121 int tcSrtmData::samples() const
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
);
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
);
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)];
189 return ((float)x00
*mdx
*mdy
+ (float)x01
*mdx
*dy
+ (float)x10
*dx
*mdy
+ (float)x11
*dx
*dy
);
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
);
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));
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
);
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
;
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));
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
];
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
);
303 m_data
[iIndex
] = alt
;
304 m_data
[iIndexC
] = alt
;
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
];
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));
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
;
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
;
372 uint16_t alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
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
;
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));
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
;
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
;
436 uint16_t alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
437 uint16_t prevAlt
= 0x7fff & m_data
[iIndex
];
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
);
445 m_data
[iIndex
] = alt
;
446 m_data
[iIndexC
] = alt
;
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
];
460 lastNonVoidValue
= value
;
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
;