@@ -136,29 +136,93 @@ expire_mode decide_expire_mode(TGEOM const &geom,
136136
137137} // anonymous namespace
138138
139- void expire_tiles_t::from_geometry (geom:: polygon_t const &geom ,
140- expire_config_t const &expire_config )
139+ void expire_tiles_t::build_tile_list (std::vector< uint32_t > *nodes ,
140+ geom:: ring_t const &ring, double tileY )
141141{
142- geom::box_t box;
143- auto const mode = decide_expire_mode (geom, expire_config, &box);
144-
145- if (mode == expire_mode::boundary_only) {
146- from_polygon_boundary (geom, expire_config);
147- return ;
142+ assert (ring.size () > 0 );
143+ for (std::size_t i = 1 ; i < ring.size (); ++i) {
144+ auto const t1 = coords_to_tile (ring[i]);
145+ auto const t2 = coords_to_tile (ring[i - 1 ]);
146+
147+ if ((t1.y () < tileY && t2.y () >= tileY) ||
148+ (t2.y () < tileY && t1.y () >= tileY)) {
149+ auto const pos =
150+ (tileY - t1.y ()) / (t2.y () - t1.y ()) * (t2.x () - t1.x ());
151+ nodes->push_back (t1.x () + pos);
152+ }
148153 }
154+ }
149155
156+ void expire_tiles_t::from_polygon_area (geom::polygon_t const &geom,
157+ geom::box_t box,
158+ expire_config_t const &expire_config)
159+ {
150160 if (!box.valid ()) {
151161 box = geom::envelope (geom);
152162 }
153- from_bbox (box, expire_config);
163+
164+ // This uses a variation on a simple polygon fill algorithm, for instance
165+ // described on https://alienryderflex.com/polygon_fill/ . For each row
166+ // of tiles we find the intersections with the area boundary and "fill" in
167+ // the tiles between them. Note that we don't need to take particular care
168+ // about the boundary, because we simply use the algorithm we use for
169+ // expiry along a line to do that, which will also take care of the buffer.
170+
171+ // Coordinates are numbered from bottom to top, tiles are numbered from top
172+ // to bottom, so "min" and "max" are switched here.
173+ auto const maxTileY = static_cast <std::uint32_t >(
174+ m_map_width * (0.5 - box.min ().y () / tile_t ::EARTH_CIRCUMFERENCE));
175+ auto const minTileY = static_cast <std::uint32_t >(
176+ m_map_width * (0.5 - box.max ().y () / tile_t ::EARTH_CIRCUMFERENCE));
177+
178+ std::vector<uint32_t > tileX_list;
179+
180+ // Loop through the tile rows from top to bottom
181+ for (std::uint32_t tileY = minTileY; tileY < maxTileY; ++tileY) {
182+
183+ // Build a list of tiles crossed by the area boundary
184+ tileX_list.clear ();
185+ build_tile_list (&tileX_list, geom.outer (), static_cast <double >(tileY));
186+ for (auto const &ring : geom.inners ()) {
187+ build_tile_list (&tileX_list, ring, static_cast <double >(tileY));
188+ }
189+
190+ // Sort list of tiles from left to right
191+ std::sort (tileX_list.begin (), tileX_list.end ());
192+
193+ // Add the tiles between entering and leaving the area to expire list
194+ assert (tileX_list.size () % 2 == 0 );
195+ for (std::size_t i = 0 ; i < tileX_list.size (); i += 2 ) {
196+ if (tileX_list[i] >= m_map_width - 1 ) {
197+ break ;
198+ }
199+ if (tileX_list[i + 1 ] > 0 ) {
200+ if (tileX_list[i] < 0 ) {
201+ tileX_list[i] = 0 ;
202+ }
203+ if (tileX_list[i + 1 ] > m_map_width - 1 ) {
204+ tileX_list[i + 1 ] = m_map_width - 1 ;
205+ }
206+ for (std::uint32_t pixelX = tileX_list[i];
207+ pixelX < tileX_list[i + 1 ]; ++pixelX) {
208+ expire_tile (pixelX, tileY);
209+ }
210+ }
211+ }
212+ }
154213}
155214
156- void expire_tiles_t::from_polygon_boundary (geom::multipolygon_t const &geom,
157- expire_config_t const &expire_config)
215+ void expire_tiles_t::from_geometry (geom::polygon_t const &geom,
216+ expire_config_t const &expire_config)
158217{
159- for (auto const &sgeom : geom) {
160- from_polygon_boundary (sgeom, expire_config);
218+ geom::box_t box;
219+ auto const mode = decide_expire_mode (geom, expire_config, &box);
220+
221+ if (mode == expire_mode::full_area) {
222+ from_polygon_area (geom, box, expire_config);
161223 }
224+
225+ from_polygon_boundary (geom, expire_config);
162226}
163227
164228void expire_tiles_t::from_geometry (geom::multipolygon_t const &geom,
@@ -167,15 +231,12 @@ void expire_tiles_t::from_geometry(geom::multipolygon_t const &geom,
167231 geom::box_t box;
168232 auto const mode = decide_expire_mode (geom, expire_config, &box);
169233
170- if (mode == expire_mode::boundary_only) {
171- from_polygon_boundary (geom, expire_config);
172- return ;
173- }
174-
175- if (!box.valid ()) {
176- box = geom::envelope (geom);
234+ for (auto const &sgeom : geom) {
235+ if (mode == expire_mode::full_area) {
236+ from_polygon_area (sgeom, geom::box_t {}, expire_config);
237+ }
238+ from_polygon_boundary (sgeom, expire_config);
177239 }
178- from_bbox (box, expire_config);
179240}
180241
181242// False positive: Apparently clang-tidy can not see through the visit()
@@ -258,35 +319,6 @@ void expire_tiles_t::from_line_segment(geom::point_t const &a,
258319 }
259320}
260321
261- /*
262- * Expire tiles within a bounding box
263- */
264- int expire_tiles_t::from_bbox (geom::box_t const &box,
265- expire_config_t const &expire_config)
266- {
267- /* Convert the box's Mercator coordinates into tile coordinates */
268- auto const tmp_min = coords_to_tile ({box.min_x (), box.max_y ()});
269- int const min_tile_x =
270- std::clamp (int (tmp_min.x () - expire_config.buffer ), 0 , m_map_width - 1 );
271- int const min_tile_y =
272- std::clamp (int (tmp_min.y () - expire_config.buffer ), 0 , m_map_width - 1 );
273-
274- auto const tmp_max = coords_to_tile ({box.max_x (), box.min_y ()});
275- int const max_tile_x =
276- std::clamp (int (tmp_max.x () + expire_config.buffer ), 0 , m_map_width - 1 );
277- int const max_tile_y =
278- std::clamp (int (tmp_max.y () + expire_config.buffer ), 0 , m_map_width - 1 );
279-
280- for (int iterator_x = min_tile_x; iterator_x <= max_tile_x; ++iterator_x) {
281- uint32_t const norm_x = normalise_tile_x_coord (iterator_x);
282- for (int iterator_y = min_tile_y; iterator_y <= max_tile_y;
283- ++iterator_y) {
284- expire_tile (norm_x, static_cast <uint32_t >(iterator_y));
285- }
286- }
287- return 0 ;
288- }
289-
290322quadkey_list_t expire_tiles_t::get_tiles ()
291323{
292324 quadkey_list_t tiles;
0 commit comments