mirror of
https://github.com/wassname/geopandas.git
synced 2026-09-11 12:10:59 +08:00
Merge remote-tracking branch 'upstream/main' into pr/wassname/416-2
This commit is contained in:
@@ -37,7 +37,7 @@ jobs:
|
||||
env:
|
||||
- ci/envs/37-minimal.yaml
|
||||
- ci/envs/38-no-optional-deps.yaml
|
||||
- ci/envs/37-pd10.yaml
|
||||
- ci/envs/37-pd11.yaml
|
||||
- ci/envs/37-latest-defaults.yaml
|
||||
- ci/envs/37-latest-conda-forge.yaml
|
||||
- ci/envs/38-latest-conda-forge.yaml
|
||||
|
||||
@@ -6,9 +6,10 @@ dependencies:
|
||||
- python=3.7
|
||||
# required
|
||||
- numpy=1.18
|
||||
- pandas==0.25
|
||||
- shapely=1.6
|
||||
- fiona=1.8.13
|
||||
- pandas==1.0.5
|
||||
- shapely=1.7
|
||||
- fiona=1.8.13.post1
|
||||
- pyproj=2.6.1.post1
|
||||
- packaging
|
||||
#- pyproj
|
||||
# testing
|
||||
@@ -19,12 +20,9 @@ dependencies:
|
||||
# optional
|
||||
- rtree
|
||||
- matplotlib
|
||||
- matplotlib=3.1
|
||||
# - mapclassify=2.4.0 - doesn't build due to conflicts
|
||||
- matplotlib=3.2
|
||||
- mapclassify=2.4.0
|
||||
- geopy
|
||||
- SQLalchemy
|
||||
- libspatialite
|
||||
- pyarrow
|
||||
- pip
|
||||
- pip:
|
||||
- pyproj==2.2.2
|
||||
|
||||
@@ -4,10 +4,11 @@ channels:
|
||||
dependencies:
|
||||
- python=3.7
|
||||
# required
|
||||
- pandas=1.0
|
||||
- pandas=1.1
|
||||
- shapely
|
||||
- fiona
|
||||
- numpy=<1.19
|
||||
- pyproj=3.1.0
|
||||
#- pyproj
|
||||
- geos
|
||||
- packaging
|
||||
@@ -24,7 +25,6 @@ dependencies:
|
||||
- libspatialite
|
||||
- pip
|
||||
- pip:
|
||||
- pyproj==3.0.1
|
||||
- geopy
|
||||
- mapclassify==2.4.0
|
||||
- pyarrow
|
||||
@@ -138,10 +138,10 @@ Dependencies
|
||||
Required dependencies:
|
||||
|
||||
- `numpy`_
|
||||
- `pandas`_ (version 0.25 or later)
|
||||
- `shapely`_ (interface to `GEOS`_)
|
||||
- `fiona`_ (interface to `GDAL`_)
|
||||
- `pyproj`_ (interface to `PROJ`_; version 2.2.0 or later)
|
||||
- `pandas`_ (version 1.0 or later)
|
||||
- `shapely`_ (interface to `GEOS`_; version 1.7 or later)
|
||||
- `fiona`_ (interface to `GDAL`_; version 1.8 or later)
|
||||
- `pyproj`_ (interface to `PROJ`_; version 2.6.1 or later)
|
||||
- `packaging`_
|
||||
|
||||
Further, optional dependencies are:
|
||||
@@ -155,7 +155,7 @@ Further, optional dependencies are:
|
||||
|
||||
For plotting, these additional packages may be used:
|
||||
|
||||
- `matplotlib`_ (>= 3.1.0)
|
||||
- `matplotlib`_ (>= 3.2.0)
|
||||
- `mapclassify`_ (>= 2.4.0)
|
||||
|
||||
|
||||
|
||||
+7
-7
@@ -5,10 +5,10 @@ dependencies:
|
||||
- python
|
||||
# required
|
||||
- fiona>=1.8
|
||||
- pandas>=0.25
|
||||
- pandas>=1.0.0
|
||||
- pygeos
|
||||
- pyproj>=2.2.0
|
||||
- shapely>=1.6
|
||||
- pyproj>=2.6.1.post1
|
||||
- shapely>=1.7
|
||||
- packaging
|
||||
|
||||
# testing
|
||||
@@ -33,10 +33,10 @@ dependencies:
|
||||
# geocoding
|
||||
- geopy
|
||||
# geodatabase access
|
||||
- psycopg2>=2.5.1
|
||||
- SQLAlchemy>=0.8.3
|
||||
- psycopg2>=2.8.0
|
||||
- SQLAlchemy>=1.3
|
||||
# plotting
|
||||
- matplotlib>=2.2
|
||||
- matplotlib>=3.2
|
||||
- mapclassify
|
||||
# spatial access methods
|
||||
- rtree>=0.8
|
||||
- rtree>=0.9
|
||||
|
||||
@@ -15,7 +15,6 @@ import shapely.geos
|
||||
# pandas compat
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
PANDAS_GE_10 = Version(pd.__version__) >= Version("1.0.0")
|
||||
PANDAS_GE_11 = Version(pd.__version__) >= Version("1.1.0")
|
||||
PANDAS_GE_115 = Version(pd.__version__) >= Version("1.1.5")
|
||||
PANDAS_GE_12 = Version(pd.__version__) >= Version("1.2.0")
|
||||
@@ -28,7 +27,6 @@ PANDAS_GE_14 = Version(pd.__version__) >= Version("1.4.0rc0")
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
SHAPELY_GE_17 = Version(shapely.__version__) >= Version("1.7.0")
|
||||
SHAPELY_GE_18 = Version(shapely.__version__) >= Version("1.8")
|
||||
SHAPELY_GE_20 = Version(shapely.__version__) >= Version("2.0")
|
||||
|
||||
@@ -222,9 +220,11 @@ try:
|
||||
except ImportError:
|
||||
HAS_RTREE = False
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# pyproj compat
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
PYPROJ_LT_3 = Version(pyproj.__version__) < Version("3")
|
||||
PYPROJ_GE_31 = Version(pyproj.__version__) >= Version("3.1")
|
||||
PYPROJ_GE_32 = Version(pyproj.__version__) >= Version("3.2")
|
||||
|
||||
@@ -56,7 +56,7 @@ def isna(value):
|
||||
return True
|
||||
elif isinstance(value, float) and np.isnan(value):
|
||||
return True
|
||||
elif compat.PANDAS_GE_10 and value is pd.NA:
|
||||
elif value is pd.NA:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
+13
-20
@@ -1,4 +1,3 @@
|
||||
from collections.abc import Iterable
|
||||
import numbers
|
||||
import operator
|
||||
import warnings
|
||||
@@ -359,24 +358,19 @@ class GeometryArray(ExtensionArray):
|
||||
if isinstance(idx, numbers.Integral):
|
||||
return _geom_to_shapely(self.data[idx])
|
||||
# array-like, slice
|
||||
if compat.PANDAS_GE_10:
|
||||
# for pandas >= 1.0, validate and convert IntegerArray/BooleanArray
|
||||
# to numpy array, pass-through non-array-like indexers
|
||||
idx = pd.api.indexers.check_array_indexer(self, idx)
|
||||
return GeometryArray(self.data[idx], crs=self.crs)
|
||||
else:
|
||||
if isinstance(idx, (Iterable, slice)):
|
||||
return GeometryArray(self.data[idx], crs=self.crs)
|
||||
else:
|
||||
raise TypeError("Index type not supported", idx)
|
||||
# validate and convert IntegerArray/BooleanArray
|
||||
# to numpy array, pass-through non-array-like indexers
|
||||
idx = pd.api.indexers.check_array_indexer(self, idx)
|
||||
return GeometryArray(self.data[idx], crs=self.crs)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if compat.PANDAS_GE_10:
|
||||
# for pandas >= 1.0, validate and convert IntegerArray/BooleanArray
|
||||
# keys to numpy array, pass-through non-array-like indexers
|
||||
key = pd.api.indexers.check_array_indexer(self, key)
|
||||
# validate and convert IntegerArray/BooleanArray
|
||||
# keys to numpy array, pass-through non-array-like indexers
|
||||
key = pd.api.indexers.check_array_indexer(self, key)
|
||||
if isinstance(value, pd.Series):
|
||||
value = value.values
|
||||
if isinstance(value, pd.DataFrame):
|
||||
value = value.values.flatten()
|
||||
if isinstance(value, (list, np.ndarray)):
|
||||
value = from_shapely(value)
|
||||
if isinstance(value, GeometryArray):
|
||||
@@ -1054,11 +1048,10 @@ class GeometryArray(ExtensionArray):
|
||||
dtype
|
||||
):
|
||||
string_values = to_wkt(self)
|
||||
if compat.PANDAS_GE_10:
|
||||
pd_dtype = pd.api.types.pandas_dtype(dtype)
|
||||
if isinstance(pd_dtype, pd.StringDtype):
|
||||
# ensure to return a pandas string array instead of numpy array
|
||||
return pd.array(string_values, dtype=pd_dtype)
|
||||
pd_dtype = pd.api.types.pandas_dtype(dtype)
|
||||
if isinstance(pd_dtype, pd.StringDtype):
|
||||
# ensure to return a pandas string array instead of numpy array
|
||||
return pd.array(string_values, dtype=pd_dtype)
|
||||
return string_values.astype(dtype, copy=False)
|
||||
else:
|
||||
return np.array(self, dtype=dtype, copy=copy)
|
||||
|
||||
@@ -1773,11 +1773,6 @@ individually so that features may have different properties
|
||||
# Overridden to fix GH1870, that return type is not preserved always
|
||||
# (and where it was, geometry col was not)
|
||||
|
||||
if not compat.PANDAS_GE_10:
|
||||
raise NotImplementedError(
|
||||
"GeoDataFrame.convert_dtypes requires pandas >= 1.0"
|
||||
)
|
||||
|
||||
return GeoDataFrame(
|
||||
super().convert_dtypes(*args, **kwargs),
|
||||
geometry=self.geometry.name,
|
||||
@@ -2249,5 +2244,5 @@ def _dataframe_set_geometry(self, col, drop=False, inplace=False, crs=None):
|
||||
|
||||
DataFrame.set_geometry = _dataframe_set_geometry
|
||||
|
||||
if compat.PANDAS_GE_10 and not compat.PANDAS_GE_11: # i.e. on pandas 1.0.x
|
||||
if not compat.PANDAS_GE_11: # i.e. on pandas 1.0.x
|
||||
_geodataframe_constructor_with_fallback._from_axes = GeoDataFrame._from_axes
|
||||
|
||||
+1
-9
@@ -369,15 +369,7 @@ def _write_postgis(
|
||||
try:
|
||||
from geoalchemy2 import Geometry
|
||||
except ImportError:
|
||||
raise ImportError("'to_postgis()' requires geoalchemy2 package. ")
|
||||
|
||||
if not compat.SHAPELY_GE_17:
|
||||
raise ImportError(
|
||||
"'to_postgis()' requires newer version of Shapely "
|
||||
"(>= '1.7.0').\nYou can update the library using "
|
||||
"'pip install shapely --upgrade' or using "
|
||||
"'conda update shapely' if using conda package manager."
|
||||
)
|
||||
raise ImportError("'to_postgis()' requires geoalchemy2 package.")
|
||||
|
||||
gdf = gdf.copy()
|
||||
geom_name = gdf.geometry.name
|
||||
|
||||
@@ -71,7 +71,7 @@ def _expand_kwargs(kwargs, multiindex):
|
||||
from typing import Iterable
|
||||
|
||||
mpl = Version(matplotlib.__version__)
|
||||
if mpl >= Version("3.4") or (mpl > Version("3.3.2") and "+" in mpl):
|
||||
if mpl >= Version("3.4"):
|
||||
# alpha is supported as array argument with matplotlib 3.4+
|
||||
scalar_kwargs = ["marker", "path_effects"]
|
||||
else:
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from geopandas._compat import PANDAS_GE_10
|
||||
|
||||
|
||||
def test_no_additional_imports():
|
||||
# test that 'import geopandas' does not import any of the optional or
|
||||
@@ -20,10 +18,8 @@ def test_no_additional_imports():
|
||||
"psycopg2",
|
||||
"geopy",
|
||||
"geoalchemy2",
|
||||
"matplotlib",
|
||||
}
|
||||
if PANDAS_GE_10:
|
||||
# pandas > 0.25 stopped importing matplotlib by default
|
||||
blacklist.add("matplotlib")
|
||||
|
||||
code = """
|
||||
import sys
|
||||
|
||||
@@ -144,9 +144,7 @@ def test_from_wkb():
|
||||
missing_values = [None]
|
||||
if not compat.USE_PYGEOS:
|
||||
missing_values.extend([b"", np.nan])
|
||||
|
||||
if compat.PANDAS_GE_10:
|
||||
missing_values.append(pd.NA)
|
||||
missing_values.append(pd.NA)
|
||||
|
||||
res = from_wkb(missing_values)
|
||||
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
|
||||
@@ -220,9 +218,7 @@ def test_from_wkt(string_type):
|
||||
missing_values = [None]
|
||||
if not compat.USE_PYGEOS:
|
||||
missing_values.extend([f(""), np.nan])
|
||||
|
||||
if compat.PANDAS_GE_10:
|
||||
missing_values.append(pd.NA)
|
||||
missing_values.append(pd.NA)
|
||||
|
||||
res = from_wkb(missing_values)
|
||||
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
|
||||
@@ -436,10 +432,6 @@ def test_binary_geo_scalar(attr):
|
||||
"is_ring",
|
||||
marks=[
|
||||
pytest.mark.filterwarnings("ignore:is_ring:FutureWarning"),
|
||||
pytest.mark.skipif(
|
||||
not compat.SHAPELY_GE_17,
|
||||
reason="is_ring on empty Polygon doesn't work in Shapely 1.6",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -482,10 +474,6 @@ def test_unary_predicates(attr):
|
||||
|
||||
# for is_ring we raise a warning about the value for Polygon changing
|
||||
@pytest.mark.filterwarnings("ignore:is_ring:FutureWarning")
|
||||
@pytest.mark.skipif(
|
||||
not compat.SHAPELY_GE_17,
|
||||
reason="is_ring on empty Polygon doesn't work in Shapely 1.6",
|
||||
)
|
||||
def test_is_ring():
|
||||
g = [
|
||||
shapely.geometry.LinearRing([(0, 0), (1, 1), (1, -1)]),
|
||||
@@ -766,6 +754,29 @@ def test_getitem():
|
||||
assert P5.equals(points[1])
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"item",
|
||||
[
|
||||
geopandas.GeoDataFrame(
|
||||
geometry=[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]
|
||||
),
|
||||
geopandas.GeoSeries(
|
||||
[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]
|
||||
),
|
||||
np.array([shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]),
|
||||
[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])],
|
||||
shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
|
||||
],
|
||||
)
|
||||
def test_setitem(item):
|
||||
points = [shapely.geometry.Point(i, i) for i in range(10)]
|
||||
P = from_shapely(points)
|
||||
|
||||
P[[0]] = item
|
||||
|
||||
assert isinstance(P[0], shapely.geometry.Polygon)
|
||||
|
||||
|
||||
def test_equality_ops():
|
||||
with pytest.raises(ValueError):
|
||||
P[:5] == P[:7]
|
||||
@@ -886,7 +897,6 @@ def test_isna(NA):
|
||||
assert t1[0] is None
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.PANDAS_GE_10, reason="pd.NA introduced in pandas 1.0")
|
||||
def test_isna_pdNA():
|
||||
t1 = T.copy()
|
||||
t1[0] = pd.NA
|
||||
@@ -922,7 +932,8 @@ class TestEstimateUtmCrs:
|
||||
self.landmarks.estimate_utm_crs()
|
||||
else:
|
||||
assert self.landmarks.estimate_utm_crs() == CRS("EPSG:32618")
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
if compat.PYPROJ_GE_32: # result is unstable in older pyproj
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
|
||||
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
|
||||
def test_estimate_utm_crs__projected(self):
|
||||
|
||||
@@ -7,7 +7,6 @@ from packaging.version import Version
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import pyproj
|
||||
from pyproj import CRS
|
||||
from pyproj.exceptions import CRSError
|
||||
from shapely.geometry import Point, Polygon
|
||||
@@ -24,7 +23,6 @@ from pandas.testing import assert_frame_equal, assert_index_equal, assert_series
|
||||
import pytest
|
||||
|
||||
|
||||
PYPROJ_LT_3 = Version(pyproj.__version__) < Version("3")
|
||||
TEST_NEAREST = compat.PYGEOS_GE_010 and compat.USE_PYGEOS
|
||||
pandas_133 = Version(pd.__version__) == Version("1.3.3")
|
||||
|
||||
@@ -794,12 +792,13 @@ class TestDataFrame:
|
||||
assert self.df.crs == unpickled.crs
|
||||
|
||||
def test_estimate_utm_crs(self):
|
||||
if PYPROJ_LT_3:
|
||||
if compat.PYPROJ_LT_3:
|
||||
with pytest.raises(RuntimeError, match=r"pyproj 3\+ required"):
|
||||
self.df.estimate_utm_crs()
|
||||
else:
|
||||
assert self.df.estimate_utm_crs() == CRS("EPSG:32618")
|
||||
assert self.df.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
if compat.PYPROJ_GE_32: # result is unstable in older pyproj
|
||||
assert self.df.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
|
||||
def test_to_wkb(self):
|
||||
wkbs0 = [
|
||||
|
||||
@@ -21,7 +21,7 @@ from shapely.geometry import (
|
||||
from shapely.geometry.base import BaseGeometry
|
||||
|
||||
from geopandas import GeoSeries, GeoDataFrame, read_file, datasets, clip
|
||||
from geopandas._compat import PYPROJ_LT_3, ignore_shapely2_warnings
|
||||
from geopandas._compat import ignore_shapely2_warnings
|
||||
from geopandas.array import GeometryArray, GeometryDtype
|
||||
from geopandas.testing import assert_geoseries_equal
|
||||
|
||||
@@ -29,6 +29,8 @@ from geopandas.tests.util import geom_equals
|
||||
from pandas.testing import assert_series_equal
|
||||
import pytest
|
||||
|
||||
import geopandas._compat as compat
|
||||
|
||||
|
||||
class TestSeries:
|
||||
def setup_method(self):
|
||||
@@ -205,27 +207,28 @@ class TestSeries:
|
||||
self.landmarks.to_crs(crs=None, epsg=None)
|
||||
|
||||
def test_estimate_utm_crs__geographic(self):
|
||||
if PYPROJ_LT_3:
|
||||
if compat.PYPROJ_LT_3:
|
||||
with pytest.raises(RuntimeError, match=r"pyproj 3\+ required"):
|
||||
self.landmarks.estimate_utm_crs()
|
||||
else:
|
||||
assert self.landmarks.estimate_utm_crs() == CRS("EPSG:32618")
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
if compat.PYPROJ_GE_32: # result is unstable in older pyproj
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
|
||||
@pytest.mark.skipif(PYPROJ_LT_3, reason="requires pyproj 3 or higher")
|
||||
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
|
||||
def test_estimate_utm_crs__projected(self):
|
||||
assert self.landmarks.to_crs("EPSG:3857").estimate_utm_crs() == CRS(
|
||||
"EPSG:32618"
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(PYPROJ_LT_3, reason="requires pyproj 3 or higher")
|
||||
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
|
||||
def test_estimate_utm_crs__out_of_bounds(self):
|
||||
with pytest.raises(RuntimeError, match="Unable to determine UTM CRS"):
|
||||
GeoSeries(
|
||||
[Polygon([(0, 90), (1, 90), (2, 90)])], crs="EPSG:4326"
|
||||
).estimate_utm_crs()
|
||||
|
||||
@pytest.mark.skipif(PYPROJ_LT_3, reason="requires pyproj 3 or higher")
|
||||
@pytest.mark.skipif(compat.PYPROJ_LT_3, reason="requires pyproj 3 or higher")
|
||||
def test_estimate_utm_crs__missing_crs(self):
|
||||
with pytest.raises(RuntimeError, match="crs must be set"):
|
||||
GeoSeries([Polygon([(0, 90), (1, 90), (2, 90)])]).estimate_utm_crs()
|
||||
|
||||
@@ -263,11 +263,6 @@ def test_astype_invalid_geodataframe():
|
||||
assert res["a"].dtype == object
|
||||
|
||||
|
||||
@pytest.mark.xfail(
|
||||
not compat.PANDAS_GE_10,
|
||||
reason="Convert dtypes new in pandas 1.0",
|
||||
raises=NotImplementedError,
|
||||
)
|
||||
def test_convert_dtypes(df):
|
||||
# https://github.com/geopandas/geopandas/issues/1870
|
||||
|
||||
@@ -662,7 +657,6 @@ def test_df_apply_returning_series(df):
|
||||
assert_series_equal(result, df["value1"].rename(None))
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.PANDAS_GE_10, reason="attrs introduced in pandas 1.0")
|
||||
def test_preserve_attrs(df):
|
||||
# https://github.com/geopandas/geopandas/issues/1654
|
||||
df.attrs["name"] = "my_name"
|
||||
|
||||
@@ -1384,9 +1384,6 @@ class TestPlotCollections:
|
||||
)
|
||||
|
||||
def test_points(self):
|
||||
# failing with matplotlib 1.4.3 (edge stays black even when specified)
|
||||
pytest.importorskip("matplotlib", "1.5.0")
|
||||
|
||||
from geopandas.plotting import _plot_point_collection, plot_point_collection
|
||||
from matplotlib.collections import PathCollection
|
||||
|
||||
@@ -1843,11 +1840,6 @@ def _get_ax(fig, label):
|
||||
Previously, we did `fig.axes[1]`, but in matplotlib 3.4 the order switched
|
||||
and the colorbar ax was first and subplot ax second.
|
||||
"""
|
||||
if Version(matplotlib.__version__) < Version("3.0.0"):
|
||||
if label == "<colorbar>":
|
||||
return fig.axes[1]
|
||||
elif label == "":
|
||||
return fig.axes[0]
|
||||
for ax in fig.axes:
|
||||
if ax.get_label() == label:
|
||||
return ax
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# required
|
||||
fiona>=1.8
|
||||
pandas>=0.25
|
||||
pyproj>=2.2.0
|
||||
shapely>=1.6
|
||||
pandas>=1.0.0
|
||||
pyproj>=2.6.1.post1
|
||||
shapely>=1.7
|
||||
packaging
|
||||
|
||||
# geodatabase access
|
||||
psycopg2>=2.5.1
|
||||
SQLAlchemy>=0.8.3
|
||||
psycopg2>=2.8.0
|
||||
SQLAlchemy>=1.3
|
||||
|
||||
# geocoding
|
||||
geopy
|
||||
|
||||
# plotting
|
||||
matplotlib>=2.2
|
||||
matplotlib>=3.2
|
||||
mapclassify
|
||||
|
||||
# testing
|
||||
@@ -22,7 +22,7 @@ pytest-cov
|
||||
codecov
|
||||
|
||||
# spatial access methods
|
||||
rtree>=0.8
|
||||
rtree>=0.9
|
||||
|
||||
# styling
|
||||
black
|
||||
|
||||
Reference in New Issue
Block a user