Skip to content

Commit 2ef3f38

Browse files
committed
Refactor of expire code: Move tile writing code out of expire_tiles
In the future we want to have different ways of reporting expired tiles. This splits out the code to write tiles to a file to make it more straightforward to add other methods in the future. The tests are also a bit more straightforward now and some now also test the order the tiles are reported in. This also adds a file tile-output.hpp which contains a streams implementation for writing out tiles. Used for debugging when tests fail.
1 parent d2392b2 commit 2ef3f38

6 files changed

Lines changed: 250 additions & 227 deletions

File tree

src/expire-tiles.cpp

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@
1515
* http://subversion.nexusuk.org/projects/openpistemap/trunk/scripts/expire_tiles.py
1616
*/
1717

18+
#include <algorithm>
1819
#include <cerrno>
1920
#include <cmath>
21+
#include <cstdio>
2022
#include <cstdlib>
2123
#include <cstring>
24+
#include <stdexcept>
2225
#include <string>
2326

2427
#include "expire-tiles.hpp"
@@ -34,44 +37,12 @@
3437
// How many tiles worth of space to leave either side of a changed feature
3538
static constexpr double const tile_expiry_leeway = 0.1;
3639

37-
tile_output_t::tile_output_t(char const *filename)
38-
: outfile(fopen(filename, "a"))
39-
{
40-
if (outfile == nullptr) {
41-
log_warn("Failed to open expired tiles file ({}). Tile expiry "
42-
"list will not be written!",
43-
std::strerror(errno));
44-
}
45-
}
46-
47-
tile_output_t::~tile_output_t()
48-
{
49-
if (outfile) {
50-
fclose(outfile);
51-
}
52-
}
53-
54-
void tile_output_t::output_dirty_tile(tile_t const &tile)
55-
{
56-
if (!outfile) {
57-
return;
58-
}
59-
60-
fmt::print(outfile, "{}/{}/{}\n", tile.zoom(), tile.x(), tile.y());
61-
}
62-
6340
expire_tiles::expire_tiles(uint32_t max_zoom, double max_bbox,
6441
std::shared_ptr<reprojection> projection)
6542
: m_projection(std::move(projection)), m_max_bbox(max_bbox),
6643
m_maxzoom(max_zoom), m_map_width(1U << m_maxzoom)
6744
{}
6845

69-
void expire_tiles::output_and_destroy(char const *filename, uint32_t minzoom)
70-
{
71-
tile_output_t output_writer{filename};
72-
output_and_destroy<tile_output_t>(output_writer, minzoom);
73-
}
74-
7546
void expire_tiles::expire_tile(uint32_t x, uint32_t y)
7647
{
7748
// Only try to insert to tile into the set if the last inserted tile
@@ -278,6 +249,16 @@ int expire_tiles::from_result(pg_result_t const &result, osmid_t osm_id)
278249
return num_tuples;
279250
}
280251

252+
std::vector<uint64_t> expire_tiles::get_tiles()
253+
{
254+
std::vector<uint64_t> tiles;
255+
tiles.reserve(m_dirty_tiles.size());
256+
tiles.assign(m_dirty_tiles.begin(), m_dirty_tiles.end());
257+
std::sort(tiles.begin(), tiles.end());
258+
m_dirty_tiles.clear();
259+
return tiles;
260+
}
261+
281262
void expire_tiles::merge_and_destroy(expire_tiles *other)
282263
{
283264
if (m_map_width != other->m_map_width) {
@@ -294,3 +275,25 @@ void expire_tiles::merge_and_destroy(expire_tiles *other)
294275
other->m_dirty_tiles.clear();
295276
}
296277
}
278+
279+
std::size_t output_tiles_to_file(std::vector<uint64_t> const &tiles_maxzoom,
280+
char const *filename, uint32_t minzoom,
281+
uint32_t maxzoom)
282+
{
283+
FILE *outfile = std::fopen(filename, "a");
284+
if (outfile == nullptr) {
285+
log_warn("Failed to open expired tiles file ({}). Tile expiry "
286+
"list will not be written!",
287+
std::strerror(errno));
288+
return 0;
289+
}
290+
291+
auto const count =
292+
for_each_tile(tiles_maxzoom, minzoom, maxzoom, [&](tile_t const &tile) {
293+
fmt::print(outfile, "{}/{}/{}\n", tile.zoom(), tile.x(), tile.y());
294+
});
295+
296+
std::fclose(outfile);
297+
298+
return count;
299+
}

src/expire-tiles.hpp

Lines changed: 77 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#include <memory>
1414
#include <unordered_set>
15+
#include <utility>
16+
#include <vector>
1517

1618
#include "geom.hpp"
1719
#include "geom-box.hpp"
@@ -22,26 +24,6 @@
2224

