venv
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +1,3 @@
|
||||
{
|
||||
"type": "FeatureCollection",
|
||||
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
|
||||
|
||||
"features": [
|
||||
{ "type": "Feature", "properties": { "Name": "Null Geometry" }, "geometry": null },
|
||||
{ "type": "Feature", "properties": { "Name": "SF to NY" }, "geometry": { "type": "LineString", "coordinates": [ [ -122.4051293283311, 37.786780113640894 ], [ -73.859832357849271, 40.487594916296196 ] ] } }
|
||||
]
|
||||
}
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5e87f89afda555a1b1d43d9dc12864169d6d0149a4f222be12d40a6a86ad8066
|
||||
size 506
|
||||
|
||||
@@ -13,8 +13,8 @@ def test_no_additional_imports():
|
||||
# "fiona",
|
||||
# "matplotlib", # matplotlib gets imported by pandas, see below
|
||||
"mapclassify",
|
||||
# 'rtree', # rtree actually gets imported if installed
|
||||
"sqlalchemy",
|
||||
"psycopg",
|
||||
"psycopg2",
|
||||
"geopy",
|
||||
"geoalchemy2",
|
||||
@@ -34,5 +34,5 @@ if mods:
|
||||
blacklist
|
||||
)
|
||||
call = [sys.executable, "-c", code]
|
||||
returncode = subprocess.run(call).returncode
|
||||
returncode = subprocess.run(call, check=False).returncode
|
||||
assert returncode == 0
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
import random
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from pyproj import CRS
|
||||
import shapely
|
||||
import shapely.affinity
|
||||
import shapely.geometry
|
||||
from shapely.geometry.base import CAP_STYLE, JOIN_STYLE, BaseGeometry
|
||||
import shapely.wkb
|
||||
import shapely.wkt
|
||||
|
||||
try:
|
||||
from shapely import geos_version
|
||||
except ImportError:
|
||||
from shapely._buildcfg import geos_version
|
||||
from shapely import geos_version
|
||||
from shapely.geometry.base import CAP_STYLE, JOIN_STYLE
|
||||
|
||||
import geopandas
|
||||
from geopandas._compat import HAS_PYPROJ
|
||||
from geopandas.array import (
|
||||
GeometryArray,
|
||||
_check_crs,
|
||||
_crs_mismatch_warn,
|
||||
from_shapely,
|
||||
from_wkb,
|
||||
from_wkt,
|
||||
points_from_xy,
|
||||
to_wkb,
|
||||
to_wkt,
|
||||
_check_crs,
|
||||
_crs_mismatch_warn,
|
||||
)
|
||||
import geopandas._compat as compat
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -143,11 +139,8 @@ def test_from_wkb():
|
||||
assert all(v.equals(t) for v, t in zip(res, points_no_missing))
|
||||
|
||||
# missing values
|
||||
# TODO(pygeos) does not support empty strings, np.nan, or pd.NA
|
||||
# TODO(shapely) does not support empty strings, np.nan, or pd.NA
|
||||
missing_values = [None]
|
||||
if not (compat.USE_SHAPELY_20 or compat.USE_PYGEOS):
|
||||
missing_values.extend([b"", np.nan])
|
||||
missing_values.append(pd.NA)
|
||||
|
||||
res = from_wkb(missing_values)
|
||||
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
|
||||
@@ -170,6 +163,24 @@ def test_from_wkb_hex():
|
||||
assert isinstance(res, GeometryArray)
|
||||
|
||||
|
||||
def test_from_wkb_on_invalid():
|
||||
# Single point LineString hex WKB: invalid
|
||||
invalid_wkb_hex = "01020000000100000000000000000008400000000000000840"
|
||||
message = "point array must contain 0 or >1 elements"
|
||||
|
||||
with pytest.raises(Exception, match=message):
|
||||
from_wkb([invalid_wkb_hex], on_invalid="raise")
|
||||
|
||||
with pytest.warns(Warning, match=message):
|
||||
res = from_wkb([invalid_wkb_hex], on_invalid="warn")
|
||||
assert res == [None]
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
res = from_wkb([invalid_wkb_hex], on_invalid="ignore")
|
||||
assert res == [None]
|
||||
|
||||
|
||||
def test_to_wkb():
|
||||
P = from_shapely(points_no_missing)
|
||||
res = to_wkb(P)
|
||||
@@ -211,13 +222,10 @@ def test_from_wkt(string_type):
|
||||
assert all(v.equals_exact(t, tolerance=tol) for v, t in zip(res, points_no_missing))
|
||||
|
||||
# missing values
|
||||
# TODO(pygeos) does not support empty strings, np.nan, or pd.NA
|
||||
# TODO(shapely) does not support empty strings, np.nan, or pd.NA
|
||||
missing_values = [None]
|
||||
if not (compat.USE_SHAPELY_20 or compat.USE_PYGEOS):
|
||||
missing_values.extend([f(""), np.nan])
|
||||
missing_values.append(pd.NA)
|
||||
|
||||
res = from_wkb(missing_values)
|
||||
res = from_wkt(missing_values)
|
||||
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
|
||||
|
||||
# single MultiPolygon
|
||||
@@ -228,6 +236,24 @@ def test_from_wkt(string_type):
|
||||
assert res[0] == multi_poly
|
||||
|
||||
|
||||
def test_from_wkt_on_invalid():
|
||||
# Single point LineString WKT: invalid
|
||||
invalid_wkt = "LINESTRING(0 0)"
|
||||
message = "point array must contain 0 or >1 elements"
|
||||
|
||||
with pytest.raises(Exception, match=message):
|
||||
from_wkt([invalid_wkt], on_invalid="raise")
|
||||
|
||||
with pytest.warns(Warning, match=message):
|
||||
res = from_wkt([invalid_wkt], on_invalid="warn")
|
||||
assert res == [None]
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
res = from_wkt([invalid_wkt], on_invalid="ignore")
|
||||
assert res == [None]
|
||||
|
||||
|
||||
def test_to_wkt():
|
||||
P = from_shapely(points_no_missing)
|
||||
res = to_wkt(P, rounding_precision=-1)
|
||||
@@ -241,22 +267,6 @@ def test_to_wkt():
|
||||
assert res[0] is None
|
||||
|
||||
|
||||
def test_data():
|
||||
arr = from_shapely(points_no_missing)
|
||||
with pytest.warns(DeprecationWarning):
|
||||
np_arr = arr.data
|
||||
|
||||
assert isinstance(np_arr, np.ndarray)
|
||||
if compat.USE_PYGEOS:
|
||||
np_arr2 = arr.to_numpy()
|
||||
assert isinstance(np_arr2[0], BaseGeometry)
|
||||
np_arr3 = np.asarray(arr)
|
||||
assert isinstance(np_arr3[0], BaseGeometry)
|
||||
else:
|
||||
assert arr.to_numpy() is np_arr
|
||||
assert np.asarray(arr) is np_arr
|
||||
|
||||
|
||||
def test_as_array():
|
||||
arr = from_shapely(points_no_missing)
|
||||
np_arr1 = np.asarray(arr)
|
||||
@@ -281,6 +291,9 @@ def test_as_array():
|
||||
("geom_almost_equals", (3,)),
|
||||
],
|
||||
)
|
||||
# filters required for attr=geom_almost_equals only
|
||||
@pytest.mark.filterwarnings(r"ignore:The \'geom_almost_equals\(\)\' method is deprecat")
|
||||
@pytest.mark.filterwarnings(r"ignore:The \'almost_equals\(\)\' method is deprecated")
|
||||
def test_predicates_vector_scalar(attr, args):
|
||||
na_value = False
|
||||
|
||||
@@ -293,9 +306,11 @@ def test_predicates_vector_scalar(attr, args):
|
||||
assert result.dtype == bool
|
||||
|
||||
expected = [
|
||||
getattr(tri, attr if "geom" not in attr else attr[5:])(other, *args)
|
||||
if tri is not None
|
||||
else na_value
|
||||
(
|
||||
getattr(tri, attr if "geom" not in attr else attr[5:])(other, *args)
|
||||
if tri is not None
|
||||
else na_value
|
||||
)
|
||||
for tri in triangles
|
||||
]
|
||||
|
||||
@@ -320,6 +335,9 @@ def test_predicates_vector_scalar(attr, args):
|
||||
("geom_almost_equals", (3,)),
|
||||
],
|
||||
)
|
||||
# filters required for attr=geom_almost_equals only
|
||||
@pytest.mark.filterwarnings(r"ignore:The \'geom_almost_equals\(\)\' method is deprecat")
|
||||
@pytest.mark.filterwarnings(r"ignore:The \'almost_equals\(\)\' method is deprecated")
|
||||
def test_predicates_vector_vector(attr, args):
|
||||
na_value = False
|
||||
empty_value = True if attr == "disjoint" else False
|
||||
@@ -449,17 +467,12 @@ def test_binary_geo_scalar(attr):
|
||||
"is_simple",
|
||||
"has_z",
|
||||
# for is_ring we raise a warning about the value for Polygon changing
|
||||
pytest.param(
|
||||
"is_ring",
|
||||
marks=[
|
||||
pytest.mark.filterwarnings("ignore:is_ring:FutureWarning"),
|
||||
],
|
||||
),
|
||||
"is_ring",
|
||||
],
|
||||
)
|
||||
def test_unary_predicates(attr):
|
||||
na_value = False
|
||||
if attr == "is_simple" and geos_version < (3, 8) and not compat.USE_PYGEOS:
|
||||
if attr == "is_simple" and geos_version < (3, 8):
|
||||
# poly.is_simple raises an error for empty polygon for GEOS < 3.8
|
||||
with pytest.raises(Exception): # noqa: B017
|
||||
T.is_simple
|
||||
@@ -471,40 +484,17 @@ def test_unary_predicates(attr):
|
||||
|
||||
result = getattr(V, attr)
|
||||
|
||||
if attr == "is_simple" and geos_version < (3, 8):
|
||||
# poly.is_simple raises an error for empty polygon for GEOS < 3.8
|
||||
# with shapely, pygeos always returns False for all GEOS versions
|
||||
if attr == "is_ring":
|
||||
expected = [
|
||||
getattr(t, attr) if t is not None and not t.is_empty else na_value
|
||||
getattr(t, attr) if t is not None and t.exterior is not None else na_value
|
||||
for t in vals
|
||||
]
|
||||
elif attr == "is_ring":
|
||||
expected = [
|
||||
getattr(t.exterior, attr)
|
||||
if t is not None and t.exterior is not None
|
||||
else na_value
|
||||
for t in vals
|
||||
]
|
||||
# empty Linearring.is_ring gives False with Shapely < 2.0
|
||||
if compat.USE_PYGEOS and not compat.SHAPELY_GE_20:
|
||||
expected[-2] = True
|
||||
elif (
|
||||
attr == "is_closed"
|
||||
and compat.USE_PYGEOS
|
||||
and compat.SHAPELY_GE_182
|
||||
and not compat.SHAPELY_GE_20
|
||||
):
|
||||
# In shapely 1.8.2, is_closed was changed to return always True for
|
||||
# Polygon/MultiPolygon, while PyGEOS returns always False
|
||||
expected = [False] * len(vals)
|
||||
else:
|
||||
expected = [getattr(t, attr) if t is not None else na_value for t in vals]
|
||||
|
||||
assert result.tolist() == expected
|
||||
|
||||
|
||||
# for is_ring we raise a warning about the value for Polygon changing
|
||||
@pytest.mark.filterwarnings("ignore:is_ring:FutureWarning")
|
||||
def test_is_ring():
|
||||
g = [
|
||||
shapely.geometry.LinearRing([(0, 0), (1, 1), (1, -1)]),
|
||||
@@ -514,11 +504,7 @@ def test_is_ring():
|
||||
shapely.wkt.loads("POLYGON EMPTY"),
|
||||
None,
|
||||
]
|
||||
expected = [True, False, True, True, True, False]
|
||||
if not compat.USE_PYGEOS and not compat.SHAPELY_GE_20:
|
||||
# empty polygon is_ring gives False with Shapely < 2.0
|
||||
expected[-2] = False
|
||||
|
||||
expected = [True, False, True, False, False, False]
|
||||
result = from_shapely(g).is_ring
|
||||
|
||||
assert result.tolist() == expected
|
||||
@@ -561,9 +547,11 @@ def test_binary_distance():
|
||||
# vector - vector
|
||||
result = P[: len(T)].distance(T[::-1])
|
||||
expected = [
|
||||
getattr(p, attr)(t)
|
||||
if not ((t is None or t.is_empty) or (p is None or p.is_empty))
|
||||
else na_value
|
||||
(
|
||||
getattr(p, attr)(t)
|
||||
if not ((t is None or t.is_empty) or (p is None or p.is_empty))
|
||||
else na_value
|
||||
)
|
||||
for t, p in zip(triangles[::-1], points)
|
||||
]
|
||||
np.testing.assert_allclose(result, expected)
|
||||
@@ -620,9 +608,11 @@ def test_binary_project(normalized):
|
||||
|
||||
result = L.project(P, normalized=normalized)
|
||||
expected = [
|
||||
line.project(p, normalized=normalized)
|
||||
if line is not None and p is not None
|
||||
else na_value
|
||||
(
|
||||
line.project(p, normalized=normalized)
|
||||
if line is not None and p is not None
|
||||
else na_value
|
||||
)
|
||||
for p, line in zip(points, lines)
|
||||
]
|
||||
np.testing.assert_allclose(result, expected)
|
||||
@@ -632,16 +622,15 @@ def test_binary_project(normalized):
|
||||
@pytest.mark.parametrize("join_style", [JOIN_STYLE.round, JOIN_STYLE.bevel])
|
||||
@pytest.mark.parametrize("resolution", [16, 25])
|
||||
def test_buffer(resolution, cap_style, join_style):
|
||||
if compat.USE_PYGEOS:
|
||||
# TODO(pygeos) need to further investigate why this test fails
|
||||
if cap_style == 1 and join_style == 3:
|
||||
pytest.skip("failing TODO")
|
||||
|
||||
na_value = None
|
||||
expected = [
|
||||
p.buffer(0.1, resolution=resolution, cap_style=cap_style, join_style=join_style)
|
||||
if p is not None
|
||||
else na_value
|
||||
(
|
||||
p.buffer(
|
||||
0.1, resolution=resolution, cap_style=cap_style, join_style=join_style
|
||||
)
|
||||
if p is not None
|
||||
else na_value
|
||||
)
|
||||
for p in points
|
||||
]
|
||||
result = P.buffer(
|
||||
@@ -676,10 +665,32 @@ def test_unary_union():
|
||||
shapely.geometry.Polygon([(0, 0), (1, 0), (1, 1)]),
|
||||
]
|
||||
G = from_shapely(geoms)
|
||||
u = G.unary_union()
|
||||
with pytest.warns(
|
||||
DeprecationWarning, match="The 'unary_union' attribute is deprecated"
|
||||
):
|
||||
u = G.unary_union()
|
||||
|
||||
expected = shapely.geometry.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
|
||||
assert u.equals(expected)
|
||||
assert u.equals(G.union_all())
|
||||
|
||||
|
||||
def test_union_all():
|
||||
geoms = [
|
||||
shapely.geometry.Polygon([(0, 0), (0, 1), (1, 1)]),
|
||||
shapely.geometry.Polygon([(0, 0), (1, 0), (1, 1)]),
|
||||
]
|
||||
G = from_shapely(geoms)
|
||||
u = G.union_all()
|
||||
|
||||
expected = shapely.geometry.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
|
||||
assert u.equals(expected)
|
||||
|
||||
u_cov = G.union_all(method="coverage")
|
||||
assert u_cov.equals(expected)
|
||||
|
||||
with pytest.raises(ValueError, match="Method 'invalid' not recognized."):
|
||||
G.union_all(method="invalid")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -810,7 +821,7 @@ def test_setitem(item):
|
||||
|
||||
def test_equality_ops():
|
||||
with pytest.raises(ValueError):
|
||||
P[:5] == P[:7]
|
||||
_ = P[:5] == P[:7]
|
||||
|
||||
a1 = from_shapely([points[1], points[2], points[3]])
|
||||
a2 = from_shapely([points[1], points[0], points[3]])
|
||||
@@ -833,7 +844,7 @@ def test_equality_ops():
|
||||
|
||||
def test_dir():
|
||||
assert "contains" in dir(P)
|
||||
assert "data" in dir(P)
|
||||
assert "to_numpy" in dir(P)
|
||||
|
||||
|
||||
def test_chaining():
|
||||
@@ -894,6 +905,7 @@ def test_astype_multipolygon():
|
||||
assert result[0] == multi_poly.wkt[:10]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not installed")
|
||||
def test_check_crs():
|
||||
t1 = T.copy()
|
||||
t1.crs = 4326
|
||||
@@ -902,6 +914,7 @@ def test_check_crs():
|
||||
assert _check_crs(t1, T, allow_none=True) is True
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not installed")
|
||||
def test_crs_mismatch_warn():
|
||||
t1 = T.copy()
|
||||
t2 = T.copy()
|
||||
@@ -921,6 +934,14 @@ def test_crs_mismatch_warn():
|
||||
_crs_mismatch_warn(t1, T)
|
||||
|
||||
|
||||
@pytest.mark.skipif(HAS_PYPROJ, reason="pyproj installed")
|
||||
def test_missing_pyproj():
|
||||
with pytest.warns(UserWarning, match="Cannot set the CRS, falling back to None"):
|
||||
t = T.copy()
|
||||
t.crs = 4326
|
||||
assert t.crs is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("NA", [None, np.nan])
|
||||
def test_isna(NA):
|
||||
t1 = T.copy()
|
||||
@@ -948,6 +969,24 @@ def test_unique_has_crs():
|
||||
assert t.unique().crs == t.crs
|
||||
|
||||
|
||||
@pytest.mark.skipif(HAS_PYPROJ, reason="pyproj installed")
|
||||
def test_to_crs_pyproj_error():
|
||||
t = T.copy()
|
||||
t.crs = 4326
|
||||
with pytest.raises(
|
||||
ImportError, match="The 'pyproj' package is required for to_crs"
|
||||
):
|
||||
t.to_crs(3857)
|
||||
|
||||
|
||||
@pytest.mark.skipif(HAS_PYPROJ, reason="pyproj installed")
|
||||
def test_estimate_utm_crs_pyproj_error():
|
||||
with pytest.raises(
|
||||
ImportError, match="The 'pyproj' package is required for estimate_utm_crs"
|
||||
):
|
||||
T.estimate_utm_crs()
|
||||
|
||||
|
||||
class TestEstimateUtmCrs:
|
||||
def setup_method(self):
|
||||
self.esb = shapely.geometry.Point(-73.9847, 40.7484)
|
||||
@@ -955,15 +994,21 @@ class TestEstimateUtmCrs:
|
||||
self.landmarks = from_shapely([self.esb, self.sol], crs="epsg:4326")
|
||||
|
||||
def test_estimate_utm_crs__geographic(self):
|
||||
assert self.landmarks.estimate_utm_crs() == CRS("EPSG:32618")
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
|
||||
assert self.landmarks.estimate_utm_crs() == pyproj.CRS("EPSG:32618")
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == pyproj.CRS("EPSG:26918")
|
||||
|
||||
def test_estimate_utm_crs__projected(self):
|
||||
assert self.landmarks.to_crs("EPSG:3857").estimate_utm_crs() == CRS(
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
|
||||
assert self.landmarks.to_crs("EPSG:3857").estimate_utm_crs() == pyproj.CRS(
|
||||
"EPSG:32618"
|
||||
)
|
||||
|
||||
def test_estimate_utm_crs__antimeridian(self):
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
|
||||
antimeridian = from_shapely(
|
||||
[
|
||||
shapely.geometry.Point(1722483.900174921, 5228058.6143420935),
|
||||
@@ -971,15 +1016,19 @@ class TestEstimateUtmCrs:
|
||||
],
|
||||
crs="EPSG:3851",
|
||||
)
|
||||
assert antimeridian.estimate_utm_crs() == CRS("EPSG:32760")
|
||||
assert antimeridian.estimate_utm_crs() == pyproj.CRS("EPSG:32760")
|
||||
|
||||
def test_estimate_utm_crs__out_of_bounds(self):
|
||||
pytest.importorskip("pyproj")
|
||||
|
||||
with pytest.raises(RuntimeError, match="Unable to determine UTM CRS"):
|
||||
from_shapely(
|
||||
[shapely.geometry.Polygon([(0, 90), (1, 90), (2, 90)])], crs="EPSG:4326"
|
||||
).estimate_utm_crs()
|
||||
|
||||
def test_estimate_utm_crs__missing_crs(self):
|
||||
pytest.importorskip("pyproj")
|
||||
|
||||
with pytest.raises(RuntimeError, match="crs must be set"):
|
||||
from_shapely(
|
||||
[shapely.geometry.Polygon([(0, 90), (1, 90), (2, 90)])]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from geopandas._compat import import_optional_dependency
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_import_optional_dependency_present():
|
||||
# pandas is not optional, but we know it is present
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import random
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pyproj
|
||||
import pytest
|
||||
from shapely.geometry import Point, Polygon, LineString
|
||||
|
||||
from geopandas import GeoSeries, GeoDataFrame, points_from_xy, datasets, read_file
|
||||
from geopandas.array import from_shapely, from_wkb, from_wkt, GeometryArray
|
||||
from shapely.geometry import LineString, Point, Polygon
|
||||
|
||||
from geopandas import GeoDataFrame, GeoSeries, points_from_xy, read_file
|
||||
from geopandas.array import GeometryArray, from_shapely, from_wkb, from_wkt
|
||||
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal
|
||||
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
|
||||
|
||||
def _create_df(x, y=None, crs=None):
|
||||
y = y or x
|
||||
@@ -82,6 +86,9 @@ def test_to_crs_dimension_z():
|
||||
assert result.has_z.all()
|
||||
|
||||
|
||||
# pyproj + numpy 1.25 trigger warning for single-element array -> recommdation is to
|
||||
# ignore the warning for now (https://github.com/pyproj4/pyproj/issues/1307)
|
||||
@pytest.mark.filterwarnings("ignore:Conversion of an array with:DeprecationWarning")
|
||||
def test_to_crs_dimension_mixed():
|
||||
s = GeoSeries([Point(1, 2), LineString([(1, 2, 3), (4, 5, 6)])], crs=2056)
|
||||
result = s.to_crs(epsg=4326)
|
||||
@@ -150,6 +157,9 @@ def test_transform2(epsg4326, epsg26918):
|
||||
assert_geodataframe_equal(df, utm, check_less_precise=True, check_crs=False)
|
||||
|
||||
|
||||
# pyproj + numpy 1.25 trigger warning for single-element array -> recommdation is to
|
||||
# ignore the warning for now (https://github.com/pyproj4/pyproj/issues/1307)
|
||||
@pytest.mark.filterwarnings("ignore:Conversion of an array with:DeprecationWarning")
|
||||
def test_crs_axis_order__always_xy():
|
||||
df = GeoDataFrame(geometry=[Point(-1683723, 6689139)], crs="epsg:26918")
|
||||
lonlat = df.to_crs("epsg:4326")
|
||||
@@ -203,7 +213,7 @@ class TestGeometryArrayCRS:
|
||||
assert s.values.crs == self.osgb
|
||||
|
||||
# manually change CRS
|
||||
s.crs = 4326
|
||||
s = s.set_crs(4326, allow_override=True)
|
||||
assert s.crs == self.wgs
|
||||
assert s.values.crs == self.wgs
|
||||
|
||||
@@ -252,7 +262,7 @@ class TestGeometryArrayCRS:
|
||||
arr = from_shapely(self.geoms)
|
||||
s = GeoSeries(arr, crs=27700)
|
||||
df = GeoDataFrame(geometry=s)
|
||||
df.crs = 4326
|
||||
df = df.set_crs(crs="epsg:4326", allow_override=True)
|
||||
assert df.crs == self.wgs
|
||||
assert df.geometry.crs == self.wgs
|
||||
assert df.geometry.values.crs == self.wgs
|
||||
@@ -319,7 +329,11 @@ class TestGeometryArrayCRS:
|
||||
df.crs = 27700
|
||||
|
||||
# geometry column without geometry
|
||||
df = GeoDataFrame({"geometry": [Point(0, 1)]}).assign(geometry=[0])
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(
|
||||
"ignore", "Geometry column does not contain geometry", UserWarning
|
||||
)
|
||||
df = GeoDataFrame({"geometry": [Point(0, 1)]}).assign(geometry=[0])
|
||||
with pytest.raises(
|
||||
ValueError,
|
||||
match="Assigning CRS to a GeoDataFrame without an active geometry",
|
||||
@@ -404,7 +418,7 @@ class TestGeometryArrayCRS:
|
||||
FutureWarning, match="You are adding a column named 'geometry'"
|
||||
):
|
||||
df["geometry"] = scalar
|
||||
df.crs = 4326
|
||||
df = df.set_crs(4326)
|
||||
assert df.crs == self.wgs
|
||||
assert df.geometry.crs == self.wgs
|
||||
assert df.geometry.values.crs == self.wgs
|
||||
@@ -415,8 +429,7 @@ class TestGeometryArrayCRS:
|
||||
df = GeoDataFrame()
|
||||
df.crs = 4326
|
||||
|
||||
def test_read_file(self):
|
||||
nybb_filename = datasets.get_path("nybb")
|
||||
def test_read_file(self, nybb_filename):
|
||||
df = read_file(nybb_filename)
|
||||
assert df.crs == pyproj.CRS(2263)
|
||||
assert df.geometry.crs == pyproj.CRS(2263)
|
||||
@@ -728,6 +741,7 @@ class TestSetCRS:
|
||||
assert non_naive.crs == "EPSG:3857"
|
||||
assert result.crs == "EPSG:3857"
|
||||
|
||||
# raise error when no crs is passed
|
||||
with pytest.raises(ValueError):
|
||||
naive.set_crs(crs=None, epsg=None)
|
||||
# set CRS to None
|
||||
result = non_naive.set_crs(crs=None, allow_override=True)
|
||||
assert result.crs is None
|
||||
assert non_naive.crs == "EPSG:3857"
|
||||
|
||||
@@ -5,8 +5,11 @@ import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_dataset", ["naturalearth_lowres", "naturalearth_cities", "nybb"]
|
||||
"test_dataset", ["naturalearth_lowres", "naturalearth_cities", "nybb", "foo"]
|
||||
)
|
||||
def test_read_paths(test_dataset):
|
||||
with pytest.warns(FutureWarning, match="The geopandas.dataset module is"):
|
||||
with pytest.raises(
|
||||
AttributeError,
|
||||
match=r"The geopandas\.dataset has been deprecated and was removed",
|
||||
):
|
||||
assert isinstance(read_file(get_path(test_dataset)), GeoDataFrame)
|
||||
|
||||
@@ -10,7 +10,6 @@ def cumsum(whatever):
|
||||
|
||||
It computes the cumulative {operation}.
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
@doc(
|
||||
@@ -27,18 +26,15 @@ def cumsum(whatever):
|
||||
method="cumavg",
|
||||
operation="average",
|
||||
)
|
||||
def cumavg(whatever):
|
||||
...
|
||||
def cumavg(whatever): ...
|
||||
|
||||
|
||||
@doc(cumsum, method="cummax", operation="maximum")
|
||||
def cummax(whatever):
|
||||
...
|
||||
def cummax(whatever): ...
|
||||
|
||||
|
||||
@doc(cummax, method="cummin", operation="minimum")
|
||||
def cummin(whatever):
|
||||
...
|
||||
def cummin(whatever): ...
|
||||
|
||||
|
||||
def test_docstring_formatting():
|
||||
|
||||
@@ -5,17 +5,15 @@ import pandas as pd
|
||||
|
||||
import geopandas
|
||||
from geopandas import GeoDataFrame, read_file
|
||||
from geopandas._compat import HAS_PYPROJ, PANDAS_GE_15, PANDAS_GE_20, PANDAS_GE_30
|
||||
|
||||
from pandas.testing import assert_frame_equal
|
||||
import pytest
|
||||
|
||||
from geopandas._compat import PANDAS_GE_15, PANDAS_GE_20
|
||||
from geopandas.testing import assert_geodataframe_equal, geom_almost_equals
|
||||
from pandas.testing import assert_frame_equal
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nybb_polydf():
|
||||
nybb_filename = geopandas.datasets.get_path("nybb")
|
||||
def nybb_polydf(nybb_filename):
|
||||
nybb_polydf = read_file(nybb_filename)
|
||||
nybb_polydf = nybb_polydf[["geometry", "BoroName", "BoroCode"]]
|
||||
nybb_polydf = nybb_polydf.rename(columns={"geometry": "myshapes"})
|
||||
@@ -32,7 +30,7 @@ def merged_shapes(nybb_polydf):
|
||||
manhattan_bronx = nybb_polydf.loc[3:4]
|
||||
others = nybb_polydf.loc[0:2]
|
||||
|
||||
collapsed = [others.geometry.unary_union, manhattan_bronx.geometry.unary_union]
|
||||
collapsed = [others.geometry.union_all(), manhattan_bronx.geometry.union_all()]
|
||||
merged_shapes = GeoDataFrame(
|
||||
{"myshapes": collapsed},
|
||||
geometry="myshapes",
|
||||
@@ -64,6 +62,7 @@ def test_geom_dissolve(nybb_polydf, first):
|
||||
assert geom_almost_equals(test, first)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not installed")
|
||||
def test_dissolve_retains_existing_crs(nybb_polydf):
|
||||
assert nybb_polydf.crs is not None
|
||||
test = nybb_polydf.dissolve("manhattan_bronx")
|
||||
@@ -71,7 +70,7 @@ def test_dissolve_retains_existing_crs(nybb_polydf):
|
||||
|
||||
|
||||
def test_dissolve_retains_nonexisting_crs(nybb_polydf):
|
||||
nybb_polydf.crs = None
|
||||
nybb_polydf.geometry.array.crs = None
|
||||
test = nybb_polydf.dissolve("manhattan_bronx")
|
||||
assert test.crs is None
|
||||
|
||||
@@ -95,7 +94,7 @@ def test_mean_dissolve(nybb_polydf, first, expected_mean):
|
||||
)
|
||||
# for non pandas "mean", numeric only cannot be applied. Drop columns manually
|
||||
test2 = nybb_polydf.drop(columns=["BoroName"]).dissolve(
|
||||
"manhattan_bronx", aggfunc=np.mean
|
||||
"manhattan_bronx", aggfunc="mean"
|
||||
)
|
||||
|
||||
assert_frame_equal(expected_mean, test, check_column_type=False)
|
||||
@@ -152,7 +151,7 @@ def test_dissolve_none(nybb_polydf):
|
||||
test = nybb_polydf.dissolve(by=None)
|
||||
expected = GeoDataFrame(
|
||||
{
|
||||
nybb_polydf.geometry.name: [nybb_polydf.geometry.unary_union],
|
||||
nybb_polydf.geometry.name: [nybb_polydf.geometry.union_all()],
|
||||
"BoroName": ["Staten Island"],
|
||||
"BoroCode": [5],
|
||||
"manhattan_bronx": [5],
|
||||
@@ -167,7 +166,7 @@ def test_dissolve_none_mean(nybb_polydf):
|
||||
test = nybb_polydf.dissolve(aggfunc="mean", numeric_only=True)
|
||||
expected = GeoDataFrame(
|
||||
{
|
||||
nybb_polydf.geometry.name: [nybb_polydf.geometry.unary_union],
|
||||
nybb_polydf.geometry.name: [nybb_polydf.geometry.union_all()],
|
||||
"BoroCode": [3.0],
|
||||
"manhattan_bronx": [5.4],
|
||||
},
|
||||
@@ -261,6 +260,7 @@ def test_dissolve_categorical():
|
||||
|
||||
# when observed=False we get an additional observation
|
||||
# that wasn't in the original data
|
||||
none_val = "GEOMETRYCOLLECTION EMPTY" if PANDAS_GE_30 else None
|
||||
expected_gdf_observed_false = geopandas.GeoDataFrame(
|
||||
{
|
||||
"cat": pd.Categorical(["a", "a", "b", "b"]),
|
||||
@@ -268,7 +268,7 @@ def test_dissolve_categorical():
|
||||
"geometry": geopandas.array.from_wkt(
|
||||
[
|
||||
"MULTIPOINT (0 0, 1 1)",
|
||||
None,
|
||||
none_val,
|
||||
"POINT (2 2)",
|
||||
"POINT (3 3)",
|
||||
]
|
||||
@@ -348,3 +348,25 @@ def test_dissolve_multi_agg(nybb_polydf, merged_shapes):
|
||||
)
|
||||
assert_geodataframe_equal(test, merged_shapes)
|
||||
assert len(record) == 0
|
||||
|
||||
|
||||
def test_coverage_dissolve(nybb_polydf):
|
||||
manhattan_bronx = nybb_polydf.loc[3:4]
|
||||
others = nybb_polydf.loc[0:2]
|
||||
|
||||
collapsed = [
|
||||
others.geometry.union_all(method="coverage"),
|
||||
manhattan_bronx.geometry.union_all(method="coverage"),
|
||||
]
|
||||
merged_shapes = GeoDataFrame(
|
||||
{"myshapes": collapsed},
|
||||
geometry="myshapes",
|
||||
index=pd.Index([5, 6], name="manhattan_bronx"),
|
||||
crs=nybb_polydf.crs,
|
||||
)
|
||||
|
||||
merged_shapes["BoroName"] = ["Staten Island", "Manhattan"]
|
||||
merged_shapes["BoroCode"] = [5, 1]
|
||||
|
||||
test = nybb_polydf.dissolve("manhattan_bronx", method="coverage")
|
||||
assert_frame_equal(merged_shapes, test, check_column_type=False)
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import geopandas as gpd
|
||||
import uuid
|
||||
from packaging.version import Version
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import shapely
|
||||
|
||||
import geopandas as gpd
|
||||
from geopandas._compat import HAS_PYPROJ
|
||||
|
||||
import pytest
|
||||
from packaging.version import Version
|
||||
|
||||
folium = pytest.importorskip("folium")
|
||||
branca = pytest.importorskip("branca")
|
||||
@@ -10,26 +17,34 @@ matplotlib = pytest.importorskip("matplotlib")
|
||||
mapclassify = pytest.importorskip("mapclassify")
|
||||
geodatasets = pytest.importorskip("geodatasets")
|
||||
|
||||
from matplotlib import cm
|
||||
from matplotlib import colors
|
||||
from branca.colormap import StepColormap
|
||||
from matplotlib import cm, colors
|
||||
|
||||
BRANCA_05 = Version(branca.__version__) > Version("0.4.2")
|
||||
FOLIUM_G_014 = Version(folium.__version__) > Version("0.14.0")
|
||||
|
||||
|
||||
class TestExplore:
|
||||
def setup_method(self):
|
||||
self.nybb = gpd.read_file(gpd.datasets.get_path("nybb"))
|
||||
self.world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
|
||||
self.cities = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))
|
||||
self.chicago = gpd.read_file(geodatasets.get_path("geoda.chicago_commpop"))
|
||||
self.world["range"] = range(len(self.world))
|
||||
self.missing = self.world.copy()
|
||||
np.random.seed(42)
|
||||
self.missing.loc[np.random.choice(self.missing.index, 40), "continent"] = np.nan
|
||||
self.missing.loc[np.random.choice(self.missing.index, 40), "pop_est"] = np.nan
|
||||
@pytest.fixture(scope="class")
|
||||
def _setup_class_test_explore(
|
||||
nybb_filename, naturalearth_lowres, naturalearth_cities, request
|
||||
):
|
||||
request.cls.nybb = gpd.read_file(nybb_filename)
|
||||
request.cls.world = gpd.read_file(naturalearth_lowres)
|
||||
request.cls.cities = gpd.read_file(naturalearth_cities)
|
||||
request.cls.chicago = gpd.read_file(geodatasets.get_path("geoda.chicago_commpop"))
|
||||
request.cls.world["range"] = range(len(request.cls.world))
|
||||
request.cls.missing = request.cls.world.copy()
|
||||
np.random.seed(42)
|
||||
request.cls.missing.loc[
|
||||
np.random.choice(request.cls.missing.index, 40), "continent"
|
||||
] = np.nan
|
||||
request.cls.missing.loc[
|
||||
np.random.choice(request.cls.missing.index, 40), "pop_est"
|
||||
] = np.nan
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_setup_class_test_explore")
|
||||
class TestExplore:
|
||||
def _fetch_map_string(self, m):
|
||||
out = m._parent.render()
|
||||
out_str = "".join(out.split())
|
||||
@@ -46,6 +61,7 @@ class TestExplore:
|
||||
"""Make sure default choropleth pass"""
|
||||
self.world.explore(column="pop_est")
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
|
||||
def test_map_settings_default(self):
|
||||
"""Check default map settings"""
|
||||
m = self.world.explore()
|
||||
@@ -64,6 +80,7 @@ class TestExplore:
|
||||
assert m.global_switches.disable_3d is False
|
||||
assert "openstreetmap" in m.to_dict()["children"].keys()
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
|
||||
def test_map_settings_custom(self):
|
||||
"""Check custom map settings"""
|
||||
m = self.nybb.explore(
|
||||
@@ -284,6 +301,42 @@ class TestExplore:
|
||||
assert '"__folium_color":"#9edae5","bool":true' in out2_str
|
||||
assert '"__folium_color":"#1f77b4","bool":false' in out2_str
|
||||
|
||||
def test_datetime(self):
|
||||
df = self.nybb.copy().head(2)
|
||||
date1 = pd.Timestamp(2022, 1, 1, 1, 22, 0, 0)
|
||||
date2 = pd.Timestamp(2025, 1, 1, 1, 22, 0, 0)
|
||||
df["datetime"] = [date1, date2]
|
||||
m1 = df.explore("datetime")
|
||||
|
||||
out1_str = self._fetch_map_string(m1)
|
||||
assert '"__folium_color":"#9edae5","datetime":"2025-01-0101:22:00"' in out1_str
|
||||
assert '"__folium_color":"#1f77b4","datetime":"2022-01-0101:22:00"' in out1_str
|
||||
|
||||
df2 = df.set_index("datetime")
|
||||
m2 = df2.explore()
|
||||
out2_str = self._fetch_map_string(m2)
|
||||
assert '"datetime":"2025-01-0101:22:00"' in out2_str
|
||||
assert '"datetime":"2022-01-0101:22:00"' in out2_str
|
||||
|
||||
def test_non_json_serialisable(self):
|
||||
df = self.nybb.copy().head(2)
|
||||
|
||||
u1 = "12345678-1234-5678-1234-567812345678"
|
||||
uuid1 = uuid.UUID(u1)
|
||||
u2 = "12345678-1234-5678-1234-567812345679"
|
||||
uuid2 = uuid.UUID(u2)
|
||||
df["object"] = [uuid1, uuid2]
|
||||
m1 = df.explore("object")
|
||||
|
||||
out1_str = self._fetch_map_string(m1)
|
||||
assert f'"__folium_color":"#9edae5","object":"{u2}"' in out1_str
|
||||
assert f'"__folium_color":"#1f77b4","object":"{u1}"' in out1_str
|
||||
df2 = df.set_index("object")
|
||||
m2 = df2.explore()
|
||||
out2_str = self._fetch_map_string(m2)
|
||||
assert f'"object":"{u2}"' in out2_str
|
||||
assert f'"object":"{u1}"' in out2_str
|
||||
|
||||
def test_string(self):
|
||||
df = self.nybb.copy()
|
||||
df["string"] = pd.array([1, 2, 3, 4, 5], dtype="string")
|
||||
@@ -333,7 +386,7 @@ class TestExplore:
|
||||
def test_no_crs(self):
|
||||
"""Naive geometry get no tiles"""
|
||||
df = self.world.copy()
|
||||
df.crs = None
|
||||
df.geometry.array.crs = None
|
||||
m = df.explore()
|
||||
assert "openstreetmap" not in m.to_dict()["children"].keys()
|
||||
|
||||
@@ -351,12 +404,12 @@ class TestExplore:
|
||||
m = self.world.explore(
|
||||
style_kwds={
|
||||
"style_function": lambda x: {
|
||||
"fillColor": "red"
|
||||
if x["properties"]["gdp_md_est"] < 10**6
|
||||
else "green",
|
||||
"color": "black"
|
||||
if x["properties"]["gdp_md_est"] < 10**6
|
||||
else "white",
|
||||
"fillColor": (
|
||||
"red" if x["properties"]["gdp_md_est"] < 10**6 else "green"
|
||||
),
|
||||
"color": (
|
||||
"black" if x["properties"]["gdp_md_est"] < 10**6 else "white"
|
||||
),
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -683,6 +736,7 @@ class TestExplore:
|
||||
== "tickValues([140.0,'','','',559086084.0,'','','',1118172028.0,'','',''])"
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
|
||||
def test_xyzservices_providers(self):
|
||||
xyzservices = pytest.importorskip("xyzservices")
|
||||
|
||||
@@ -697,8 +751,9 @@ class TestExplore:
|
||||
'attribution":"\\u0026copy;\\u003cahref=\\"https://www.openstreetmap.org'
|
||||
in out_str
|
||||
)
|
||||
assert '"maxNativeZoom":20,"maxZoom":20,"minZoom":0' in out_str
|
||||
assert '"maxZoom":20,"minZoom":0' in out_str
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
|
||||
def test_xyzservices_query_name(self):
|
||||
pytest.importorskip("xyzservices")
|
||||
|
||||
@@ -713,8 +768,9 @@ class TestExplore:
|
||||
'attribution":"\\u0026copy;\\u003cahref=\\"https://www.openstreetmap.org'
|
||||
in out_str
|
||||
)
|
||||
assert '"maxNativeZoom":20,"maxZoom":20,"minZoom":0' in out_str
|
||||
assert '"maxZoom":20,"minZoom":0' in out_str
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
|
||||
def test_xyzservices_providers_min_zoom_override(self):
|
||||
xyzservices = pytest.importorskip("xyzservices")
|
||||
|
||||
@@ -723,8 +779,9 @@ class TestExplore:
|
||||
)
|
||||
out_str = self._fetch_map_string(m)
|
||||
|
||||
assert '"maxNativeZoom":20,"maxZoom":20,"minZoom":3' in out_str
|
||||
assert '"maxZoom":20,"minZoom":3' in out_str
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
|
||||
def test_xyzservices_providers_max_zoom_override(self):
|
||||
xyzservices = pytest.importorskip("xyzservices")
|
||||
|
||||
@@ -733,8 +790,9 @@ class TestExplore:
|
||||
)
|
||||
out_str = self._fetch_map_string(m)
|
||||
|
||||
assert '"maxNativeZoom":12,"maxZoom":12,"minZoom":0' in out_str
|
||||
assert '"maxZoom":12,"minZoom":0' in out_str
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="requires pyproj")
|
||||
def test_xyzservices_providers_both_zooms_override(self):
|
||||
xyzservices = pytest.importorskip("xyzservices")
|
||||
|
||||
@@ -745,7 +803,7 @@ class TestExplore:
|
||||
)
|
||||
out_str = self._fetch_map_string(m)
|
||||
|
||||
assert '"maxNativeZoom":12,"maxZoom":12,"minZoom":3' in out_str
|
||||
assert '"maxZoom":12,"minZoom":3' in out_str
|
||||
|
||||
def test_linearrings(self):
|
||||
rings = self.nybb.explode(index_parts=True).exterior
|
||||
@@ -940,3 +998,51 @@ class TestExplore:
|
||||
"zoom_control": False,
|
||||
}
|
||||
)
|
||||
|
||||
def test_none_geometry(self):
|
||||
# None and empty geoms are dropped prior plotting
|
||||
df = self.nybb.copy()
|
||||
df.loc[0, df.geometry.name] = None
|
||||
m = df.explore()
|
||||
self._fetch_map_string(m)
|
||||
|
||||
def test_empty_geometry(self):
|
||||
# None and empty geoms are dropped prior plotting
|
||||
df = self.nybb.copy()
|
||||
df.loc[0, df.geometry.name] = shapely.Point()
|
||||
m = df.explore()
|
||||
self._fetch_map_string(m)
|
||||
|
||||
def test_all_empty(self):
|
||||
with_crs = gpd.GeoDataFrame(
|
||||
geometry=[shapely.Point(), shapely.Point()], crs=4326
|
||||
)
|
||||
with pytest.warns(
|
||||
UserWarning,
|
||||
match="The GeoSeries you are attempting to plot is composed of empty",
|
||||
):
|
||||
m = with_crs.explore()
|
||||
out_str = self._fetch_map_string(m)
|
||||
if HAS_PYPROJ:
|
||||
assert "center:[0.0,0.0],crs:L.CRS.EPSG3857" in out_str
|
||||
|
||||
no_crs = gpd.GeoDataFrame(geometry=[shapely.Point(), shapely.Point()])
|
||||
with pytest.warns(
|
||||
UserWarning,
|
||||
match="The GeoSeries you are attempting to plot is composed of empty",
|
||||
):
|
||||
m = no_crs.explore()
|
||||
out_str = self._fetch_map_string(m)
|
||||
assert "center:[0.0,0.0],crs:L.CRS.Simple" in out_str
|
||||
|
||||
def test_add_all_empty_named_index(self):
|
||||
gdf1 = gpd.GeoDataFrame(geometry=[shapely.Point(0, 0), shapely.Point(1, 1)])
|
||||
gdf2 = gpd.GeoDataFrame(geometry=[shapely.Point(), shapely.Point()])
|
||||
m = gdf1.rename_axis(index="index_name").explore()
|
||||
with pytest.warns(
|
||||
UserWarning,
|
||||
match="The GeoSeries you are attempting to plot is composed of empty",
|
||||
):
|
||||
m = gdf2.rename_axis(index="index_name").explore(m=m, color="red")
|
||||
out_str = self._fetch_map_string(m)
|
||||
assert "center:[0.5,0.5],crs:L.CRS.Simple" in out_str
|
||||
|
||||
@@ -13,21 +13,22 @@ A set of fixtures are defined to provide data for the tests (the fixtures
|
||||
expected to be available to pytest by the inherited pandas tests).
|
||||
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import operator
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal
|
||||
import pandas as pd
|
||||
from pandas.testing import assert_series_equal
|
||||
from pandas.tests.extension import base as extension_tests
|
||||
|
||||
import shapely.geometry
|
||||
from shapely.geometry import Point
|
||||
|
||||
from geopandas._compat import PANDAS_GE_15, PANDAS_GE_21, PANDAS_GE_22
|
||||
from geopandas.array import GeometryArray, GeometryDtype, from_shapely
|
||||
from geopandas._compat import ignore_shapely2_warnings, SHAPELY_GE_20, PANDAS_GE_15
|
||||
|
||||
import pytest
|
||||
from pandas.testing import assert_frame_equal, assert_series_equal
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Compat with extension tests in older pandas versions
|
||||
@@ -36,9 +37,6 @@ import pytest
|
||||
|
||||
not_yet_implemented = pytest.mark.skip(reason="Not yet implemented")
|
||||
no_minmax = pytest.mark.skip(reason="Min/max not supported")
|
||||
requires_shapely2 = pytest.mark.skipif(
|
||||
not SHAPELY_GE_20, reason="Requires hashable geometries"
|
||||
)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -54,8 +52,7 @@ def dtype():
|
||||
|
||||
def make_data():
|
||||
a = np.empty(100, dtype=object)
|
||||
with ignore_shapely2_warnings():
|
||||
a[:] = [shapely.geometry.Point(i, i) for i in range(100)]
|
||||
a[:] = [shapely.geometry.Point(i, i) for i in range(100)]
|
||||
ga = from_shapely(a)
|
||||
return ga
|
||||
|
||||
@@ -316,20 +313,6 @@ class TestDtype(extension_tests.BaseDtypeTests):
|
||||
|
||||
|
||||
class TestInterface(extension_tests.BaseInterfaceTests):
|
||||
def test_array_interface(self, data):
|
||||
# we are overriding this base test because the creation of `expected`
|
||||
# potentially doesn't work for shapely geometries
|
||||
# TODO can be removed with Shapely 2.0
|
||||
result = np.array(data)
|
||||
assert result[0] == data[0]
|
||||
|
||||
result = np.array(data, dtype=object)
|
||||
# expected = np.array(list(data), dtype=object)
|
||||
expected = np.empty(len(data), dtype=object)
|
||||
with ignore_shapely2_warnings():
|
||||
expected[:] = list(data)
|
||||
assert_array_equal(result, expected)
|
||||
|
||||
def test_contains(self, data, data_missing):
|
||||
# overridden due to the inconsistency between
|
||||
# GeometryDtype.na_value = np.nan
|
||||
@@ -352,7 +335,74 @@ class TestConstructors(extension_tests.BaseConstructorsTests):
|
||||
|
||||
|
||||
class TestReshaping(extension_tests.BaseReshapingTests):
|
||||
pass
|
||||
|
||||
# NOTE: this test is copied from pandas/tests/extension/base/reshaping.py
|
||||
# because starting with pandas 3.0 the assert_frame_equal is strict regarding
|
||||
# the exact missing value (None vs NaN)
|
||||
# Our `result` uses None, but the way the `expected` is created results in
|
||||
# NaNs (and specifying to use None as fill value in unstack also does not
|
||||
# help)
|
||||
# -> the only change compared to the upstream test is marked
|
||||
@pytest.mark.parametrize(
|
||||
"index",
|
||||
[
|
||||
# Two levels, uniform.
|
||||
pd.MultiIndex.from_product(([["A", "B"], ["a", "b"]]), names=["a", "b"]),
|
||||
# non-uniform
|
||||
pd.MultiIndex.from_tuples([("A", "a"), ("A", "b"), ("B", "b")]),
|
||||
# three levels, non-uniform
|
||||
pd.MultiIndex.from_product([("A", "B"), ("a", "b", "c"), (0, 1, 2)]),
|
||||
pd.MultiIndex.from_tuples(
|
||||
[
|
||||
("A", "a", 1),
|
||||
("A", "b", 0),
|
||||
("A", "a", 0),
|
||||
("B", "a", 0),
|
||||
("B", "c", 1),
|
||||
]
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.parametrize("obj", ["series", "frame"])
|
||||
def test_unstack(self, data, index, obj):
|
||||
data = data[: len(index)]
|
||||
if obj == "series":
|
||||
ser = pd.Series(data, index=index)
|
||||
else:
|
||||
ser = pd.DataFrame({"A": data, "B": data}, index=index)
|
||||
|
||||
n = index.nlevels
|
||||
levels = list(range(n))
|
||||
# [0, 1, 2]
|
||||
# [(0,), (1,), (2,), (0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
|
||||
combinations = itertools.chain.from_iterable(
|
||||
itertools.permutations(levels, i) for i in range(1, n)
|
||||
)
|
||||
|
||||
for level in combinations:
|
||||
result = ser.unstack(level=level)
|
||||
assert all(
|
||||
isinstance(result[col].array, type(data)) for col in result.columns
|
||||
)
|
||||
|
||||
if obj == "series":
|
||||
# We should get the same result with to_frame+unstack+droplevel
|
||||
df = ser.to_frame()
|
||||
|
||||
alt = df.unstack(level=level).droplevel(0, axis=1)
|
||||
assert_frame_equal(result, alt)
|
||||
|
||||
obj_ser = ser.astype(object)
|
||||
|
||||
expected = obj_ser.unstack(level=level, fill_value=data.dtype.na_value)
|
||||
if obj == "series":
|
||||
assert (expected.dtypes == object).all()
|
||||
# <------------ next line is added
|
||||
expected[expected.isna()] = None
|
||||
# ------------->
|
||||
|
||||
result = result.astype(object)
|
||||
assert_frame_equal(result, expected)
|
||||
|
||||
|
||||
class TestGetitem(extension_tests.BaseGetitemTests):
|
||||
@@ -403,24 +453,38 @@ class TestMissing(extension_tests.BaseMissingTests):
|
||||
# `geopandas\tests\test_pandas_methods.py::test_fillna_scalar`
|
||||
# and `geopandas\tests\test_pandas_methods.py::test_fillna_series`.
|
||||
|
||||
@pytest.mark.skip("fillna method not supported")
|
||||
@pytest.mark.skipif(
|
||||
not PANDAS_GE_21, reason="fillna method not supported with older pandas"
|
||||
)
|
||||
def test_fillna_limit_pad(self, data_missing):
|
||||
pass
|
||||
super().test_fillna_limit_pad(data_missing)
|
||||
|
||||
@pytest.mark.skip("fillna method not supported")
|
||||
@pytest.mark.skipif(
|
||||
not PANDAS_GE_21, reason="fillna method not supported with older pandas"
|
||||
)
|
||||
def test_fillna_limit_backfill(self, data_missing):
|
||||
pass
|
||||
super().test_fillna_limit_backfill(data_missing)
|
||||
|
||||
@pytest.mark.skip("fillna method not supported")
|
||||
def test_fillna_series_method(self, data_missing, method):
|
||||
pass
|
||||
@pytest.mark.skipif(
|
||||
not PANDAS_GE_21, reason="fillna method not supported with older pandas"
|
||||
)
|
||||
def test_fillna_series_method(self, data_missing, fillna_method):
|
||||
super().test_fillna_series_method(data_missing, fillna_method)
|
||||
|
||||
@pytest.mark.skip("fillna method not supported")
|
||||
@pytest.mark.skipif(
|
||||
not PANDAS_GE_21, reason="fillna method not supported with older pandas"
|
||||
)
|
||||
def test_fillna_no_op_returns_copy(self, data):
|
||||
pass
|
||||
super().test_fillna_no_op_returns_copy(data)
|
||||
|
||||
|
||||
class TestReduce(extension_tests.BaseNoReduceTests):
|
||||
if PANDAS_GE_22:
|
||||
from pandas.tests.extension.base import BaseReduceTests
|
||||
else:
|
||||
from pandas.tests.extension.base import BaseNoReduceTests as BaseReduceTests
|
||||
|
||||
|
||||
class TestReduce(BaseReduceTests):
|
||||
@pytest.mark.skip("boolean reduce (any/all) tested in test_pandas_methods")
|
||||
def test_reduce_series_boolean(self):
|
||||
pass
|
||||
@@ -506,7 +570,6 @@ class TestMethods(extension_tests.BaseMethodsTests):
|
||||
def test_value_counts_with_normalize(self, data):
|
||||
pass
|
||||
|
||||
@requires_shapely2
|
||||
@pytest.mark.parametrize("ascending", [True, False])
|
||||
def test_sort_values_frame(self, data_for_sorting, ascending):
|
||||
super().test_sort_values_frame(data_for_sorting, ascending)
|
||||
@@ -555,16 +618,13 @@ class TestCasting(extension_tests.BaseCastingTests):
|
||||
|
||||
|
||||
class TestGroupby(extension_tests.BaseGroupbyTests):
|
||||
@requires_shapely2
|
||||
@pytest.mark.parametrize("as_index", [True, False])
|
||||
def test_groupby_extension_agg(self, as_index, data_for_grouping):
|
||||
super().test_groupby_extension_agg(as_index, data_for_grouping)
|
||||
|
||||
@requires_shapely2
|
||||
def test_groupby_extension_transform(self, data_for_grouping):
|
||||
super().test_groupby_extension_transform(data_for_grouping)
|
||||
|
||||
@requires_shapely2
|
||||
@pytest.mark.parametrize(
|
||||
"op",
|
||||
[
|
||||
|
||||
@@ -3,13 +3,14 @@ import pandas as pd
|
||||
from shapely.geometry import Point
|
||||
|
||||
from geopandas import GeoDataFrame, GeoSeries
|
||||
from geopandas._compat import HAS_PYPROJ
|
||||
from geopandas.tools import geocode, reverse_geocode
|
||||
from geopandas.tools.geocoding import _prepare_geocode_result
|
||||
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal
|
||||
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")
|
||||
|
||||
@@ -71,7 +72,8 @@ def test_prepare_result():
|
||||
|
||||
df = _prepare_geocode_result(d)
|
||||
assert type(df) is GeoDataFrame
|
||||
assert df.crs == "EPSG:4326"
|
||||
if HAS_PYPROJ:
|
||||
assert df.crs == "EPSG:4326"
|
||||
assert len(df) == 2
|
||||
assert "address" in df
|
||||
|
||||
@@ -93,18 +95,15 @@ def test_prepare_result_none():
|
||||
|
||||
df = _prepare_geocode_result(d)
|
||||
assert type(df) is GeoDataFrame
|
||||
assert df.crs == "EPSG:4326"
|
||||
if HAS_PYPROJ:
|
||||
assert df.crs == "EPSG:4326"
|
||||
assert len(df) == 2
|
||||
assert "address" in df
|
||||
|
||||
row = df.loc["b"]
|
||||
# The shapely.geometry.Point() is actually a GeometryCollection, and thus
|
||||
# gets converted to that in conversion to pygeos. When converting back
|
||||
# on access, you now get a GeometryCollection object instead of Point,
|
||||
# which has no coords
|
||||
# see https://github.com/Toblerity/Shapely/issues/742/#issuecomment-545296708
|
||||
|
||||
# TODO we should probably replace this with a missing value instead of point?
|
||||
# assert len(row["geometry"].coords) == 0
|
||||
assert len(row["geometry"].coords) == 0
|
||||
assert row["geometry"].is_empty
|
||||
assert row["address"] is None
|
||||
|
||||
|
||||
@@ -6,23 +6,17 @@ import tempfile
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from pyproj import CRS
|
||||
from pyproj.exceptions import CRSError
|
||||
from shapely.geometry import Point, Polygon
|
||||
from shapely.geometry import Point, Polygon, box
|
||||
|
||||
import geopandas
|
||||
import geopandas._compat as compat
|
||||
from geopandas import GeoDataFrame, GeoSeries, points_from_xy, read_file
|
||||
from geopandas.array import GeometryArray, GeometryDtype, from_shapely
|
||||
from geopandas._compat import ignore_shapely2_warnings
|
||||
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
|
||||
from geopandas.tests.util import PACKAGE_DIR, validate_boro_df
|
||||
from pandas.testing import assert_frame_equal, assert_index_equal, assert_series_equal
|
||||
import pytest
|
||||
|
||||
|
||||
TEST_NEAREST = compat.USE_SHAPELY_20 or (compat.PYGEOS_GE_010 and compat.USE_PYGEOS)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -51,12 +45,13 @@ def how(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_setup_class_nybb_filename")
|
||||
class TestDataFrame:
|
||||
def setup_method(self):
|
||||
N = 10
|
||||
|
||||
nybb_filename = geopandas.datasets.get_path("nybb")
|
||||
self.df = read_file(nybb_filename)
|
||||
# self.nybb_filename attached via _setup_class_nybb_filename
|
||||
self.df = read_file(self.nybb_filename)
|
||||
# TODO re-write instance variables to be fixtures
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.crs = "epsg:4326"
|
||||
self.df2 = GeoDataFrame(
|
||||
@@ -75,9 +70,13 @@ class TestDataFrame:
|
||||
|
||||
def test_df_init(self):
|
||||
assert type(self.df2) is GeoDataFrame
|
||||
assert self.df2.crs == self.crs
|
||||
if compat.HAS_PYPROJ:
|
||||
assert self.df2.crs == self.crs
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="Requires pyproj")
|
||||
def test_different_geo_colname(self):
|
||||
from pyproj.exceptions import CRSError
|
||||
|
||||
data = {
|
||||
"A": range(5),
|
||||
"B": range(-5, 0),
|
||||
@@ -198,10 +197,11 @@ class TestDataFrame:
|
||||
assert_geoseries_equal(df.geometry, new_geom)
|
||||
assert_geoseries_equal(df["geometry"], new_geom)
|
||||
|
||||
# new crs
|
||||
gs = new_geom.to_crs(crs="epsg:3857")
|
||||
df.geometry = gs
|
||||
assert df.crs == "epsg:3857"
|
||||
if compat.HAS_PYPROJ:
|
||||
# new crs
|
||||
gs = new_geom.to_crs(crs="epsg:3857")
|
||||
df.geometry = gs
|
||||
assert df.crs == "epsg:3857"
|
||||
|
||||
def test_geometry_property_errors(self):
|
||||
with pytest.raises(AttributeError):
|
||||
@@ -266,6 +266,10 @@ class TestDataFrame:
|
||||
with pytest.raises(ValueError):
|
||||
self.df.set_geometry(self.df)
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="Requires pyproj")
|
||||
def test_set_geometry_crs(self):
|
||||
geom = GeoSeries([Point(x, y) for x, y in zip(range(5), range(5))])
|
||||
|
||||
# new crs - setting should default to GeoSeries' crs
|
||||
gs = GeoSeries(geom, crs="epsg:3857")
|
||||
new_df = self.df.set_geometry(gs)
|
||||
@@ -292,7 +296,8 @@ class TestDataFrame:
|
||||
assert_geoseries_equal(df2.geometry, g_simplified)
|
||||
|
||||
# If True, drops column and renames to geometry
|
||||
df3 = self.df.set_geometry("simplified_geometry", drop=True)
|
||||
with pytest.warns(FutureWarning):
|
||||
df3 = self.df.set_geometry("simplified_geometry", drop=True)
|
||||
assert "simplified_geometry" not in df3
|
||||
assert_geoseries_equal(df3.geometry, g_simplified)
|
||||
|
||||
@@ -368,6 +373,35 @@ class TestDataFrame:
|
||||
with pytest.raises(AttributeError, match=msg_geo_col_missing):
|
||||
df.geometry
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="Requires pyproj")
|
||||
def test_override_existing_crs_warning(self):
|
||||
with pytest.warns(
|
||||
DeprecationWarning,
|
||||
match="Overriding the CRS of a GeoSeries that already has CRS",
|
||||
):
|
||||
self.df.geometry.crs = "epsg:2100"
|
||||
|
||||
with pytest.warns(
|
||||
DeprecationWarning,
|
||||
match="Overriding the CRS of a GeoDataFrame that already has CRS",
|
||||
):
|
||||
self.df.crs = "epsg:4326"
|
||||
|
||||
def test_active_geometry_name(self):
|
||||
# default single active called "geometry"
|
||||
assert self.df.active_geometry_name == "geometry"
|
||||
|
||||
# one GeoSeries, not active
|
||||
no_active = GeoDataFrame({"foo": self.df.BoroName, "bar": self.df.geometry})
|
||||
assert no_active.active_geometry_name is None
|
||||
assert no_active.set_geometry("bar").active_geometry_name == "bar"
|
||||
|
||||
# multiple, none active
|
||||
multiple = GeoDataFrame({"foo": self.df.geometry, "bar": self.df.geometry})
|
||||
assert multiple.active_geometry_name is None
|
||||
assert multiple.set_geometry("foo").active_geometry_name == "foo"
|
||||
assert multiple.set_geometry("bar").active_geometry_name == "bar"
|
||||
|
||||
def test_align(self):
|
||||
df = self.df2
|
||||
|
||||
@@ -379,14 +413,14 @@ class TestDataFrame:
|
||||
assert_geodataframe_equal(res1, df)
|
||||
assert_geodataframe_equal(res2, df)
|
||||
|
||||
# assert crs is / is not preserved on mixed dataframes
|
||||
df_nocrs = df.copy()
|
||||
df_nocrs.crs = None
|
||||
res1, res2 = df.align(df_nocrs)
|
||||
assert_geodataframe_equal(res1, df)
|
||||
assert res1.crs is not None
|
||||
assert_geodataframe_equal(res2, df_nocrs)
|
||||
assert res2.crs is None
|
||||
if compat.HAS_PYPROJ:
|
||||
# assert crs is / is not preserved on mixed dataframes
|
||||
df_nocrs = df.copy().set_crs(None, allow_override=True)
|
||||
res1, res2 = df.align(df_nocrs)
|
||||
assert_geodataframe_equal(res1, df)
|
||||
assert res1.crs is not None
|
||||
assert_geodataframe_equal(res2, df_nocrs)
|
||||
assert res2.crs is None
|
||||
|
||||
# mixed GeoDataFrame / DataFrame
|
||||
df_nogeom = pd.DataFrame(df.drop("geometry", axis=1))
|
||||
@@ -407,15 +441,14 @@ class TestDataFrame:
|
||||
assert_geodataframe_equal(res1, exp1)
|
||||
assert_geodataframe_equal(res2, exp2)
|
||||
|
||||
df2_nocrs = df2.copy()
|
||||
df2_nocrs.crs = None
|
||||
exp2_nocrs = exp2.copy()
|
||||
exp2_nocrs.crs = None
|
||||
res1, res2 = df1.align(df2_nocrs)
|
||||
assert_geodataframe_equal(res1, exp1)
|
||||
assert res1.crs is not None
|
||||
assert_geodataframe_equal(res2, exp2_nocrs)
|
||||
assert res2.crs is None
|
||||
if compat.HAS_PYPROJ:
|
||||
df2_nocrs = df2.copy().set_crs(None, allow_override=True)
|
||||
exp2_nocrs = exp2.copy().set_crs(None, allow_override=True)
|
||||
res1, res2 = df1.align(df2_nocrs)
|
||||
assert_geodataframe_equal(res1, exp1)
|
||||
assert res1.crs is not None
|
||||
assert_geodataframe_equal(res2, exp2_nocrs)
|
||||
assert res2.crs is None
|
||||
|
||||
df2_nogeom = pd.DataFrame(df2.drop("geometry", axis=1))
|
||||
exp2_nogeom = pd.DataFrame(exp2.drop("geometry", axis=1))
|
||||
@@ -424,6 +457,7 @@ class TestDataFrame:
|
||||
assert type(res2) == pd.DataFrame
|
||||
assert_frame_equal(res2, exp2_nogeom)
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="Requires pyproj")
|
||||
def test_to_json(self):
|
||||
text = self.df.to_json(to_wgs84=True)
|
||||
data = json.loads(text)
|
||||
@@ -443,7 +477,7 @@ class TestDataFrame:
|
||||
assert coord == [970217.0223999023, 145643.33221435547]
|
||||
|
||||
def test_to_json_no_crs(self):
|
||||
self.df.crs = None
|
||||
self.df.geometry.array.crs = None
|
||||
with pytest.raises(ValueError, match="CRS is not set"):
|
||||
self.df.to_json(to_wgs84=True)
|
||||
|
||||
@@ -586,23 +620,28 @@ class TestDataFrame:
|
||||
def test_from_dict(self):
|
||||
data = {"A": [1], "geometry": [Point(0.0, 0.0)]}
|
||||
df = GeoDataFrame.from_dict(data, crs=3857)
|
||||
assert df.crs == "epsg:3857"
|
||||
if compat.HAS_PYPROJ:
|
||||
assert df.crs == "epsg:3857"
|
||||
else:
|
||||
assert df.crs is None
|
||||
assert df._geometry_column_name == "geometry"
|
||||
|
||||
data = {"B": [1], "location": [Point(0.0, 0.0)]}
|
||||
df = GeoDataFrame.from_dict(data, geometry="location")
|
||||
assert df._geometry_column_name == "location"
|
||||
|
||||
def test_from_features(self):
|
||||
def test_from_features(self, nybb_filename):
|
||||
fiona = pytest.importorskip("fiona")
|
||||
nybb_filename = geopandas.datasets.get_path("nybb")
|
||||
with fiona.open(nybb_filename) as f:
|
||||
features = list(f)
|
||||
crs = f.crs_wkt
|
||||
|
||||
df = GeoDataFrame.from_features(features, crs=crs)
|
||||
validate_boro_df(df, case_sensitive=True)
|
||||
assert df.crs == crs
|
||||
if compat.HAS_PYPROJ:
|
||||
assert df.crs == crs
|
||||
else:
|
||||
assert df.crs is None
|
||||
|
||||
def test_from_features_unaligned_properties(self):
|
||||
p1 = Point(1, 1)
|
||||
@@ -781,7 +820,8 @@ class TestDataFrame:
|
||||
assert gf.geometry.name == "location"
|
||||
assert "geometry" not in gf
|
||||
|
||||
gf2 = df.set_geometry("location", crs=self.df.crs, drop=True)
|
||||
with pytest.warns(FutureWarning):
|
||||
gf2 = df.set_geometry("location", crs=self.df.crs, drop=True)
|
||||
assert isinstance(df, pd.DataFrame)
|
||||
assert isinstance(gf2, GeoDataFrame)
|
||||
assert gf2.geometry.name == "geometry"
|
||||
@@ -833,40 +873,40 @@ class TestDataFrame:
|
||||
df.loc[0, "BoroName"] = np.nan
|
||||
# when containing missing values
|
||||
# null: output the missing entries as JSON null
|
||||
result = list(df.iterfeatures(na="null"))[0]["properties"]
|
||||
result = next(iter(df.iterfeatures(na="null")))["properties"]
|
||||
assert result["BoroName"] is None
|
||||
# drop: remove the property from the feature.
|
||||
result = list(df.iterfeatures(na="drop"))[0]["properties"]
|
||||
result = next(iter(df.iterfeatures(na="drop")))["properties"]
|
||||
assert "BoroName" not in result.keys()
|
||||
# keep: output the missing entries as NaN
|
||||
result = list(df.iterfeatures(na="keep"))[0]["properties"]
|
||||
result = next(iter(df.iterfeatures(na="keep")))["properties"]
|
||||
assert np.isnan(result["BoroName"])
|
||||
|
||||
# test for checking that the (non-null) features are python scalars and
|
||||
# not numpy scalars
|
||||
assert type(df.loc[0, "Shape_Leng"]) is np.float64
|
||||
# null
|
||||
result = list(df.iterfeatures(na="null"))[0]
|
||||
assert type(result["properties"]["Shape_Leng"]) is float
|
||||
result = next(iter(df.iterfeatures(na="null")))
|
||||
assert isinstance(result["properties"]["Shape_Leng"], float)
|
||||
# drop
|
||||
result = list(df.iterfeatures(na="drop"))[0]
|
||||
assert type(result["properties"]["Shape_Leng"]) is float
|
||||
result = next(iter(df.iterfeatures(na="drop")))
|
||||
assert isinstance(result["properties"]["Shape_Leng"], float)
|
||||
# keep
|
||||
result = list(df.iterfeatures(na="keep"))[0]
|
||||
assert type(result["properties"]["Shape_Leng"]) is float
|
||||
result = next(iter(df.iterfeatures(na="keep")))
|
||||
assert isinstance(result["properties"]["Shape_Leng"], float)
|
||||
|
||||
# when only having numerical columns
|
||||
df_only_numerical_cols = df[["Shape_Leng", "Shape_Area", "geometry"]]
|
||||
assert type(df_only_numerical_cols.loc[0, "Shape_Leng"]) is np.float64
|
||||
# null
|
||||
result = list(df_only_numerical_cols.iterfeatures(na="null"))[0]
|
||||
assert type(result["properties"]["Shape_Leng"]) is float
|
||||
result = next(iter(df_only_numerical_cols.iterfeatures(na="null")))
|
||||
assert isinstance(result["properties"]["Shape_Leng"], float)
|
||||
# drop
|
||||
result = list(df_only_numerical_cols.iterfeatures(na="drop"))[0]
|
||||
assert type(result["properties"]["Shape_Leng"]) is float
|
||||
result = next(iter(df_only_numerical_cols.iterfeatures(na="drop")))
|
||||
assert isinstance(result["properties"]["Shape_Leng"], float)
|
||||
# keep
|
||||
result = list(df_only_numerical_cols.iterfeatures(na="keep"))[0]
|
||||
assert type(result["properties"]["Shape_Leng"]) is float
|
||||
result = next(iter(df_only_numerical_cols.iterfeatures(na="keep")))
|
||||
assert isinstance(result["properties"]["Shape_Leng"], float)
|
||||
|
||||
with pytest.raises(
|
||||
ValueError, match="GeoDataFrame cannot contain duplicated column names."
|
||||
@@ -888,25 +928,25 @@ class TestDataFrame:
|
||||
)
|
||||
# null
|
||||
expected = {"non-scalar": [1, 2], "test_col": None}
|
||||
result = list(df.iterfeatures(na="null"))[0].get("properties")
|
||||
result = next(iter(df.iterfeatures(na="null"))).get("properties")
|
||||
assert expected == result
|
||||
# drop
|
||||
expected = {"non-scalar": [1, 2]}
|
||||
result = list(df.iterfeatures(na="drop"))[0].get("properties")
|
||||
result = next(iter(df.iterfeatures(na="drop"))).get("properties")
|
||||
assert expected == result
|
||||
# keep
|
||||
expected = {"non-scalar": [1, 2], "test_col": None}
|
||||
result = list(df.iterfeatures(na="keep"))[0].get("properties")
|
||||
result = next(iter(df.iterfeatures(na="keep"))).get("properties")
|
||||
assert expected == result
|
||||
|
||||
def test_geodataframe_geojson_no_bbox(self):
|
||||
geo = self.df._to_geo(na="null", show_bbox=False)
|
||||
geo = self.df.to_geo_dict(na="null", show_bbox=False)
|
||||
assert "bbox" not in geo.keys()
|
||||
for feature in geo["features"]:
|
||||
assert "bbox" not in feature.keys()
|
||||
|
||||
def test_geodataframe_geojson_bbox(self):
|
||||
geo = self.df._to_geo(na="null", show_bbox=True)
|
||||
geo = self.df.to_geo_dict(na="null", show_bbox=True)
|
||||
assert "bbox" in geo.keys()
|
||||
assert len(geo["bbox"]) == 4
|
||||
assert isinstance(geo["bbox"], tuple)
|
||||
@@ -927,29 +967,31 @@ class TestDataFrame:
|
||||
assert self.df.crs == unpickled.crs
|
||||
|
||||
def test_estimate_utm_crs(self):
|
||||
assert self.df.estimate_utm_crs() == CRS("EPSG:32618")
|
||||
assert self.df.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
|
||||
assert self.df.estimate_utm_crs() == pyproj.CRS("EPSG:32618")
|
||||
assert self.df.estimate_utm_crs("NAD83") == pyproj.CRS("EPSG:26918")
|
||||
|
||||
def test_to_wkb(self):
|
||||
wkbs0 = [
|
||||
(
|
||||
( # POINT (0 0)
|
||||
b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
), # POINT (0 0)
|
||||
(
|
||||
),
|
||||
( # POINT (1 1)
|
||||
b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?"
|
||||
), # POINT (1 1)
|
||||
),
|
||||
]
|
||||
wkbs1 = [
|
||||
(
|
||||
( # POINT (2 2)
|
||||
b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x00@\x00\x00\x00\x00\x00\x00\x00@"
|
||||
), # POINT (2 2)
|
||||
(
|
||||
),
|
||||
( # POINT (3 3)
|
||||
b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
b"\x00\x08@\x00\x00\x00\x00\x00\x00\x08@"
|
||||
), # POINT (3 3)
|
||||
),
|
||||
]
|
||||
gs0 = GeoSeries.from_wkb(wkbs0)
|
||||
gs1 = GeoSeries.from_wkb(wkbs1)
|
||||
@@ -970,40 +1012,51 @@ class TestDataFrame:
|
||||
|
||||
@pytest.mark.parametrize("how", ["left", "inner", "right"])
|
||||
@pytest.mark.parametrize("predicate", ["intersects", "within", "contains"])
|
||||
@pytest.mark.skipif(
|
||||
not (compat.USE_PYGEOS or compat.USE_SHAPELY_20 or compat.HAS_RTREE),
|
||||
reason="sjoin needs `rtree` or `pygeos` dependency",
|
||||
)
|
||||
def test_sjoin(self, how, predicate):
|
||||
def test_sjoin(self, how, predicate, naturalearth_cities, naturalearth_lowres):
|
||||
"""
|
||||
Basic test for availability of the GeoDataFrame method. Other
|
||||
sjoin tests are located in /tools/tests/test_sjoin.py
|
||||
"""
|
||||
left = read_file(geopandas.datasets.get_path("naturalearth_cities"))
|
||||
right = read_file(geopandas.datasets.get_path("naturalearth_lowres"))
|
||||
left = read_file(naturalearth_cities)
|
||||
right = read_file(naturalearth_lowres)
|
||||
|
||||
expected = geopandas.sjoin(left, right, how=how, predicate=predicate)
|
||||
result = left.sjoin(right, how=how, predicate=predicate)
|
||||
assert_geodataframe_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize("how", ["left", "inner", "right"])
|
||||
@pytest.mark.parametrize("distance", [0, 3])
|
||||
@pytest.mark.skipif(
|
||||
not compat.GEOS_GE_310,
|
||||
reason="`dwithin` requires GEOS 3.10",
|
||||
)
|
||||
def test_sjoin_dwithin(self, how, distance):
|
||||
"""
|
||||
Basic test for predicate='dwithin' availability of the GeoDataFrame method.
|
||||
Other sjoin tests are located in /tools/tests/test_sjoin.py
|
||||
"""
|
||||
left = GeoDataFrame(geometry=points_from_xy([0, 1, 2], [0, 1, 1]))
|
||||
right = GeoDataFrame(geometry=[box(0, 0, 1, 1)])
|
||||
|
||||
expected = geopandas.sjoin(
|
||||
left, right, how=how, predicate="dwithin", distance=distance
|
||||
)
|
||||
result = left.sjoin(right, how=how, predicate="dwithin", distance=distance)
|
||||
assert_geodataframe_equal(result, expected)
|
||||
|
||||
@pytest.mark.parametrize("how", ["left", "inner", "right"])
|
||||
@pytest.mark.parametrize("max_distance", [None, 1])
|
||||
@pytest.mark.parametrize("distance_col", [None, "distance"])
|
||||
@pytest.mark.skipif(
|
||||
not TEST_NEAREST,
|
||||
reason=(
|
||||
"PyGEOS >= 0.10.0"
|
||||
" must be installed and activated via the geopandas.compat module to"
|
||||
" test sjoin_nearest"
|
||||
),
|
||||
)
|
||||
def test_sjoin_nearest(self, how, max_distance, distance_col):
|
||||
@pytest.mark.filterwarnings("ignore:Geometry is in a geographic CRS:UserWarning")
|
||||
def test_sjoin_nearest(
|
||||
self, how, max_distance, distance_col, naturalearth_cities, naturalearth_lowres
|
||||
):
|
||||
"""
|
||||
Basic test for availability of the GeoDataFrame method. Other
|
||||
sjoin tests are located in /tools/tests/test_sjoin.py
|
||||
"""
|
||||
left = read_file(geopandas.datasets.get_path("naturalearth_cities"))
|
||||
right = read_file(geopandas.datasets.get_path("naturalearth_lowres"))
|
||||
left = read_file(naturalearth_cities)
|
||||
right = read_file(naturalearth_lowres)
|
||||
|
||||
expected = geopandas.sjoin_nearest(
|
||||
left, right, how=how, max_distance=max_distance, distance_col=distance_col
|
||||
@@ -1013,21 +1066,42 @@ class TestDataFrame:
|
||||
)
|
||||
assert_geodataframe_equal(result, expected)
|
||||
|
||||
@pytest.mark.skip_no_sindex
|
||||
def test_clip(self):
|
||||
def test_clip(self, naturalearth_cities, naturalearth_lowres):
|
||||
"""
|
||||
Basic test for availability of the GeoDataFrame method. Other
|
||||
clip tests are located in /tools/tests/test_clip.py
|
||||
"""
|
||||
left = read_file(geopandas.datasets.get_path("naturalearth_cities"))
|
||||
world = read_file(geopandas.datasets.get_path("naturalearth_lowres"))
|
||||
left = read_file(naturalearth_cities)
|
||||
world = read_file(naturalearth_lowres)
|
||||
south_america = world[world["continent"] == "South America"]
|
||||
|
||||
expected = geopandas.clip(left, south_america)
|
||||
result = left.clip(south_america)
|
||||
assert_geodataframe_equal(result, expected)
|
||||
|
||||
@pytest.mark.skip_no_sindex
|
||||
def test_clip_sorting(self, naturalearth_cities, naturalearth_lowres):
|
||||
"""
|
||||
Test sorting of geodataframe when clipping.
|
||||
"""
|
||||
cities = read_file(naturalearth_cities)
|
||||
world = read_file(naturalearth_lowres)
|
||||
south_america = world[world["continent"] == "South America"]
|
||||
|
||||
unsorted_clipped_cities = geopandas.clip(cities, south_america, sort=False)
|
||||
sorted_clipped_cities = geopandas.clip(cities, south_america, sort=True)
|
||||
|
||||
expected_sorted_index = pd.Index(
|
||||
[55, 59, 62, 88, 101, 114, 122, 169, 181, 189, 210, 230, 236, 238, 239]
|
||||
)
|
||||
|
||||
assert not (
|
||||
sorted(unsorted_clipped_cities.index) == unsorted_clipped_cities.index
|
||||
).all()
|
||||
assert (
|
||||
sorted(sorted_clipped_cities.index) == sorted_clipped_cities.index
|
||||
).all()
|
||||
assert_index_equal(expected_sorted_index, sorted_clipped_cities.index)
|
||||
|
||||
def test_overlay(self, dfs, how):
|
||||
"""
|
||||
Basic test for availability of the GeoDataFrame method. Other
|
||||
@@ -1138,8 +1212,7 @@ class TestConstructor:
|
||||
"B": np.arange(3.0),
|
||||
"geometry": [Point(x, x) for x in range(3)],
|
||||
}
|
||||
with ignore_shapely2_warnings():
|
||||
a = np.array([data["A"], data["B"], data["geometry"]], dtype=object).T
|
||||
a = np.array([data["A"], data["B"], data["geometry"]], dtype=object).T
|
||||
|
||||
df = GeoDataFrame(a, columns=["A", "B", "geometry"])
|
||||
check_geodataframe(df)
|
||||
@@ -1154,8 +1227,7 @@ class TestConstructor:
|
||||
"geometry": [Point(x, x) for x in range(3)],
|
||||
}
|
||||
gpdf = GeoDataFrame(data)
|
||||
with ignore_shapely2_warnings():
|
||||
pddf = pd.DataFrame(data)
|
||||
pddf = pd.DataFrame(data)
|
||||
check_geodataframe(gpdf)
|
||||
assert type(pddf) == pd.DataFrame
|
||||
|
||||
@@ -1184,8 +1256,7 @@ class TestConstructor:
|
||||
|
||||
gpdf = GeoDataFrame(data, geometry="other_geom")
|
||||
check_geodataframe(gpdf, "other_geom")
|
||||
with ignore_shapely2_warnings():
|
||||
pddf = pd.DataFrame(data)
|
||||
pddf = pd.DataFrame(data)
|
||||
|
||||
for df in [gpdf, pddf]:
|
||||
res = GeoDataFrame(df, geometry="other_geom")
|
||||
@@ -1240,7 +1311,7 @@ class TestConstructor:
|
||||
geometry="geometry",
|
||||
)
|
||||
check_geodataframe(gdf)
|
||||
gdf.columns == ["geometry", "a"]
|
||||
assert list(gdf.columns) == ["geometry", "a"]
|
||||
|
||||
# with non-default index
|
||||
gdf = GeoDataFrame(
|
||||
@@ -1250,27 +1321,24 @@ class TestConstructor:
|
||||
geometry="geometry",
|
||||
)
|
||||
check_geodataframe(gdf)
|
||||
gdf.columns == ["geometry", "a"]
|
||||
assert list(gdf.columns) == ["geometry", "a"]
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_preserve_series_name(self):
|
||||
def test_do_not_preserve_series_name_in_constructor(self):
|
||||
# GH3337
|
||||
# GeoDataFrame(... geometry=...) should always create geom col "geometry"
|
||||
geoms = [Point(1, 1), Point(2, 2), Point(3, 3)]
|
||||
gs = GeoSeries(geoms)
|
||||
gdf = GeoDataFrame({"a": [1, 2, 3]}, geometry=gs)
|
||||
|
||||
check_geodataframe(gdf, geometry_column="geometry")
|
||||
|
||||
geoms = [Point(1, 1), Point(2, 2), Point(3, 3)]
|
||||
# still get "geometry", even with custom geoseries name
|
||||
gs = GeoSeries(geoms, name="my_geom")
|
||||
gdf = GeoDataFrame({"a": [1, 2, 3]}, geometry=gs)
|
||||
|
||||
check_geodataframe(gdf, geometry_column="my_geom")
|
||||
check_geodataframe(gdf, geometry_column="geometry")
|
||||
|
||||
def test_overwrite_geometry(self):
|
||||
# GH602
|
||||
data = pd.DataFrame({"geometry": [1, 2, 3], "col1": [4, 5, 6]})
|
||||
with ignore_shapely2_warnings():
|
||||
geoms = pd.Series([Point(i, i) for i in range(3)])
|
||||
geoms = pd.Series([Point(i, i) for i in range(3)])
|
||||
# passed geometry kwarg should overwrite geometry column in data
|
||||
res = GeoDataFrame(data, geometry=geoms)
|
||||
assert_geoseries_equal(res.geometry, GeoSeries(geoms))
|
||||
@@ -1329,7 +1397,8 @@ class TestConstructor:
|
||||
):
|
||||
gdf5["geometry"] = "foo"
|
||||
assert gdf5._geometry_column_name is None
|
||||
gdf3 = gdf.copy().assign(geometry=geo_col)
|
||||
with pytest.warns(FutureWarning, match=match):
|
||||
gdf3 = gdf.copy().assign(geometry=geo_col)
|
||||
assert gdf3._geometry_column_name == "geometry"
|
||||
|
||||
# Check that adding a GeoSeries to a column called "geometry" to a
|
||||
@@ -1355,8 +1424,9 @@ class TestConstructor:
|
||||
y_col = df["location", "y"]
|
||||
|
||||
gdf = GeoDataFrame(df, crs=crs, geometry=points_from_xy(x_col, y_col))
|
||||
assert gdf.crs == crs
|
||||
assert gdf.geometry.crs == crs
|
||||
if compat.HAS_PYPROJ:
|
||||
assert gdf.crs == crs
|
||||
assert gdf.geometry.crs == crs
|
||||
assert gdf.geometry.dtype == "geometry"
|
||||
assert gdf._geometry_column_name == "geometry"
|
||||
assert gdf.geometry.name == "geometry"
|
||||
@@ -1378,8 +1448,9 @@ class TestConstructor:
|
||||
y_col = df["foo", "location", "y"]
|
||||
|
||||
gdf = GeoDataFrame(df, crs=crs, geometry=points_from_xy(x_col, y_col))
|
||||
assert gdf.crs == crs
|
||||
assert gdf.geometry.crs == crs
|
||||
if compat.HAS_PYPROJ:
|
||||
assert gdf.crs == crs
|
||||
assert gdf.geometry.crs == crs
|
||||
assert gdf.geometry.dtype == "geometry"
|
||||
assert gdf._geometry_column_name == "geometry"
|
||||
assert gdf.geometry.name == "geometry"
|
||||
@@ -1400,17 +1471,18 @@ class TestConstructor:
|
||||
df["geometry"] = GeoSeries.from_xy(x_col, y_col)
|
||||
df2 = df.copy()
|
||||
gdf = df.set_geometry("geometry", crs=crs)
|
||||
assert gdf.crs == crs
|
||||
if compat.HAS_PYPROJ:
|
||||
assert gdf.crs == crs
|
||||
assert gdf._geometry_column_name == "geometry"
|
||||
assert gdf.geometry.name == "geometry"
|
||||
# test again setting with tuple col name
|
||||
gdf = df2.set_geometry(("geometry", "", ""), crs=crs)
|
||||
assert gdf.crs == crs
|
||||
if compat.HAS_PYPROJ:
|
||||
assert gdf.crs == crs
|
||||
assert gdf._geometry_column_name == ("geometry", "", "")
|
||||
assert gdf.geometry.name == ("geometry", "", "")
|
||||
|
||||
def test_assign_cols_using_index(self):
|
||||
nybb_filename = geopandas.datasets.get_path("nybb")
|
||||
def test_assign_cols_using_index(self, nybb_filename):
|
||||
df = read_file(nybb_filename)
|
||||
other_df = pd.DataFrame({"foo": range(5), "bar": range(5)})
|
||||
expected = pd.concat([df, other_df], axis=1)
|
||||
@@ -1418,6 +1490,7 @@ class TestConstructor:
|
||||
assert_geodataframe_equal(df, expected)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_geodataframe_crs():
|
||||
gdf = GeoDataFrame(columns=["geometry"])
|
||||
gdf.crs = "IGNF:ETRS89UTM28"
|
||||
@@ -1436,6 +1509,7 @@ def test_geodataframe_nocrs_json():
|
||||
assert "crs" not in gdf_geojson
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_geodataframe_crs_json():
|
||||
gdf = GeoDataFrame(columns=["geometry"])
|
||||
gdf.crs = 25833
|
||||
@@ -1449,6 +1523,7 @@ def test_geodataframe_crs_json():
|
||||
assert "crs" not in gdf_geointerface
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
@pytest.mark.parametrize(
|
||||
"crs",
|
||||
["+proj=cea +lon_0=0 +lat_ts=45 +x_0=0 +y_0=0 +ellps=WGS84 +units=m", "IGNF:WGS84"],
|
||||
@@ -1472,3 +1547,71 @@ def test_geodataframe_crs_colname():
|
||||
assert gdf.crs is None
|
||||
assert gdf["crs"].iloc[0] == 1
|
||||
assert getattr(gdf, "crs") is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("geo_col_name", ["geometry", "polygons"])
|
||||
def test_set_geometry_supply_colname(dfs, geo_col_name):
|
||||
df, _ = dfs
|
||||
if geo_col_name != "geometry":
|
||||
df = df.rename_geometry(geo_col_name)
|
||||
df["centroid"] = df.geometry.centroid
|
||||
res = df.set_geometry("centroid")
|
||||
assert res.active_geometry_name == "centroid"
|
||||
assert geo_col_name in res.columns
|
||||
|
||||
# Test that drop=False explicitly warns
|
||||
deprecated = "The `drop` keyword argument is deprecated"
|
||||
with pytest.warns(FutureWarning, match=deprecated):
|
||||
res2 = df.set_geometry("centroid", drop=False)
|
||||
assert_geodataframe_equal(res, res2)
|
||||
|
||||
with pytest.warns(FutureWarning, match=deprecated):
|
||||
res3 = df.set_geometry("centroid", drop=True)
|
||||
# drop=True should preserve previous geometry col name (keep old behaviour)
|
||||
assert res3.active_geometry_name == geo_col_name
|
||||
assert "centroid" not in res3.columns
|
||||
|
||||
# Test that alternative suggested without using drop=True is equivalent
|
||||
assert_geodataframe_equal(
|
||||
res3,
|
||||
df.set_geometry("centroid")
|
||||
.drop(columns=geo_col_name)
|
||||
.rename_geometry(geo_col_name),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("geo_col_name", ["geometry", "polygons"])
|
||||
def test_set_geometry_supply_arraylike(dfs, geo_col_name):
|
||||
df, _ = dfs
|
||||
if geo_col_name != "geometry":
|
||||
df = df.rename_geometry(geo_col_name)
|
||||
centroids = df.geometry.centroid
|
||||
res = df.set_geometry(centroids)
|
||||
assert res.active_geometry_name == geo_col_name
|
||||
# drop should do nothing if the column already exists
|
||||
match_str = (
|
||||
"The `drop` keyword argument is deprecated and has no effect when "
|
||||
"`col` is an array-like value"
|
||||
)
|
||||
with pytest.warns(
|
||||
FutureWarning,
|
||||
match=match_str,
|
||||
):
|
||||
res2 = df.set_geometry(centroids, drop=True)
|
||||
assert res2.active_geometry_name == geo_col_name
|
||||
|
||||
centroids = centroids.rename("centroids")
|
||||
res3 = df.set_geometry(centroids)
|
||||
# Should preserve the geoseries name
|
||||
# (and old geometry column should be kept)
|
||||
assert res3.active_geometry_name == "centroids"
|
||||
assert geo_col_name in res3.columns
|
||||
|
||||
# Drop should not remove previous active geometry colname for arraylike inputs
|
||||
with pytest.warns(
|
||||
FutureWarning,
|
||||
match=match_str,
|
||||
):
|
||||
res4 = df.set_geometry(centroids, drop=True)
|
||||
assert res4.active_geometry_name == "centroids"
|
||||
assert geo_col_name in res4.columns
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal
|
||||
import pandas as pd
|
||||
from pandas.testing import assert_index_equal
|
||||
|
||||
from pyproj import CRS
|
||||
from shapely.geometry import (
|
||||
GeometryCollection,
|
||||
LineString,
|
||||
@@ -23,14 +19,15 @@ from shapely.geometry import (
|
||||
)
|
||||
from shapely.geometry.base import BaseGeometry
|
||||
|
||||
from geopandas import GeoSeries, GeoDataFrame, read_file, datasets, clip
|
||||
from geopandas._compat import ignore_shapely2_warnings
|
||||
import geopandas._compat as compat
|
||||
from geopandas import GeoDataFrame, GeoSeries, clip, read_file
|
||||
from geopandas.array import GeometryArray, GeometryDtype
|
||||
from geopandas.testing import assert_geoseries_equal, geom_almost_equals
|
||||
|
||||
from geopandas.tests.util import geom_equals
|
||||
from pandas.testing import assert_series_equal
|
||||
import pytest
|
||||
from geopandas.testing import assert_geoseries_equal, geom_almost_equals
|
||||
from geopandas.tests.util import geom_equals
|
||||
from numpy.testing import assert_array_equal
|
||||
from pandas.testing import assert_index_equal, assert_series_equal
|
||||
|
||||
|
||||
class TestSeries:
|
||||
@@ -41,8 +38,7 @@ class TestSeries:
|
||||
self.sq = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
|
||||
self.g1 = GeoSeries([self.t1, self.sq])
|
||||
self.g2 = GeoSeries([self.sq, self.t1])
|
||||
self.g3 = GeoSeries([self.t1, self.t2])
|
||||
self.g3.crs = "epsg:4326"
|
||||
self.g3 = GeoSeries([self.t1, self.t2], crs="epsg:4326")
|
||||
self.g4 = GeoSeries([self.t2, self.t1])
|
||||
self.na = GeoSeries([self.t1, self.t2, Polygon()])
|
||||
self.na_none = GeoSeries([self.t1, self.t2, None])
|
||||
@@ -56,6 +52,9 @@ class TestSeries:
|
||||
self.l1 = LineString([(0, 0), (0, 1), (1, 1)])
|
||||
self.l2 = LineString([(0, 0), (1, 0), (1, 1), (0, 1)])
|
||||
self.g5 = GeoSeries([self.l1, self.l2])
|
||||
self.esb3857 = Point(-8235939.130493107, 4975301.253789809)
|
||||
self.sol3857 = Point(-8242607.167991625, 4966620.938285081)
|
||||
self.landmarks3857 = GeoSeries([self.esb3857, self.sol3857], crs="epsg:3857")
|
||||
|
||||
def teardown_method(self):
|
||||
shutil.rmtree(self.tempdir)
|
||||
@@ -82,18 +81,16 @@ class TestSeries:
|
||||
assert a1["B"].equals(a2["B"])
|
||||
assert a1["C"] is None
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_align_crs(self):
|
||||
a1 = self.a1
|
||||
a1.crs = "epsg:4326"
|
||||
a2 = self.a2
|
||||
a2.crs = "epsg:31370"
|
||||
a1 = self.a1.set_crs("epsg:4326")
|
||||
a2 = self.a2.set_crs("epsg:31370")
|
||||
|
||||
res1, res2 = a1.align(a2)
|
||||
assert res1.crs == "epsg:4326"
|
||||
assert res2.crs == "epsg:31370"
|
||||
|
||||
a2.crs = None
|
||||
res1, res2 = a1.align(a2)
|
||||
res1, res2 = a1.align(a2.set_crs(None, allow_override=True))
|
||||
assert res1.crs == "epsg:4326"
|
||||
assert res2.crs is None
|
||||
|
||||
@@ -110,11 +107,11 @@ class TestSeries:
|
||||
# Test that warning is issued when operating on non-aligned series
|
||||
|
||||
# _series_op
|
||||
with pytest.warns(UserWarning, match="The indices .+ different"):
|
||||
with pytest.warns(UserWarning, match="The indices .+ not equal"):
|
||||
self.a1.contains(self.a2)
|
||||
|
||||
# _geo_op
|
||||
with pytest.warns(UserWarning, match="The indices .+ different"):
|
||||
with pytest.warns(UserWarning, match="The indices .+ not equal"):
|
||||
self.a1.union(self.a2)
|
||||
|
||||
def test_no_warning_if_aligned(self):
|
||||
@@ -136,8 +133,7 @@ class TestSeries:
|
||||
assert_array_equal(self.g1.geom_equals(self.sq), [False, True])
|
||||
|
||||
def test_geom_equals_align(self):
|
||||
with pytest.warns(UserWarning, match="The indices .+ different"):
|
||||
a = self.a1.geom_equals(self.a2, align=True)
|
||||
a = self.a1.geom_equals(self.a2, align=True)
|
||||
exp = pd.Series([False, True, False], index=["A", "B", "C"])
|
||||
assert_series_equal(a, exp)
|
||||
|
||||
@@ -145,27 +141,39 @@ class TestSeries:
|
||||
exp = pd.Series([False, False], index=["A", "B"])
|
||||
assert_series_equal(a, exp)
|
||||
|
||||
@pytest.mark.filterwarnings(r"ignore:The 'geom_almost_equals\(\)':FutureWarning")
|
||||
def test_geom_almost_equals(self):
|
||||
# TODO: test decimal parameter
|
||||
with pytest.warns(FutureWarning, match=re.escape("The 'geom_almost_equals()'")):
|
||||
assert np.all(self.g1.geom_almost_equals(self.g1))
|
||||
assert_array_equal(self.g1.geom_almost_equals(self.sq), [False, True])
|
||||
|
||||
assert_array_equal(
|
||||
self.a1.geom_almost_equals(self.a2, align=True), [False, True, False]
|
||||
assert np.all(self.g1.geom_almost_equals(self.g1))
|
||||
assert_array_equal(self.g1.geom_almost_equals(self.sq), [False, True])
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(
|
||||
"ignore",
|
||||
"The indices of the left and right GeoSeries' are not equal",
|
||||
UserWarning,
|
||||
)
|
||||
assert_array_equal(
|
||||
self.a1.geom_almost_equals(self.a2, align=False), [False, False]
|
||||
self.a1.geom_almost_equals(self.a2, align=True),
|
||||
[False, True, False],
|
||||
)
|
||||
assert_array_equal(
|
||||
self.a1.geom_almost_equals(self.a2, align=False), [False, False]
|
||||
)
|
||||
|
||||
def test_geom_equals_exact(self):
|
||||
# TODO: test tolerance parameter
|
||||
assert np.all(self.g1.geom_equals_exact(self.g1, 0.001))
|
||||
assert_array_equal(self.g1.geom_equals_exact(self.sq, 0.001), [False, True])
|
||||
|
||||
assert_array_equal(
|
||||
self.a1.geom_equals_exact(self.a2, 0.001, align=True), [False, True, False]
|
||||
)
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(
|
||||
"ignore",
|
||||
"The indices of the left and right GeoSeries' are not equal",
|
||||
UserWarning,
|
||||
)
|
||||
assert_array_equal(
|
||||
self.a1.geom_equals_exact(self.a2, 0.001, align=True),
|
||||
[False, True, False],
|
||||
)
|
||||
assert_array_equal(
|
||||
self.a1.geom_equals_exact(self.a2, 0.001, align=False), [False, False]
|
||||
)
|
||||
@@ -190,15 +198,69 @@ class TestSeries:
|
||||
Test whether GeoSeries.to_json works and returns an actual json file.
|
||||
"""
|
||||
json_str = self.g3.to_json()
|
||||
json.loads(json_str)
|
||||
data = json.loads(json_str)
|
||||
assert "id" in data["features"][0].keys()
|
||||
assert "bbox" in data["features"][0].keys()
|
||||
# TODO : verify the output is a valid GeoJSON.
|
||||
|
||||
def test_to_json_drop_id(self):
|
||||
"""
|
||||
Test whether GeoSeries.to_json works when drop_id is True.
|
||||
"""
|
||||
json_str = self.g3.to_json(drop_id=True)
|
||||
data = json.loads(json_str)
|
||||
assert "id" not in data["features"][0].keys()
|
||||
|
||||
def test_to_json_no_bbox(self):
|
||||
"""
|
||||
Test whether GeoSeries.to_json works when show_bbox is False.
|
||||
"""
|
||||
json_str = self.g3.to_json(show_bbox=False)
|
||||
data = json.loads(json_str)
|
||||
assert "bbox" not in data["features"][0].keys()
|
||||
|
||||
def test_to_json_no_bbox_drop_id(self):
|
||||
"""
|
||||
Test whether GeoSeries.to_json works when show_bbox is False
|
||||
and drop_id is True.
|
||||
"""
|
||||
json_str = self.g3.to_json(show_bbox=False, drop_id=True)
|
||||
data = json.loads(json_str)
|
||||
assert "id" not in data["features"][0].keys()
|
||||
assert "bbox" not in data["features"][0].keys()
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="Requires pyproj")
|
||||
def test_to_json_wgs84(self):
|
||||
"""
|
||||
Test whether the wgs84 conversion works as intended.
|
||||
"""
|
||||
text = self.landmarks3857.to_json(to_wgs84=True)
|
||||
data = json.loads(text)
|
||||
assert data["type"] == "FeatureCollection"
|
||||
assert "id" in data["features"][0].keys()
|
||||
coord1 = data["features"][0]["geometry"]["coordinates"]
|
||||
coord2 = data["features"][1]["geometry"]["coordinates"]
|
||||
np.testing.assert_allclose(coord1, self.esb.coords[0])
|
||||
np.testing.assert_allclose(coord2, self.sol.coords[0])
|
||||
|
||||
def test_to_json_wgs84_false(self):
|
||||
"""
|
||||
Ensure no conversion to wgs84
|
||||
"""
|
||||
text = self.landmarks3857.to_json()
|
||||
data = json.loads(text)
|
||||
coord1 = data["features"][0]["geometry"]["coordinates"]
|
||||
coord2 = data["features"][1]["geometry"]["coordinates"]
|
||||
assert coord1 == [-8235939.130493107, 4975301.253789809]
|
||||
assert coord2 == [-8242607.167991625, 4966620.938285081]
|
||||
|
||||
def test_representative_point(self):
|
||||
assert np.all(self.g1.contains(self.g1.representative_point()))
|
||||
assert np.all(self.g2.contains(self.g2.representative_point()))
|
||||
assert np.all(self.g3.contains(self.g3.representative_point()))
|
||||
assert np.all(self.g4.contains(self.g4.representative_point()))
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_transform(self):
|
||||
utm18n = self.landmarks.to_crs(epsg=26918)
|
||||
lonlat = utm18n.to_crs(epsg=4326)
|
||||
@@ -209,20 +271,24 @@ class TestSeries:
|
||||
self.landmarks.to_crs(crs=None, epsg=None)
|
||||
|
||||
def test_estimate_utm_crs__geographic(self):
|
||||
assert self.landmarks.estimate_utm_crs() == CRS("EPSG:32618")
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == CRS("EPSG:26918")
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
assert self.landmarks.estimate_utm_crs() == pyproj.CRS("EPSG:32618")
|
||||
assert self.landmarks.estimate_utm_crs("NAD83") == pyproj.CRS("EPSG:26918")
|
||||
|
||||
def test_estimate_utm_crs__projected(self):
|
||||
assert self.landmarks.to_crs("EPSG:3857").estimate_utm_crs() == CRS(
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
assert self.landmarks.to_crs("EPSG:3857").estimate_utm_crs() == pyproj.CRS(
|
||||
"EPSG:32618"
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
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(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
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()
|
||||
@@ -258,6 +324,7 @@ class TestSeries:
|
||||
assert self.g1.__geo_interface__["type"] == "FeatureCollection"
|
||||
assert len(self.g1.__geo_interface__["features"]) == self.g1.shape[0]
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_proj4strings(self):
|
||||
# As string
|
||||
reprojected = self.g3.to_crs("+proj=utm +zone=30")
|
||||
@@ -270,8 +337,7 @@ class TestSeries:
|
||||
assert geom_almost_equals(self.g3, reprojected_back)
|
||||
|
||||
# Set to equivalent string, convert, compare to original
|
||||
copy = self.g3.copy()
|
||||
copy.crs = "epsg:4326"
|
||||
copy = self.g3.copy().set_crs("epsg:4326", allow_override=True)
|
||||
reprojected = copy.to_crs({"proj": "utm", "zone": "30"})
|
||||
reprojected_back = reprojected.to_crs(epsg=4326)
|
||||
assert geom_almost_equals(self.g3, reprojected_back)
|
||||
@@ -284,6 +350,23 @@ class TestSeries:
|
||||
def test_from_wkb(self):
|
||||
assert_geoseries_equal(self.g1, GeoSeries.from_wkb([self.t1.wkb, self.sq.wkb]))
|
||||
|
||||
def test_from_wkb_on_invalid(self):
|
||||
# Single point LineString hex WKB: invalid
|
||||
invalid_wkb_hex = "01020000000100000000000000000008400000000000000840"
|
||||
message = "point array must contain 0 or >1 elements"
|
||||
|
||||
with pytest.raises(Exception, match=message):
|
||||
GeoSeries.from_wkb([invalid_wkb_hex], on_invalid="raise")
|
||||
|
||||
with pytest.warns(Warning, match=message):
|
||||
res = GeoSeries.from_wkb([invalid_wkb_hex], on_invalid="warn")
|
||||
assert res[0] is None
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
res = GeoSeries.from_wkb([invalid_wkb_hex], on_invalid="ignore")
|
||||
assert res[0] is None
|
||||
|
||||
def test_from_wkb_series(self):
|
||||
s = pd.Series([self.t1.wkb, self.sq.wkb], index=[1, 2])
|
||||
expected = self.g1.copy()
|
||||
@@ -299,6 +382,23 @@ class TestSeries:
|
||||
def test_from_wkt(self):
|
||||
assert_geoseries_equal(self.g1, GeoSeries.from_wkt([self.t1.wkt, self.sq.wkt]))
|
||||
|
||||
def test_from_wkt_on_invalid(self):
|
||||
# Single point LineString WKT: invalid
|
||||
invalid_wkt = "LINESTRING(0 0)"
|
||||
message = "point array must contain 0 or >1 elements"
|
||||
|
||||
with pytest.raises(Exception, match=message):
|
||||
GeoSeries.from_wkt([invalid_wkt], on_invalid="raise")
|
||||
|
||||
with pytest.warns(Warning, match=message):
|
||||
res = GeoSeries.from_wkt([invalid_wkt], on_invalid="warn")
|
||||
assert res[0] is None
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
res = GeoSeries.from_wkt([invalid_wkt], on_invalid="ignore")
|
||||
assert res[0] is None
|
||||
|
||||
def test_from_wkt_series(self):
|
||||
s = pd.Series([self.t1.wkt, self.sq.wkt], index=[1, 2])
|
||||
expected = self.g1.copy()
|
||||
@@ -320,16 +420,38 @@ class TestSeries:
|
||||
def test_to_wkt(self):
|
||||
assert_series_equal(pd.Series([self.t1.wkt, self.sq.wkt]), self.g1.to_wkt())
|
||||
|
||||
@pytest.mark.skip_no_sindex
|
||||
def test_clip(self):
|
||||
left = read_file(datasets.get_path("naturalearth_cities"))
|
||||
world = read_file(datasets.get_path("naturalearth_lowres"))
|
||||
def test_clip(self, naturalearth_lowres, naturalearth_cities):
|
||||
left = read_file(naturalearth_cities)
|
||||
world = read_file(naturalearth_lowres)
|
||||
south_america = world[world["continent"] == "South America"]
|
||||
|
||||
expected = clip(left.geometry, south_america)
|
||||
result = left.geometry.clip(south_america)
|
||||
assert_geoseries_equal(result, expected)
|
||||
|
||||
def test_clip_sorting(self, naturalearth_cities, naturalearth_lowres):
|
||||
"""
|
||||
Test sorting of geodseries when clipping.
|
||||
"""
|
||||
cities = read_file(naturalearth_cities)
|
||||
world = read_file(naturalearth_lowres)
|
||||
south_america = world[world["continent"] == "South America"]
|
||||
|
||||
unsorted_clipped_cities = clip(cities, south_america, sort=False)
|
||||
sorted_clipped_cities = clip(cities, south_america, sort=True)
|
||||
|
||||
expected_sorted_index = pd.Index(
|
||||
[55, 59, 62, 88, 101, 114, 122, 169, 181, 189, 210, 230, 236, 238, 239]
|
||||
)
|
||||
|
||||
assert not (
|
||||
sorted(unsorted_clipped_cities.index) == unsorted_clipped_cities.index
|
||||
).all()
|
||||
assert (
|
||||
sorted(sorted_clipped_cities.index) == sorted_clipped_cities.index
|
||||
).all()
|
||||
assert_index_equal(expected_sorted_index, sorted_clipped_cities.index)
|
||||
|
||||
def test_from_xy_points(self):
|
||||
x = self.landmarks.x.values
|
||||
y = self.landmarks.y.values
|
||||
@@ -375,6 +497,13 @@ class TestSeries:
|
||||
expected = GeoSeries([Point(0, 2, -1), Point(3, 5, 4)])
|
||||
assert_geoseries_equal(expected, GeoSeries.from_xy(x, y, z))
|
||||
|
||||
@pytest.mark.skipif(compat.HAS_PYPROJ, reason="pyproj installed")
|
||||
def test_set_crs_pyproj_error(self):
|
||||
with pytest.raises(
|
||||
ImportError, match="The 'pyproj' package is required for set_crs"
|
||||
):
|
||||
self.g1.set_crs(3857)
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore::UserWarning")
|
||||
def test_missing_values():
|
||||
@@ -406,12 +535,22 @@ def test_isna_empty_geoseries():
|
||||
assert_series_equal(result, pd.Series([], dtype="bool"))
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_geoseries_crs():
|
||||
gs = GeoSeries()
|
||||
gs.crs = "IGNF:ETRS89UTM28"
|
||||
gs = GeoSeries().set_crs("IGNF:ETRS89UTM28")
|
||||
assert gs.crs.to_authority() == ("IGNF", "ETRS89UTM28")
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="Requires pyproj")
|
||||
def test_geoseries_override_existing_crs_warning():
|
||||
gs = GeoSeries(crs="epsg:4326")
|
||||
with pytest.warns(
|
||||
DeprecationWarning,
|
||||
match="Overriding the CRS of a GeoSeries that already has CRS",
|
||||
):
|
||||
gs.crs = "epsg:2100"
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# # Constructor tests
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -513,10 +652,8 @@ class TestConstructor:
|
||||
Polygon([(random.random(), random.random()) for _ in range(3)])
|
||||
for _ in range(10)
|
||||
]
|
||||
with ignore_shapely2_warnings():
|
||||
# the warning here is not suppressed by GeoPandas, as this is a pure
|
||||
# pandas construction call
|
||||
s = pd.Series(shapes, index=list("abcdefghij"), name="foo")
|
||||
|
||||
s = pd.Series(shapes, index=list("abcdefghij"), name="foo")
|
||||
g = GeoSeries(s)
|
||||
check_geoseries(g)
|
||||
|
||||
@@ -524,6 +661,37 @@ class TestConstructor:
|
||||
assert s.name == g.name
|
||||
assert s.index is g.index
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_from_series_no_set_crs_on_construction(self):
|
||||
# https://github.com/geopandas/geopandas/issues/2492
|
||||
# also when passing Series[geometry], ensure we don't change crs of
|
||||
# original data
|
||||
gs = GeoSeries([Point(1, 1), Point(2, 2), Point(3, 3)])
|
||||
s = pd.Series(gs)
|
||||
result = GeoSeries(s, crs=4326)
|
||||
assert s.values.crs is None
|
||||
assert gs.crs is None
|
||||
assert result.crs == "EPSG:4326"
|
||||
|
||||
def test_copy(self):
|
||||
# default is to copy with CoW / pandas 3+
|
||||
arr = np.array([Point(x, x) for x in range(3)], dtype=object)
|
||||
result = GeoSeries(arr)
|
||||
# modifying result doesn't change original array
|
||||
result.loc[0] = Point(10, 10)
|
||||
if compat.PANDAS_GE_30 or getattr(pd.options.mode, "copy_on_write", False):
|
||||
assert arr[0] == Point(0, 0)
|
||||
else:
|
||||
assert arr[0] == Point(10, 10)
|
||||
|
||||
# avoid copy with copy=False
|
||||
arr = np.array([Point(x, x) for x in range(3)], dtype=object)
|
||||
result = GeoSeries(arr, copy=False)
|
||||
assert result.array._data.flags.writeable
|
||||
# now modifying result also updates original array
|
||||
result.loc[0] = Point(10, 10)
|
||||
assert arr[0] == Point(10, 10)
|
||||
|
||||
# GH 1216
|
||||
@pytest.mark.parametrize("name", [None, "geometry", "Points"])
|
||||
@pytest.mark.parametrize("crs", [None, "epsg:4326"])
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import warnings
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal
|
||||
from pandas.testing import assert_index_equal
|
||||
|
||||
from shapely.geometry import Point
|
||||
|
||||
from geopandas import GeoDataFrame, GeoSeries
|
||||
from geopandas._compat import HAS_PYPROJ, PANDAS_GE_21
|
||||
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal
|
||||
from pandas.testing import assert_index_equal
|
||||
|
||||
|
||||
class TestMerging:
|
||||
@@ -59,6 +61,7 @@ class TestMerging:
|
||||
assert isinstance(res, GeoSeries)
|
||||
assert isinstance(res.geometry, GeoSeries)
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_concat_axis0_crs(self):
|
||||
# CRS not set for both GeoDataFrame
|
||||
res = pd.concat([self.gdf, self.gdf])
|
||||
@@ -100,6 +103,7 @@ class TestMerging:
|
||||
[self.gdf, self.gdf.set_crs("epsg:4326"), self.gdf.set_crs("epsg:4327")]
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_concat_axis0_unaligned_cols(self):
|
||||
# https://github.com/geopandas/geopandas/issues/2679
|
||||
gdf = self.gdf.set_crs("epsg:4326").assign(
|
||||
@@ -133,6 +137,40 @@ class TestMerging:
|
||||
partial_none_case.iloc[0] = None
|
||||
pd.concat([single_geom_col, partial_none_case])
|
||||
|
||||
def test_concat_axis0_crs_wkt_mismatch(self):
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
|
||||
# https://github.com/geopandas/geopandas/issues/326#issuecomment-1727958475
|
||||
wkt_template = """GEOGCRS["WGS 84",
|
||||
ENSEMBLE["World Geodetic System 1984 ensemble",
|
||||
MEMBER["World Geodetic System 1984 (Transit)"],
|
||||
MEMBER["World Geodetic System 1984 (G730)"],
|
||||
MEMBER["World Geodetic System 1984 (G873)"],
|
||||
MEMBER["World Geodetic System 1984 (G1150)"],
|
||||
MEMBER["World Geodetic System 1984 (G1674)"],
|
||||
MEMBER["World Geodetic System 1984 (G1762)"],
|
||||
MEMBER["World Geodetic System 1984 (G2139)"],
|
||||
ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],
|
||||
ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,
|
||||
ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],
|
||||
AXIS["geodetic latitude (Lat)",north,ORDER[1],
|
||||
ANGLEUNIT["degree",0.0174532925199433]],
|
||||
AXIS["geodetic longitude (Lon)",east,ORDER[2],
|
||||
ANGLEUNIT["degree",0.0174532925199433]],
|
||||
USAGE[SCOPE["Horizontal component of 3D system."],
|
||||
AREA["World.{}"],BBOX[-90,-180,90,180]],ID["EPSG",4326]]"""
|
||||
wkt_v1 = wkt_template.format("")
|
||||
wkt_v2 = wkt_template.format(" ") # add additional whitespace
|
||||
crs1 = pyproj.CRS.from_wkt(wkt_v1)
|
||||
crs2 = pyproj.CRS.from_wkt(wkt_v2)
|
||||
# pyproj crs __hash__ based on WKT strings means these are distinct in a
|
||||
# set are but equal by equality
|
||||
assert len({crs1, crs2}) == 2
|
||||
assert crs1 == crs2
|
||||
expected = pd.concat([self.gdf, self.gdf]).set_crs(crs1)
|
||||
res = pd.concat([self.gdf.set_crs(crs1), self.gdf.set_crs(crs2)])
|
||||
assert_geodataframe_equal(expected, res)
|
||||
|
||||
def test_concat_axis1(self):
|
||||
res = pd.concat([self.gdf, self.df], axis=1)
|
||||
|
||||
@@ -145,10 +183,18 @@ class TestMerging:
|
||||
# https://github.com/geopandas/geopandas/issues/1230
|
||||
# Expect that concat should fail gracefully if duplicate column names belonging
|
||||
# to geometry columns are introduced.
|
||||
expected_err = (
|
||||
"GeoDataFrame does not support multiple columns using the geometry"
|
||||
" column name 'geometry'"
|
||||
)
|
||||
if PANDAS_GE_21:
|
||||
# _constructor_from_mgr changes mean we now get the concat specific error
|
||||
# message in this case too
|
||||
expected_err = (
|
||||
"Concat operation has resulted in multiple columns using the geometry "
|
||||
"column name 'geometry'."
|
||||
)
|
||||
else:
|
||||
expected_err = (
|
||||
"GeoDataFrame does not support multiple columns using the geometry"
|
||||
" column name 'geometry'"
|
||||
)
|
||||
with pytest.raises(ValueError, match=expected_err):
|
||||
pd.concat([self.gdf, self.gdf], axis=1)
|
||||
|
||||
@@ -161,10 +207,11 @@ class TestMerging:
|
||||
with pytest.raises(ValueError, match=expected_err2):
|
||||
pd.concat([df2, df2], axis=1)
|
||||
|
||||
# Check that two geometry columns is fine, if they have different names
|
||||
res3 = pd.concat([df2.set_crs("epsg:4326"), self.gdf], axis=1)
|
||||
# check metadata comes from first df
|
||||
self._check_metadata(res3, geometry_column_name="geom", crs="epsg:4326")
|
||||
if HAS_PYPROJ:
|
||||
# Check that two geometry columns is fine, if they have different names
|
||||
res3 = pd.concat([df2.set_crs("epsg:4326"), self.gdf], axis=1)
|
||||
# check metadata comes from first df
|
||||
self._check_metadata(res3, geometry_column_name="geom", crs="epsg:4326")
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:Accessing CRS")
|
||||
def test_concat_axis1_geoseries(self):
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pyproj
|
||||
import pytest
|
||||
|
||||
from shapely.geometry import Point
|
||||
import numpy as np
|
||||
|
||||
import geopandas
|
||||
from geopandas import GeoDataFrame, GeoSeries
|
||||
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal
|
||||
|
||||
pyproj = pytest.importorskip("pyproj")
|
||||
|
||||
crs_osgb = pyproj.CRS(27700)
|
||||
crs_wgs = pyproj.CRS(4326)
|
||||
@@ -144,6 +147,32 @@ def test_loc(df):
|
||||
assert_object(df.loc[:, "value1"], pd.Series)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"geom_name",
|
||||
[
|
||||
"geometry",
|
||||
pytest.param(
|
||||
"geom",
|
||||
marks=pytest.mark.xfail(
|
||||
reason="pre-regression behaviour only works for geometry col geometry"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_loc_add_row(geom_name, nybb_filename):
|
||||
# https://github.com/geopandas/geopandas/issues/3119
|
||||
|
||||
nybb = geopandas.read_file(nybb_filename)[["BoroCode", "geometry"]]
|
||||
if geom_name != "geometry":
|
||||
nybb = nybb.rename_geometry(geom_name)
|
||||
# crs_orig = nybb.crs
|
||||
|
||||
# add a new row
|
||||
nybb.loc[5] = [6, nybb.geometry.iloc[0]]
|
||||
assert nybb.geometry.dtype == "geometry"
|
||||
assert nybb.crs is None # TODO this should be crs_orig, regressed in #2373
|
||||
|
||||
|
||||
def test_iloc(df):
|
||||
geo_name = df.geometry.name
|
||||
assert_object(df.iloc[:, 0:2], pd.DataFrame)
|
||||
@@ -284,7 +313,7 @@ def test_expandim_in_groupby_aggregate_multiple_funcs():
|
||||
s = GeoSeries.from_xy([0, 1, 2], [0, 1, 3])
|
||||
|
||||
def union(s):
|
||||
return s.unary_union
|
||||
return s.union_all()
|
||||
|
||||
def total_area(s):
|
||||
return s.area.sum()
|
||||
@@ -370,3 +399,13 @@ def test_constructor_sliced_in_pandas_methods(df2):
|
||||
assert type(hashable_test_df.duplicated()) == pd.Series
|
||||
assert type(df2.quantile(numeric_only=True)) == pd.Series
|
||||
assert type(df2.memory_usage()) == pd.Series
|
||||
|
||||
|
||||
def test_merge_preserve_geodataframe():
|
||||
# https://github.com/geopandas/geopandas/issues/2932
|
||||
ser = GeoSeries.from_xy([1], [1])
|
||||
df = GeoDataFrame({"geo": ser})
|
||||
res = df.merge(df, left_index=True, right_index=True)
|
||||
assert_obj_no_active_geo_col(res, GeoDataFrame, geo_colname=None)
|
||||
expected = GeoDataFrame({"geo_x": ser, "geo_y": ser})
|
||||
assert_geodataframe_equal(expected, res)
|
||||
|
||||
@@ -3,14 +3,15 @@ import os
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from shapely.geometry import Point, Polygon, LineString, GeometryCollection, box
|
||||
from shapely import make_valid
|
||||
from shapely.geometry import GeometryCollection, LineString, Point, Polygon, box
|
||||
|
||||
import geopandas
|
||||
from geopandas import GeoDataFrame, GeoSeries, overlay, read_file
|
||||
from geopandas._compat import PANDAS_GE_20
|
||||
from geopandas._compat import HAS_PYPROJ, PANDAS_GE_20
|
||||
|
||||
from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
|
||||
|
||||
try:
|
||||
from fiona.errors import DriverError
|
||||
@@ -23,9 +24,6 @@ except ImportError:
|
||||
DATA = os.path.join(os.path.abspath(os.path.dirname(__file__)), "data", "overlay")
|
||||
|
||||
|
||||
pytestmark = pytest.mark.skip_no_sindex
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dfs(request):
|
||||
s1 = GeoSeries(
|
||||
@@ -83,7 +81,7 @@ def test_overlay(dfs_index, how):
|
||||
expected = read_file(
|
||||
os.path.join(DATA, "polys", "df1_df2-{0}.geojson".format(name))
|
||||
)
|
||||
expected.crs = None
|
||||
expected.geometry.array.crs = None
|
||||
for col in expected.columns[expected.dtypes == "int32"]:
|
||||
expected[col] = expected[col].astype("int64")
|
||||
return expected
|
||||
@@ -115,8 +113,8 @@ def test_overlay(dfs_index, how):
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:GeoSeries crs mismatch:UserWarning")
|
||||
def test_overlay_nybb(how):
|
||||
polydf = read_file(geopandas.datasets.get_path("nybb"))
|
||||
def test_overlay_nybb(how, nybb_filename):
|
||||
polydf = read_file(nybb_filename)
|
||||
|
||||
# The circles have been constructed and saved at the time the expected
|
||||
# results were created (exact output of buffer algorithm can slightly
|
||||
@@ -212,6 +210,10 @@ def test_overlay_nybb(how):
|
||||
expected.loc[24, "geometry"] = None
|
||||
result.loc[24, "geometry"] = None
|
||||
|
||||
# missing values get read as None in read_file for a string column, but
|
||||
# are introduced as NaN by overlay
|
||||
expected["BoroName"] = expected["BoroName"].fillna(np.nan)
|
||||
|
||||
assert_geodataframe_equal(
|
||||
result,
|
||||
expected,
|
||||
@@ -347,6 +349,7 @@ def test_geoseries_warning(dfs):
|
||||
overlay(df1, df2.geometry, how="union")
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_preserve_crs(dfs, how):
|
||||
df1, df2 = dfs
|
||||
result = overlay(df1, df2, how=how)
|
||||
@@ -358,6 +361,7 @@ def test_preserve_crs(dfs, how):
|
||||
assert result.crs == crs
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_crs_mismatch(dfs, how):
|
||||
df1, df2 = dfs
|
||||
df1.crs = 4326
|
||||
@@ -514,6 +518,12 @@ def test_overlay_strict(how, keep_geom_type, geom_types):
|
||||
expected = expected.sort_values(cols, axis=0).reset_index(drop=True)
|
||||
result = result.sort_values(cols, axis=0).reset_index(drop=True)
|
||||
|
||||
# some columns are all-NaN in the result, but get read as object dtype
|
||||
# column of None values in read_file
|
||||
for col in ["col1", "col3", "col4"]:
|
||||
if col in expected.columns and expected[col].isna().all():
|
||||
expected[col] = expected[col].astype("float64")
|
||||
|
||||
assert_geodataframe_equal(
|
||||
result,
|
||||
expected,
|
||||
@@ -693,11 +703,11 @@ def test_keep_geom_type_geometry_collection_difference():
|
||||
assert_geodataframe_equal(result1, expected1)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("make_valid", [True, False])
|
||||
def test_overlap_make_valid(make_valid):
|
||||
@pytest.mark.parametrize("should_make_valid", [True, False])
|
||||
def test_overlap_make_valid(should_make_valid):
|
||||
bowtie = Polygon([(1, 1), (9, 9), (9, 1), (1, 9), (1, 1)])
|
||||
assert not bowtie.is_valid
|
||||
fixed_bowtie = bowtie.buffer(0)
|
||||
fixed_bowtie = make_valid(bowtie)
|
||||
assert fixed_bowtie.is_valid
|
||||
|
||||
df1 = GeoDataFrame({"col1": ["region"], "geometry": GeoSeries([box(0, 0, 10, 10)])})
|
||||
@@ -705,17 +715,17 @@ def test_overlap_make_valid(make_valid):
|
||||
{"col1": ["invalid", "valid"], "geometry": GeoSeries([bowtie, fixed_bowtie])}
|
||||
)
|
||||
|
||||
if make_valid:
|
||||
df_overlay_bowtie = overlay(df1, df_bowtie, make_valid=make_valid)
|
||||
if should_make_valid:
|
||||
df_overlay_bowtie = overlay(df1, df_bowtie, make_valid=should_make_valid)
|
||||
assert df_overlay_bowtie.at[0, "geometry"].equals(fixed_bowtie)
|
||||
assert df_overlay_bowtie.at[1, "geometry"].equals(fixed_bowtie)
|
||||
else:
|
||||
with pytest.raises(ValueError, match="1 invalid input geometries"):
|
||||
overlay(df1, df_bowtie, make_valid=make_valid)
|
||||
overlay(df1, df_bowtie, make_valid=should_make_valid)
|
||||
|
||||
|
||||
def test_empty_overlay_return_non_duplicated_columns():
|
||||
nybb = geopandas.read_file(geopandas.datasets.get_path("nybb"))
|
||||
def test_empty_overlay_return_non_duplicated_columns(nybb_filename):
|
||||
nybb = geopandas.read_file(nybb_filename)
|
||||
nybb2 = nybb.copy()
|
||||
nybb2.geometry = nybb2.translate(20000000)
|
||||
|
||||
@@ -854,7 +864,7 @@ class TestOverlayWikiExample:
|
||||
|
||||
def test_intersection(self):
|
||||
df_result = overlay(self.layer_a, self.layer_b, how="intersection")
|
||||
assert df_result.geom_equals(self.intersection).bool()
|
||||
assert df_result.geom_equals(self.intersection).all()
|
||||
|
||||
def test_union(self):
|
||||
df_result = overlay(self.layer_a, self.layer_b, how="union")
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import os
|
||||
from packaging.version import Version
|
||||
import warnings
|
||||
from packaging.version import Version
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal
|
||||
import pandas as pd
|
||||
|
||||
import shapely
|
||||
from shapely.geometry import Point, GeometryCollection, LineString, LinearRing
|
||||
from shapely.geometry import GeometryCollection, LinearRing, LineString, Point
|
||||
|
||||
import geopandas
|
||||
from geopandas import GeoDataFrame, GeoSeries
|
||||
import geopandas._compat as compat
|
||||
from geopandas import GeoDataFrame, GeoSeries
|
||||
from geopandas.array import from_shapely
|
||||
|
||||
from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
|
||||
from pandas.testing import assert_frame_equal, assert_series_equal
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
|
||||
from numpy.testing import assert_array_equal
|
||||
from pandas.testing import assert_frame_equal, assert_series_equal
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -41,6 +41,7 @@ def test_repr(s, df):
|
||||
assert "POINT" in df._repr_html_()
|
||||
|
||||
|
||||
@pytest.mark.skipif(shapely.geos_version < (3, 9, 0), reason="requires GEOS>=3.9")
|
||||
def test_repr_boxed_display_precision():
|
||||
# geographic coordinates
|
||||
p1 = Point(10.123456789, 50.123456789)
|
||||
@@ -90,7 +91,7 @@ def test_repr_empty():
|
||||
|
||||
def test_repr_linearring():
|
||||
# https://github.com/geopandas/geopandas/pull/2689
|
||||
# specifically, checking internal shapely/pygeos/wkt/wkb conversions
|
||||
# specifically, checking internal shapely/wkt/wkb conversions
|
||||
# preserve LinearRing
|
||||
s = GeoSeries([LinearRing([(0, 0), (1, 1), (1, -1)])])
|
||||
assert "LINEARRING" in str(s.iloc[0]) # shapely scalar repr
|
||||
@@ -304,17 +305,18 @@ def test_convert_dtypes(df):
|
||||
res2 = df[["value1", "value2", "geometry"]].convert_dtypes()
|
||||
assert_geodataframe_equal(expected1[["value1", "value2", "geometry"]], res2)
|
||||
|
||||
# Test again with crs set and custom geom col name
|
||||
df2 = df.set_crs(epsg=4326).rename_geometry("points")
|
||||
expected2 = GeoDataFrame(
|
||||
pd.DataFrame(df2).convert_dtypes(), crs=df2.crs, geometry=df2.geometry.name
|
||||
)
|
||||
res3 = df2.convert_dtypes()
|
||||
assert_geodataframe_equal(expected2, res3)
|
||||
if compat.HAS_PYPROJ:
|
||||
# Test again with crs set and custom geom col name
|
||||
df2 = df.set_crs(epsg=4326).rename_geometry("points")
|
||||
expected2 = GeoDataFrame(
|
||||
pd.DataFrame(df2).convert_dtypes(), crs=df2.crs, geometry=df2.geometry.name
|
||||
)
|
||||
res3 = df2.convert_dtypes()
|
||||
assert_geodataframe_equal(expected2, res3)
|
||||
|
||||
# Test geom last, geom_col=geometry
|
||||
res4 = df2[["value1", "value2", "points"]].convert_dtypes()
|
||||
assert_geodataframe_equal(expected2[["value1", "value2", "points"]], res4)
|
||||
# Test geom last, geom_col=geometry
|
||||
res4 = df2[["value1", "value2", "points"]].convert_dtypes()
|
||||
assert_geodataframe_equal(expected2[["value1", "value2", "points"]], res4)
|
||||
|
||||
|
||||
def test_to_csv(df):
|
||||
@@ -558,10 +560,9 @@ def test_value_counts():
|
||||
name = "count"
|
||||
else:
|
||||
name = None
|
||||
with compat.ignore_shapely2_warnings():
|
||||
exp = pd.Series(
|
||||
[2, 1], index=pd14_compat_index([Point(0, 0), Point(1, 1)]), name=name
|
||||
)
|
||||
exp = pd.Series(
|
||||
[2, 1], index=pd14_compat_index([Point(0, 0), Point(1, 1)]), name=name
|
||||
)
|
||||
assert_series_equal(res, exp)
|
||||
# Check crs doesn't make a difference - note it is not kept in output index anyway
|
||||
s2 = GeoSeries([Point(0, 0), Point(1, 1), Point(0, 0)], crs="EPSG:4326")
|
||||
@@ -575,20 +576,17 @@ def test_value_counts():
|
||||
s3 = GeoSeries([Point(0, 0), LineString([[1, 1], [2, 2]]), Point(0, 0)])
|
||||
res3 = s3.value_counts()
|
||||
index = pd14_compat_index([Point(0, 0), LineString([[1, 1], [2, 2]])])
|
||||
with compat.ignore_shapely2_warnings():
|
||||
exp3 = pd.Series([2, 1], index=index, name=name)
|
||||
exp3 = pd.Series([2, 1], index=index, name=name)
|
||||
assert_series_equal(res3, exp3)
|
||||
|
||||
# check None is handled
|
||||
s4 = GeoSeries([Point(0, 0), None, Point(0, 0)])
|
||||
res4 = s4.value_counts(dropna=True)
|
||||
with compat.ignore_shapely2_warnings():
|
||||
exp4_dropna = pd.Series([2], index=pd14_compat_index([Point(0, 0)]), name=name)
|
||||
exp4_dropna = pd.Series([2], index=pd14_compat_index([Point(0, 0)]), name=name)
|
||||
assert_series_equal(res4, exp4_dropna)
|
||||
with compat.ignore_shapely2_warnings():
|
||||
exp4_keepna = pd.Series(
|
||||
[2, 1], index=pd14_compat_index([Point(0, 0), None]), name=name
|
||||
)
|
||||
exp4_keepna = pd.Series(
|
||||
[2, 1], index=pd14_compat_index([Point(0, 0), None]), name=name
|
||||
)
|
||||
res4_keepna = s4.value_counts(dropna=False)
|
||||
assert_series_equal(res4_keepna, exp4_keepna)
|
||||
|
||||
@@ -636,7 +634,7 @@ def test_groupby(df):
|
||||
assert_frame_equal(res, exp)
|
||||
|
||||
# applying on the geometry column
|
||||
res = df.groupby("value2")["geometry"].apply(lambda x: x.unary_union)
|
||||
res = df.groupby("value2")["geometry"].apply(lambda x: x.union_all())
|
||||
|
||||
exp = GeoSeries(
|
||||
[shapely.geometry.MultiPoint([(0, 0), (2, 2)]), Point(1, 1)],
|
||||
@@ -646,7 +644,7 @@ def test_groupby(df):
|
||||
assert_series_equal(res, exp)
|
||||
|
||||
# apply on geometry column not resulting in new geometry
|
||||
res = df.groupby("value2")["geometry"].apply(lambda x: x.unary_union.area)
|
||||
res = df.groupby("value2")["geometry"].apply(lambda x: x.union_all().area)
|
||||
exp = pd.Series([0.0, 0.0], index=pd.Index([1, 2], name="value2"), name="geometry")
|
||||
|
||||
assert_series_equal(res, exp)
|
||||
@@ -660,45 +658,60 @@ def test_groupby_groups(df):
|
||||
assert_frame_equal(res, exp)
|
||||
|
||||
|
||||
@pytest.mark.skip_no_sindex
|
||||
@pytest.mark.parametrize("crs", [None, "EPSG:4326"])
|
||||
def test_groupby_metadata(crs):
|
||||
@pytest.mark.parametrize("geometry_name", ["geometry", "geom"])
|
||||
def test_groupby_metadata(crs, geometry_name):
|
||||
if crs and not compat.HAS_PYPROJ:
|
||||
pytest.skip("requires pyproj")
|
||||
# https://github.com/geopandas/geopandas/issues/2294
|
||||
df = GeoDataFrame(
|
||||
{
|
||||
"geometry": [Point(0, 0), Point(1, 1), Point(0, 0)],
|
||||
geometry_name: [Point(0, 0), Point(1, 1), Point(0, 0)],
|
||||
"value1": np.arange(3, dtype="int64"),
|
||||
"value2": np.array([1, 2, 1], dtype="int64"),
|
||||
},
|
||||
crs=crs,
|
||||
geometry=geometry_name,
|
||||
)
|
||||
|
||||
kwargs = {}
|
||||
if compat.PANDAS_GE_22:
|
||||
# pandas is deprecating that the group key is present as column in the
|
||||
# dataframe passed to `func`. To suppress this warning, it introduced
|
||||
# a new include_groups keyword
|
||||
kwargs = dict(include_groups=False)
|
||||
|
||||
# dummy test asserting we can access the crs
|
||||
def func(group):
|
||||
assert isinstance(group, GeoDataFrame)
|
||||
assert group.crs == crs
|
||||
|
||||
df.groupby("value2").apply(func)
|
||||
df.groupby("value2").apply(func, **kwargs)
|
||||
# selecting the non-group columns -> no need to pass the keyword
|
||||
if (
|
||||
compat.PANDAS_GE_22
|
||||
or (compat.PANDAS_GE_20 and geometry_name == "geometry")
|
||||
or not compat.PANDAS_GE_20
|
||||
):
|
||||
df.groupby("value2")[[geometry_name, "value1"]].apply(func)
|
||||
else:
|
||||
# https://github.com/geopandas/geopandas/pull/2966#issuecomment-1878816712
|
||||
# with pandas 2.0 and 2.1 with geom col != geometry this is failing
|
||||
with pytest.raises(AttributeError):
|
||||
df.groupby("value2")[[geometry_name, "value1"]].apply(func)
|
||||
|
||||
# actual test with functionality
|
||||
res = df.groupby("value2").apply(
|
||||
lambda x: geopandas.sjoin(x, x[["geometry", "value1"]], how="inner")
|
||||
lambda x: geopandas.sjoin(x, x[[geometry_name, "value1"]], how="inner"),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
if compat.PANDAS_GE_22:
|
||||
# merge sort behaviour changed in pandas #54611
|
||||
take_indices = [0, 0, 2, 2, 1]
|
||||
value_right = [0, 2, 0, 2, 1]
|
||||
else:
|
||||
take_indices = [0, 2, 0, 2, 1]
|
||||
value_right = [0, 0, 2, 2, 1]
|
||||
|
||||
expected = (
|
||||
df.take(take_indices)
|
||||
.set_index("value2", drop=False, append=True)
|
||||
df.take([0, 0, 2, 2, 1])
|
||||
.set_index("value2", drop=compat.PANDAS_GE_22, append=True)
|
||||
.swaplevel()
|
||||
.rename(columns={"value1": "value1_left"})
|
||||
.assign(value1_right=value_right)
|
||||
.assign(value1_right=[0, 2, 0, 2, 1])
|
||||
)
|
||||
assert_geodataframe_equal(res.drop(columns=["index_right"]), expected)
|
||||
|
||||
@@ -733,6 +746,7 @@ def test_apply_loc_len1(df):
|
||||
np.testing.assert_allclose(result, expected)
|
||||
|
||||
|
||||
@pytest.mark.skipif(compat.PANDAS_GE_30, reason="convert_dtype is removed in pandas 3")
|
||||
def test_apply_convert_dtypes_keyword(s):
|
||||
# ensure the convert_dtypes keyword is accepted
|
||||
if not compat.PANDAS_GE_21:
|
||||
@@ -754,6 +768,8 @@ def test_apply_convert_dtypes_keyword(s):
|
||||
@pytest.mark.parametrize("crs", [None, "EPSG:4326"])
|
||||
def test_apply_no_geometry_result(df, crs):
|
||||
if crs:
|
||||
if not compat.HAS_PYPROJ:
|
||||
pytest.skip("requires pyproj")
|
||||
df = df.set_crs(crs)
|
||||
result = df.apply(lambda col: col.astype(str), axis=0)
|
||||
assert type(result) is pd.DataFrame
|
||||
@@ -858,3 +874,17 @@ def test_preserve_flags(df):
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
pd.concat([df, df])
|
||||
|
||||
|
||||
def test_ufunc():
|
||||
# this is calling a shapely ufunc, but we currently rely on pandas' implementation
|
||||
# of `__array_ufunc__` to wrap the result back into a GeoSeries
|
||||
ser = GeoSeries([Point(1, 1), Point(2, 2), Point(3, 3)])
|
||||
result = shapely.buffer(ser, 2)
|
||||
assert isinstance(result, GeoSeries)
|
||||
|
||||
# ensure the result is still writeable
|
||||
# (https://github.com/geopandas/geopandas/issues/3178)
|
||||
assert result.array._data.flags.writeable
|
||||
result.loc[0] = Point(10, 10)
|
||||
assert result.iloc[0] == Point(10, 10)
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
import itertools
|
||||
from packaging.version import Version
|
||||
import warnings
|
||||
from packaging.version import Version
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from shapely import wkt
|
||||
from shapely.affinity import rotate
|
||||
from shapely.geometry import (
|
||||
MultiPolygon,
|
||||
Polygon,
|
||||
LineString,
|
||||
LinearRing,
|
||||
Point,
|
||||
MultiPoint,
|
||||
MultiLineString,
|
||||
GeometryCollection,
|
||||
LinearRing,
|
||||
LineString,
|
||||
MultiLineString,
|
||||
MultiPoint,
|
||||
MultiPolygon,
|
||||
Point,
|
||||
Polygon,
|
||||
box,
|
||||
)
|
||||
|
||||
|
||||
from geopandas import GeoDataFrame, GeoSeries, read_file
|
||||
from geopandas.datasets import get_path
|
||||
import geopandas._compat as compat
|
||||
from geopandas import GeoDataFrame, GeoSeries, read_file
|
||||
from geopandas.plotting import GeoplotAccessor
|
||||
|
||||
import pytest
|
||||
@@ -304,17 +301,9 @@ class TestPointPlotting:
|
||||
assert len(ax.collections) == 0
|
||||
|
||||
def test_empty_geometry(self):
|
||||
if compat.USE_PYGEOS:
|
||||
s = GeoSeries([wkt.loads("POLYGON EMPTY")])
|
||||
s = GeoSeries(
|
||||
[Polygon([(0, 0), (1, 0), (1, 1)]), wkt.loads("POLYGON EMPTY")]
|
||||
)
|
||||
ax = s.plot()
|
||||
assert len(ax.collections) == 1
|
||||
if not compat.USE_PYGEOS:
|
||||
s = GeoSeries([Polygon([(0, 0), (1, 0), (1, 1)]), Polygon()])
|
||||
ax = s.plot()
|
||||
assert len(ax.collections) == 1
|
||||
s = GeoSeries([Polygon([(0, 0), (1, 0), (1, 1)]), Polygon()])
|
||||
ax = s.plot()
|
||||
assert len(ax.collections) == 1
|
||||
|
||||
# more complex case with GEOMETRYCOLLECTION EMPTY, POINT EMPTY and NONE
|
||||
poly = Polygon([(-1, -1), (-1, 2), (2, 2), (2, -1), (-1, -1)])
|
||||
@@ -324,7 +313,14 @@ class TestPointPlotting:
|
||||
|
||||
gdf = GeoDataFrame(geometry=[point, empty_point, point_])
|
||||
gdf["geometry"] = gdf.intersection(poly)
|
||||
gdf.loc[3] = [None]
|
||||
with warnings.catch_warnings():
|
||||
# loc to add row calls concat internally, warning for pandas >=2.1
|
||||
warnings.filterwarnings(
|
||||
"ignore",
|
||||
"The behavior of DataFrame concatenation with empty",
|
||||
FutureWarning,
|
||||
)
|
||||
gdf.loc[3] = [None]
|
||||
ax = gdf.plot()
|
||||
assert len(ax.collections) == 1
|
||||
|
||||
@@ -498,6 +494,28 @@ class TestLineStringPlotting:
|
||||
)
|
||||
self.df3 = GeoDataFrame({"geometry": self.linearrings, "values": values})
|
||||
|
||||
def test_autolim_false(self):
|
||||
"""Test linestring plot preserving axes limits."""
|
||||
ax = self.lines[: self.N // 2].plot()
|
||||
ylim = ax.get_ylim()
|
||||
self.lines.plot(ax=ax, autolim=False)
|
||||
assert ax.get_ylim() == ylim
|
||||
ax = self.df[: self.N // 2].plot()
|
||||
ylim = ax.get_ylim()
|
||||
self.df.plot(ax=ax, autolim=False)
|
||||
assert ax.get_ylim() == ylim
|
||||
|
||||
def test_autolim_true(self):
|
||||
"""Test linestring plot autoscaling axes limits."""
|
||||
ax = self.lines[: self.N // 2].plot()
|
||||
ylim = ax.get_ylim()
|
||||
self.lines.plot(ax=ax, autolim=True)
|
||||
assert ax.get_ylim() != ylim
|
||||
ax = self.df[: self.N // 2].plot()
|
||||
ylim = ax.get_ylim()
|
||||
self.df.plot(ax=ax, autolim=True)
|
||||
assert ax.get_ylim() != ylim
|
||||
|
||||
def test_single_color(self):
|
||||
ax = self.lines.plot(color="green")
|
||||
_check_colors(self.N, ax.collections[0].get_colors(), ["green"] * self.N)
|
||||
@@ -641,6 +659,28 @@ class TestPolygonPlotting:
|
||||
df_nan = GeoDataFrame({"geometry": t3, "values": [np.nan]})
|
||||
self.df3 = pd.concat([self.df, df_nan])
|
||||
|
||||
def test_autolim_false(self):
|
||||
"""Test polygon plot preserving axes limits."""
|
||||
ax = self.polys[:1].plot()
|
||||
xlim = ax.get_xlim()
|
||||
self.polys.plot(ax=ax, autolim=False)
|
||||
assert ax.get_xlim() == xlim
|
||||
ax = self.df[:1].plot()
|
||||
xlim = ax.get_xlim()
|
||||
self.df.plot(ax=ax, autolim=False)
|
||||
assert ax.get_xlim() == xlim
|
||||
|
||||
def test_autolim_true(self):
|
||||
"""Test polygon plot autoscaling axes limits."""
|
||||
ax = self.polys[:1].plot()
|
||||
xlim = ax.get_xlim()
|
||||
self.polys.plot(ax=ax, autolim=True)
|
||||
assert ax.get_xlim() != xlim
|
||||
ax = self.df[:1].plot()
|
||||
xlim = ax.get_xlim()
|
||||
self.df.plot(ax=ax, autolim=True)
|
||||
assert ax.get_xlim() != xlim
|
||||
|
||||
def test_single_color(self):
|
||||
ax = self.polys.plot(color="green")
|
||||
_check_colors(2, ax.collections[0].get_facecolors(), ["green"] * 2)
|
||||
@@ -1070,16 +1110,20 @@ class TestNonuniformGeometryPlotting:
|
||||
# )
|
||||
|
||||
|
||||
class TestGeographicAspect:
|
||||
def setup_class(self):
|
||||
pth = get_path("naturalearth_lowres")
|
||||
df = read_file(pth)
|
||||
self.north = df.loc[df.continent == "North America"]
|
||||
self.north_proj = self.north.to_crs("ESRI:102008")
|
||||
bounds = self.north.total_bounds
|
||||
y_coord = np.mean([bounds[1], bounds[3]])
|
||||
self.exp = 1 / np.cos(y_coord * np.pi / 180)
|
||||
@pytest.fixture(scope="class")
|
||||
def _setup_class_geographic_aspect(naturalearth_lowres, request):
|
||||
"""Attach naturalearth_lowres class attribute for unittest style setup_method"""
|
||||
df = read_file(naturalearth_lowres)
|
||||
request.cls.north = df.loc[df.continent == "North America"]
|
||||
request.cls.north_proj = request.cls.north.to_crs("ESRI:102008")
|
||||
bounds = request.cls.north.total_bounds
|
||||
y_coord = np.mean([bounds[1], bounds[3]])
|
||||
request.cls.exp = 1 / np.cos(y_coord * np.pi / 180)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_setup_class_geographic_aspect")
|
||||
@pytest.mark.skipif(not compat.HAS_PYPROJ, reason="pyproj not available")
|
||||
class TestGeographicAspect:
|
||||
def test_auto(self):
|
||||
ax = self.north.geometry.plot()
|
||||
assert ax.get_aspect() == self.exp
|
||||
@@ -1133,6 +1177,9 @@ class TestGeographicAspect:
|
||||
assert ax3.get_aspect() == 0.5
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings(
|
||||
"ignore:Numba not installed. Using slow pure python version.:UserWarning"
|
||||
)
|
||||
class TestMapclassifyPlotting:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
@@ -1143,31 +1190,40 @@ class TestMapclassifyPlotting:
|
||||
cls.mc = mapclassify
|
||||
cls.classifiers = list(mapclassify.classifiers.CLASSIFIERS)
|
||||
cls.classifiers.remove("UserDefined")
|
||||
pth = get_path("naturalearth_lowres")
|
||||
cls.df = read_file(pth)
|
||||
cls.df["NEGATIVES"] = np.linspace(-10, 10, len(cls.df.index))
|
||||
cls.df["low_vals"] = np.linspace(0, 0.3, cls.df.shape[0])
|
||||
cls.df["mid_vals"] = np.linspace(0.3, 0.7, cls.df.shape[0])
|
||||
cls.df["high_vals"] = np.linspace(0.7, 1.0, cls.df.shape[0])
|
||||
cls.df.loc[cls.df.index[:20:2], "high_vals"] = np.nan
|
||||
cls.nybb = read_file(get_path("nybb"))
|
||||
cls.nybb["vals"] = [0.001, 0.002, 0.003, 0.004, 0.005]
|
||||
|
||||
def test_legend(self):
|
||||
@pytest.fixture
|
||||
def df(self, naturalearth_lowres):
|
||||
# version of naturalearth_lowres for mapclassify plotting tests
|
||||
df = read_file(naturalearth_lowres)
|
||||
df["NEGATIVES"] = np.linspace(-10, 10, len(df.index))
|
||||
df["low_vals"] = np.linspace(0, 0.3, df.shape[0])
|
||||
df["mid_vals"] = np.linspace(0.3, 0.7, df.shape[0])
|
||||
df["high_vals"] = np.linspace(0.7, 1.0, df.shape[0])
|
||||
df.loc[df.index[:20:2], "high_vals"] = np.nan
|
||||
return df
|
||||
|
||||
@pytest.fixture
|
||||
def nybb(self, nybb_filename):
|
||||
# version of nybb for mapclassify plotting tests
|
||||
df = read_file(nybb_filename)
|
||||
df["vals"] = [0.001, 0.002, 0.003, 0.004, 0.005]
|
||||
return df
|
||||
|
||||
def test_legend(self, df):
|
||||
with warnings.catch_warnings(record=True) as _: # don't print warning
|
||||
# warning coming from scipy.stats
|
||||
ax = self.df.plot(
|
||||
ax = df.plot(
|
||||
column="pop_est", scheme="QUANTILES", k=3, cmap="OrRd", legend=True
|
||||
)
|
||||
labels = [t.get_text() for t in ax.get_legend().get_texts()]
|
||||
expected = [
|
||||
s.split("|")[0][1:-2]
|
||||
for s in str(self.mc.Quantiles(self.df["pop_est"], k=3)).split("\n")[4:]
|
||||
for s in str(self.mc.Quantiles(df["pop_est"], k=3)).split("\n")[4:]
|
||||
]
|
||||
assert labels == expected
|
||||
|
||||
def test_bin_labels(self):
|
||||
ax = self.df.plot(
|
||||
def test_bin_labels(self, df):
|
||||
ax = df.plot(
|
||||
column="pop_est",
|
||||
scheme="QUANTILES",
|
||||
k=3,
|
||||
@@ -1179,9 +1235,9 @@ class TestMapclassifyPlotting:
|
||||
expected = ["foo", "bar", "baz"]
|
||||
assert labels == expected
|
||||
|
||||
def test_invalid_labels_length(self):
|
||||
def test_invalid_labels_length(self, df):
|
||||
with pytest.raises(ValueError):
|
||||
self.df.plot(
|
||||
df.plot(
|
||||
column="pop_est",
|
||||
scheme="QUANTILES",
|
||||
k=3,
|
||||
@@ -1190,16 +1246,16 @@ class TestMapclassifyPlotting:
|
||||
legend_kwds={"labels": ["foo", "bar"]},
|
||||
)
|
||||
|
||||
def test_negative_legend(self):
|
||||
ax = self.df.plot(
|
||||
def test_negative_legend(self, df):
|
||||
ax = df.plot(
|
||||
column="NEGATIVES", scheme="FISHER_JENKS", k=3, cmap="OrRd", legend=True
|
||||
)
|
||||
labels = [t.get_text() for t in ax.get_legend().get_texts()]
|
||||
expected = ["-10.00, -3.41", " -3.41, 3.30", " 3.30, 10.00"]
|
||||
assert labels == expected
|
||||
|
||||
def test_fmt(self):
|
||||
ax = self.df.plot(
|
||||
def test_fmt(self, df):
|
||||
ax = df.plot(
|
||||
column="NEGATIVES",
|
||||
scheme="FISHER_JENKS",
|
||||
k=3,
|
||||
@@ -1211,8 +1267,8 @@ class TestMapclassifyPlotting:
|
||||
expected = ["-10, -3", " -3, 3", " 3, 10"]
|
||||
assert labels == expected
|
||||
|
||||
def test_interval(self):
|
||||
ax = self.df.plot(
|
||||
def test_interval(self, df):
|
||||
ax = df.plot(
|
||||
column="NEGATIVES",
|
||||
scheme="FISHER_JENKS",
|
||||
k=3,
|
||||
@@ -1225,17 +1281,17 @@ class TestMapclassifyPlotting:
|
||||
assert labels == expected
|
||||
|
||||
@pytest.mark.parametrize("scheme", ["FISHER_JENKS", "FISHERJENKS"])
|
||||
def test_scheme_name_compat(self, scheme):
|
||||
ax = self.df.plot(column="NEGATIVES", scheme=scheme, k=3, legend=True)
|
||||
def test_scheme_name_compat(self, scheme, df):
|
||||
ax = df.plot(column="NEGATIVES", scheme=scheme, k=3, legend=True)
|
||||
assert len(ax.get_legend().get_texts()) == 3
|
||||
|
||||
def test_schemes(self):
|
||||
def test_schemes(self, df):
|
||||
# test if all available classifiers pass
|
||||
for scheme in self.classifiers:
|
||||
self.df.plot(column="pop_est", scheme=scheme, legend=True)
|
||||
df.plot(column="pop_est", scheme=scheme, legend=True)
|
||||
|
||||
def test_classification_kwds(self):
|
||||
ax = self.df.plot(
|
||||
def test_classification_kwds(self, df):
|
||||
ax = df.plot(
|
||||
column="pop_est",
|
||||
scheme="percentiles",
|
||||
k=3,
|
||||
@@ -1246,21 +1302,19 @@ class TestMapclassifyPlotting:
|
||||
labels = [t.get_text() for t in ax.get_legend().get_texts()]
|
||||
expected = [
|
||||
s.split("|")[0][1:-2]
|
||||
for s in str(self.mc.Percentiles(self.df["pop_est"], pct=[50, 100])).split(
|
||||
"\n"
|
||||
)[4:]
|
||||
for s in str(self.mc.Percentiles(df["pop_est"], pct=[50, 100])).split("\n")[
|
||||
4:
|
||||
]
|
||||
]
|
||||
|
||||
assert labels == expected
|
||||
|
||||
def test_invalid_scheme(self):
|
||||
def test_invalid_scheme(self, df):
|
||||
with pytest.raises(ValueError):
|
||||
scheme = "invalid_scheme_*#&)(*#"
|
||||
self.df.plot(
|
||||
column="gdp_md_est", scheme=scheme, k=3, cmap="OrRd", legend=True
|
||||
)
|
||||
df.plot(column="gdp_md_est", scheme=scheme, k=3, cmap="OrRd", legend=True)
|
||||
|
||||
def test_cax_legend_passing(self):
|
||||
def test_cax_legend_passing(self, df):
|
||||
"""Pass a 'cax' argument to 'df.plot(.)', that is valid only if 'ax' is
|
||||
passed as well (if not, a new figure is created ad hoc, and 'cax' is
|
||||
ignored)
|
||||
@@ -1271,15 +1325,15 @@ class TestMapclassifyPlotting:
|
||||
divider = make_axes_locatable(ax)
|
||||
cax = divider.append_axes("right", size="5%", pad=0.1)
|
||||
with pytest.raises(ValueError):
|
||||
ax = self.df.plot(column="pop_est", cmap="OrRd", legend=True, cax=cax)
|
||||
ax = df.plot(column="pop_est", cmap="OrRd", legend=True, cax=cax)
|
||||
|
||||
def test_cax_legend_height(self):
|
||||
def test_cax_legend_height(self, df):
|
||||
"""Pass a cax argument to 'df.plot(.)', the legend location must be
|
||||
aligned with those of main plot
|
||||
"""
|
||||
# base case
|
||||
with warnings.catch_warnings(record=True) as _: # don't print warning
|
||||
ax = self.df.plot(column="pop_est", cmap="OrRd", legend=True)
|
||||
ax = df.plot(column="pop_est", cmap="OrRd", legend=True)
|
||||
plot_height = _get_ax(ax.get_figure(), "").get_position().height
|
||||
legend_height = _get_ax(ax.get_figure(), "<colorbar>").get_position().height
|
||||
assert abs(plot_height - legend_height) >= 1e-6
|
||||
@@ -1290,16 +1344,14 @@ class TestMapclassifyPlotting:
|
||||
divider = make_axes_locatable(ax2)
|
||||
cax = divider.append_axes("right", size="5%", pad=0.1, label="fixed_colorbar")
|
||||
with warnings.catch_warnings(record=True) as _:
|
||||
ax2 = self.df.plot(
|
||||
column="pop_est", cmap="OrRd", legend=True, cax=cax, ax=ax2
|
||||
)
|
||||
ax2 = df.plot(column="pop_est", cmap="OrRd", legend=True, cax=cax, ax=ax2)
|
||||
plot_height = _get_ax(fig, "").get_position().height
|
||||
legend_height = _get_ax(fig, "fixed_colorbar").get_position().height
|
||||
assert abs(plot_height - legend_height) < 1e-6
|
||||
|
||||
def test_empty_bins(self):
|
||||
def test_empty_bins(self, df):
|
||||
bins = np.arange(1, 11) / 10
|
||||
ax = self.df.plot(
|
||||
ax = df.plot(
|
||||
"low_vals",
|
||||
scheme="UserDefined",
|
||||
classification_kwds={"bins": bins},
|
||||
@@ -1348,7 +1400,7 @@ class TestMapclassifyPlotting:
|
||||
line.get_markerfacecolor() for line in ax.get_legend().get_lines()
|
||||
] == legend_colors_exp
|
||||
|
||||
ax2 = self.df.plot(
|
||||
ax2 = df.plot(
|
||||
"mid_vals",
|
||||
scheme="UserDefined",
|
||||
classification_kwds={"bins": bins},
|
||||
@@ -1386,7 +1438,7 @@ class TestMapclassifyPlotting:
|
||||
line.get_markerfacecolor() for line in ax2.get_legend().get_lines()
|
||||
] == legend_colors_exp
|
||||
|
||||
ax3 = self.df.plot(
|
||||
ax3 = df.plot(
|
||||
"high_vals",
|
||||
scheme="UserDefined",
|
||||
classification_kwds={"bins": bins},
|
||||
@@ -1411,8 +1463,8 @@ class TestMapclassifyPlotting:
|
||||
line.get_markerfacecolor() for line in ax3.get_legend().get_lines()
|
||||
] == legend_colors_exp
|
||||
|
||||
def test_equally_formatted_bins(self):
|
||||
ax = self.nybb.plot(
|
||||
def test_equally_formatted_bins(self, nybb):
|
||||
ax = nybb.plot(
|
||||
"vals",
|
||||
scheme="quantiles",
|
||||
legend=True,
|
||||
@@ -1427,7 +1479,7 @@ class TestMapclassifyPlotting:
|
||||
]
|
||||
assert labels == expected
|
||||
|
||||
ax2 = self.nybb.plot(
|
||||
ax2 = nybb.plot(
|
||||
"vals", scheme="quantiles", legend=True, legend_kwds={"fmt": "{:.3f}"}
|
||||
)
|
||||
labels = [t.get_text() for t in ax2.get_legend().get_texts()]
|
||||
@@ -1454,9 +1506,10 @@ class TestPlotCollections:
|
||||
)
|
||||
|
||||
def test_points(self):
|
||||
from geopandas.plotting import _plot_point_collection, plot_point_collection
|
||||
from matplotlib.collections import PathCollection
|
||||
|
||||
from geopandas.plotting import _plot_point_collection
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
coll = _plot_point_collection(ax, self.points)
|
||||
assert isinstance(coll, PathCollection)
|
||||
@@ -1508,10 +1561,6 @@ class TestPlotCollections:
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
_plot_point_collection(ax, self.points, color="not color")
|
||||
|
||||
# check FutureWarning
|
||||
with pytest.warns(FutureWarning):
|
||||
plot_point_collection(ax, self.points)
|
||||
|
||||
def test_points_values(self):
|
||||
from geopandas.plotting import _plot_point_collection
|
||||
|
||||
@@ -1526,12 +1575,10 @@ class TestPlotCollections:
|
||||
# _check_colors(self.N, coll.get_edgecolors(), expected_colors)
|
||||
|
||||
def test_linestrings(self):
|
||||
from geopandas.plotting import (
|
||||
_plot_linestring_collection,
|
||||
plot_linestring_collection,
|
||||
)
|
||||
from matplotlib.collections import LineCollection
|
||||
|
||||
from geopandas.plotting import _plot_linestring_collection
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
coll = _plot_linestring_collection(ax, self.lines)
|
||||
assert isinstance(coll, LineCollection)
|
||||
@@ -1581,9 +1628,6 @@ class TestPlotCollections:
|
||||
# not a color
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
_plot_linestring_collection(ax, self.lines, color="not color")
|
||||
# check FutureWarning
|
||||
with pytest.warns(FutureWarning):
|
||||
plot_linestring_collection(ax, self.lines)
|
||||
|
||||
def test_linestrings_values(self):
|
||||
from geopandas.plotting import _plot_linestring_collection
|
||||
@@ -1615,9 +1659,10 @@ class TestPlotCollections:
|
||||
ax.cla()
|
||||
|
||||
def test_polygons(self):
|
||||
from geopandas.plotting import _plot_polygon_collection, plot_polygon_collection
|
||||
from matplotlib.collections import PatchCollection
|
||||
|
||||
from geopandas.plotting import _plot_polygon_collection
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
coll = _plot_polygon_collection(ax, self.polygons)
|
||||
assert isinstance(coll, PatchCollection)
|
||||
@@ -1673,9 +1718,6 @@ class TestPlotCollections:
|
||||
# not a color
|
||||
with pytest.raises((TypeError, ValueError)):
|
||||
_plot_polygon_collection(ax, self.polygons, color="not color")
|
||||
# check FutureWarning
|
||||
with pytest.warns(FutureWarning):
|
||||
plot_polygon_collection(ax, self.polygons)
|
||||
|
||||
def test_polygons_values(self):
|
||||
from geopandas.plotting import _plot_polygon_collection
|
||||
@@ -1830,9 +1872,10 @@ def test_column_values():
|
||||
def test_polygon_patch():
|
||||
# test adapted from descartes by Sean Gillies
|
||||
# (BSD license, https://pypi.org/project/descartes).
|
||||
from geopandas.plotting import _PolygonPatch
|
||||
from matplotlib.patches import PathPatch
|
||||
|
||||
from geopandas.plotting import _PolygonPatch
|
||||
|
||||
polygon = (
|
||||
Point(0, 0).buffer(10.0).difference(MultiPoint([(-5, 0), (5, 0)]).buffer(3.0))
|
||||
)
|
||||
|
||||
@@ -33,11 +33,11 @@ def test_get_deps_info():
|
||||
assert "fiona" in deps_info
|
||||
assert "numpy" in deps_info
|
||||
assert "shapely" in deps_info
|
||||
assert "rtree" in deps_info
|
||||
assert "pyproj" in deps_info
|
||||
assert "matplotlib" in deps_info
|
||||
assert "mapclassify" in deps_info
|
||||
assert "geopy" in deps_info
|
||||
assert "psycopg" in deps_info
|
||||
assert "psycopg2" in deps_info
|
||||
assert "geoalchemy2" in deps_info
|
||||
|
||||
|
||||
@@ -1,30 +1,25 @@
|
||||
from math import sqrt
|
||||
|
||||
import numpy as np
|
||||
|
||||
import shapely
|
||||
from shapely.geometry import (
|
||||
Point,
|
||||
Polygon,
|
||||
MultiPolygon,
|
||||
box,
|
||||
GeometryCollection,
|
||||
LineString,
|
||||
MultiPolygon,
|
||||
Point,
|
||||
Polygon,
|
||||
box,
|
||||
)
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
import geopandas
|
||||
from geopandas import GeoDataFrame, GeoSeries, read_file
|
||||
from geopandas import _compat as compat
|
||||
from geopandas import GeoDataFrame, GeoSeries, read_file, datasets
|
||||
|
||||
import pytest
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
if compat.USE_SHAPELY_20:
|
||||
import shapely as mod
|
||||
elif compat.USE_PYGEOS:
|
||||
import pygeos as mod
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
|
||||
@pytest.mark.skip_no_sindex
|
||||
class TestSeriesSindex:
|
||||
def test_has_sindex(self):
|
||||
"""Test the has_sindex method."""
|
||||
@@ -114,7 +109,6 @@ class TestSeriesSindex:
|
||||
assert sliced.sindex is not original_index
|
||||
|
||||
|
||||
@pytest.mark.skip_no_sindex
|
||||
class TestFrameSindex:
|
||||
def setup_method(self):
|
||||
data = {
|
||||
@@ -171,15 +165,15 @@ class TestFrameSindex:
|
||||
"""Selecting a subset of columns preserves the index."""
|
||||
original_index = self.df.sindex
|
||||
# Selecting a subset of columns preserves the index for pandas < 2.0
|
||||
# with pandas 2.0, the column is now copied, losing the index (although
|
||||
# with Copy-on-Write, this will again be preserved)
|
||||
# with pandas 2.0, the column is now copied, losing the index. But
|
||||
# with pandas >= 3.0 and Copy-on-Write this is preserved again
|
||||
subset1 = self.df[["geom", "A"]]
|
||||
if compat.PANDAS_GE_20 and not pd.options.mode.copy_on_write:
|
||||
if compat.PANDAS_GE_20 and not compat.PANDAS_GE_30:
|
||||
assert subset1.sindex is not original_index
|
||||
else:
|
||||
assert subset1.sindex is original_index
|
||||
subset2 = self.df[["A", "geom"]]
|
||||
if compat.PANDAS_GE_20 and not pd.options.mode.copy_on_write:
|
||||
if compat.PANDAS_GE_20 and not compat.PANDAS_GE_30:
|
||||
assert subset2.sindex is not original_index
|
||||
else:
|
||||
assert subset2.sindex is original_index
|
||||
@@ -209,12 +203,12 @@ class TestFrameSindex:
|
||||
assert old_sindex is new_sindex
|
||||
|
||||
|
||||
# Skip to accommodate Shapely geometries being unhashable
|
||||
# Skip to accommodate Shapely geometries being unhashable # TODO unskip?
|
||||
@pytest.mark.skip
|
||||
@pytest.mark.usefixtures("_setup_class_nybb_filename")
|
||||
class TestJoinSindex:
|
||||
def setup_method(self):
|
||||
nybb_filename = geopandas.datasets.get_path("nybb")
|
||||
self.boros = read_file(nybb_filename)
|
||||
self.boros = read_file(self.nybb_filename)
|
||||
|
||||
def test_merge_geo(self):
|
||||
# First check that we gets hits from the boros frame.
|
||||
@@ -247,8 +241,7 @@ class TestJoinSindex:
|
||||
assert res == ["Bronx", "Queens"]
|
||||
|
||||
|
||||
@pytest.mark.skip_no_sindex
|
||||
class TestPygeosInterface:
|
||||
class TestShapelyInterface:
|
||||
def setup_method(self):
|
||||
data = {
|
||||
"geom": [Point(x, y) for x, y in zip(range(5), range(5))]
|
||||
@@ -275,15 +268,9 @@ class TestPygeosInterface:
|
||||
@pytest.mark.parametrize("test_geom", ((-1, -1, -0.5), -0.5, None, Point(0, 0)))
|
||||
def test_intersection_invalid_bounds_tuple(self, test_geom):
|
||||
"""Tests the `intersection` method with invalid inputs."""
|
||||
if compat.USE_PYGEOS:
|
||||
with pytest.raises(TypeError):
|
||||
# we raise a useful TypeError
|
||||
self.df.sindex.intersection(test_geom)
|
||||
else:
|
||||
with pytest.raises((TypeError, Exception)):
|
||||
# catch a general exception
|
||||
# rtree raises an RTreeError which we need to catch
|
||||
self.df.sindex.intersection(test_geom)
|
||||
with pytest.raises(TypeError):
|
||||
# we raise a useful TypeError
|
||||
self.df.sindex.intersection(test_geom)
|
||||
|
||||
# ------------------------------ `query` tests ------------------------------ #
|
||||
@pytest.mark.parametrize(
|
||||
@@ -394,6 +381,108 @@ class TestPygeosInterface:
|
||||
with pytest.raises(TypeError):
|
||||
self.df.sindex.query("notavalidgeom")
|
||||
|
||||
@pytest.mark.skipif(not compat.GEOS_GE_310, reason="Requires GEOS 3.10")
|
||||
@pytest.mark.parametrize(
|
||||
"distance, test_geom, expected",
|
||||
(
|
||||
# bounds don't intersect and not within distance=0
|
||||
(
|
||||
0,
|
||||
box(9.0, 9.0, 9.9, 9.9),
|
||||
[],
|
||||
),
|
||||
# bounds don't intersect but is within distance=1
|
||||
(
|
||||
1,
|
||||
box(9.0, 9.0, 9.9, 9.9),
|
||||
[5],
|
||||
),
|
||||
# within 1-D absolute distance in both axes, but not euclidean distance
|
||||
(
|
||||
0.5,
|
||||
Point(0.5, 0.5),
|
||||
[],
|
||||
),
|
||||
# same as before but within euclidean distance
|
||||
(
|
||||
sqrt(2 * 0.5**2) + 1e-9,
|
||||
Point(0.5, 0.5),
|
||||
[0, 1],
|
||||
),
|
||||
# less than euclidean distance between points, multi-object
|
||||
(
|
||||
sqrt(2) - 1e-9,
|
||||
[
|
||||
Polygon([(0, 0), (1, 0), (1, 1)]),
|
||||
Polygon([(1, 1), (2, 1), (2, 2)]),
|
||||
], # multi-object test
|
||||
[[0, 0, 1, 1], [0, 1, 1, 2]],
|
||||
),
|
||||
# more than euclidean distance between points, multi-object
|
||||
(
|
||||
sqrt(2) + 1e-9,
|
||||
[
|
||||
Polygon([(0, 0), (1, 0), (1, 1)]),
|
||||
Polygon([(1, 1), (2, 1), (2, 2)]),
|
||||
],
|
||||
[[0, 0, 0, 1, 1, 1, 1], [0, 1, 2, 0, 1, 2, 3]],
|
||||
),
|
||||
# distance is array-like, broadcastable to geometry
|
||||
(
|
||||
[2, 10],
|
||||
[Point(0.5, 0.5), Point(1, 1)],
|
||||
[[0, 0, 1, 1, 1, 1, 1], [0, 1, 0, 1, 2, 3, 4]],
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_query_dwithin(self, distance, test_geom, expected):
|
||||
"""Tests the `query` method with predicates that require keyword arguments."""
|
||||
res = self.df.sindex.query(test_geom, predicate="dwithin", distance=distance)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
@pytest.mark.skipif(not compat.GEOS_GE_310, reason="Requires GEOS 3.10")
|
||||
def test_dwithin_no_distance(self):
|
||||
"""Tests the `query` method with keyword arguments that are
|
||||
invalid for certain predicates."""
|
||||
with pytest.raises(
|
||||
ValueError, match="'distance' parameter is required for 'dwithin' predicate"
|
||||
):
|
||||
self.df.sindex.query(Point(0, 0), predicate="dwithin")
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"predicate",
|
||||
[
|
||||
None,
|
||||
"contains",
|
||||
"contains_properly",
|
||||
"covered_by",
|
||||
"covers",
|
||||
"crosses",
|
||||
"intersects",
|
||||
"overlaps",
|
||||
"touches",
|
||||
"within",
|
||||
],
|
||||
)
|
||||
def test_query_distance_invalid(self, predicate):
|
||||
"""Tests the `query` method with keyword arguments that are
|
||||
invalid for certain predicates."""
|
||||
msg = "'distance' parameter is only supported in combination with 'dwithin'"
|
||||
with pytest.raises(ValueError, match=msg):
|
||||
self.df.sindex.query(Point(0, 0), predicate=predicate, distance=0)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
compat.GEOS_GE_310, reason="Test for 'dwithin'-incompatible versions of GEOS"
|
||||
)
|
||||
def test_dwithin_requirements(self):
|
||||
"""Tests whether a ValueError is raised when trying to use dwithin with
|
||||
incompatible versions of shapely or pyGEOS
|
||||
"""
|
||||
with pytest.raises(
|
||||
ValueError, match="predicate = 'dwithin' requires GEOS >= 3.10.0"
|
||||
):
|
||||
self.df.sindex.query(Point(0, 0), predicate="dwithin", distance=0)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"test_geom, expected_value",
|
||||
[
|
||||
@@ -440,13 +529,8 @@ class TestPygeosInterface:
|
||||
)
|
||||
expected = [0, 1, 2]
|
||||
|
||||
# pass through GeoSeries to have GeoPandas
|
||||
# determine if it should use shapely or pygeos geometry objects
|
||||
tree_df = geopandas.GeoDataFrame(geometry=tree_polys)
|
||||
test_df = geopandas.GeoDataFrame(geometry=test_polys)
|
||||
|
||||
test_geo = test_df.geometry.values[0]
|
||||
res = tree_df.sindex.query(test_geo, sort=sort)
|
||||
test_geo = test_polys.values[0]
|
||||
res = tree_polys.sindex.query(test_geo, sort=sort)
|
||||
|
||||
# asserting the same elements
|
||||
assert sorted(res) == sorted(expected)
|
||||
@@ -564,15 +648,12 @@ class TestPygeosInterface:
|
||||
),
|
||||
)
|
||||
def test_query_bulk(self, predicate, test_geom, expected):
|
||||
"""Tests the `query_bulk` method with valid
|
||||
"""Tests the `query` method with valid
|
||||
inputs and valid predicates.
|
||||
"""
|
||||
# pass through GeoSeries to have GeoPandas
|
||||
# determine if it should use shapely or pygeos geometry objects
|
||||
test_geom = geopandas.GeoSeries(
|
||||
[box(*geom) for geom in test_geom], index=range(len(test_geom))
|
||||
res = self.df.sindex.query(
|
||||
[box(*geom) for geom in test_geom], predicate=predicate
|
||||
)
|
||||
res = self.df.sindex.query(test_geom, predicate=predicate)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -587,16 +668,12 @@ class TestPygeosInterface:
|
||||
],
|
||||
)
|
||||
def test_query_bulk_empty_geometry(self, test_geoms, expected_value):
|
||||
"""Tests the `query_bulk` method with an empty geometry."""
|
||||
# pass through GeoSeries to have GeoPandas
|
||||
# determine if it should use shapely or pygeos geometry objects
|
||||
# note: for this test, test_geoms (note plural) is a list already
|
||||
test_geoms = geopandas.GeoSeries(test_geoms, index=range(len(test_geoms)))
|
||||
"""Tests the `query` method with an empty geometries."""
|
||||
res = self.df.sindex.query(test_geoms)
|
||||
assert_array_equal(res, expected_value)
|
||||
|
||||
def test_query_bulk_empty_input_array(self):
|
||||
"""Tests the `query_bulk` method with an empty input array."""
|
||||
"""Tests the `query` method with an empty input array."""
|
||||
test_array = np.array([], dtype=object)
|
||||
expected_value = [[], []]
|
||||
res = self.df.sindex.query(test_array)
|
||||
@@ -604,23 +681,19 @@ class TestPygeosInterface:
|
||||
|
||||
def test_query_bulk_invalid_input_geometry(self):
|
||||
"""
|
||||
Tests the `query_bulk` method with invalid input for the `geometry` parameter.
|
||||
Tests the `query` method with invalid input for the `geometry` parameter.
|
||||
"""
|
||||
test_array = "notanarray"
|
||||
with pytest.raises(TypeError):
|
||||
self.df.sindex.query(test_array)
|
||||
|
||||
def test_query_bulk_invalid_predicate(self):
|
||||
"""Tests the `query_bulk` method with invalid predicates."""
|
||||
"""Tests the `query` method with invalid predicates."""
|
||||
test_geom_bounds = (-1, -1, -0.5, -0.5)
|
||||
test_predicate = "test"
|
||||
|
||||
# pass through GeoSeries to have GeoPandas
|
||||
# determine if it should use shapely or pygeos geometry objects
|
||||
test_geom = geopandas.GeoSeries([box(*test_geom_bounds)], index=["0"])
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
self.df.sindex.query(test_geom.geometry, predicate=test_predicate)
|
||||
self.df.sindex.query([box(*test_geom_bounds)], predicate=test_predicate)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"predicate, test_geom, expected",
|
||||
@@ -631,11 +704,10 @@ class TestPygeosInterface:
|
||||
),
|
||||
)
|
||||
def test_query_bulk_input_type(self, predicate, test_geom, expected):
|
||||
"""Tests that query_bulk can accept a GeoSeries, GeometryArray or
|
||||
"""Tests that query can accept a GeoSeries, GeometryArray or
|
||||
numpy array.
|
||||
"""
|
||||
# pass through GeoSeries to have GeoPandas
|
||||
# determine if it should use shapely or pygeos geometry objects
|
||||
# pass through GeoSeries to test input type
|
||||
test_geom = geopandas.GeoSeries([box(*test_geom)], index=["0"])
|
||||
|
||||
# test GeoSeries
|
||||
@@ -667,7 +739,7 @@ class TestPygeosInterface:
|
||||
),
|
||||
)
|
||||
def test_query_bulk_sorting(self, sort, expected):
|
||||
"""Check that results from `query_bulk` don't depend
|
||||
"""Check that results from `query` don't depend
|
||||
on the order of geometries.
|
||||
"""
|
||||
# these geometries come from a reported issue:
|
||||
@@ -682,12 +754,7 @@ class TestPygeosInterface:
|
||||
]
|
||||
)
|
||||
|
||||
# pass through GeoSeries to have GeoPandas
|
||||
# determine if it should use shapely or pygeos geometry objects
|
||||
tree_df = geopandas.GeoDataFrame(geometry=tree_polys)
|
||||
test_df = geopandas.GeoDataFrame(geometry=test_polys)
|
||||
|
||||
res = tree_df.sindex.query(test_df.geometry, sort=sort)
|
||||
res = tree_polys.sindex.query(test_polys, sort=sort)
|
||||
|
||||
# asserting the same elements
|
||||
assert sorted(res[0]) == sorted(expected[0])
|
||||
@@ -706,30 +773,6 @@ class TestPygeosInterface:
|
||||
raise e
|
||||
|
||||
# ------------------------- `nearest` tests ------------------------- #
|
||||
@pytest.mark.skipif(
|
||||
compat.USE_PYGEOS or compat.USE_SHAPELY_20,
|
||||
reason=("RTree supports sindex.nearest with different behaviour"),
|
||||
)
|
||||
def test_rtree_nearest_warns(self):
|
||||
df = geopandas.GeoDataFrame({"geometry": []})
|
||||
with pytest.warns(
|
||||
FutureWarning, match="sindex.nearest using the rtree backend"
|
||||
):
|
||||
df.sindex.nearest((0, 0, 1, 1), num_results=2)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
compat.USE_SHAPELY_20 or not (compat.USE_PYGEOS and not compat.PYGEOS_GE_010),
|
||||
reason=("PyGEOS < 0.10 does not support sindex.nearest"),
|
||||
)
|
||||
def test_pygeos_error(self):
|
||||
df = geopandas.GeoDataFrame({"geometry": []})
|
||||
with pytest.raises(NotImplementedError, match="requires pygeos >= 0.10"):
|
||||
df.sindex.nearest(None)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)),
|
||||
reason=("PyGEOS >= 0.10 is required to test sindex.nearest"),
|
||||
)
|
||||
@pytest.mark.parametrize("return_all", [True, False])
|
||||
@pytest.mark.parametrize(
|
||||
"geometry,expected",
|
||||
@@ -739,21 +782,17 @@ class TestPygeosInterface:
|
||||
],
|
||||
)
|
||||
def test_nearest_single(self, geometry, expected, return_all):
|
||||
geoms = mod.points(np.arange(10), np.arange(10))
|
||||
geoms = shapely.points(np.arange(10), np.arange(10))
|
||||
df = geopandas.GeoDataFrame({"geometry": geoms})
|
||||
|
||||
p = Point(geometry)
|
||||
res = df.sindex.nearest(p, return_all=return_all)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
p = mod.points(geometry)
|
||||
p = shapely.points(geometry)
|
||||
res = df.sindex.nearest(p, return_all=return_all)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)),
|
||||
reason=("PyGEOS >= 0.10 is required to test sindex.nearest"),
|
||||
)
|
||||
@pytest.mark.parametrize("return_all", [True, False])
|
||||
@pytest.mark.parametrize(
|
||||
"geometry,expected",
|
||||
@@ -763,14 +802,14 @@ class TestPygeosInterface:
|
||||
],
|
||||
)
|
||||
def test_nearest_multi(self, geometry, expected, return_all):
|
||||
geoms = mod.points(np.arange(10), np.arange(10))
|
||||
geoms = shapely.points(np.arange(10), np.arange(10))
|
||||
df = geopandas.GeoDataFrame({"geometry": geoms})
|
||||
|
||||
ps = [Point(p) for p in geometry]
|
||||
res = df.sindex.nearest(ps, return_all=return_all)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
ps = mod.points(geometry)
|
||||
ps = shapely.points(geometry)
|
||||
res = df.sindex.nearest(ps, return_all=return_all)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
@@ -783,10 +822,6 @@ class TestPygeosInterface:
|
||||
res = df.sindex.nearest(ga, return_all=return_all)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)),
|
||||
reason=("PyGEOS >= 0.10 is required to test sindex.nearest"),
|
||||
)
|
||||
@pytest.mark.parametrize("return_all", [True, False])
|
||||
@pytest.mark.parametrize(
|
||||
"geometry,expected",
|
||||
@@ -796,16 +831,12 @@ class TestPygeosInterface:
|
||||
],
|
||||
)
|
||||
def test_nearest_none(self, geometry, expected, return_all):
|
||||
geoms = mod.points(np.arange(10), np.arange(10))
|
||||
geoms = shapely.points(np.arange(10), np.arange(10))
|
||||
df = geopandas.GeoDataFrame({"geometry": geoms})
|
||||
|
||||
res = df.sindex.nearest(geometry, return_all=return_all)
|
||||
assert_array_equal(res, expected)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not (compat.USE_SHAPELY_20 or (compat.USE_PYGEOS and compat.PYGEOS_GE_010)),
|
||||
reason=("PyGEOS >= 0.10 is required to test sindex.nearest"),
|
||||
)
|
||||
@pytest.mark.parametrize("return_distance", [True, False])
|
||||
@pytest.mark.parametrize(
|
||||
"return_all,max_distance,expected",
|
||||
@@ -819,7 +850,7 @@ class TestPygeosInterface:
|
||||
def test_nearest_max_distance(
|
||||
self, expected, max_distance, return_all, return_distance
|
||||
):
|
||||
geoms = mod.points(np.arange(10), np.arange(10))
|
||||
geoms = shapely.points(np.arange(10), np.arange(10))
|
||||
df = geopandas.GeoDataFrame({"geometry": geoms})
|
||||
|
||||
ps = [Point(0.5, 0.5), Point(0, 10)]
|
||||
@@ -835,12 +866,6 @@ class TestPygeosInterface:
|
||||
else:
|
||||
assert_array_equal(res, expected[0])
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not (compat.USE_SHAPELY_20),
|
||||
reason=(
|
||||
"shapely >= 2.0 is required to test sindex.nearest with parameter exclusive"
|
||||
),
|
||||
)
|
||||
@pytest.mark.parametrize("return_distance", [True, False])
|
||||
@pytest.mark.parametrize(
|
||||
"return_all,max_distance,exclusive,expected",
|
||||
@@ -861,7 +886,7 @@ class TestPygeosInterface:
|
||||
def test_nearest_exclusive(
|
||||
self, expected, max_distance, return_all, return_distance, exclusive
|
||||
):
|
||||
geoms = mod.points(np.arange(5), np.arange(5))
|
||||
geoms = shapely.points(np.arange(5), np.arange(5))
|
||||
if max_distance:
|
||||
# add a non grid point
|
||||
geoms = np.append(geoms, [Point(1, 2)])
|
||||
@@ -882,19 +907,6 @@ class TestPygeosInterface:
|
||||
else:
|
||||
assert_array_equal(res, expected[0])
|
||||
|
||||
@pytest.mark.skipif(
|
||||
compat.USE_SHAPELY_20 or not (compat.USE_PYGEOS and not compat.PYGEOS_GE_010),
|
||||
reason="sindex.nearest exclusive parameter requires shapely >= 2.0",
|
||||
)
|
||||
def test_nearest_exclusive_unavailable(self):
|
||||
from shapely.geometry import Point
|
||||
|
||||
geoms = [Point((x, y)) for (x, y) in zip(np.arange(5), np.arange(5))]
|
||||
df = geopandas.GeoDataFrame(geometry=geoms)
|
||||
|
||||
with pytest.raises(NotImplementedError, match="requires shapely >= 2.0"):
|
||||
df.sindex.nearest(geoms, exclusive=True)
|
||||
|
||||
# --------------------------- misc tests ---------------------------- #
|
||||
|
||||
def test_empty_tree_geometries(self):
|
||||
@@ -936,23 +948,12 @@ class TestPygeosInterface:
|
||||
("touches", (2, 0)),
|
||||
],
|
||||
)
|
||||
def test_integration_natural_earth(self, predicate, expected_shape):
|
||||
def test_integration_natural_earth(
|
||||
self, predicate, expected_shape, naturalearth_lowres, naturalearth_cities
|
||||
):
|
||||
"""Tests output sizes for the naturalearth datasets."""
|
||||
world = read_file(datasets.get_path("naturalearth_lowres"))
|
||||
capitals = read_file(datasets.get_path("naturalearth_cities"))
|
||||
world = read_file(naturalearth_lowres)
|
||||
capitals = read_file(naturalearth_cities)
|
||||
|
||||
res = world.sindex.query(capitals.geometry, predicate)
|
||||
assert res.shape == expected_shape
|
||||
|
||||
|
||||
@pytest.mark.skipif(not compat.HAS_RTREE, reason="no rtree installed")
|
||||
def test_old_spatial_index_deprecated():
|
||||
t1 = Polygon([(0, 0), (1, 0), (1, 1)])
|
||||
t2 = Polygon([(0, 0), (1, 1), (0, 1)])
|
||||
|
||||
stream = ((i, item.bounds, None) for i, item in enumerate([t1, t2]))
|
||||
|
||||
with pytest.warns(FutureWarning):
|
||||
idx = geopandas.sindex.SpatialIndex(stream)
|
||||
|
||||
assert list(idx.intersection((0, 0, 1, 1))) == [0, 1]
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import warnings
|
||||
|
||||
import numpy as np
|
||||
|
||||
from shapely.geometry import Point, Polygon
|
||||
import pandas as pd
|
||||
from pandas import DataFrame, Series
|
||||
|
||||
from shapely.geometry import Point, Polygon
|
||||
|
||||
from geopandas import GeoDataFrame, GeoSeries
|
||||
from geopandas._compat import HAS_PYPROJ
|
||||
from geopandas.array import from_shapely
|
||||
|
||||
from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
|
||||
import pytest
|
||||
from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
|
||||
|
||||
s1 = GeoSeries(
|
||||
[
|
||||
@@ -46,9 +47,9 @@ df1 = GeoDataFrame({"col1": [1, 2], "geometry": s1})
|
||||
df2 = GeoDataFrame({"col1": [1, 2], "geometry": s2})
|
||||
|
||||
s4 = s1.copy()
|
||||
s4.crs = 4326
|
||||
s4.array.crs = 4326
|
||||
s5 = s2.copy()
|
||||
s5.crs = 27700
|
||||
s5.array.crs = 27700
|
||||
|
||||
s6 = GeoSeries(
|
||||
[
|
||||
@@ -102,9 +103,10 @@ def test_geodataframe():
|
||||
assert_geodataframe_equal(df1, df3)
|
||||
|
||||
assert_geodataframe_equal(df5, df4, check_like=True)
|
||||
df5.geom2.crs = 3857
|
||||
with pytest.raises(AssertionError):
|
||||
assert_geodataframe_equal(df5, df4, check_like=True)
|
||||
if HAS_PYPROJ:
|
||||
df5["geom2"] = df5.geom2.set_crs(3857, allow_override=True)
|
||||
with pytest.raises(AssertionError):
|
||||
assert_geodataframe_equal(df5, df4, check_like=True)
|
||||
|
||||
|
||||
def test_equal_nans():
|
||||
@@ -119,6 +121,7 @@ def test_no_crs():
|
||||
assert_geodataframe_equal(df1, df2)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_PYPROJ, reason="pyproj not available")
|
||||
def test_ignore_crs_mismatch():
|
||||
df1 = GeoDataFrame({"col1": [1, 2], "geometry": s1.copy()}, crs="EPSG:4326")
|
||||
df2 = GeoDataFrame({"col1": [1, 2], "geometry": s1}, crs="EPSG:31370")
|
||||
|
||||
@@ -13,6 +13,15 @@ from geopandas.testing import ( # noqa: F401
|
||||
HERE = os.path.abspath(os.path.dirname(__file__))
|
||||
PACKAGE_DIR = os.path.dirname(os.path.dirname(HERE))
|
||||
|
||||
_TEST_DATA_DIR = os.path.join(PACKAGE_DIR, "geopandas", "tests", "data")
|
||||
_NYBB = "zip://" + os.path.join(_TEST_DATA_DIR, "nybb_16a.zip")
|
||||
_NATURALEARTH_CITIES = os.path.join(
|
||||
_TEST_DATA_DIR, "naturalearth_cities", "naturalearth_cities.shp"
|
||||
)
|
||||
_NATURALEARTH_LOWRES = os.path.join(
|
||||
_TEST_DATA_DIR, "naturalearth_lowres", "naturalearth_lowres.shp"
|
||||
)
|
||||
|
||||
|
||||
# mock not used here, but the import from here is used in other modules
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user