diff --git a/lib/cartopy/io/img_tiles.py b/lib/cartopy/io/img_tiles.py index 9950e6e36..acb882629 100644 --- a/lib/cartopy/io/img_tiles.py +++ b/lib/cartopy/io/img_tiles.py @@ -125,6 +125,13 @@ def _find_images(self, target_domain, target_z, start_tile=(0, 0, 0)): # Recursively drill down to the images at the target zoom. x0, x1, y0, y1 = self._tileextent(start_tile) domain = sgeom.box(x0, y0, x1, y1) + + if hasattr(target_domain, 'geoms') and len(target_domain.geoms) > 1: + # For MultiPolygon, create a unified coverage area that includes + # the space between disconnected parts. This ensures tiles spanning + # across dateline discontinuities are included. + target_domain = sgeom.box(*target_domain.bounds) + if domain.intersects(target_domain): if start_tile[2] == target_z: yield start_tile diff --git a/lib/cartopy/tests/test_img_tiles.py b/lib/cartopy/tests/test_img_tiles.py index d92224ee7..0d96d1b7a 100644 --- a/lib/cartopy/tests/test_img_tiles.py +++ b/lib/cartopy/tests/test_img_tiles.py @@ -138,6 +138,33 @@ def test_tile_find_images(): [(7, 4, 4), (7, 5, 4), (8, 4, 4), (8, 5, 4)]) +def test_multipolygon_find_images(): + # Test that _find_images handles MultiPolygon geometries correctly, + # particularly for orthographic projections that cross the dateline. + gt = cimgt.GoogleTiles() + + # Create a MultiPolygon that simulates the dateline crossing scenario + # Two separate rectangles representing the split geometry parts + geom1 = sgeom.box(-20037508, -8000000, -12000000, 8000000) # Left part + geom2 = sgeom.box(12000000, -8000000, 20037508, 8000000) # Right part + multi_geom = sgeom.MultiPolygon([geom1, geom2]) + + tiles = list(gt.find_images(multi_geom, 2)) + tile_x_coords = {tile[0] for tile in tiles} + + # Should include all x coordinates from 0 to 3 for complete coverage + expected_x_coords = {0, 1, 2, 3} + assert tile_x_coords == expected_x_coords + + # Request tiles for the individual pieces + tiles_part1 = list(gt.find_images(geom1, 2)) + tiles_part2 = list(gt.find_images(geom2, 2)) + + # Combined individual parts should have fewer tiles than the MultiPolygon + # because the MultiPolygon includes intermediate tiles for continuity + assert len(tiles) > len(tiles_part1) + len(tiles_part2) + + @pytest.mark.network def test_image_for_domain(): gt = cimgt.GoogleTiles()