Repository: geopandas/geopandas

Issue:
upgrading to pandas 0.20.0 breaks geopandas 0.2.1 import
I just upgraded pandas to v0.20.0 and it breaks geopandas on import:

```
>>>import geopandas

---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-c9ee6bd24dbc> in <module>()
----> 1 import geopandas

C:\Anaconda\lib\site-packages\geopandas\__init__.py in <module>()
----> 1 from geopandas.geoseries import GeoSeries
      2 from geopandas.geodataframe import GeoDataFrame
      3 
      4 from geopandas.io.file import read_file
      5 from geopandas.io.sql import read_postgis

C:\Anaconda\lib\site-packages\geopandas\geoseries.py in <module>()
      6 from pandas import Series, DataFrame
      7 from pandas.core.indexing import _NDFrameIndexer
----> 8 from pandas.util.decorators import cache_readonly
      9 import pyproj
     10 from shapely.geometry import box, shape, Polygon, Point

ImportError: No module named 'pandas.util.decorators'
```

Here's my relevant package info:

```
# packages in environment at C:\Anaconda:
#
geopandas                 0.2.1                    py35_3    conda-forge
pandas                    0.20.0              np112py35_1    conda-forge
```

Confirmed this behavior on Linux and Windows, Python 2.7, 3.5, 3.6.


Discussion:
Looks like pandas made the submodules of pandas.util private earlier today in this commit: https://github.com/pandas-dev/pandas/commit/e99f56e43ab65710a34440067efe372d5f74280b

https://github.com/pandas-dev/pandas/pull/16223

https://github.com/pandas-dev/pandas/issues/13634
Hmm, we should have added a deprecation shim then ... 
Can do a quick 0.2.2 release to fix this.
This will be fixed with a pandas 0.20.1. 
But apart from that, we have to fix it here as well of course, so leaving the issue open (but no immediate geopandas 0.2.2 is needed). PR always welcome!
It is fixed on the pandas side with the release 0.20.01: https://github.com/pandas-dev/pandas/releases/tag/v0.20.1 

Resolving patch:
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -1,8 +1,3 @@
-try:
-    from collections import OrderedDict
-except ImportError:
-    # Python 2.6
-    from ordereddict import OrderedDict
 import json
 import os
 import sys
@@ -11,7 +6,7 @@
 from pandas import DataFrame, Series, Index
 from shapely.geometry import mapping, shape
 from shapely.geometry.base import BaseGeometry
-from six import string_types
+from six import string_types, PY3
 
 from geopandas import GeoSeries
 from geopandas.base import GeoPandasBase
@@ -20,7 +15,6 @@
 
 
 DEFAULT_GEO_COLUMN_NAME = 'geometry'
-PY3 = sys.version_info[0] == 3
 
 
 class GeoDataFrame(GeoPandasBase, DataFrame):
diff --git a/geopandas/geoseries.py b/geopandas/geoseries.py
--- a/geopandas/geoseries.py
+++ b/geopandas/geoseries.py
@@ -5,7 +5,7 @@
 import numpy as np
 from pandas import Series, DataFrame
 from pandas.core.indexing import _NDFrameIndexer
-from pandas.util.decorators import cache_readonly
+
 import pyproj
 from shapely.geometry import box, shape, Polygon, Point
 from shapely.geometry.collection import GeometryCollection
@@ -15,7 +15,6 @@
 from geopandas.plotting import plot_series
 from geopandas.base import GeoPandasBase
 
-OLD_PANDAS = issubclass(Series, np.ndarray)
 
 def _is_empty(x):
     try:
@@ -23,10 +22,6 @@ def _is_empty(x):
     except:
         return False
 
-def _convert_array_args(args):
-    if len(args) == 1 and isinstance(args[0], BaseGeometry):
-        args = ([args[0]],)
-    return args
 
 class _CoordinateIndexer(_NDFrameIndexer):
     """ Indexing by coordinate slices """
@@ -56,19 +51,17 @@ class GeoSeries(GeoPandasBase, Series):
 
     def __new__(cls, *args, **kwargs):
         kwargs.pop('crs', None)
-        if OLD_PANDAS:
-            args = _convert_array_args(args)
-            arr = Series.__new__(cls, *args, **kwargs)
-        else:
-            arr = Series.__new__(cls)
+        arr = Series.__new__(cls)
         if type(arr) is GeoSeries:
             return arr
         else:
             return arr.view(GeoSeries)
 
     def __init__(self, *args, **kwargs):
-        if not OLD_PANDAS:
-            args = _convert_array_args(args)
+        # fix problem for scalar geometries passed
+        if len(args) == 1 and isinstance(args[0], BaseGeometry):
+            args = ([args[0]],)
+
         crs = kwargs.pop('crs', None)
 
         super(GeoSeries, self).__init__(*args, **kwargs)
@@ -197,21 +190,8 @@ def fillna(self, value=None, method=None, inplace=False,
         """
         if value is None:
             value = Point()
-        if not OLD_PANDAS:
-            return super(GeoSeries, self).fillna(value=value, method=method,
-                                                 inplace=inplace, **kwargs)
-        else:
-            # FIXME: this is an ugly way to support pandas <= 0.12
-            if method is not None:
-                raise NotImplementedError('Fill method is currently not implemented for GeoSeries')
-            if isinstance(value, BaseGeometry):
-                result = self.copy() if not inplace else self
-                mask = self.isnull()
-                result[mask] = value
-                if not inplace:
-                    return GeoSeries(result)
-            else:
-                raise ValueError('Non-geometric fill values not allowed for GeoSeries')
+        return super(GeoSeries, self).fillna(value=value, method=method,
+                                             inplace=inplace, **kwargs)
 
     def align(self, other, join='outer', level=None, copy=True,
               fill_value=None, **kwargs):


Test patch: