Repository: geopandas/geopandas

Issue:
BUG: rename_geometry should check if duplicate column name is created
Currently if you use the name of an existing column in `rename_geometry`, you get a bit confusing error message:

```python
>>> df = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))

# this gives a not super helpful error message
>>> df.rename_geometry("continent")
...
TypeError: Input must be valid geometry objects: ['Oceania'
 <shapely.geometry.multipolygon.MultiPolygon object at 0x7fdf63d03be0>]

# this raises the same error, but in addition also leaves the df in a broken state
>>> df.rename_geometry("continent", inplace=True)
...
TypeError: Input must be valid geometry objects: ['Oceania'
 <shapely.geometry.multipolygon.MultiPolygon object at 0x7fdf633e6310>]

>>> df.geometry
...
AttributeError: No geometry data set yet (expected in column 'geometry'.)
```

(originally from https://github.com/CartoDB/cartoframes/issues/1517)

Discussion:

Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..7c7a7068
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,39 @@
+---
+
+name: Bug Report
+about: Create a bug report to help us improve geopandas
+title: "BUG:"
+labels: "bug, needs triage"
+
+---
+
+- [ ] I have checked that this issue has not already been reported.
+
+- [ ] I have confirmed this bug exists on the latest version of geopandas.
+
+- [ ] (optional) I have confirmed this bug exists on the master branch of geopandas.
+
+---
+
+**Note**: Please read [this guide](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) detailing how to provide the necessary information for us to reproduce your bug.
+
+#### Code Sample, a copy-pastable example
+
+```python
+# Your code here
+
+```
+
+#### Problem description
+
+[this should explain **why** the current behaviour is a problem and why the expected output is a better solution]
+
+#### Expected Output
+
+#### Output of ``geopandas.show_versions()``
+
+<details>
+
+[paste the output of ``geopandas.show_versions()`` here leaving a blank line after the details tag]
+
+</details>
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..cdb5ba02
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,34 @@
+---
+
+name: Feature Request
+about: Suggest an idea for geopandas
+title: "ENH:"
+labels: "enhancement"
+
+---
+
+#### Is your feature request related to a problem?
+
+[this should provide a description of what the problem is, e.g. "I wish I could use geopandas to do [...]"]
+
+#### Describe the solution you'd like
+
+[this should provide a description of the feature request, e.g. "`GeoDataFrame.foo` should get a new parameter `bar` that [...]", try to write a docstring for the desired feature]
+
+#### API breaking implications
+
+[this should provide a description of how this feature will affect the API]
+
+#### Describe alternatives you've considered
+
+[this should provide a description of any alternative solutions or features you've considered]
+
+#### Additional context
+
+[add any other context, code examples, or references to existing implementations about the feature request here]
+
+```python
+# Your code here, if applicable
+
+```
+
diff --git a/.github/ISSUE_TEMPLATE/installation_issue.md b/.github/ISSUE_TEMPLATE/installation_issue.md
new file mode 100644
index 00000000..d0564330
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/installation_issue.md
@@ -0,0 +1,29 @@
+---
+
+name: Installation Issue 
+about: Ask about installing geopandas 
+title: ""
+labels: "installation"
+
+---
+
+- [ ] I have read the [documentation on installation](https://geopandas.org/install.html) and followed the instructions provided.
+
+- [ ] I have looked through [issues labeled "installation"](https://github.com/geopandas/geopandas/labels/installation) in the geopandas repo.
+
+---
+
+#### System information
+
+[what operating system do you have and what package management system are you
+using]
+
+#### Environment details
+
+<details>
+
+[if using conda, paste the output of `conda info` and `conda list`; if using
+pip, `pip freeze`] 
+
+</details>
+
diff --git a/.github/ISSUE_TEMPLATE/submit_question.md b/.github/ISSUE_TEMPLATE/submit_question.md
new file mode 100644
index 00000000..c118c3d9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/submit_question.md
@@ -0,0 +1,24 @@
+---
+
+name: Submit Question
+about: Ask a general question about geopandas
+title: "QST:"
+labels: "question"
+
+---
+
+- [ ] I have searched the [geopandas] tag on [StackOverflow](https://stackoverflow.com/questions/tagged/geopandas) and [GIS StackExchange](https://gis.stackexchange.com/questions/tagged/geopandas) for similar questions.
+
+- [ ] I have asked my usage related question on [StackOverflow](https://stackoverflow.com) or [GIS StackExhange](https://gis.stackexchange.com).
+
+---
+
+#### Question about geopandas
+
+**Note**: If you'd still like to submit a question, please read [this guide](
+https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) detailing how to provide the necessary information for us to reproduce your question.
+
+```python
+# Your code here, if applicable
+
+```
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b6390b7b..0ec40998 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,3 +1,4 @@
+files: 'geopandas\/'
 repos:
 -   repo: https://github.com/python/black
     rev: 20.8b1
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 4ba4225c..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-language: python
-
-sudo: false
-
-matrix:
-  include:
-    # One build with minimum versions of dependencies
-    - env: ENV_FILE="ci/travis/36-minimal.yaml"
-
-    # one build with no optional dependencies
-    - env: ENV_FILE="ci/travis/38-no-optional-deps.yaml"
-
-    # Python 3.6 test all supported Pandas versions
-    - env: ENV_FILE="ci/travis/36-pd025.yaml" PYGEOS=true
-
-    - env: ENV_FILE="ci/travis/37-latest-defaults.yaml" STYLE=true PYGEOS=true
-    - env: ENV_FILE="ci/travis/37-latest-conda-forge.yaml" PYGEOS=true
-
-    - env: ENV_FILE="ci/travis/38-latest-conda-forge.yaml" PYGEOS=true POSTGIS=true PGUSER=postgres PGPASSWORD=postgres
-
-    - env: ENV_FILE="ci/travis/37-dev.yaml" DEV=true PYGEOS=true
-
-  allow_failures:
-    - env: ENV_FILE="ci/travis/37-dev.yaml" DEV=true PYGEOS=true
-
-before_install:
-  - chmod +x ci/travis/setup_postgres.sh
-
-install:
-  # Install conda
-  - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
-  - bash miniconda.sh -b -p $HOME/miniconda
-  - export PATH="$HOME/miniconda/bin:$PATH"
-  - conda config --set always_yes yes --set changeps1 no
-  - conda update conda
-  - conda info
-
-  # free channel needed for older envs (< py37), see
-  # https://github.com/conda/conda/issues/8849
-  - conda config --set restore_free_channel true
-
-  # Install dependencies
-  - conda env create --file="${ENV_FILE}"
-  - source activate test
-  - if [ "$DEV" ]; then pip install git+https://github.com/pydata/pandas.git; fi
-  - if [ "$DEV" ]; then pip install git+https://github.com/matplotlib/matplotlib.git; fi
-  - if [ "$DEV" ]; then pip install git+https://github.com/Toblerity/Shapely.git; fi
-  - if [ "$STYLE" ]; then pip install black flake8; fi
-  - if [ "$POSTGIS" ]; then conda install postgis -c conda-forge; fi
-  - pip install -e .
-
-  # List environment
-  - conda list
-  - python -c "import geopandas; geopandas.show_versions();"
-
-  # Set-up database
-  - if [ "$POSTGIS" ]; then ci/travis/setup_postgres.sh; fi
-
-script:
-  - echo "Testing without PyGEOS"
-  - USE_PYGEOS=0 pytest geopandas -v -r s --cov geopandas --cov-report term-missing
-  - if [ "$PYGEOS" ]; then echo "Testing with PyGEOS"; fi
-  - if [ "$PYGEOS" ]; then USE_PYGEOS=1 pytest geopandas -v -r s --cov-append --cov geopandas --cov-report term-missing; fi
-  - if [ "$STYLE" ]; then black --check geopandas; fi
-  - if [ "$STYLE" ]; then flake8 geopandas; fi
-
-after_success:
-  - codecov
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 19d20d9a..00000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-# With infos from
-# http://tjelvarolsson.com/blog/how-to-continuously-test-your-python-code-on-windows-using-appveyor/
-# https://packaging.python.org/en/latest/appveyor/
-# https://github.com/rmcgibbo/python-appveyor-conda-example
-
-environment:
-  matrix:
-    - PYTHON_VERSION: "3.7"
-      MINICONDA: C:\Miniconda37-x64
-      ENV_FILE: "ci/travis/37-latest-conda-forge.yaml"
-
-    - PYTHON_VERSION: "3.8"
-      MINICONDA: C:\Miniconda37-x64
-      ENV_FILE: "ci/travis/38-latest-conda-forge.yaml"
-
-# all our python builds have to happen in tests_script...
-build: false
-
-init:
-  - "ECHO %PYTHON_VERSION% %MINICONDA%"
-
-install:
-  # cancel older builds for the same PR
-  - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
-        https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
-        Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
-        throw "There are newer queued builds for this pull request, failing early." }
-
-  # set up environment
-  - CALL "%MINICONDA%\\Scripts\\activate.bat"
-  - conda config --set always_yes yes --set show_channel_urls true --set changeps1 no
-  - conda update conda
-  # this is basically equivalent to what conda init does.  It changes the "conda" to
-  #    be a .bat script that sets appropriate PATH entries before conda hits problems.
-  #  This PATH modification only works with conda 4.6+, but it won't hurt other versions.
-  - set "PATH=%MINICONDA%\condabin:%PATH%"
-  - conda info -a
-  - conda config --add channels conda-forge
-  - conda config --set channel_priority strict
-  - conda env create --file="${ENV_FILE}"
-
-test_script:
-  # this uses condabin/conda.bat because of our PATH modification above
-  - conda activate test
-  - conda list
-  - pytest geopandas -v
diff --git a/ci/travis/36-minimal.yaml b/ci/travis/36-minimal.yaml
index 3533fc12..16e9d996 100644
--- a/ci/travis/36-minimal.yaml
+++ b/ci/travis/36-minimal.yaml
@@ -13,7 +13,7 @@ dependencies:
   # testing
   - pytest
   - pytest-cov
-  - codecov
+  - pytest-xdist
   - fsspec
   # optional
   - rtree
diff --git a/ci/travis/36-pd025.yaml b/ci/travis/36-pd025.yaml
index cf5df536..c089da9e 100644
--- a/ci/travis/36-pd025.yaml
+++ b/ci/travis/36-pd025.yaml
@@ -12,8 +12,8 @@ dependencies:
   # testing
   - pytest
   - pytest-cov
+  - pytest-xdist
   - fsspec
-  #- codecov
   # optional
   - rtree
   - matplotlib
@@ -24,7 +24,6 @@ dependencies:
   - pyarrow
   - pip:
       - pyproj==2.3.1
-      - codecov
       - geopy
       - mapclassify==2.2.0
       - git+https://github.com/pygeos/pygeos.git
diff --git a/ci/travis/37-dev.yaml b/ci/travis/37-dev.yaml
index 2b463640..266537f6 100644
--- a/ci/travis/37-dev.yaml
+++ b/ci/travis/37-dev.yaml
@@ -5,26 +5,26 @@ dependencies:
   - python=3.7.3
   - cython
   # required
-  - pandas
-  - shapely
   - fiona
   - pyproj
   - geos
   # testing
   - pytest
   - pytest-cov
+  - pytest-xdist
   - fsspec
-  #- codecov
   # optional
   - rtree
-  - matplotlib
   - descartes
   #- geopy
   - SQLalchemy
   - libspatialite
   - pyarrow
   - pip:
-    - codecov
     - geopy
     - mapclassify>=2.2.0
-    - git+https://github.com/pygeos/pygeos.git
+    # dev versions of packages
+    - git+https://github.com/pydata/pandas.git@master
+    - git+https://github.com/matplotlib/matplotlib.git@master
+    - git+https://github.com/Toblerity/Shapely.git@master
+    - git+https://github.com/pygeos/pygeos.git@master
diff --git a/ci/travis/38-no-optional-deps.yaml b/ci/travis/38-no-optional-deps.yaml
index d6fe127e..d1d3f0cc 100644
--- a/ci/travis/38-no-optional-deps.yaml
+++ b/ci/travis/38-no-optional-deps.yaml
@@ -11,4 +11,4 @@ dependencies:
   # testing
   - pytest
   - pytest-cov
-  - codecov
+  - pytest-xdist
diff --git a/ci/travis/setup_postgres.sh b/ci/travis/setup_postgres.sh
index 96645f3d..32defae0 100644
--- a/ci/travis/setup_postgres.sh
+++ b/ci/travis/setup_postgres.sh
@@ -14,7 +14,7 @@ while [ ! -e /tmp/.s.PGSQL.5432 ]; do
     echo -n '.'
 done
 
-createuser -U travis -s postgres
+createuser -U ${USER} -s postgres
 createdb --owner=postgres test_geopandas
 psql -d test_geopandas -q -c "CREATE EXTENSION postgis"
 
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 00000000..c6d92a61
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,6 @@
+coverage:
+  status:
+    project:
+      default:
+        target: 95%    # the required coverage value
+        threshold: 0.2%  # the leniency in hitting the target
diff --git a/geopandas/_vectorized.py b/geopandas/_vectorized.py
index d31cfc9f..291a3dfc 100644
--- a/geopandas/_vectorized.py
+++ b/geopandas/_vectorized.py
@@ -474,23 +474,28 @@ def is_simple(data):
 
 
 def is_ring(data):
-    if compat.USE_PYGEOS:
-        return pygeos.is_ring(pygeos.get_exterior_ring(data))
-    else:
-        # operates on the exterior, so can't use _unary_op()
-        # XXX needed to change this because there is now a geometry collection
-        # in the shapely ones that was something else before?
-        return np.array(
-            [
-                geom.exterior.is_ring
-                if geom is not None
-                and hasattr(geom, "exterior")
-                and geom.exterior is not None
-                else False
-                for geom in data
-            ],
-            dtype=bool,
+    if "Polygon" in geom_type(data):
+        warnings.warn(
+            "is_ring currently returns True for Polygons, which is not correct. "
+            "This will be corrected to False in a future release.",
+            FutureWarning,
+            stacklevel=3,
         )
+    if compat.USE_PYGEOS:
+        return pygeos.is_ring(data) | pygeos.is_ring(pygeos.get_exterior_ring(data))
+    else:
+        # for polygons operates on the exterior, so can't use _unary_op()
+        results = []
+        for geom in data:
+            if geom is None:
+                results.append(False)
+            elif geom.type == "Polygon":
+                results.append(geom.exterior.is_ring)
+            elif geom.type in ["LineString", "LinearRing"]:
+                results.append(geom.is_ring)
+            else:
+                results.append(False)
+        return np.array(results, dtype=bool)
 
 
 def is_closed(data):
@@ -886,6 +891,9 @@ def transform(data, func):
         result = np.empty(n, dtype=object)
         for i in range(n):
             geom = data[i]
-            result[i] = transform(func, geom)
+            if _isna(geom):
+                result[i] = geom
+            else:
+                result[i] = transform(func, geom)
 
         return result
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
index a764b178..8b7d82ec 100644
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -289,10 +289,15 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
         geodataframe : GeoDataFrame
         """
         geometry_col = self.geometry.name
-        if not inplace:
-            return self.rename(columns={geometry_col: col}).set_geometry(col, inplace)
-        self.rename(columns={geometry_col: col}, inplace=inplace)
-        self.set_geometry(col, inplace=inplace)
+        if col in self.columns:
+            raise ValueError(f"Column named {col} already exists")
+        else:
+            if not inplace:
+                return self.rename(columns={geometry_col: col}).set_geometry(
+                    col, inplace
+                )
+            self.rename(columns={geometry_col: col}, inplace=inplace)
+            self.set_geometry(col, inplace=inplace)
 
     @property
     def crs(self):
diff --git a/geopandas/geoseries.py b/geopandas/geoseries.py
index 24f084a3..b79f598e 100644
--- a/geopandas/geoseries.py
+++ b/geopandas/geoseries.py
@@ -313,8 +313,8 @@ class GeoSeries(GeoPandasBase, Series):
         return self._wrapped_pandas_method("select", *args, **kwargs)
 
     @inherit_doc(pd.Series)
-    def apply(self, func, args=(), **kwargs):
-        result = super().apply(func, args=args, **kwargs)
+    def apply(self, func, convert_dtype=True, args=(), **kwargs):
+        result = super().apply(func, convert_dtype=convert_dtype, args=args, **kwargs)
         if isinstance(result, GeoSeries):
             if self.crs is not None:
                 result.set_crs(self.crs, inplace=True)
diff --git a/geopandas/plotting.py b/geopandas/plotting.py
index e82c012f..72949567 100644
--- a/geopandas/plotting.py
+++ b/geopandas/plotting.py
@@ -754,7 +754,7 @@ def plot_dataframe(
             **style_kwds
         )
 
-    if missing_kwds is not None:
+    if missing_kwds is not None and not expl_series[nan_idx].empty:
         if color:
             if "color" not in missing_kwds:
                 missing_kwds["color"] = color
diff --git a/geopandas/sindex.py b/geopandas/sindex.py
index 20a31001..fd46c124 100644
--- a/geopandas/sindex.py
+++ b/geopandas/sindex.py
@@ -252,11 +252,19 @@ if compat.HAS_RTREE:
 
         @property
         def size(self):
-            return len(self.leaves()[0][1])
+            if hasattr(self, "_size"):
+                size = self._size
+            else:
+                # self.leaves are lists of tuples of (int, lists...)
+                # index [0][1] always has an element, even for empty sindex
+                # for an empty index, it will be an empty list
+                size = len(self.leaves()[0][1])
+                self._size = size
+            return size
 
         @property
         def is_empty(self):
-            return self.size == 0
+            return self.geometries.size == 0 or self.size == 0
 
         def __len__(self):
             return self.size
diff --git a/geopandas/tools/geocoding.py b/geopandas/tools/geocoding.py
index 4e3a78f7..0398773d 100644
--- a/geopandas/tools/geocoding.py
+++ b/geopandas/tools/geocoding.py
@@ -1,7 +1,6 @@
 from collections import defaultdict
 import time
 
-import numpy as np
 import pandas as pd
 
 from shapely.geometry import Point
@@ -169,16 +168,19 @@ def _prepare_geocode_result(results):
     index = []
 
     for i, s in results.items():
-        address, loc = s
 
-        # loc is lat, lon and we want lon, lat
-        if loc is None:
+        if s is None:
             p = Point()
+            address = None
+
         else:
-            p = Point(loc[1], loc[0])
+            address, loc = s
 
-        if address is None:
-            address = np.nan
+            # loc is lat, lon and we want lon, lat
+            if loc is None:
+                p = Point()
+            else:
+                p = Point(loc[1], loc[0])
 
         d["geometry"].append(p)
         d["address"].append(address)


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
new file mode 100644
index 00000000..5861d903
--- /dev/null
+++ b/.github/workflows/tests.yaml
@@ -0,0 +1,121 @@
+name: Tests
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+  schedule:
+    - cron:  '0 0 * * *'
+
+jobs:
+  Linting:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-python@v2
+      - uses: pre-commit/action@v2.0.0
+
+  Test:
+    needs: Linting
+    name: ${{ matrix.os }}, ${{ matrix.env }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+        postgis: [false]
+        dev: [false]
+        env:
+          - ci/travis/36-minimal.yaml
+          - ci/travis/38-no-optional-deps.yaml
+          - ci/travis/36-pd025.yaml
+          - ci/travis/37-latest-defaults.yaml
+          - ci/travis/37-latest-conda-forge.yaml
+          - ci/travis/38-latest-conda-forge.yaml
+        include:
+          - env: ci/travis/37-latest-conda-forge.yaml
+            os: macos-latest
+            postgis: false
+            dev: false
+          - env: ci/travis/38-latest-conda-forge.yaml
+            os: macos-latest
+            postgis: false
+            dev: false
+          - env: ci/travis/37-latest-conda-forge.yaml
+            os: windows-latest
+            postgis: false
+            dev: false
+          - env: ci/travis/38-latest-conda-forge.yaml
+            os: windows-latest
+            postgis: false
+            dev: false
+          - env: ci/travis/37-dev.yaml
+            os: ubuntu-latest
+            dev: true
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Setup Conda
+        uses: s-weigand/setup-conda@v1.0.4
+        with:
+          activate-conda: false
+
+      - name: Install Env
+        shell: bash
+        run: conda env create -f ${{ matrix.env }}
+
+
+      - name: Check and Log Environment
+        shell: bash
+        run: |
+          source activate test
+          python -V
+          python -c "import geopandas; geopandas.show_versions();"
+          conda info
+          # save conda list to file and print out
+          # so that we can do the HAS_PYGEOS check without calling conda again
+          conda list 2>&1 | tee conda.txt
+          if ( cat conda.txt | grep -q pygeos  )
+          then
+            echo "Setting HAS_PYGEOS=1"
+            echo '::set-env name=HAS_PYGEOS::1'
+          else
+            echo "Setting HAS_PYGEOS=0"
+            echo '::set-env name=HAS_PYGEOS::0'
+          fi
+
+      - name: Test without PyGEOS
+        shell: bash
+        continue-on-error: ${{ matrix.dev }}
+        env:
+          USE_PYGEOS: 0
+        run: |
+          source activate test
+          pytest -v -r s -n auto --color=yes --cov=geopandas --cov-append --cov-report term-missing --cov-report xml geopandas/
+
+      - name: Test with PyGEOS
+        shell: bash
+        continue-on-error: ${{ matrix.dev }}
+        if: env.HAS_PYGEOS == 1
+        env:
+          USE_PYGEOS: 1
+        run: |
+          source activate test
+          pytest -v -r s -n auto --color=yes --cov=geopandas --cov-append --cov-report term-missing --cov-report xml geopandas/
+
+      - name: Test with PostGIS
+        shell: bash
+        if: contains(matrix.env, '38-latest-conda-forge.yaml') && contains(matrix.os, 'ubuntu')
+        env:
+          PGUSER: postgres
+          PGPASSWORD: postgres
+          PGHOST: "127.0.0.1"
+        run: |
+          source activate test
+          conda install postgis -c conda-forge
+          source ci/travis/setup_postgres.sh
+          pytest -v -r s -color=yes --cov=geopandas --cov-append --cov-report term-missing --cov-report xml geopandas/io/tests/test_sql.py | tee /dev/stderr | if grep SKIPPED >/dev/null;then echo "TESTS SKIPPED, FAILING" && exit 1;fi
+
+      - uses: codecov/codecov-action@v1
diff --git a/ci/travis/37-latest-conda-forge.yaml b/ci/travis/37-latest-conda-forge.yaml
index 71f87fca..6023ab61 100644
--- a/ci/travis/37-latest-conda-forge.yaml
+++ b/ci/travis/37-latest-conda-forge.yaml
@@ -12,7 +12,7 @@ dependencies:
   # testing
   - pytest
   - pytest-cov
-  - codecov
+  - pytest-xdist
   - fsspec
   # optional
   - rtree
@@ -23,3 +23,4 @@ dependencies:
   - SQLalchemy
   - libspatialite
   - pyarrow
+ 
diff --git a/ci/travis/37-latest-defaults.yaml b/ci/travis/37-latest-defaults.yaml
index ad96f2c6..d03871bd 100644
--- a/ci/travis/37-latest-defaults.yaml
+++ b/ci/travis/37-latest-defaults.yaml
@@ -12,8 +12,8 @@ dependencies:
   # testing
   - pytest
   - pytest-cov
+  - pytest-xdist
   - fsspec
-  #- codecov
   # optional
   - rtree
   - matplotlib
@@ -23,7 +23,6 @@ dependencies:
   - libspatialite
   - pyarrow
   - pip:
-    - codecov
     - geopy
     - mapclassify
     - git+https://github.com/pygeos/pygeos.git
diff --git a/ci/travis/38-latest-conda-forge.yaml b/ci/travis/38-latest-conda-forge.yaml
index 951d7a41..a572785b 100644
--- a/ci/travis/38-latest-conda-forge.yaml
+++ b/ci/travis/38-latest-conda-forge.yaml
@@ -12,7 +12,7 @@ dependencies:
   # testing
   - pytest
   - pytest-cov
-  - codecov
+  - pytest-xdist
   - fsspec
   # optional
   - rtree
@@ -27,3 +27,4 @@ dependencies:
   - libspatialite
   - geoalchemy2
   - pyarrow
+ 
\ No newline at end of file
diff --git a/geopandas/io/tests/test_file.py b/geopandas/io/tests/test_file.py
index 09b5a355..96b11a17 100644
--- a/geopandas/io/tests/test_file.py
+++ b/geopandas/io/tests/test_file.py
@@ -261,7 +261,7 @@ def test_append_file(tmpdir, df_nybb, df_null, driver, ext):
     assert "geometry" in df
     assert len(df) == (5 * 2)
     expected = pd.concat([df_nybb] * 2, ignore_index=True)
-    assert_geodataframe_equal(df, expected)
+    assert_geodataframe_equal(df, expected, check_less_precise=True)
 
     # Write layer with null geometry out to file
     tempfilename = os.path.join(str(tmpdir), "null_geom." + ext)
@@ -272,7 +272,7 @@ def test_append_file(tmpdir, df_nybb, df_null, driver, ext):
     assert "geometry" in df
     assert len(df) == (2 * 2)
     expected = pd.concat([df_null] * 2, ignore_index=True)
-    assert_geodataframe_equal(df, expected)
+    assert_geodataframe_equal(df, expected, check_less_precise=True)
 
 
 # -----------------------------------------------------------------------------
diff --git a/geopandas/tests/test_array.py b/geopandas/tests/test_array.py
index 79d2d64f..94d7f50b 100644
--- a/geopandas/tests/test_array.py
+++ b/geopandas/tests/test_array.py
@@ -479,6 +479,22 @@ def test_unary_predicates(attr):
     assert result.tolist() == expected
 
 
+def test_is_ring():
+    g = [
+        shapely.geometry.LinearRing([(0, 0), (1, 1), (1, -1)]),
+        shapely.geometry.LineString([(0, 0), (1, 1), (1, -1)]),
+        shapely.geometry.LineString([(0, 0), (1, 1), (1, -1), (0, 0)]),
+        shapely.geometry.Polygon([(0, 0), (1, 1), (1, -1)]),
+        shapely.geometry.Polygon(),
+        None,
+    ]
+    expected = [True, False, True, True, False, False]
+
+    result = from_shapely(g).is_ring
+
+    assert result.tolist() == expected
+
+
 @pytest.mark.parametrize("attr", ["area", "length"])
 def test_unary_float(attr):
     na_value = np.nan
diff --git a/geopandas/tests/test_crs.py b/geopandas/tests/test_crs.py
index 2936d00b..a804d4b7 100644
--- a/geopandas/tests/test_crs.py
+++ b/geopandas/tests/test_crs.py
@@ -49,6 +49,15 @@ def test_to_crs_transform():
     assert_geodataframe_equal(df, utm, check_less_precise=True)
 
 
+def test_to_crs_transform__missing_data():
+    # https://github.com/geopandas/geopandas/issues/1573
+    df = df_epsg26918()
+    df.loc[3, "geometry"] = None
+    lonlat = df.to_crs(epsg=4326)
+    utm = lonlat.to_crs(epsg=26918)
+    assert_geodataframe_equal(df, utm, check_less_precise=True)
+
+
 def test_to_crs_inplace():
     df = df_epsg26918()
     lonlat = df.to_crs(epsg=4326)
diff --git a/geopandas/tests/test_geocode.py b/geopandas/tests/test_geocode.py
index dd5de8a3..ec8740cc 100644
--- a/geopandas/tests/test_geocode.py
+++ b/geopandas/tests/test_geocode.py
@@ -1,4 +1,3 @@
-import numpy as np
 import pandas as pd
 
 from shapely.geometry import Point
@@ -9,6 +8,7 @@ from geopandas.tools.geocoding import _prepare_geocode_result
 
 from geopandas.tests.util import assert_geoseries_equal, mock
 from pandas.testing import assert_series_equal
+from geopandas.testing import assert_geodataframe_equal
 import pytest
 
 geopy = pytest.importorskip("geopy")
@@ -106,7 +106,21 @@ def test_prepare_result_none():
     # TODO we should probably replace this with a missing value instead of point?
     # assert len(row["geometry"].coords) == 0
     assert row["geometry"].is_empty
-    assert np.isnan(row["address"])
+    assert row["address"] is None
+
+
+@pytest.mark.parametrize("geocode_result", (None, (None, None)))
+def test_prepare_geocode_result_when_result_is(geocode_result):
+
+    result = {0: geocode_result}
+    expected_output = GeoDataFrame(
+        {"geometry": [Point()], "address": [None]},
+        crs="EPSG:4326",
+    )
+
+    output = _prepare_geocode_result(result)
+
+    assert_geodataframe_equal(output, expected_output)
 
 
 def test_bad_provider_forward():
diff --git a/geopandas/tests/test_geodataframe.py b/geopandas/tests/test_geodataframe.py
index e28d5a3e..ed9518cb 100644
--- a/geopandas/tests/test_geodataframe.py
+++ b/geopandas/tests/test_geodataframe.py
@@ -209,6 +209,13 @@ class TestDataFrame:
         assert df2 is None
         assert self.df.geometry.name == "new_name"
 
+        # existing column error
+        msg = "Column named Shape_Area already exists"
+        with pytest.raises(ValueError, match=msg):
+            df2 = self.df.rename_geometry("Shape_Area")
+        with pytest.raises(ValueError, match=msg):
+            self.df.rename_geometry("Shape_Area", inplace=True)
+
     def test_set_geometry(self):
         geom = GeoSeries([Point(x, y) for x, y in zip(range(5), range(5))])
         original_geom = self.df.geometry
diff --git a/geopandas/tests/test_pandas_methods.py b/geopandas/tests/test_pandas_methods.py
index 354a6373..7d9ac56a 100644
--- a/geopandas/tests/test_pandas_methods.py
+++ b/geopandas/tests/test_pandas_methods.py
@@ -512,3 +512,9 @@ def test_apply_loc_len1(df):
     result = subset.apply(lambda geom: geom.is_empty)
     expected = subset.is_empty
     np.testing.assert_allclose(result, expected)
+
+
+def test_apply_convert_dtypes_keyword(s):
+    # ensure the convert_dtypes keyword is accepted
+    res = s.apply(lambda x: x, convert_dtype=True, args=())
+    assert_geoseries_equal(res, s)