2325
class reprojection;
2426

25-
/**
26-
* Implementation of the output of the tile expiry list to a file.
27-
*/
28-
class tile_output_t
29-
{
30-
FILE *outfile;
31-
32-
public:
33-
explicit tile_output_t(char const *filename);
34-
35-
~tile_output_t();
36-
37-
/**
38-
* Output dirty tile.
39-
*
40-
* \param tile The tile to write out.
41-
*/
42-
void output_dirty_tile(tile_t const &tile);
43-
};
44-
4527
class expire_tiles
4628
{
4729
public:
@@ -58,7 +40,7 @@ class expire_tiles
5840
* Expire tiles based on an osm id.
5941
*
6042
* \param result Result of a database query into some table returning the
61-
* geometries. (This is usally done using the "get_wkb"
43+
* geometries. (This is usually done using the "get_wkb"
6244
* prepared statement.)
6345
* \param osm_id The OSM id to look for.
6446
* \return The number of elements that refer to the osm_id or -1 if
@@ -67,66 +49,15 @@ class expire_tiles
6749
int from_result(pg_result_t const &result, osmid_t osm_id);
6850

6951
/**
70-
* Write the list of expired tiles to a file.
71-
*
72-
* You will probably use tile_output_t as template argument for production code
73-
* and another class which does not write to a file for unit tests.
74-
*
75-
* \param filename name of the file
76-
* \param minzoom minimum zoom level
52+
* Get tiles as a vector of quadkeys and remove them from the expire_tiles
53+
* object.
7754
*/
78-
void output_and_destroy(char const *filename, uint32_t minzoom);
55+
std::vector<uint64_t> get_tiles();
7956

8057
/**
81-
* Output expired tiles on all requested zoom levels.
82-
*
83-
* \tparam TILE_WRITER class which implements the method
84-
* output_dirty_tile(tile_t const &tile) which usually writes the tile ID
85-
* to a file (production code) or does something else (usually unit tests).
86-
*
87-
* \param minzoom minimum zoom level
58+
* Merge the list of expired tiles in the other object into this
59+
* object, destroying the list in the other object.
8860
*/
89-
template <class TILE_WRITER>
90-
void output_and_destroy(TILE_WRITER &output_writer, uint32_t minzoom)
91-
{
92-
assert(minzoom <= m_maxzoom);
93-
// build a sorted vector of all expired tiles
94-
std::vector<uint64_t> tiles_maxzoom(m_dirty_tiles.begin(),
95-
m_dirty_tiles.end());
96-
std::sort(tiles_maxzoom.begin(), tiles_maxzoom.end());
97-
/* Loop over all requested zoom levels (from maximum down to the minimum zoom level).
98-
* Tile IDs of the tiles enclosing this tile at lower zoom levels are calculated using
99-
* bit shifts.
100-
*
101-
* last_quadkey is initialized with a value which is not expected to exist
102-
* (larger than largest possible quadkey). */
103-
uint64_t last_quadkey = 1ULL << (2 * m_maxzoom);
104-
std::size_t count = 0;
105-
for (auto const quadkey : tiles_maxzoom) {
106-
for (uint32_t dz = 0; dz <= m_maxzoom - minzoom; ++dz) {
107-
// scale down to the current zoom level
108-
uint64_t qt_current = quadkey >> (dz * 2);
109-
/* If dz > 0, there are propably multiple elements whose quadkey
110-
* is equal because they are all sub-tiles of the same tile at the current
111-
* zoom level. We skip all of them after we have written the first sibling.
112-
*/
113-
if (qt_current == last_quadkey >> (dz * 2)) {
114-
continue;
115-
}
116-
auto const tile =
117-
tile_t::from_quadkey(qt_current, m_maxzoom - dz);
118-
output_writer.output_dirty_tile(tile);
119-
++count;
120-
}
121-
last_quadkey = quadkey;
122-
}
123-
log_info("Wrote {} entries to expired tiles list", count);
124-
}
125-
126-
/**
127-
* merge the list of expired tiles in the other object into this
128-
* object, destroying the list in the other object.
129-
*/
13061
void merge_and_destroy(expire_tiles *other);
13162

13263
private:
@@ -161,4 +92,73 @@ class expire_tiles
16192
int m_map_width;
16293
};
16394

95+
/**
96+
* Iterate over tiles and call output function for each tile on all requested
97+
* zoom levels.
98+
*
99+
* \tparam OUTPUT Class with operator() taking a tile_t argument
100+
*
101+
* \param tiles The list of tiles at maximum zoom level
102+
* \param minzoom Minimum zoom level
103+
* \param maxzoom Maximum zoom level
104+
* \param output Output function
105+
*/
106+
template <class OUTPUT>
107+
std::size_t for_each_tile(std::vector<uint64_t> const &tiles, uint32_t minzoom,
108+
uint32_t maxzoom, OUTPUT &&output)
109+
{
110+
assert(minzoom <= maxzoom);
111+
112+
if (minzoom == maxzoom) {
113+
for (auto const quadkey : tiles) {
114+
std::forward<OUTPUT>(output)(
115+
tile_t::from_quadkey(quadkey, maxzoom));
116+
}
117+
return tiles.size();
118+
}
119+
120+
/**
121+
* Loop over all requested zoom levels (from maximum down to the minimum
122+
* zoom level). Tile IDs of the tiles enclosing this tile at lower zoom
123+
* levels are calculated using bit shifts.
124+
*
125+
* last_quadkey is initialized with a value which is not expected to exist
126+
* (larger than largest possible quadkey).
127+
*/
128+
uint64_t last_quadkey = 1ULL << (2 * maxzoom);
129+
std::size_t count = 0;
130+
for (auto const quadkey : tiles) {
131+
for (uint32_t dz = 0; dz <= maxzoom - minzoom; ++dz) {
132+
// scale down to the current zoom level
133+
uint64_t const qt_current = quadkey >> (dz * 2);
134+
/**
135+
* If dz > 0, there are probably multiple elements whose quadkey
136+
* is equal because they are all sub-tiles of the same tile at the
137+
* current zoom level. We skip all of them after we have written
138+
* the first sibling.
139+
*/
140+
if (qt_current == last_quadkey >> (dz * 2)) {
141+
continue;
142+
}
143+
auto const tile = tile_t::from_quadkey(qt_current, maxzoom - dz);
144+
std::forward<OUTPUT>(output)(tile);
145+
++count;
146+
}
147+
last_quadkey = quadkey;
148+
}
149+
return count;
150+
}
151+
152+
/**
153+
* Write the list of tiles to a file.
154+
*
155+
* \param tiles The list of tiles at maximum zoom level
156+
* \param filename Name of the file
157+
* \param minzoom Minimum zoom level
158+
* \param maxzoom Maximum zoom level
159+
*/
160+
std::size_t output_tiles_to_file(std::vector<uint64_t> const &tiles,
161+
char const *filename, uint32_t minzoom,
162+
uint32_t maxzoom);
163+
164164
#endif // OSM2PGSQL_EXPIRE_TILES_HPP

src/output-flex.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,9 +1857,11 @@ void output_flex_t::stop()
18571857
}
18581858

