We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ValueError when saving geodataframe with pandas timestamps to shapefile
Is there any proper way to fix this error inside geopandas (not fiona)?

(Or maybe I'm trying to do the wrong thing and for example should convert my timestamps into strings before writing?)

```
C:\Users\kkrasnoschekov\_soft\WinPython-64bit-3.4.3.2_1\python-3.4.3.amd64\lib\site-packages\geopandas-0.1.0.dev_6163bc9-py3.4.egg\geopandas\geodataframe.py in to_file(self, filename, driver, schema, **kwargs)
    326         """
    327         from geopandas.io.file import to_file
--> 328         to_file(self, filename, driver, schema, **kwargs)
    329 
    330     def to_crs(self, crs=None, epsg=None, inplace=False):

C:\Users\kkrasnoschekov\_soft\WinPython-64bit-3.4.3.2_1\python-3.4.3.amd64\lib\site-packages\geopandas-0.1.0.dev_6163bc9-py3.4.egg\geopandas\io\file.py in to_file(df, filename, driver, schema, **kwargs)
     60                     schema=schema, **kwargs) as c:
     61         for feature in df.iterfeatures():
---> 62             c.write(feature)
     63 
     64 

C:\Users\kkrasnoschekov\_soft\WinPython-64bit-3.4.3.2_1\python-3.4.3.amd64\lib\site-packages\fiona\collection.py in write(self, record)
    323     def write(self, record):
    324         """Stages a record for writing to disk."""
--> 325         self.writerecords([record])
    326 
    327     def validate_record(self, record):

C:\Users\kkrasnoschekov\_soft\WinPython-64bit-3.4.3.2_1\python-3.4.3.amd64\lib\site-packages\fiona\collection.py in writerecords(self, records)
    317         if self.mode not in ('a', 'w'):
    318             raise IOError("collection not open for writing")
--> 319         self.session.writerecs(records, self)
    320         self._len = self.session.get_length()
    321         self._bounds = self.session.get_extent()

fiona/ogrext.pyx in fiona.ogrext.WritingSession.writerecs (fiona/ogrext.c:15333)()

fiona/ogrext.pyx in fiona.ogrext.OGRFeatureBuilder.build (fiona/ogrext.c:5751)()

ValueError: Invalid field type <class 'pandas.tslib.Timestamp'>
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `geopandas/io/file.py`
Content:
```
1 import os
2 from distutils.version import LooseVersion
3 
4 import fiona
5 import numpy as np
6 
7 import six
8 
9 try:
10     from fiona import Env as fiona_env
11 except ImportError:
12     from fiona import drivers as fiona_env
13 
14 from geopandas import GeoDataFrame, GeoSeries
15 
16 
17 _FIONA18 = LooseVersion(fiona.__version__) >= LooseVersion('1.8')
18 
19 
20 # Adapted from pandas.io.common
21 if six.PY3:
22     from urllib.request import urlopen as _urlopen
23     from urllib.parse import urlparse as parse_url
24     from urllib.parse import uses_relative, uses_netloc, uses_params
25 else:
26     from urllib2 import urlopen as _urlopen
27     from urlparse import urlparse as parse_url
28     from urlparse import uses_relative, uses_netloc, uses_params
29 
30 _VALID_URLS = set(uses_relative + uses_netloc + uses_params)
31 _VALID_URLS.discard('')
32 
33 
34 def _is_url(url):
35     """Check to see if *url* has a valid protocol."""
36     try:
37         return parse_url(url).scheme in _VALID_URLS
38     except:
39         return False
40 
41 
42 def read_file(filename, bbox=None, **kwargs):
43     """
44     Returns a GeoDataFrame from a file or URL.
45 
46     Parameters
47     ----------
48     filename: str
49         Either the absolute or relative path to the file or URL to
50         be opened.
51     bbox : tuple | GeoDataFrame or GeoSeries, default None
52         Filter features by given bounding box, GeoSeries, or GeoDataFrame.
53         CRS mis-matches are resolved if given a GeoSeries or GeoDataFrame.
54     **kwargs:
55         Keyword args to be passed to the `open` or `BytesCollection` method
56         in the fiona library when opening the file. For more information on
57         possible keywords, type:
58         ``import fiona; help(fiona.open)``
59 
60     Examples
61     --------
62     >>> df = geopandas.read_file("nybb.shp")
63 
64     Returns
65     -------
66     geodataframe : GeoDataFrame
67     """
68     if _is_url(filename):
69         req = _urlopen(filename)
70         path_or_bytes = req.read()
71         reader = fiona.BytesCollection
72     else:
73         path_or_bytes = filename
74         reader = fiona.open
75 
76     with fiona_env():
77         with reader(path_or_bytes, **kwargs) as features:
78 
79             # In a future Fiona release the crs attribute of features will
80             # no longer be a dict. The following code will be both forward
81             # and backward compatible.
82             if hasattr(features.crs, 'to_dict'):
83                 crs = features.crs.to_dict()
84             else:
85                 crs = features.crs
86 
87             if bbox is not None:
88                 if isinstance(bbox, GeoDataFrame) or isinstance(bbox, GeoSeries):
89                     bbox = tuple(bbox.to_crs(crs).total_bounds)
90                 assert len(bbox) == 4
91                 f_filt = features.filter(bbox=bbox)
92             else:
93                 f_filt = features
94 
95             columns = list(features.meta["schema"]["properties"]) + ["geometry"]
96             gdf = GeoDataFrame.from_features(f_filt, crs=crs, columns=columns)
97 
98     return gdf
99 
100 
101 def to_file(df, filename, driver="ESRI Shapefile", schema=None,
102             **kwargs):
103     """
104     Write this GeoDataFrame to an OGR data source
105 
106     A dictionary of supported OGR providers is available via:
107     >>> import fiona
108     >>> fiona.supported_drivers
109 
110     Parameters
111     ----------
112     df : GeoDataFrame to be written
113     filename : string
114         File path or file handle to write to.
115     driver : string, default 'ESRI Shapefile'
116         The OGR format driver used to write the vector file.
117     schema : dict, default None
118         If specified, the schema dictionary is passed to Fiona to
119         better control how the file is written. If None, GeoPandas
120         will determine the schema based on each column's dtype
121 
122     The *kwargs* are passed to fiona.open and can be used to write
123     to multi-layer data, store data within archives (zip files), etc.
124     """
125     if schema is None:
126         schema = infer_schema(df)
127     filename = os.path.abspath(os.path.expanduser(filename))
128     with fiona_env():
129         with fiona.open(filename, 'w', driver=driver, crs=df.crs,
130                         schema=schema, **kwargs) as colxn:
131             colxn.writerecords(df.iterfeatures())
132 
133 
134 def infer_schema(df):
135     try:
136         from collections import OrderedDict
137     except ImportError:
138         from ordereddict import OrderedDict
139 
140     def convert_type(column, in_type):
141         if in_type == object:
142             return 'str'
143         out_type = type(np.zeros(1, in_type).item()).__name__
144         if out_type == 'long':
145             out_type = 'int'
146         if not _FIONA18 and out_type == 'bool':
147             raise ValueError('column "{}" is boolean type, '.format(column) +
148                              'which is unsupported in file writing with fiona '
149                              '< 1.8. Consider casting the column to int type.')
150         return out_type
151 
152     properties = OrderedDict([
153         (col, convert_type(col, _type)) for col, _type in
154         zip(df.columns, df.dtypes) if col != df._geometry_column_name
155     ])
156 
157     if df.empty:
158         raise ValueError("Cannot write empty DataFrame to file.")
159 
160     # Since https://github.com/Toblerity/Fiona/issues/446 resolution,
161     # Fiona allows a list of geometry types
162     geom_types = _geometry_types(df)
163 
164     schema = {'geometry': geom_types, 'properties': properties}
165 
166     return schema
167 
168 
169 def _geometry_types(df):
170     """
171     Determine the geometry types in the GeoDataFrame for the schema.
172     """
173     if _FIONA18:
174         # Starting from Fiona 1.8, schema submitted to fiona to write a gdf
175         # can have mixed geometries:
176         # - 3D and 2D shapes can coexist in inferred schema
177         # - Shape and MultiShape types can (and must) coexist in inferred
178         #   schema
179         geom_types_2D = df[~df.geometry.has_z].geometry.geom_type.unique()
180         geom_types_2D = [gtype for gtype in geom_types_2D if gtype is not None]
181         geom_types_3D = df[df.geometry.has_z].geometry.geom_type.unique()
182         geom_types_3D = ["3D " + gtype for gtype in geom_types_3D
183                          if gtype is not None]
184         geom_types = geom_types_3D + geom_types_2D
185 
186     else:
187         # Before Fiona 1.8, schema submitted to write a gdf should have
188         # one single geometry type whenever possible:
189         # - 3D and 2D shapes cannot coexist in inferred schema
190         # - Shape and MultiShape can not coexist in inferred schema
191         geom_types = _geometry_types_back_compat(df)
192 
193     if len(geom_types) == 0:
194         # Default geometry type supported by Fiona
195         # (Since https://github.com/Toblerity/Fiona/issues/446 resolution)
196         return 'Unknown'
197 
198     if len(geom_types) == 1:
199         geom_types = geom_types[0]
200 
201     return geom_types
202 
203 
204 def _geometry_types_back_compat(df):
205     """
206     for backward compatibility with Fiona<1.8 only
207     """
208     unique_geom_types = df.geometry.geom_type.unique()
209     unique_geom_types = [
210         gtype for gtype in unique_geom_types if gtype is not None]
211 
212     # merge single and Multi types (eg Polygon and MultiPolygon)
213     unique_geom_types = [
214         gtype for gtype in unique_geom_types
215         if not gtype.startswith('Multi') or gtype[5:] not in unique_geom_types]
216 
217     if df.geometry.has_z.any():
218         # declare all geometries as 3D geometries
219         unique_geom_types = ["3D " + type for type in unique_geom_types]
220     # by default, all geometries are 2D geometries
221 
222     return unique_geom_types
223
```
--- 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/geopandas/io/file.py b/geopandas/io/file.py
--- a/geopandas/io/file.py
+++ b/geopandas/io/file.py
@@ -140,6 +140,9 @@
     def convert_type(column, in_type):
         if in_type == object:
             return 'str'
+        if in_type.name.startswith('datetime64'):
+            # numpy datetime type regardless of frequency
+            return 'datetime'
         out_type = type(np.zeros(1, in_type).item()).__name__
         if out_type == 'long':
             out_type = 'int'

{"golden_diff": "diff --git a/geopandas/io/file.py b/geopandas/io/file.py\n--- a/geopandas/io/file.py\n+++ b/geopandas/io/file.py\n@@ -140,6 +140,9 @@\n     def convert_type(column, in_type):\n         if in_type == object:\n             return 'str'\n+        if in_type.name.startswith('datetime64'):\n+            # numpy datetime type regardless of frequency\n+            return 'datetime'\n         out_type = type(np.zeros(1, in_type).item()).__name__\n         if out_type == 'long':\n             out_type = 'int'\n", "issue": "ValueError when saving geodataframe with pandas timestamps to shapefile\nIs there any proper way to fix this error inside geopandas (not fiona)?\n\n(Or maybe I'm trying to do the wrong thing and for example should convert my timestamps into strings before writing?)\n\n```\nC:\\Users\\kkrasnoschekov\\_soft\\WinPython-64bit-3.4.3.2_1\\python-3.4.3.amd64\\lib\\site-packages\\geopandas-0.1.0.dev_6163bc9-py3.4.egg\\geopandas\\geodataframe.py in to_file(self, filename, driver, schema, **kwargs)\n    326         \"\"\"\n    327         from geopandas.io.file import to_file\n--> 328         to_file(self, filename, driver, schema, **kwargs)\n    329 \n    330     def to_crs(self, crs=None, epsg=None, inplace=False):\n\nC:\\Users\\kkrasnoschekov\\_soft\\WinPython-64bit-3.4.3.2_1\\python-3.4.3.amd64\\lib\\site-packages\\geopandas-0.1.0.dev_6163bc9-py3.4.egg\\geopandas\\io\\file.py in to_file(df, filename, driver, schema, **kwargs)\n     60                     schema=schema, **kwargs) as c:\n     61         for feature in df.iterfeatures():\n---> 62             c.write(feature)\n     63 \n     64 \n\nC:\\Users\\kkrasnoschekov\\_soft\\WinPython-64bit-3.4.3.2_1\\python-3.4.3.amd64\\lib\\site-packages\\fiona\\collection.py in write(self, record)\n    323     def write(self, record):\n    324         \"\"\"Stages a record for writing to disk.\"\"\"\n--> 325         self.writerecords([record])\n    326 \n    327     def validate_record(self, record):\n\nC:\\Users\\kkrasnoschekov\\_soft\\WinPython-64bit-3.4.3.2_1\\python-3.4.3.amd64\\lib\\site-packages\\fiona\\collection.py in writerecords(self, records)\n    317         if self.mode not in ('a', 'w'):\n    318             raise IOError(\"collection not open for writing\")\n--> 319         self.session.writerecs(records, self)\n    320         self._len = self.session.get_length()\n    321         self._bounds = self.session.get_extent()\n\nfiona/ogrext.pyx in fiona.ogrext.WritingSession.writerecs (fiona/ogrext.c:15333)()\n\nfiona/ogrext.pyx in fiona.ogrext.OGRFeatureBuilder.build (fiona/ogrext.c:5751)()\n\nValueError: Invalid field type <class 'pandas.tslib.Timestamp'>\n```\n\n", "before_files": [{"content": "import os\nfrom distutils.version import LooseVersion\n\nimport fiona\nimport numpy as np\n\nimport six\n\ntry:\n    from fiona import Env as fiona_env\nexcept ImportError:\n    from fiona import drivers as fiona_env\n\nfrom geopandas import GeoDataFrame, GeoSeries\n\n\n_FIONA18 = LooseVersion(fiona.__version__) >= LooseVersion('1.8')\n\n\n# Adapted from pandas.io.common\nif six.PY3:\n    from urllib.request import urlopen as _urlopen\n    from urllib.parse import urlparse as parse_url\n    from urllib.parse import uses_relative, uses_netloc, uses_params\nelse:\n    from urllib2 import urlopen as _urlopen\n    from urlparse import urlparse as parse_url\n    from urlparse import uses_relative, uses_netloc, uses_params\n\n_VALID_URLS = set(uses_relative + uses_netloc + uses_params)\n_VALID_URLS.discard('')\n\n\ndef _is_url(url):\n    \"\"\"Check to see if *url* has a valid protocol.\"\"\"\n    try:\n        return parse_url(url).scheme in _VALID_URLS\n    except:\n        return False\n\n\ndef read_file(filename, bbox=None, **kwargs):\n    \"\"\"\n    Returns a GeoDataFrame from a file or URL.\n\n    Parameters\n    ----------\n    filename: str\n        Either the absolute or relative path to the file or URL to\n        be opened.\n    bbox : tuple | GeoDataFrame or GeoSeries, default None\n        Filter features by given bounding box, GeoSeries, or GeoDataFrame.\n        CRS mis-matches are resolved if given a GeoSeries or GeoDataFrame.\n    **kwargs:\n        Keyword args to be passed to the `open` or `BytesCollection` method\n        in the fiona library when opening the file. For more information on\n        possible keywords, type:\n        ``import fiona; help(fiona.open)``\n\n    Examples\n    --------\n    >>> df = geopandas.read_file(\"nybb.shp\")\n\n    Returns\n    -------\n    geodataframe : GeoDataFrame\n    \"\"\"\n    if _is_url(filename):\n        req = _urlopen(filename)\n        path_or_bytes = req.read()\n        reader = fiona.BytesCollection\n    else:\n        path_or_bytes = filename\n        reader = fiona.open\n\n    with fiona_env():\n        with reader(path_or_bytes, **kwargs) as features:\n\n            # In a future Fiona release the crs attribute of features will\n            # no longer be a dict. The following code will be both forward\n            # and backward compatible.\n            if hasattr(features.crs, 'to_dict'):\n                crs = features.crs.to_dict()\n            else:\n                crs = features.crs\n\n            if bbox is not None:\n                if isinstance(bbox, GeoDataFrame) or isinstance(bbox, GeoSeries):\n                    bbox = tuple(bbox.to_crs(crs).total_bounds)\n                assert len(bbox) == 4\n                f_filt = features.filter(bbox=bbox)\n            else:\n                f_filt = features\n\n            columns = list(features.meta[\"schema\"][\"properties\"]) + [\"geometry\"]\n            gdf = GeoDataFrame.from_features(f_filt, crs=crs, columns=columns)\n\n    return gdf\n\n\ndef to_file(df, filename, driver=\"ESRI Shapefile\", schema=None,\n            **kwargs):\n    \"\"\"\n    Write this GeoDataFrame to an OGR data source\n\n    A dictionary of supported OGR providers is available via:\n    >>> import fiona\n    >>> fiona.supported_drivers\n\n    Parameters\n    ----------\n    df : GeoDataFrame to be written\n    filename : string\n        File path or file handle to write to.\n    driver : string, default 'ESRI Shapefile'\n        The OGR format driver used to write the vector file.\n    schema : dict, default None\n        If specified, the schema dictionary is passed to Fiona to\n        better control how the file is written. If None, GeoPandas\n        will determine the schema based on each column's dtype\n\n    The *kwargs* are passed to fiona.open and can be used to write\n    to multi-layer data, store data within archives (zip files), etc.\n    \"\"\"\n    if schema is None:\n        schema = infer_schema(df)\n    filename = os.path.abspath(os.path.expanduser(filename))\n    with fiona_env():\n        with fiona.open(filename, 'w', driver=driver, crs=df.crs,\n                        schema=schema, **kwargs) as colxn:\n            colxn.writerecords(df.iterfeatures())\n\n\ndef infer_schema(df):\n    try:\n        from collections import OrderedDict\n    except ImportError:\n        from ordereddict import OrderedDict\n\n    def convert_type(column, in_type):\n        if in_type == object:\n            return 'str'\n        out_type = type(np.zeros(1, in_type).item()).__name__\n        if out_type == 'long':\n            out_type = 'int'\n        if not _FIONA18 and out_type == 'bool':\n            raise ValueError('column \"{}\" is boolean type, '.format(column) +\n                             'which is unsupported in file writing with fiona '\n                             '< 1.8. Consider casting the column to int type.')\n        return out_type\n\n    properties = OrderedDict([\n        (col, convert_type(col, _type)) for col, _type in\n        zip(df.columns, df.dtypes) if col != df._geometry_column_name\n    ])\n\n    if df.empty:\n        raise ValueError(\"Cannot write empty DataFrame to file.\")\n\n    # Since https://github.com/Toblerity/Fiona/issues/446 resolution,\n    # Fiona allows a list of geometry types\n    geom_types = _geometry_types(df)\n\n    schema = {'geometry': geom_types, 'properties': properties}\n\n    return schema\n\n\ndef _geometry_types(df):\n    \"\"\"\n    Determine the geometry types in the GeoDataFrame for the schema.\n    \"\"\"\n    if _FIONA18:\n        # Starting from Fiona 1.8, schema submitted to fiona to write a gdf\n        # can have mixed geometries:\n        # - 3D and 2D shapes can coexist in inferred schema\n        # - Shape and MultiShape types can (and must) coexist in inferred\n        #   schema\n        geom_types_2D = df[~df.geometry.has_z].geometry.geom_type.unique()\n        geom_types_2D = [gtype for gtype in geom_types_2D if gtype is not None]\n        geom_types_3D = df[df.geometry.has_z].geometry.geom_type.unique()\n        geom_types_3D = [\"3D \" + gtype for gtype in geom_types_3D\n                         if gtype is not None]\n        geom_types = geom_types_3D + geom_types_2D\n\n    else:\n        # Before Fiona 1.8, schema submitted to write a gdf should have\n        # one single geometry type whenever possible:\n        # - 3D and 2D shapes cannot coexist in inferred schema\n        # - Shape and MultiShape can not coexist in inferred schema\n        geom_types = _geometry_types_back_compat(df)\n\n    if len(geom_types) == 0:\n        # Default geometry type supported by Fiona\n        # (Since https://github.com/Toblerity/Fiona/issues/446 resolution)\n        return 'Unknown'\n\n    if len(geom_types) == 1:\n        geom_types = geom_types[0]\n\n    return geom_types\n\n\ndef _geometry_types_back_compat(df):\n    \"\"\"\n    for backward compatibility with Fiona<1.8 only\n    \"\"\"\n    unique_geom_types = df.geometry.geom_type.unique()\n    unique_geom_types = [\n        gtype for gtype in unique_geom_types if gtype is not None]\n\n    # merge single and Multi types (eg Polygon and MultiPolygon)\n    unique_geom_types = [\n        gtype for gtype in unique_geom_types\n        if not gtype.startswith('Multi') or gtype[5:] not in unique_geom_types]\n\n    if df.geometry.has_z.any():\n        # declare all geometries as 3D geometries\n        unique_geom_types = [\"3D \" + type for type in unique_geom_types]\n    # by default, all geometries are 2D geometries\n\n    return unique_geom_types\n", "path": "geopandas/io/file.py"}], "after_files": [{"content": "import os\nfrom distutils.version import LooseVersion\n\nimport fiona\nimport numpy as np\n\nimport six\n\ntry:\n    from fiona import Env as fiona_env\nexcept ImportError:\n    from fiona import drivers as fiona_env\n\nfrom geopandas import GeoDataFrame, GeoSeries\n\n\n_FIONA18 = LooseVersion(fiona.__version__) >= LooseVersion('1.8')\n\n\n# Adapted from pandas.io.common\nif six.PY3:\n    from urllib.request import urlopen as _urlopen\n    from urllib.parse import urlparse as parse_url\n    from urllib.parse import uses_relative, uses_netloc, uses_params\nelse:\n    from urllib2 import urlopen as _urlopen\n    from urlparse import urlparse as parse_url\n    from urlparse import uses_relative, uses_netloc, uses_params\n\n_VALID_URLS = set(uses_relative + uses_netloc + uses_params)\n_VALID_URLS.discard('')\n\n\ndef _is_url(url):\n    \"\"\"Check to see if *url* has a valid protocol.\"\"\"\n    try:\n        return parse_url(url).scheme in _VALID_URLS\n    except:\n        return False\n\n\ndef read_file(filename, bbox=None, **kwargs):\n    \"\"\"\n    Returns a GeoDataFrame from a file or URL.\n\n    Parameters\n    ----------\n    filename: str\n        Either the absolute or relative path to the file or URL to\n        be opened.\n    bbox : tuple | GeoDataFrame or GeoSeries, default None\n        Filter features by given bounding box, GeoSeries, or GeoDataFrame.\n        CRS mis-matches are resolved if given a GeoSeries or GeoDataFrame.\n    **kwargs:\n        Keyword args to be passed to the `open` or `BytesCollection` method\n        in the fiona library when opening the file. For more information on\n        possible keywords, type:\n        ``import fiona; help(fiona.open)``\n\n    Examples\n    --------\n    >>> df = geopandas.read_file(\"nybb.shp\")\n\n    Returns\n    -------\n    geodataframe : GeoDataFrame\n    \"\"\"\n    if _is_url(filename):\n        req = _urlopen(filename)\n        path_or_bytes = req.read()\n        reader = fiona.BytesCollection\n    else:\n        path_or_bytes = filename\n        reader = fiona.open\n\n    with fiona_env():\n        with reader(path_or_bytes, **kwargs) as features:\n\n            # In a future Fiona release the crs attribute of features will\n            # no longer be a dict. The following code will be both forward\n            # and backward compatible.\n            if hasattr(features.crs, 'to_dict'):\n                crs = features.crs.to_dict()\n            else:\n                crs = features.crs\n\n            if bbox is not None:\n                if isinstance(bbox, GeoDataFrame) or isinstance(bbox, GeoSeries):\n                    bbox = tuple(bbox.to_crs(crs).total_bounds)\n                assert len(bbox) == 4\n                f_filt = features.filter(bbox=bbox)\n            else:\n                f_filt = features\n\n            columns = list(features.meta[\"schema\"][\"properties\"]) + [\"geometry\"]\n            gdf = GeoDataFrame.from_features(f_filt, crs=crs, columns=columns)\n\n    return gdf\n\n\ndef to_file(df, filename, driver=\"ESRI Shapefile\", schema=None,\n            **kwargs):\n    \"\"\"\n    Write this GeoDataFrame to an OGR data source\n\n    A dictionary of supported OGR providers is available via:\n    >>> import fiona\n    >>> fiona.supported_drivers\n\n    Parameters\n    ----------\n    df : GeoDataFrame to be written\n    filename : string\n        File path or file handle to write to.\n    driver : string, default 'ESRI Shapefile'\n        The OGR format driver used to write the vector file.\n    schema : dict, default None\n        If specified, the schema dictionary is passed to Fiona to\n        better control how the file is written. If None, GeoPandas\n        will determine the schema based on each column's dtype\n\n    The *kwargs* are passed to fiona.open and can be used to write\n    to multi-layer data, store data within archives (zip files), etc.\n    \"\"\"\n    if schema is None:\n        schema = infer_schema(df)\n    filename = os.path.abspath(os.path.expanduser(filename))\n    with fiona_env():\n        with fiona.open(filename, 'w', driver=driver, crs=df.crs,\n                        schema=schema, **kwargs) as colxn:\n            colxn.writerecords(df.iterfeatures())\n\n\ndef infer_schema(df):\n    try:\n        from collections import OrderedDict\n    except ImportError:\n        from ordereddict import OrderedDict\n\n    def convert_type(column, in_type):\n        if in_type == object:\n            return 'str'\n        if in_type.name.startswith('datetime64'):\n            # numpy datetime type regardless of frequency\n            return 'datetime'\n        out_type = type(np.zeros(1, in_type).item()).__name__\n        if out_type == 'long':\n            out_type = 'int'\n        if not _FIONA18 and out_type == 'bool':\n            raise ValueError('column \"{}\" is boolean type, '.format(column) +\n                             'which is unsupported in file writing with fiona '\n                             '< 1.8. Consider casting the column to int type.')\n        return out_type\n\n    properties = OrderedDict([\n        (col, convert_type(col, _type)) for col, _type in\n        zip(df.columns, df.dtypes) if col != df._geometry_column_name\n    ])\n\n    if df.empty:\n        raise ValueError(\"Cannot write empty DataFrame to file.\")\n\n    # Since https://github.com/Toblerity/Fiona/issues/446 resolution,\n    # Fiona allows a list of geometry types\n    geom_types = _geometry_types(df)\n\n    schema = {'geometry': geom_types, 'properties': properties}\n\n    return schema\n\n\ndef _geometry_types(df):\n    \"\"\"\n    Determine the geometry types in the GeoDataFrame for the schema.\n    \"\"\"\n    if _FIONA18:\n        # Starting from Fiona 1.8, schema submitted to fiona to write a gdf\n        # can have mixed geometries:\n        # - 3D and 2D shapes can coexist in inferred schema\n        # - Shape and MultiShape types can (and must) coexist in inferred\n        #   schema\n        geom_types_2D = df[~df.geometry.has_z].geometry.geom_type.unique()\n        geom_types_2D = [gtype for gtype in geom_types_2D if gtype is not None]\n        geom_types_3D = df[df.geometry.has_z].geometry.geom_type.unique()\n        geom_types_3D = [\"3D \" + gtype for gtype in geom_types_3D\n                         if gtype is not None]\n        geom_types = geom_types_3D + geom_types_2D\n\n    else:\n        # Before Fiona 1.8, schema submitted to write a gdf should have\n        # one single geometry type whenever possible:\n        # - 3D and 2D shapes cannot coexist in inferred schema\n        # - Shape and MultiShape can not coexist in inferred schema\n        geom_types = _geometry_types_back_compat(df)\n\n    if len(geom_types) == 0:\n        # Default geometry type supported by Fiona\n        # (Since https://github.com/Toblerity/Fiona/issues/446 resolution)\n        return 'Unknown'\n\n    if len(geom_types) == 1:\n        geom_types = geom_types[0]\n\n    return geom_types\n\n\ndef _geometry_types_back_compat(df):\n    \"\"\"\n    for backward compatibility with Fiona<1.8 only\n    \"\"\"\n    unique_geom_types = df.geometry.geom_type.unique()\n    unique_geom_types = [\n        gtype for gtype in unique_geom_types if gtype is not None]\n\n    # merge single and Multi types (eg Polygon and MultiPolygon)\n    unique_geom_types = [\n        gtype for gtype in unique_geom_types\n        if not gtype.startswith('Multi') or gtype[5:] not in unique_geom_types]\n\n    if df.geometry.has_z.any():\n        # declare all geometries as 3D geometries\n        unique_geom_types = [\"3D \" + type for type in unique_geom_types]\n    # by default, all geometries are 2D geometries\n\n    return unique_geom_types\n", "path": "geopandas/io/file.py"}]}