@@ -136,28 +136,90 @@ 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 > *tile_x_list ,
140+ geom:: ring_t const &ring, double tile_y )
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.empty ());
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 () < tile_y && t2.y () >= tile_y) ||
148+ (t2.y () < tile_y && t1.y () >= tile_y)) {
149+ auto const pos =
150+ (tile_y - t1.y ()) / (t2.y () - t1.y ()) * (t2.x () - t1.x ());
151+ tile_x_list->push_back (static_cast <uint32_t >(std::clamp (
152+ t1.x () + pos, 0.0 , static_cast <double >(m_map_width - 1 ))));
153+ }
148154 }
155+ }
149156
157+ void expire_tiles_t::from_polygon_area (geom::polygon_t const &geom,
158+ geom::box_t box)
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 max_tile_y = static_cast <std::uint32_t >(
174+ m_map_width * (0.5 - box.min ().y () / tile_t ::EARTH_CIRCUMFERENCE));
175+ auto const min_tile_y = static_cast <std::uint32_t >(
176+ m_map_width * (0.5 - box.max ().y () / tile_t ::EARTH_CIRCUMFERENCE));
177+
178+ std::vector<uint32_t > tile_x_list;
179+
180+ // Loop through the tile rows from top to bottom
181+ for (std::uint32_t tile_y = min_tile_y; tile_y < max_tile_y; ++tile_y) {
182+
183+ // Build a list of tiles crossed by the area boundary
184+ tile_x_list.clear ();
185+ build_tile_list (&tile_x_list, geom.outer (),
186+ static_cast <double >(tile_y));
187+ for (auto const &ring : geom.inners ()) {
188+ build_tile_list (&tile_x_list, ring, static_cast <double >(tile_y));
189+ }
190+
191+ // Sort list of tiles from left to right
192+ std::sort (tile_x_list.begin (), tile_x_list.end ());
193+
194+ // Add the tiles between entering and leaving the area to expire list
195+ assert (tile_x_list.size () % 2 == 0 );
196+ for (std::size_t i = 0 ; i < tile_x_list.size (); i += 2 ) {
197+ if (tile_x_list[i] >= static_cast <uint32_t >(m_map_width - 1 )) {
198+ break ;
199+ }
200+ if (tile_x_list[i + 1 ] > 0 ) {
201+ for (std::uint32_t tile_x = tile_x_list[i];
202+ tile_x < tile_x_list[i + 1 ]; ++tile_x) {
203+ expire_tile (tile_x, tile_y);
204+ }
205+ }
206+ }
207+ }
154208}
155209
156- void expire_tiles_t::from_polygon_boundary (geom::multipolygon_t const &geom,
157- expire_config_t const &expire_config)
210+ void expire_tiles_t::from_geometry (geom::polygon_t const &geom,
211+ expire_config_t const &expire_config)
158212{
159- for (auto const &sgeom : geom) {
160- from_polygon_boundary (sgeom, expire_config);
213+ geom::box_t box;
214+ auto const mode = decide_expire_mode (geom, expire_config, &box);
215+
216+ from_polygon_boundary (geom, expire_config);
217+
218+ // Only need to expire area if in full_area mode. If there is only a
219+ // single tile expired the whole polygon is inside that tile and we
220+ // don't need to do the polygon expire.
221+ if (mode == expire_mode::full_area && m_dirty_tiles.size () > 1 ) {
222+ from_polygon_area (geom, box);
161223 }
162224}
163225
@@ -167,15 +229,18 @@ void expire_tiles_t::from_geometry(geom::multipolygon_t const &geom,
167229 geom::box_t box;
168230 auto const mode = decide_expire_mode (geom, expire_config, &box);
169231
170- if (mode == expire_mode::boundary_only) {
171- from_polygon_boundary (geom, expire_config);
172- return ;
232+ for (auto const &sgeom : geom) {
233+ from_polygon_boundary (sgeom, expire_config);
173234 }
174235
175- if (!box.valid ()) {
176- box = geom::envelope (geom);
236+ // Only need to expire area if in full_area mode. If there is only a
237+ // single tile expired the whole polygon is inside that tile and we
238+ // don't need to do the polygon expire.
239+ if (mode == expire_mode::full_area && m_dirty_tiles.size () > 1 ) {
240+ for (auto const &sgeom : geom) {
241+ from_polygon_area (sgeom, geom::box_t {});
242+ }
177243 }
178- from_bbox (box, expire_config);
179244}
180245
181246// False positive: Apparently clang-tidy can not see through the visit()
@@ -258,35 +323,6 @@ void expire_tiles_t::from_line_segment(geom::point_t const &a,
258323 }
259324}
260325
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-
290326quadkey_list_t expire_tiles_t::get_tiles ()
291327{
292328 quadkey_list_t tiles;
0 commit comments