@@ -312,84 +312,17 @@ def _project_multiline(self, geometry, src_crs):
312312 else :
313313 return []
314314
315- def _project_multipolygon (self , geometry , src_crs ):
316- # Project the polygon exterior/interior rings.
317- # Each source ring will result in either a ring, or one or more
318- # lines.
319- rings = []
320- multi_lines = []
321- for polygon in geometry :
322- if src_crs .is_geodetic ():
323- is_ccw = True
324- else :
325- is_ccw = polygon .exterior .is_ccw
326- for src_ring in [polygon .exterior ] + list (polygon .interiors ):
327- p_rings , p_mline = self ._project_linear_ring (src_ring , src_crs )
328- if p_rings :
329- rings .extend (p_rings )
330- if len (p_mline ) > 0 :
331- multi_lines .append (p_mline )
332-
333- # Convert any lines to rings by attaching them to the boundary.
334- if multi_lines :
335- rings .extend (self ._attach_lines_to_boundary (multi_lines ,
336- is_ccw ))
337-
338- # Resolve all the inside vs. outside rings, and convert to the
339- # final MultiPolygon.
340- exteriors , interiors = self ._rings_to_multi_polygon (rings , is_ccw )
341- if exteriors or interiors :
342- if not interiors :
343- multi_poly = sgeom .MultiPolygon (exteriors )
344- elif not exteriors :
345- multi_poly_holes = ops .unary_union (interiors )
346- boundary_poly = self .domain
347- multi_poly = boundary_poly .difference (multi_poly_holes )
348- if isinstance (multi_poly , sgeom .Polygon ):
349- multi_poly = sgeom .MultiPolygon ([multi_poly ])
350- else :
351- multi_poly_bits = ops .unary_union (exteriors )
352- multi_poly_holes = ops .unary_union (interiors )
353- holes_envelope = multi_poly_holes .envelope
354- if holes_envelope .contains (multi_poly_bits .envelope ):
355- print ("everything flipped" )
356- multi_poly = holes_envelope .difference (
357- multi_poly_holes .difference (multi_poly_bits )
358- )
359- else :
360- multi_poly = multi_poly_bits .difference (multi_poly_holes )
361- if isinstance (multi_poly , sgeom .Polygon ):
362- multi_poly = sgeom .MultiPolygon ([multi_poly ])
363- else :
364- multi_poly = sgeom .MultiPolygon ()
365- # geoms = []
366- # for geom in geometry.geoms:
367- # r = self._project_polygon(geom, src_crs)
368- # if r:
369- # geoms.extend(r.geoms)
370- # if geoms:
371- # result = sgeom.MultiPolygon(geoms)
372- # else:
373- # result = sgeom.MultiPolygon()
374- return multi_poly
375-
376- def _project_polygon (self , polygon , src_crs ):
377- """
378- Return the projected polygon(s) derived from the given polygon.
379-
380- """
315+ def _make_rings_lines (self , polygon , rings , multi_lines , src_crs ):
381316 # Determine orientation of polygon.
382317 # TODO: Consider checking the internal rings have the opposite
383318 # orientation to the external rings?
384319 if src_crs .is_geodetic ():
385320 is_ccw = True
386321 else :
387322 is_ccw = polygon .exterior .is_ccw
388- # Project the polygon exterior/interior rings.
323+ # Project the polygon exterior/interior rings
389324 # Each source ring will result in either a ring, or one or more
390325 # lines.
391- rings = []
392- multi_lines = []
393326 for src_ring in [polygon .exterior ] + list (polygon .interiors ):
394327 p_rings , p_mline = self ._project_linear_ring (src_ring , src_crs )
395328 if p_rings :
@@ -400,7 +333,9 @@ def _project_polygon(self, polygon, src_crs):
400333 # Convert any lines to rings by attaching them to the boundary.
401334 if multi_lines :
402335 rings .extend (self ._attach_lines_to_boundary (multi_lines , is_ccw ))
336+ return rings , multi_lines , is_ccw
403337
338+ def _construct_multipolygon (self , rings , is_ccw ):
404339 # Resolve all the inside vs. outside rings, and convert to the
405340 # final MultiPolygon.
406341 exteriors , interiors = self ._rings_to_multi_polygon (rings , is_ccw )
@@ -418,7 +353,6 @@ def _project_polygon(self, polygon, src_crs):
418353 multi_poly_holes = ops .unary_union (interiors )
419354 holes_envelope = multi_poly_holes .envelope
420355 if holes_envelope .contains (multi_poly_bits .envelope ):
421- print ("everything flipped" )
422356 multi_poly = holes_envelope .difference (
423357 multi_poly_holes .difference (multi_poly_bits )
424358 )
@@ -431,6 +365,31 @@ def _project_polygon(self, polygon, src_crs):
431365
432366 return multi_poly
433367
368+ def _project_multipolygon (self , geometry , src_crs ):
369+ rings = []
370+ multi_lines = []
371+ polygons = []
372+ for polygon in geometry :
373+ rings , multi_lines , is_ccw = self ._make_rings_lines (
374+ polygon , rings , multi_lines , src_crs
375+ )
376+ polygons .extend (list (self ._construct_multipolygon (rings , is_ccw )))
377+ rings = []
378+ multi_lines = []
379+ return sgeom .MultiPolygon (polygons )
380+
381+ def _project_polygon (self , polygon , src_crs ):
382+ """
383+ Return the projected polygon(s) derived from the given polygon.
384+
385+ """
386+ rings = []
387+ multi_lines = []
388+ rings , multi_lines , is_ccw = self ._make_rings_lines (
389+ polygon , rings , multi_lines , src_crs
390+ )
391+ return self ._construct_multipolygon (rings , is_ccw )
392+
434393 def _attach_lines_to_boundary (self , multi_line_strings , is_ccw ):
435394 """
436395 Return a list of LinearRings by attaching the ends of the given lines
@@ -627,6 +586,13 @@ def makes_valid_ring(line_string):
627586 return linear_rings
628587
629588 def _rings_to_multi_polygon (self , rings , is_ccw ):
589+ # _project_linear_rings sometimes creates multiple exterior rings
590+ # encircling the entire domain or almost the entire domain
591+ # In this case, the method is:
592+ # for each exterior ring find all interior rings contained by it
593+ # create polygons
594+ # if there are leftover interior rings, store them as Polygons to
595+ # be added as holes by upstream multipolygon constructors
630596 exterior_rings = []
631597 interior_rings = []
632598 for ring in rings :
@@ -646,38 +612,35 @@ def _rings_to_multi_polygon(self, rings, is_ccw):
646612 for i , interior_ring in enumerate (interior_rings [:]):
647613 if prep_polygon .contains (interior_ring ):
648614 holes .append (interior_ring )
649- # interior_rings.remove(interior_ring)
650615 interior_ring_flags .append (i )
651616 elif polygon .crosses (interior_ring ):
652617 # Likely that we have an invalid geometry such as
653618 # that from #509 or #537.
654619 holes .append (interior_ring )
655- # interior_rings.remove(interior_ring)
656620 interior_ring_flags .append (i )
657- # if polygon.bounds == self.domain.bounds and not holes:
658- # pass
659621 else :
660622 polygon = sgeom .Polygon (exterior_ring , holes = holes )
661623 polygon_bits .append (polygon )
662624 interior_rings = [ring for i , ring in enumerate (interior_rings )
663625 if i not in interior_ring_flags ]
664626
665627 extra_holes = []
666- # Any left over "interior" rings need "inverting" with respect
667- # to the boundary.
668628 if interior_rings :
669629 boundary_poly = self .domain
670- x3 , y3 , x4 , y4 = boundary_poly .bounds
671- bx = (x4 - x3 ) * 0.1
672- by = (y4 - y3 ) * 0.1
673- x3 -= bx
674- y3 -= by
675- x4 += bx
676- y4 += by
677630 polygon_bits = [poly .buffer (0 ) for poly in polygon_bits ]
678631 if isinstance (self , PlateCarree ):
679- # This should only run when called by
680- # _ViewClippedPathPatch.draw
632+ # to set the initial plot extent _gen_axes_patch calls
633+ # _ViewClippedPathPatch.draw, which calls
634+ # _rings_to_multi_polygon with a geometry from project_geometry
635+ #
636+ # if set_extent() is not invoked this geometry represents the
637+ # region in [-180, 180, -90, 90] that projects to infinity in
638+ # the target projection
639+ #
640+ # the geometry sometimes is an interior ring with no exterior
641+ # ring
642+ #
643+ # if this happens we have to invert the interior ring
681644 polygon = sgeom .Polygon (interior_rings [0 ])
682645 # Invert the polygon
683646 polygon = boundary_poly .difference (polygon )
@@ -689,22 +652,6 @@ def _rings_to_multi_polygon(self, rings, is_ccw):
689652 polygon = polygon .buffer (0 )
690653 if not polygon .is_empty :
691654 extra_holes .append (polygon )
692- # if polygon_bits or extra_holes:
693- # if not extra_holes:
694- # multi_poly = sgeom.MultiPolygon(polygon_bits)
695- # elif not polygon_bits:
696- # multi_poly_holes = ops.unary_union(extra_holes)
697- # multi_poly = boundary_poly.difference(multi_poly_holes)
698- # if isinstance(multi_poly, sgeom.Polygon):
699- # multi_poly = sgeom.MultiPolygon([multi_poly])
700- # else:
701- # multi_poly_bits = ops.unary_union(polygon_bits)
702- # multi_poly_holes = ops.unary_union(extra_holes)
703- # multi_poly = multi_poly_bits.difference(multi_poly_holes)
704- # if isinstance(multi_poly, sgeom.Polygon):
705- # multi_poly = sgeom.MultiPolygon([multi_poly])
706- # else:
707- # multi_poly = sgeom.MultiPolygon()
708655 return polygon_bits , extra_holes
709656
710657 def quick_vertices_transform (self , vertices , src_crs ):
0 commit comments