18591859
if (get_options()->expire_tiles_zoom_min > 0) {
1860-
m_expire.output_and_destroy(
1861-
get_options()->expire_tiles_filename.c_str(),
1862-
get_options()->expire_tiles_zoom_min);
1860+
auto const count = output_tiles_to_file(
1861+
m_expire.get_tiles(), get_options()->expire_tiles_filename.c_str(),
1862+
get_options()->expire_tiles_zoom_min,
1863+
get_options()->expire_tiles_zoom);
1864+
log_info("Wrote {} entries to expired tiles list", count);
18631865
}
18641866
}
18651867

src/output-pgsql.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,11 @@ void output_pgsql_t::stop()
146146
}
147147

148148
if (get_options()->expire_tiles_zoom_min > 0) {
149-
m_expire.output_and_destroy(
150-
get_options()->expire_tiles_filename.c_str(),
151-
get_options()->expire_tiles_zoom_min);
149+
auto const count = output_tiles_to_file(
150+
m_expire.get_tiles(), get_options()->expire_tiles_filename.c_str(),
151+
get_options()->expire_tiles_zoom_min,
152+
get_options()->expire_tiles_zoom);
153+
log_info("Wrote {} entries to expired tiles list", count);
152154
}
153155
}
154156

src/tile-output.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef OSM2PGSQL_TILE_OUTPUT_HPP
2+
#define OSM2PGSQL_TILE_OUTPUT_HPP
3+
4+
/**
5+
* SPDX-License-Identifier: GPL-2.0-or-later
6+
*
7+
* This file is part of osm2pgsql (https://osm2pgsql.org/).
8+
*
9+
* Copyright (C) 2006-2022 by the osm2pgsql developer community.
10+
* For a full list of authors see the git log.
11+
*/
12+
13+
#include "tile.hpp"
14+
15+
#include <iosfwd>
16+
17+
template <typename TChar, typename TTraits>
18+
std::basic_ostream<TChar, TTraits> &
19+
operator<<(std::basic_ostream<TChar, TTraits> &out, const tile_t &tile)
20+
{
21+
return out << "TILE(" << tile.zoom() << ", " << tile.x() << ", " << tile.y()
22+
<< ')';
23+
}
24+
25+
#endif // OSM2PGSQL_TILE_OUTPUT_HPP

0 commit comments

Comments
 (0)