4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "Terrain/RasterTileCache.hpp"
25 #include "Terrain/RasterLocation.hpp"
26 #include "jasper/jas_image.h"
27 #include "Math/Angle.hpp"
28 #include "IO/ZipLineReader.hpp"
29 #include "Operation/Operation.hpp"
30 #include "Math/FastMath.h"
36 RasterTileCache::GetImageBuffer(unsigned index
)
38 if (TileRequest(index
))
39 return tiles
.GetLinear(index
).GetImageBuffer();
45 RasterTileCache::SetTile(unsigned index
,
46 int xstart
, int ystart
, int xend
, int yend
)
48 if (!segments
.empty() && !segments
.last().IsTileSegment())
49 /* link current marker segment with this tile */
50 segments
.last().tile
= index
;
52 tiles
.GetLinear(index
).Set(xstart
, ystart
, xend
, yend
);
55 struct RTDistanceSort
{
56 const RasterTileCache
&rtc
;
58 RTDistanceSort(RasterTileCache
&_rtc
):rtc(_rtc
) {}
60 bool operator()(unsigned short ai
, unsigned short bi
) const {
61 const RasterTile
&a
= rtc
.tiles
.GetLinear(ai
);
62 const RasterTile
&b
= rtc
.tiles
.GetLinear(bi
);
64 return a
.GetDistance() < b
.GetDistance();
69 RasterTileCache::PollTiles(int x
, int y
, unsigned radius
)
74 /* tiles are usually 256 pixels wide; with a radius smaller than
75 that, the (optimized) tile distance calculations may fail;
76 additionally, this ensures that tiles which are slightly out of
77 the screen will be loaded in advance */
82 * Maximum number of tiles loaded at a time, to reduce system load
85 MAX_ACTIVATE
= MAX_ACTIVE_TILES
> 32 ? 16 : MAX_ACTIVE_TILES
/ 2,
88 /* query all tiles; all tiles which are either in range or already
89 loaded are added to RequestTiles */
91 request_tiles
.clear();
92 for (int i
= tiles
.GetSize() - 1; i
>= 0 && !request_tiles
.full(); --i
)
93 if (tiles
.GetLinear(i
).VisibilityChanged(x
, y
, radius
))
94 request_tiles
.append(i
);
96 /* reduce if there are too many */
98 if (request_tiles
.size() > MAX_ACTIVE_TILES
) {
99 /* sort by distance */
100 const RTDistanceSort
sort(*this);
101 std::sort(request_tiles
.begin(), request_tiles
.end(), sort
);
103 /* dispose all tiles which are out of range */
104 for (unsigned i
= MAX_ACTIVE_TILES
; i
< request_tiles
.size(); ++i
) {
105 RasterTile
&tile
= tiles
.GetLinear(request_tiles
[i
]);
109 request_tiles
.shrink(MAX_ACTIVE_TILES
);
112 /* fill ActiveTiles and request new tiles */
116 unsigned num_activate
= 0;
117 for (unsigned i
= 0; i
< request_tiles
.size(); ++i
) {
118 RasterTile
&tile
= tiles
.GetLinear(request_tiles
[i
]);
119 if (tile
.IsEnabled())
122 if (++num_activate
<= MAX_ACTIVATE
)
123 /* request the tile in the current iteration */
126 /* this tile will be loaded in the next iteration */
130 return num_activate
> 0;
134 RasterTileCache::TileRequest(unsigned index
)
136 RasterTile
&tile
= tiles
.GetLinear(index
);
138 if (!tile
.IsRequested())
142 return true; // want to load this one!
146 RasterTileCache::GetHeight(unsigned px
, unsigned py
) const
148 if (px
>= width
|| py
>= height
)
149 // outside overall bounds
150 return RasterBuffer::TERRAIN_INVALID
;
152 const RasterTile
&tile
= tiles
.Get(px
/ tile_width
, py
/ tile_height
);
153 if (tile
.IsEnabled())
154 return tile
.GetHeight(px
, py
);
156 // still not found, so go to overview
157 return overview
.GetInterpolated(px
<< (SUBPIXEL_BITS
- OVERVIEW_BITS
),
158 py
<< (SUBPIXEL_BITS
- OVERVIEW_BITS
));
162 RasterTileCache::GetInterpolatedHeight(unsigned int lx
, unsigned int ly
) const
164 if ((lx
>= overview_width_fine
) || (ly
>= overview_height_fine
))
165 // outside overall bounds
166 return RasterBuffer::TERRAIN_INVALID
;
168 unsigned px
= lx
, py
= ly
;
169 const unsigned int ix
= CombinedDivAndMod(px
);
170 const unsigned int iy
= CombinedDivAndMod(py
);
172 const RasterTile
&tile
= tiles
.Get(px
/ tile_width
, py
/ tile_height
);
173 if (tile
.IsEnabled())
174 return tile
.GetInterpolatedHeight(px
, py
, ix
, iy
);
176 // still not found, so go to overview
177 return overview
.GetInterpolated(lx
>> OVERVIEW_BITS
,
178 ly
>> OVERVIEW_BITS
);
182 RasterTileCache::SetSize(unsigned _width
, unsigned _height
,
183 unsigned _tile_width
, unsigned _tile_height
,
184 unsigned tile_columns
, unsigned tile_rows
)
188 tile_width
= _tile_width
;
189 tile_height
= _tile_height
;
191 overview
.Resize(width
>> OVERVIEW_BITS
, height
>> OVERVIEW_BITS
);
192 overview_width_fine
= width
<< SUBPIXEL_BITS
;
193 overview_height_fine
= height
<< SUBPIXEL_BITS
;
195 tiles
.GrowDiscard(tile_columns
, tile_rows
);
199 RasterTileCache::SetLatLonBounds(double _lon_min
, double _lon_max
,
200 double _lat_min
, double _lat_max
)
202 const Angle
lon_min(Angle::Degrees(_lon_min
));
203 const Angle
lon_max(Angle::Degrees(_lon_max
));
204 const Angle
lat_min(Angle::Degrees(_lat_min
));
205 const Angle
lat_max(Angle::Degrees(_lat_max
));
207 bounds
= GeoBounds(GeoPoint(std::min(lon_min
, lon_max
),
208 std::max(lat_min
, lat_max
)),
209 GeoPoint(std::max(lon_min
, lon_max
),
210 std::min(lat_min
, lat_max
)));
211 bounds_initialised
= true;
215 RasterTileCache::Reset()
220 bounds_initialised
= false;
222 scan_overview
= true;
226 for (auto it
= tiles
.begin(), end
= tiles
.end(); it
!= end
; ++it
)
231 const RasterTileCache::MarkerSegmentInfo
*
232 RasterTileCache::FindMarkerSegment(uint32_t file_offset
) const
234 for (const MarkerSegmentInfo
*p
= segments
.begin(); p
< segments
.end(); ++p
)
235 if (p
->file_offset
>= file_offset
)
242 RasterTileCache::SkipMarkerSegment(long file_offset
) const
245 /* use all segments when loading the overview */
248 if (remaining_segments
> 0) {
249 /* enable the follow-up segment */
250 --remaining_segments
;
254 const MarkerSegmentInfo
*segment
= FindMarkerSegment(file_offset
);
256 /* past the end of the recorded segment list; shouldn't happen */
259 long skip_to
= segment
->file_offset
;
260 while (segment
->IsTileSegment() &&
261 !tiles
.GetLinear(segment
->tile
).IsRequested()) {
263 if (segment
>= segments
.end())
264 /* last segment is hidden; shouldn't happen either, because we
268 skip_to
= segment
->file_offset
;
271 remaining_segments
= segment
->count
;
272 return skip_to
- file_offset
;
276 * Does this segment belong to the preceding tile? If yes, then it
277 * inherits the tile number.
280 is_tile_segment(unsigned id
)
282 return id
== 0xff93 /* SOD */ ||
283 id
== 0xff52 /* COD */ ||
284 id
== 0xff53 /* COC */ ||
285 id
== 0xff5c /* QCD */ ||
286 id
== 0xff5d /* QCC */ ||
287 id
== 0xff5e /* RGN */ ||
288 id
== 0xff5f /* POC */ ||
289 id
== 0xff61 /* PPT */ ||
290 id
== 0xff58 /* PLT */ ||
291 id
== 0xff64 /* COM */;
295 RasterTileCache::MarkerSegment(long file_offset
, unsigned id
)
297 if (!scan_overview
|| segments
.full())
300 if (operation
!= NULL
)
301 operation
->SetProgressPosition(file_offset
/ 65536);
303 if (is_tile_segment(id
) && !segments
.empty() &&
304 segments
.last().IsTileSegment()) {
305 /* this segment belongs to the same tile as the preceding SOT
307 ++segments
.last().count
;
311 if (segments
.size() >= 2 && !segments
.last().IsTileSegment() &&
312 !segments
[segments
.size() - 2].IsTileSegment()) {
313 /* the last two segments are both "generic" segments and can be merged*/
314 assert(segments
.last().count
== 0);
316 ++segments
[segments
.size() - 2].count
;
318 /* reuse the second segment */
319 segments
.last().file_offset
= file_offset
;
321 segments
.append(MarkerSegmentInfo(file_offset
,
322 MarkerSegmentInfo::NO_TILE
));
325 extern RasterTileCache
*raster_tile_current
;
328 RasterTileCache::LoadJPG2000(const char *jp2_filename
)
332 raster_tile_current
= this;
334 in
= jas_stream_fopen(jp2_filename
, "rb");
340 if (operation
!= NULL
)
341 operation
->SetProgressRange(jas_stream_length(in
) / 65536);
343 jp2_decode(in
, scan_overview
? "xcsoar=2" : "xcsoar=1");
344 jas_stream_close(in
);
348 RasterTileCache::LoadWorldFile(const TCHAR
*path
)
350 ZipLineReaderA
reader(path
);
355 const char *line
= reader
.ReadLine(); // x scale
356 double x_scale
= strtod(line
, &endptr
);
360 line
= reader
.ReadLine(); // y rotation
364 double y_rotation
= strtod(line
, &endptr
);
365 if (endptr
== line
|| y_rotation
< -0.01 || y_rotation
> 0.01)
366 /* we don't support rotation */
369 line
= reader
.ReadLine(); // x rotation
373 double x_rotation
= strtod(line
, &endptr
);
374 if (endptr
== line
|| x_rotation
< -0.01 || x_rotation
> 0.01)
375 /* we don't support rotation */
378 line
= reader
.ReadLine(); // y scale
382 double y_scale
= strtod(line
, &endptr
);
386 line
= reader
.ReadLine(); // x origin
390 double x_origin
= strtod(line
, &endptr
);
394 line
= reader
.ReadLine(); // y origin
398 double y_origin
= strtod(line
, &endptr
);
402 SetLatLonBounds(x_origin
, x_origin
+ GetWidth() * x_scale
,
403 y_origin
, y_origin
+ GetHeight() * y_scale
);
408 RasterTileCache::LoadOverview(const char *path
, const TCHAR
*world_file
,
409 OperationEnvironment
&_operation
)
411 assert(operation
== NULL
);
412 operation
= &_operation
;
417 scan_overview
= false;
419 if (initialised
&& world_file
!= NULL
)
420 LoadWorldFile(world_file
);
422 if (initialised
&& !bounds_initialised
)
433 RasterTileCache::UpdateTiles(const char *path
, int x
, int y
, unsigned radius
)
435 if (!PollTiles(x
, y
, radius
))
438 remaining_segments
= 0;
442 /* permanently disable the requested tiles which are still not
443 loaded, to prevent trying to reload them over and over in a busy
445 for (auto it
= request_tiles
.begin(), end
= request_tiles
.end();
447 RasterTile
&tile
= tiles
.GetLinear(*it
);
448 if (tile
.IsRequested() && !tile
.IsEnabled())
456 RasterTileCache::SaveCache(FILE *file
) const
461 assert(bounds_initialised
);
466 /* zero-fill all implicit padding bytes (to make valgrind happy) */
467 memset(&header
, 0, sizeof(header
));
469 header
.version
= CacheHeader::VERSION
;
470 header
.width
= width
;
471 header
.height
= height
;
472 header
.tile_width
= tile_width
;
473 header
.tile_height
= tile_height
;
474 header
.tile_columns
= tiles
.GetWidth();
475 header
.tile_rows
= tiles
.GetHeight();
476 header
.num_marker_segments
= segments
.size();
477 header
.bounds
= bounds
;
479 if (fwrite(&header
, sizeof(header
), 1, file
) != 1 ||
480 /* .. and segments */
481 fwrite(segments
.begin(), sizeof(*segments
.begin()), segments
.size(), file
) != segments
.size())
486 for (i
= 0; i
< tiles
.GetSize(); ++i
)
487 if (tiles
.GetLinear(i
).IsDefined() &&
488 (fwrite(&i
, sizeof(i
), 1, file
) != 1 ||
489 !tiles
.GetLinear(i
).SaveCache(file
)))
493 if (fwrite(&i
, sizeof(i
), 1, file
) != 1)
497 size_t overview_size
= overview
.GetWidth() * overview
.GetHeight();
498 if (fwrite(overview
.GetData(), sizeof(*overview
.GetData()),
499 overview_size
, file
) != overview_size
)
507 RasterTileCache::LoadCache(FILE *file
)
513 if (fread(&header
, sizeof(header
), 1, file
) != 1 ||
514 header
.version
!= CacheHeader::VERSION
||
515 header
.width
< 1024 || header
.width
> 1024 * 1024 ||
516 header
.height
< 1024 || header
.height
> 1024 * 1024 ||
517 header
.num_marker_segments
< 4 ||
518 header
.num_marker_segments
> segments
.capacity() ||
519 header
.bounds
.IsEmpty())
522 SetSize(header
.width
, header
.height
,
523 header
.tile_width
, header
.tile_height
,
524 header
.tile_columns
, header
.tile_rows
);
525 bounds
= header
.bounds
;
526 bounds_initialised
= true;
529 for (unsigned i
= 0; i
< header
.num_marker_segments
; ++i
) {
530 MarkerSegmentInfo
&segment
= segments
.append();
531 if (fread(&segment
, sizeof(segment
), 1, file
) != 1)
538 if (fread(&i
, sizeof(i
), 1, file
) != 1)
541 if (i
== (unsigned)-1)
544 if (i
>= tiles
.GetSize())
547 if (!tiles
.GetLinear(i
).LoadCache(file
))
552 size_t overview_size
= overview
.GetWidth() * overview
.GetHeight();
553 if (fread(overview
.GetData(), sizeof(*overview
.GetData()),
554 overview_size
, file
) != overview_size
)
558 scan_overview
= false;