We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove path_to_geos heuristics
Ultimately it'd be nicer to remove the heuristic guesswork from path_to_geos by modifying MPL to actually use the CLOSE_POLY vertex code when a path describes a polygon. In the meantime, we can get a chunk of that benefit by removing the heuristics and making GeoAxes.contourf() ensure the resulting paths use CLOSE_POLY.

(Originally raised in #63.)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/cartopy/mpl/patch.py`
Content:
```
1 # (C) British Crown Copyright 2011 - 2018, Met Office
2 #
3 # This file is part of cartopy.
4 #
5 # cartopy is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU Lesser General Public License as published by the
7 # Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # cartopy is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with cartopy.  If not, see <https://www.gnu.org/licenses/>.
17 """
18 Provide shapely geometry <-> matplotlib path support.
19 
20 
21 See also `Shapely Geometric Objects <see_also_shapely>`_
22 and `Matplotlib Path API <http://matplotlib.org/api/path_api.html>`_.
23 
24 .. see_also_shapely:
25    http://toblerity.org/shapely/manual.html#geometric-objects
26 
27 """
28 
29 from __future__ import (absolute_import, division, print_function)
30 
31 import numpy as np
32 from matplotlib.path import Path
33 import shapely.geometry as sgeom
34 
35 
36 def geos_to_path(shape):
37     """
38     Create a list of :class:`matplotlib.path.Path` objects that describe
39     a shape.
40 
41     Parameters
42     ----------
43     shape
44         A list, tuple or single instance of any of the following
45         types: :class:`shapely.geometry.point.Point`,
46         :class:`shapely.geometry.linestring.LineString`,
47         :class:`shapely.geometry.polygon.Polygon`,
48         :class:`shapely.geometry.multipoint.MultiPoint`,
49         :class:`shapely.geometry.multipolygon.MultiPolygon`,
50         :class:`shapely.geometry.multilinestring.MultiLineString`,
51         :class:`shapely.geometry.collection.GeometryCollection`,
52         or any type with a _as_mpl_path() method.
53 
54     Returns
55     -------
56     paths
57         A list of :class:`matplotlib.path.Path` objects.
58 
59     """
60     if isinstance(shape, (list, tuple)):
61         paths = []
62         for shp in shape:
63             paths.extend(geos_to_path(shp))
64         return paths
65 
66     if isinstance(shape, (sgeom.LineString, sgeom.Point)):
67         return [Path(np.column_stack(shape.xy))]
68     elif isinstance(shape, sgeom.Polygon):
69         def poly_codes(poly):
70             codes = np.ones(len(poly.xy[0])) * Path.LINETO
71             codes[0] = Path.MOVETO
72             return codes
73         if shape.is_empty:
74             return []
75         vertices = np.concatenate([np.array(shape.exterior.xy)] +
76                                   [np.array(ring.xy) for ring in
77                                    shape.interiors], 1).T
78         codes = np.concatenate([poly_codes(shape.exterior)] +
79                                [poly_codes(ring) for ring in shape.interiors])
80         return [Path(vertices, codes)]
81     elif isinstance(shape, (sgeom.MultiPolygon, sgeom.GeometryCollection,
82                             sgeom.MultiLineString, sgeom.MultiPoint)):
83         paths = []
84         for geom in shape.geoms:
85             paths.extend(geos_to_path(geom))
86         return paths
87     elif hasattr(shape, '_as_mpl_path'):
88         vertices, codes = shape._as_mpl_path()
89         return [Path(vertices, codes)]
90     else:
91         raise ValueError('Unsupported shape type {}.'.format(type(shape)))
92 
93 
94 def path_segments(path, **kwargs):
95     """
96     Create an array of vertices and a corresponding array of codes from a
97     :class:`matplotlib.path.Path`.
98 
99     Parameters
100     ----------
101     path
102         A :class:`matplotlib.path.Path` instance.
103 
104     Other Parameters
105     ----------------
106     kwargs
107         See :func:`matplotlib.path.iter_segments` for details of the keyword
108         arguments.
109 
110     Returns
111     -------
112     vertices, codes
113         A (vertices, codes) tuple, where vertices is a numpy array of
114         coordinates, and codes is a numpy array of matplotlib path codes.
115         See :class:`matplotlib.path.Path` for information on the types of
116         codes and their meanings.
117 
118     """
119     pth = path.cleaned(**kwargs)
120     return pth.vertices[:-1, :], pth.codes[:-1]
121 
122 
123 def path_to_geos(path, force_ccw=False):
124     """
125     Create a list of Shapely geometric objects from a
126     :class:`matplotlib.path.Path`.
127 
128     Parameters
129     ----------
130     path
131         A :class:`matplotlib.path.Path` instance.
132 
133     Other Parameters
134     ----------------
135     force_ccw
136         Boolean flag determining whether the path can be inverted to enforce
137         ccw. Defaults to False.
138 
139     Returns
140     -------
141     A list of instances of the following type(s):
142         :class:`shapely.geometry.polygon.Polygon`,
143         :class:`shapely.geometry.linestring.LineString` and/or
144         :class:`shapely.geometry.multilinestring.MultiLineString`.
145 
146     """
147     # Convert path into numpy array of vertices (and associated codes)
148     path_verts, path_codes = path_segments(path, curves=False)
149 
150     # Split into subarrays such that each subarray consists of connected
151     # line segments based on the start of each one being marked by a
152     # matplotlib MOVETO code.
153     verts_split_inds = np.where(path_codes == Path.MOVETO)[0]
154     verts_split = np.split(path_verts, verts_split_inds)
155     codes_split = np.split(path_codes, verts_split_inds)
156 
157     # Iterate through the vertices generating a list of
158     # (external_geom, [internal_polygons]) tuples.
159     other_result_geoms = []
160     collection = []
161     for path_verts, path_codes in zip(verts_split, codes_split):
162         if len(path_verts) == 0:
163             continue
164 
165         # XXX A path can be given which does not end with close poly, in that
166         # situation, we have to guess?
167         verts_same_as_first = np.all(path_verts[0, :] == path_verts[1:, :],
168                                      axis=1)
169         if all(verts_same_as_first):
170             geom = sgeom.Point(path_verts[0, :])
171         elif path_verts.shape[0] > 4 and path_codes[-1] == Path.CLOSEPOLY:
172             geom = sgeom.Polygon(path_verts[:-1, :])
173         elif path_verts.shape[0] > 3 and verts_same_as_first[-1]:
174             geom = sgeom.Polygon(path_verts)
175         else:
176             geom = sgeom.LineString(path_verts)
177 
178         # If geom is a Polygon and is contained within the last geom in
179         # collection, add it to its list of internal polygons, otherwise
180         # simply append it as a new external geom.
181         if geom.is_empty:
182             pass
183         elif (len(collection) > 0 and
184                 isinstance(collection[-1][0], sgeom.Polygon) and
185                 isinstance(geom, sgeom.Polygon) and
186                 collection[-1][0].contains(geom.exterior)):
187             collection[-1][1].append(geom.exterior)
188         elif isinstance(geom, sgeom.Point):
189             other_result_geoms.append(geom)
190         else:
191             collection.append((geom, []))
192 
193     # Convert each (external_geom, [internal_polygons]) pair into a
194     # a shapely Polygon that encapsulates the internal polygons, if the
195     # external geom is a LineString leave it alone.
196     geom_collection = []
197     for external_geom, internal_polys in collection:
198         if internal_polys:
199             # XXX worry about islands within lakes
200             geom = sgeom.Polygon(external_geom.exterior, internal_polys)
201         else:
202             geom = external_geom
203 
204         # Correctly orientate the polygon (ccw)
205         if isinstance(geom, sgeom.Polygon):
206             if force_ccw and not geom.exterior.is_ccw:
207                 geom = sgeom.polygon.orient(geom)
208 
209         geom_collection.append(geom)
210 
211     # If the geom_collection only contains LineStrings combine them
212     # into a single MultiLinestring.
213     if geom_collection and all(isinstance(geom, sgeom.LineString) for
214                                geom in geom_collection):
215         geom_collection = [sgeom.MultiLineString(geom_collection)]
216 
217     # Remove any zero area Polygons
218     def not_zero_poly(geom):
219         return ((isinstance(geom, sgeom.Polygon) and not geom._is_empty and
220                  geom.area != 0) or
221                 not isinstance(geom, sgeom.Polygon))
222 
223     result = list(filter(not_zero_poly, geom_collection))
224 
225     return result + other_result_geoms
226
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/cartopy/mpl/patch.py b/lib/cartopy/mpl/patch.py
--- a/lib/cartopy/mpl/patch.py
+++ b/lib/cartopy/mpl/patch.py
@@ -29,6 +29,7 @@
 from __future__ import (absolute_import, division, print_function)
 
 import numpy as np
+import matplotlib
 from matplotlib.path import Path
 import shapely.geometry as sgeom
 
@@ -69,6 +70,7 @@
         def poly_codes(poly):
             codes = np.ones(len(poly.xy[0])) * Path.LINETO
             codes[0] = Path.MOVETO
+            codes[-1] = Path.CLOSEPOLY
             return codes
         if shape.is_empty:
             return []
@@ -162,15 +164,16 @@
         if len(path_verts) == 0:
             continue
 
-        # XXX A path can be given which does not end with close poly, in that
-        # situation, we have to guess?
         verts_same_as_first = np.all(path_verts[0, :] == path_verts[1:, :],
                                      axis=1)
         if all(verts_same_as_first):
             geom = sgeom.Point(path_verts[0, :])
         elif path_verts.shape[0] > 4 and path_codes[-1] == Path.CLOSEPOLY:
             geom = sgeom.Polygon(path_verts[:-1, :])
-        elif path_verts.shape[0] > 3 and verts_same_as_first[-1]:
+        elif (matplotlib.__version__ < '2.2.0' and
+                # XXX A path can be given which does not end with close poly,
+                # in that situation, we have to guess?
+                path_verts.shape[0] > 3 and verts_same_as_first[-1]):
             geom = sgeom.Polygon(path_verts)
         else:
             geom = sgeom.LineString(path_verts)

{"golden_diff": "diff --git a/lib/cartopy/mpl/patch.py b/lib/cartopy/mpl/patch.py\n--- a/lib/cartopy/mpl/patch.py\n+++ b/lib/cartopy/mpl/patch.py\n@@ -29,6 +29,7 @@\n from __future__ import (absolute_import, division, print_function)\n \n import numpy as np\n+import matplotlib\n from matplotlib.path import Path\n import shapely.geometry as sgeom\n \n@@ -69,6 +70,7 @@\n         def poly_codes(poly):\n             codes = np.ones(len(poly.xy[0])) * Path.LINETO\n             codes[0] = Path.MOVETO\n+            codes[-1] = Path.CLOSEPOLY\n             return codes\n         if shape.is_empty:\n             return []\n@@ -162,15 +164,16 @@\n         if len(path_verts) == 0:\n             continue\n \n-        # XXX A path can be given which does not end with close poly, in that\n-        # situation, we have to guess?\n         verts_same_as_first = np.all(path_verts[0, :] == path_verts[1:, :],\n                                      axis=1)\n         if all(verts_same_as_first):\n             geom = sgeom.Point(path_verts[0, :])\n         elif path_verts.shape[0] > 4 and path_codes[-1] == Path.CLOSEPOLY:\n             geom = sgeom.Polygon(path_verts[:-1, :])\n-        elif path_verts.shape[0] > 3 and verts_same_as_first[-1]:\n+        elif (matplotlib.__version__ < '2.2.0' and\n+                # XXX A path can be given which does not end with close poly,\n+                # in that situation, we have to guess?\n+                path_verts.shape[0] > 3 and verts_same_as_first[-1]):\n             geom = sgeom.Polygon(path_verts)\n         else:\n             geom = sgeom.LineString(path_verts)\n", "issue": "Remove path_to_geos heuristics\nUltimately it'd be nicer to remove the heuristic guesswork from path_to_geos by modifying MPL to actually use the CLOSE_POLY vertex code when a path describes a polygon. In the meantime, we can get a chunk of that benefit by removing the heuristics and making GeoAxes.contourf() ensure the resulting paths use CLOSE_POLY.\n\n(Originally raised in #63.)\n\n", "before_files": [{"content": "# (C) British Crown Copyright 2011 - 2018, Met Office\n#\n# This file is part of cartopy.\n#\n# cartopy is free software: you can redistribute it and/or modify it under\n# the terms of the GNU Lesser General Public License as published by the\n# Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# cartopy is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Lesser General Public License for more details.\n#\n# You should have received a copy of the GNU Lesser General Public License\n# along with cartopy.  If not, see <https://www.gnu.org/licenses/>.\n\"\"\"\nProvide shapely geometry <-> matplotlib path support.\n\n\nSee also `Shapely Geometric Objects <see_also_shapely>`_\nand `Matplotlib Path API <http://matplotlib.org/api/path_api.html>`_.\n\n.. see_also_shapely:\n   http://toblerity.org/shapely/manual.html#geometric-objects\n\n\"\"\"\n\nfrom __future__ import (absolute_import, division, print_function)\n\nimport numpy as np\nfrom matplotlib.path import Path\nimport shapely.geometry as sgeom\n\n\ndef geos_to_path(shape):\n    \"\"\"\n    Create a list of :class:`matplotlib.path.Path` objects that describe\n    a shape.\n\n    Parameters\n    ----------\n    shape\n        A list, tuple or single instance of any of the following\n        types: :class:`shapely.geometry.point.Point`,\n        :class:`shapely.geometry.linestring.LineString`,\n        :class:`shapely.geometry.polygon.Polygon`,\n        :class:`shapely.geometry.multipoint.MultiPoint`,\n        :class:`shapely.geometry.multipolygon.MultiPolygon`,\n        :class:`shapely.geometry.multilinestring.MultiLineString`,\n        :class:`shapely.geometry.collection.GeometryCollection`,\n        or any type with a _as_mpl_path() method.\n\n    Returns\n    -------\n    paths\n        A list of :class:`matplotlib.path.Path` objects.\n\n    \"\"\"\n    if isinstance(shape, (list, tuple)):\n        paths = []\n        for shp in shape:\n            paths.extend(geos_to_path(shp))\n        return paths\n\n    if isinstance(shape, (sgeom.LineString, sgeom.Point)):\n        return [Path(np.column_stack(shape.xy))]\n    elif isinstance(shape, sgeom.Polygon):\n        def poly_codes(poly):\n            codes = np.ones(len(poly.xy[0])) * Path.LINETO\n            codes[0] = Path.MOVETO\n            return codes\n        if shape.is_empty:\n            return []\n        vertices = np.concatenate([np.array(shape.exterior.xy)] +\n                                  [np.array(ring.xy) for ring in\n                                   shape.interiors], 1).T\n        codes = np.concatenate([poly_codes(shape.exterior)] +\n                               [poly_codes(ring) for ring in shape.interiors])\n        return [Path(vertices, codes)]\n    elif isinstance(shape, (sgeom.MultiPolygon, sgeom.GeometryCollection,\n                            sgeom.MultiLineString, sgeom.MultiPoint)):\n        paths = []\n        for geom in shape.geoms:\n            paths.extend(geos_to_path(geom))\n        return paths\n    elif hasattr(shape, '_as_mpl_path'):\n        vertices, codes = shape._as_mpl_path()\n        return [Path(vertices, codes)]\n    else:\n        raise ValueError('Unsupported shape type {}.'.format(type(shape)))\n\n\ndef path_segments(path, **kwargs):\n    \"\"\"\n    Create an array of vertices and a corresponding array of codes from a\n    :class:`matplotlib.path.Path`.\n\n    Parameters\n    ----------\n    path\n        A :class:`matplotlib.path.Path` instance.\n\n    Other Parameters\n    ----------------\n    kwargs\n        See :func:`matplotlib.path.iter_segments` for details of the keyword\n        arguments.\n\n    Returns\n    -------\n    vertices, codes\n        A (vertices, codes) tuple, where vertices is a numpy array of\n        coordinates, and codes is a numpy array of matplotlib path codes.\n        See :class:`matplotlib.path.Path` for information on the types of\n        codes and their meanings.\n\n    \"\"\"\n    pth = path.cleaned(**kwargs)\n    return pth.vertices[:-1, :], pth.codes[:-1]\n\n\ndef path_to_geos(path, force_ccw=False):\n    \"\"\"\n    Create a list of Shapely geometric objects from a\n    :class:`matplotlib.path.Path`.\n\n    Parameters\n    ----------\n    path\n        A :class:`matplotlib.path.Path` instance.\n\n    Other Parameters\n    ----------------\n    force_ccw\n        Boolean flag determining whether the path can be inverted to enforce\n        ccw. Defaults to False.\n\n    Returns\n    -------\n    A list of instances of the following type(s):\n        :class:`shapely.geometry.polygon.Polygon`,\n        :class:`shapely.geometry.linestring.LineString` and/or\n        :class:`shapely.geometry.multilinestring.MultiLineString`.\n\n    \"\"\"\n    # Convert path into numpy array of vertices (and associated codes)\n    path_verts, path_codes = path_segments(path, curves=False)\n\n    # Split into subarrays such that each subarray consists of connected\n    # line segments based on the start of each one being marked by a\n    # matplotlib MOVETO code.\n    verts_split_inds = np.where(path_codes == Path.MOVETO)[0]\n    verts_split = np.split(path_verts, verts_split_inds)\n    codes_split = np.split(path_codes, verts_split_inds)\n\n    # Iterate through the vertices generating a list of\n    # (external_geom, [internal_polygons]) tuples.\n    other_result_geoms = []\n    collection = []\n    for path_verts, path_codes in zip(verts_split, codes_split):\n        if len(path_verts) == 0:\n            continue\n\n        # XXX A path can be given which does not end with close poly, in that\n        # situation, we have to guess?\n        verts_same_as_first = np.all(path_verts[0, :] == path_verts[1:, :],\n                                     axis=1)\n        if all(verts_same_as_first):\n            geom = sgeom.Point(path_verts[0, :])\n        elif path_verts.shape[0] > 4 and path_codes[-1] == Path.CLOSEPOLY:\n            geom = sgeom.Polygon(path_verts[:-1, :])\n        elif path_verts.shape[0] > 3 and verts_same_as_first[-1]:\n            geom = sgeom.Polygon(path_verts)\n        else:\n            geom = sgeom.LineString(path_verts)\n\n        # If geom is a Polygon and is contained within the last geom in\n        # collection, add it to its list of internal polygons, otherwise\n        # simply append it as a new external geom.\n        if geom.is_empty:\n            pass\n        elif (len(collection) > 0 and\n                isinstance(collection[-1][0], sgeom.Polygon) and\n                isinstance(geom, sgeom.Polygon) and\n                collection[-1][0].contains(geom.exterior)):\n            collection[-1][1].append(geom.exterior)\n        elif isinstance(geom, sgeom.Point):\n            other_result_geoms.append(geom)\n        else:\n            collection.append((geom, []))\n\n    # Convert each (external_geom, [internal_polygons]) pair into a\n    # a shapely Polygon that encapsulates the internal polygons, if the\n    # external geom is a LineString leave it alone.\n    geom_collection = []\n    for external_geom, internal_polys in collection:\n        if internal_polys:\n            # XXX worry about islands within lakes\n            geom = sgeom.Polygon(external_geom.exterior, internal_polys)\n        else:\n            geom = external_geom\n\n        # Correctly orientate the polygon (ccw)\n        if isinstance(geom, sgeom.Polygon):\n            if force_ccw and not geom.exterior.is_ccw:\n                geom = sgeom.polygon.orient(geom)\n\n        geom_collection.append(geom)\n\n    # If the geom_collection only contains LineStrings combine them\n    # into a single MultiLinestring.\n    if geom_collection and all(isinstance(geom, sgeom.LineString) for\n                               geom in geom_collection):\n        geom_collection = [sgeom.MultiLineString(geom_collection)]\n\n    # Remove any zero area Polygons\n    def not_zero_poly(geom):\n        return ((isinstance(geom, sgeom.Polygon) and not geom._is_empty and\n                 geom.area != 0) or\n                not isinstance(geom, sgeom.Polygon))\n\n    result = list(filter(not_zero_poly, geom_collection))\n\n    return result + other_result_geoms\n", "path": "lib/cartopy/mpl/patch.py"}], "after_files": [{"content": "# (C) British Crown Copyright 2011 - 2018, Met Office\n#\n# This file is part of cartopy.\n#\n# cartopy is free software: you can redistribute it and/or modify it under\n# the terms of the GNU Lesser General Public License as published by the\n# Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# cartopy is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU Lesser General Public License for more details.\n#\n# You should have received a copy of the GNU Lesser General Public License\n# along with cartopy.  If not, see <https://www.gnu.org/licenses/>.\n\"\"\"\nProvide shapely geometry <-> matplotlib path support.\n\n\nSee also `Shapely Geometric Objects <see_also_shapely>`_\nand `Matplotlib Path API <http://matplotlib.org/api/path_api.html>`_.\n\n.. see_also_shapely:\n   http://toblerity.org/shapely/manual.html#geometric-objects\n\n\"\"\"\n\nfrom __future__ import (absolute_import, division, print_function)\n\nimport numpy as np\nimport matplotlib\nfrom matplotlib.path import Path\nimport shapely.geometry as sgeom\n\n\ndef geos_to_path(shape):\n    \"\"\"\n    Create a list of :class:`matplotlib.path.Path` objects that describe\n    a shape.\n\n    Parameters\n    ----------\n    shape\n        A list, tuple or single instance of any of the following\n        types: :class:`shapely.geometry.point.Point`,\n        :class:`shapely.geometry.linestring.LineString`,\n        :class:`shapely.geometry.polygon.Polygon`,\n        :class:`shapely.geometry.multipoint.MultiPoint`,\n        :class:`shapely.geometry.multipolygon.MultiPolygon`,\n        :class:`shapely.geometry.multilinestring.MultiLineString`,\n        :class:`shapely.geometry.collection.GeometryCollection`,\n        or any type with a _as_mpl_path() method.\n\n    Returns\n    -------\n    paths\n        A list of :class:`matplotlib.path.Path` objects.\n\n    \"\"\"\n    if isinstance(shape, (list, tuple)):\n        paths = []\n        for shp in shape:\n            paths.extend(geos_to_path(shp))\n        return paths\n\n    if isinstance(shape, (sgeom.LineString, sgeom.Point)):\n        return [Path(np.column_stack(shape.xy))]\n    elif isinstance(shape, sgeom.Polygon):\n        def poly_codes(poly):\n            codes = np.ones(len(poly.xy[0])) * Path.LINETO\n            codes[0] = Path.MOVETO\n            codes[-1] = Path.CLOSEPOLY\n            return codes\n        if shape.is_empty:\n            return []\n        vertices = np.concatenate([np.array(shape.exterior.xy)] +\n                                  [np.array(ring.xy) for ring in\n                                   shape.interiors], 1).T\n        codes = np.concatenate([poly_codes(shape.exterior)] +\n                               [poly_codes(ring) for ring in shape.interiors])\n        return [Path(vertices, codes)]\n    elif isinstance(shape, (sgeom.MultiPolygon, sgeom.GeometryCollection,\n                            sgeom.MultiLineString, sgeom.MultiPoint)):\n        paths = []\n        for geom in shape.geoms:\n            paths.extend(geos_to_path(geom))\n        return paths\n    elif hasattr(shape, '_as_mpl_path'):\n        vertices, codes = shape._as_mpl_path()\n        return [Path(vertices, codes)]\n    else:\n        raise ValueError('Unsupported shape type {}.'.format(type(shape)))\n\n\ndef path_segments(path, **kwargs):\n    \"\"\"\n    Create an array of vertices and a corresponding array of codes from a\n    :class:`matplotlib.path.Path`.\n\n    Parameters\n    ----------\n    path\n        A :class:`matplotlib.path.Path` instance.\n\n    Other Parameters\n    ----------------\n    kwargs\n        See :func:`matplotlib.path.iter_segments` for details of the keyword\n        arguments.\n\n    Returns\n    -------\n    vertices, codes\n        A (vertices, codes) tuple, where vertices is a numpy array of\n        coordinates, and codes is a numpy array of matplotlib path codes.\n        See :class:`matplotlib.path.Path` for information on the types of\n        codes and their meanings.\n\n    \"\"\"\n    pth = path.cleaned(**kwargs)\n    return pth.vertices[:-1, :], pth.codes[:-1]\n\n\ndef path_to_geos(path, force_ccw=False):\n    \"\"\"\n    Create a list of Shapely geometric objects from a\n    :class:`matplotlib.path.Path`.\n\n    Parameters\n    ----------\n    path\n        A :class:`matplotlib.path.Path` instance.\n\n    Other Parameters\n    ----------------\n    force_ccw\n        Boolean flag determining whether the path can be inverted to enforce\n        ccw. Defaults to False.\n\n    Returns\n    -------\n    A list of instances of the following type(s):\n        :class:`shapely.geometry.polygon.Polygon`,\n        :class:`shapely.geometry.linestring.LineString` and/or\n        :class:`shapely.geometry.multilinestring.MultiLineString`.\n\n    \"\"\"\n    # Convert path into numpy array of vertices (and associated codes)\n    path_verts, path_codes = path_segments(path, curves=False)\n\n    # Split into subarrays such that each subarray consists of connected\n    # line segments based on the start of each one being marked by a\n    # matplotlib MOVETO code.\n    verts_split_inds = np.where(path_codes == Path.MOVETO)[0]\n    verts_split = np.split(path_verts, verts_split_inds)\n    codes_split = np.split(path_codes, verts_split_inds)\n\n    # Iterate through the vertices generating a list of\n    # (external_geom, [internal_polygons]) tuples.\n    other_result_geoms = []\n    collection = []\n    for path_verts, path_codes in zip(verts_split, codes_split):\n        if len(path_verts) == 0:\n            continue\n\n        verts_same_as_first = np.all(path_verts[0, :] == path_verts[1:, :],\n                                     axis=1)\n        if all(verts_same_as_first):\n            geom = sgeom.Point(path_verts[0, :])\n        elif path_verts.shape[0] > 4 and path_codes[-1] == Path.CLOSEPOLY:\n            geom = sgeom.Polygon(path_verts[:-1, :])\n        elif (matplotlib.__version__ < '2.2.0' and\n                # XXX A path can be given which does not end with close poly,\n                # in that situation, we have to guess?\n                path_verts.shape[0] > 3 and verts_same_as_first[-1]):\n            geom = sgeom.Polygon(path_verts)\n        else:\n            geom = sgeom.LineString(path_verts)\n\n        # If geom is a Polygon and is contained within the last geom in\n        # collection, add it to its list of internal polygons, otherwise\n        # simply append it as a new external geom.\n        if geom.is_empty:\n            pass\n        elif (len(collection) > 0 and\n                isinstance(collection[-1][0], sgeom.Polygon) and\n                isinstance(geom, sgeom.Polygon) and\n                collection[-1][0].contains(geom.exterior)):\n            collection[-1][1].append(geom.exterior)\n        elif isinstance(geom, sgeom.Point):\n            other_result_geoms.append(geom)\n        else:\n            collection.append((geom, []))\n\n    # Convert each (external_geom, [internal_polygons]) pair into a\n    # a shapely Polygon that encapsulates the internal polygons, if the\n    # external geom is a LineString leave it alone.\n    geom_collection = []\n    for external_geom, internal_polys in collection:\n        if internal_polys:\n            # XXX worry about islands within lakes\n            geom = sgeom.Polygon(external_geom.exterior, internal_polys)\n        else:\n            geom = external_geom\n\n        # Correctly orientate the polygon (ccw)\n        if isinstance(geom, sgeom.Polygon):\n            if force_ccw and not geom.exterior.is_ccw:\n                geom = sgeom.polygon.orient(geom)\n\n        geom_collection.append(geom)\n\n    # If the geom_collection only contains LineStrings combine them\n    # into a single MultiLinestring.\n    if geom_collection and all(isinstance(geom, sgeom.LineString) for\n                               geom in geom_collection):\n        geom_collection = [sgeom.MultiLineString(geom_collection)]\n\n    # Remove any zero area Polygons\n    def not_zero_poly(geom):\n        return ((isinstance(geom, sgeom.Polygon) and not geom._is_empty and\n                 geom.area != 0) or\n                not isinstance(geom, sgeom.Polygon))\n\n    result = list(filter(not_zero_poly, geom_collection))\n\n    return result + other_result_geoms\n", "path": "lib/cartopy/mpl/patch.py"}]}