Files
2025-01-26 19:24:23 -08:00

1036 lines
29 KiB
Python

import random
import warnings
import numpy as np
import pandas as pd
import shapely
import shapely.affinity
import shapely.geometry
import shapely.wkb
import shapely.wkt
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,
)
import pytest
triangle_no_missing = [
shapely.geometry.Polygon([(random.random(), random.random()) for i in range(3)])
for _ in range(10)
]
triangles = triangle_no_missing + [shapely.wkt.loads("POLYGON EMPTY"), None]
T = from_shapely(triangles)
points_no_missing = [
shapely.geometry.Point(random.random(), random.random()) for _ in range(20)
]
points = points_no_missing + [None]
P = from_shapely(points)
def equal_geometries(result, expected):
for r, e in zip(result, expected):
if r is None or e is None:
if not (r is None and e is None):
return False
elif not r.equals(e):
return False
return True
def test_points():
x = np.arange(10).astype(np.float64)
y = np.arange(10).astype(np.float64) ** 2
points = points_from_xy(x, y)
assert isinstance(points, GeometryArray)
for i in range(10):
assert isinstance(points[i], shapely.geometry.Point)
assert points[i].x == x[i]
assert points[i].y == y[i]
def test_points_from_xy():
# testing the top-level interface
# using DataFrame column
df = pd.DataFrame([{"x": x, "y": x, "z": x} for x in range(10)])
gs = [shapely.geometry.Point(x, x) for x in range(10)]
gsz = [shapely.geometry.Point(x, x, x) for x in range(10)]
geometry1 = geopandas.points_from_xy(df["x"], df["y"])
geometry2 = geopandas.points_from_xy(df["x"], df["y"], df["z"])
assert isinstance(geometry1, GeometryArray)
assert isinstance(geometry2, GeometryArray)
assert list(geometry1) == gs
assert list(geometry2) == gsz
# using Series or numpy arrays or lists
for s in [pd.Series(range(10)), np.arange(10), list(range(10))]:
geometry1 = geopandas.points_from_xy(s, s)
geometry2 = geopandas.points_from_xy(s, s, s)
assert isinstance(geometry1, GeometryArray)
assert isinstance(geometry2, GeometryArray)
assert list(geometry1) == gs
assert list(geometry2) == gsz
# using different lengths should throw error
arr_10 = np.arange(10)
arr_20 = np.arange(20)
with pytest.raises(ValueError):
geopandas.points_from_xy(x=arr_10, y=arr_20)
geopandas.points_from_xy(x=arr_10, y=arr_10, z=arr_20)
# Using incomplete arguments should throw error
with pytest.raises(TypeError):
geopandas.points_from_xy(x=s)
geopandas.points_from_xy(y=s)
geopandas.points_from_xy(z=s)
def test_from_shapely():
assert isinstance(T, GeometryArray)
assert equal_geometries(T, triangles)
def test_from_shapely_geo_interface():
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
@property
def __geo_interface__(self):
return {"type": "Point", "coordinates": (self.x, self.y)}
result = from_shapely([Point(1.0, 2.0), Point(3.0, 4.0)])
expected = from_shapely(
[shapely.geometry.Point(1.0, 2.0), shapely.geometry.Point(3.0, 4.0)]
)
assert all(v.equals(t) for v, t in zip(result, expected))
def test_from_wkb():
# list
L_wkb = [p.wkb for p in points_no_missing]
res = from_wkb(L_wkb)
assert isinstance(res, GeometryArray)
assert all(v.equals(t) for v, t in zip(res, points_no_missing))
# array
res = from_wkb(np.array(L_wkb, dtype=object))
assert isinstance(res, GeometryArray)
assert all(v.equals(t) for v, t in zip(res, points_no_missing))
# missing values
# TODO(shapely) does not support empty strings, np.nan, or pd.NA
missing_values = [None]
res = from_wkb(missing_values)
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
# single MultiPolygon
multi_poly = shapely.geometry.MultiPolygon(
[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(3, 3, 4, 4)]
)
res = from_wkb([multi_poly.wkb])
assert res[0] == multi_poly
def test_from_wkb_hex():
geometry_hex = ["0101000000CDCCCCCCCCCC1440CDCCCCCCCC0C4A40"]
res = from_wkb(geometry_hex)
assert isinstance(res, GeometryArray)
# array
res = from_wkb(np.array(geometry_hex, dtype=object))
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)
exp = np.array([p.wkb for p in points_no_missing], dtype=object)
assert isinstance(res, np.ndarray)
np.testing.assert_array_equal(res, exp)
res = to_wkb(P, hex=True)
exp = np.array([p.wkb_hex for p in points_no_missing], dtype=object)
assert isinstance(res, np.ndarray)
np.testing.assert_array_equal(res, exp)
# missing values
a = from_shapely([None, points_no_missing[0]])
res = to_wkb(a)
assert res[0] is None
@pytest.mark.parametrize("string_type", ["str", "bytes"])
def test_from_wkt(string_type):
if string_type == "str":
f = str
else:
def f(x):
return bytes(x, "utf8")
# list
L_wkt = [f(p.wkt) for p in points_no_missing]
res = from_wkt(L_wkt)
assert isinstance(res, GeometryArray)
tol = 0.5 * 10 ** (-6)
assert all(v.equals_exact(t, tolerance=tol) for v, t in zip(res, points_no_missing))
assert all(v.equals_exact(t, tolerance=tol) for v, t in zip(res, points_no_missing))
# array
res = from_wkt(np.array(L_wkt, dtype=object))
assert isinstance(res, GeometryArray)
assert all(v.equals_exact(t, tolerance=tol) for v, t in zip(res, points_no_missing))
# missing values
# TODO(shapely) does not support empty strings, np.nan, or pd.NA
missing_values = [None]
res = from_wkt(missing_values)
np.testing.assert_array_equal(res, np.full(len(missing_values), None))
# single MultiPolygon
multi_poly = shapely.geometry.MultiPolygon(
[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(3, 3, 4, 4)]
)
res = from_wkt([f(multi_poly.wkt)])
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)
exp = np.array([p.wkt for p in points_no_missing], dtype=object)
assert isinstance(res, np.ndarray)
np.testing.assert_array_equal(res, exp)
# missing values
a = from_shapely([None, points_no_missing[0]])
res = to_wkt(a)
assert res[0] is None
def test_as_array():
arr = from_shapely(points_no_missing)
np_arr1 = np.asarray(arr)
np_arr2 = arr.to_numpy()
assert np_arr1[0] == arr[0]
np.testing.assert_array_equal(np_arr1, np_arr2)
@pytest.mark.parametrize(
"attr,args",
[
("contains", ()),
("covers", ()),
("crosses", ()),
("disjoint", ()),
("geom_equals", ()),
("intersects", ()),
("overlaps", ()),
("touches", ()),
("within", ()),
("geom_equals_exact", (0.1,)),
("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
point = points[0]
tri = triangles[0]
for other in [point, tri, shapely.geometry.Polygon()]:
result = getattr(T, attr)(other, *args)
assert isinstance(result, np.ndarray)
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
)
for tri in triangles
]
assert result.tolist() == expected
# TODO other is missing
@pytest.mark.parametrize(
"attr,args",
[
("contains", ()),
("covers", ()),
("crosses", ()),
("disjoint", ()),
("geom_equals", ()),
("intersects", ()),
("overlaps", ()),
("touches", ()),
("within", ()),
("geom_equals_exact", (0.1,)),
("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
A = (
[shapely.geometry.Polygon(), None]
+ [
shapely.geometry.Polygon(
[(random.random(), random.random()) for i in range(3)]
)
for _ in range(100)
]
+ [None]
)
B = [
shapely.geometry.Polygon([(random.random(), random.random()) for i in range(3)])
for _ in range(100)
] + [shapely.geometry.Polygon(), None, None]
vec_A = from_shapely(A)
vec_B = from_shapely(B)
result = getattr(vec_A, attr)(vec_B, *args)
assert isinstance(result, np.ndarray)
assert result.dtype == bool
expected = []
for a, b in zip(A, B):
if a is None or b is None:
expected.append(na_value)
elif a.is_empty or b.is_empty:
expected.append(empty_value)
else:
expected.append(
getattr(a, attr if "geom" not in attr else attr[5:])(b, *args)
)
assert result.tolist() == expected
@pytest.mark.parametrize(
"attr",
[
"boundary",
"centroid",
"convex_hull",
"envelope",
"exterior",
# 'interiors',
],
)
def test_unary_geo(attr):
na_value = None
result = getattr(T, attr)
expected = [getattr(t, attr) if t is not None else na_value for t in triangles]
assert equal_geometries(result, expected)
@pytest.mark.parametrize("attr", ["representative_point"])
def test_unary_geo_callable(attr):
na_value = None
result = getattr(T, attr)()
expected = [getattr(t, attr)() if t is not None else na_value for t in triangles]
assert equal_geometries(result, expected)
@pytest.mark.parametrize(
"attr", ["difference", "symmetric_difference", "union", "intersection"]
)
def test_binary_geo_vector(attr):
na_value = None
quads = [shapely.geometry.Polygon(), None]
while len(quads) < 12:
geom = shapely.geometry.Polygon(
[(random.random(), random.random()) for i in range(4)]
)
if geom.is_valid:
quads.append(geom)
Q = from_shapely(quads)
result = getattr(T, attr)(Q)
expected = [
getattr(t, attr)(q) if t is not None and q is not None else na_value
for t, q in zip(triangles, quads)
]
assert equal_geometries(result, expected)
@pytest.mark.parametrize(
"attr", ["difference", "symmetric_difference", "union", "intersection"]
)
def test_binary_geo_scalar(attr):
na_value = None
quads = []
while len(quads) < 1:
geom = shapely.geometry.Polygon(
[(random.random(), random.random()) for i in range(4)]
)
if geom.is_valid:
quads.append(geom)
q = quads[0]
for other in [q, shapely.geometry.Polygon()]:
result = getattr(T, attr)(other)
expected = [
getattr(t, attr)(other) if t is not None else na_value for t in triangles
]
assert equal_geometries(result, expected)
@pytest.mark.parametrize(
"attr",
[
"is_closed",
"is_valid",
"is_empty",
"is_simple",
"has_z",
# for is_ring we raise a warning about the value for Polygon changing
"is_ring",
],
)
def test_unary_predicates(attr):
na_value = False
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
vals = triangle_no_missing
V = from_shapely(vals)
else:
vals = triangles
V = T
result = getattr(V, attr)
if attr == "is_ring":
expected = [
getattr(t, attr) if t is not None and t.exterior is not None else na_value
for t in vals
]
else:
expected = [getattr(t, attr) if t is not None else na_value for t in vals]
assert result.tolist() == expected
def test_is_ring():
g = [
shapely.geometry.LinearRing([(0, 0), (1, 1), (1, -1)]),
shapely.geometry.LineString([(0, 0), (1, 1), (1, -1)]),
shapely.geometry.LineString([(0, 0), (1, 1), (1, -1), (0, 0)]),
shapely.geometry.Polygon([(0, 0), (1, 1), (1, -1)]),
shapely.wkt.loads("POLYGON EMPTY"),
None,
]
expected = [True, False, True, False, False, False]
result = from_shapely(g).is_ring
assert result.tolist() == expected
@pytest.mark.parametrize("attr", ["area", "length"])
def test_unary_float(attr):
na_value = np.nan
result = getattr(T, attr)
assert isinstance(result, np.ndarray)
assert result.dtype == np.dtype("float64")
expected = [getattr(t, attr) if t is not None else na_value for t in triangles]
np.testing.assert_allclose(result, expected)
def test_geom_types():
cat = T.geom_type
# empty polygon has GeometryCollection type
assert list(cat) == ["Polygon"] * (len(T) - 1) + [None]
def test_geom_types_null_mixed():
geoms = [
shapely.geometry.Polygon([(0, 0), (0, 1), (1, 1)]),
None,
shapely.geometry.Point(0, 1),
]
G = from_shapely(geoms)
cat = G.geom_type
assert list(cat) == ["Polygon", None, "Point"]
def test_binary_distance():
attr = "distance"
na_value = np.nan
# also use nan for empty
# 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
)
for t, p in zip(triangles[::-1], points)
]
np.testing.assert_allclose(result, expected)
# vector - scalar
p = points[0]
result = T.distance(p)
expected = [
getattr(t, attr)(p) if not (t is None or t.is_empty) else na_value
for t in triangles
]
np.testing.assert_allclose(result, expected)
# other is empty
result = T.distance(shapely.geometry.Polygon())
expected = [na_value] * len(T)
np.testing.assert_allclose(result, expected)
# TODO other is None
def test_binary_relate():
attr = "relate"
na_value = None
# vector - vector
result = getattr(P[: len(T)], attr)(T[::-1])
expected = [
getattr(p, attr)(t) if t is not None and p is not None else na_value
for t, p in zip(triangles[::-1], points)
]
assert list(result) == expected
# vector - scalar
p = points[0]
result = getattr(T, attr)(p)
expected = [getattr(t, attr)(p) if t is not None else na_value for t in triangles]
assert list(result) == expected
@pytest.mark.parametrize("normalized", [True, False])
def test_binary_project(normalized):
na_value = np.nan
lines = (
[None]
+ [
shapely.geometry.LineString(
[(random.random(), random.random()) for _ in range(2)]
)
for _ in range(len(P) - 2)
]
+ [None]
)
L = from_shapely(lines)
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
)
for p, line in zip(points, lines)
]
np.testing.assert_allclose(result, expected)
@pytest.mark.parametrize("cap_style", [CAP_STYLE.round, CAP_STYLE.square])
@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):
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
)
for p in points
]
result = P.buffer(
0.1, resolution=resolution, cap_style=cap_style, join_style=join_style
)
assert equal_geometries(expected, result)
dist = np.array([0.1] * len(P))
result = P.buffer(
dist, resolution=resolution, cap_style=cap_style, join_style=join_style
)
assert equal_geometries(expected, result)
def test_simplify():
triangles = [
shapely.geometry.Polygon(
[(random.random(), random.random()) for i in range(3)]
).buffer(10)
for _ in range(10)
]
T = from_shapely(triangles)
result = T.simplify(1)
expected = [t.simplify(1) for t in triangles]
assert all(a.equals(b) for a, b in zip(expected, result))
def test_unary_union():
geoms = [
shapely.geometry.Polygon([(0, 0), (0, 1), (1, 1)]),
shapely.geometry.Polygon([(0, 0), (1, 0), (1, 1)]),
]
G = from_shapely(geoms)
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(
"attr, arg",
[
("affine_transform", ([0, 1, 1, 0, 0, 0],)),
("translate", ()),
("rotate", (10,)),
("scale", ()),
("skew", ()),
],
)
def test_affinity_methods(attr, arg):
result = getattr(T, attr)(*arg)
expected = [
getattr(shapely.affinity, attr)(t, *arg) if not (t is None or t.is_empty) else t
for t in triangles
]
assert equal_geometries(result, expected)
# def test_coords():
# L = T.exterior.coords
# assert L == [tuple(t.exterior.coords) for t in triangles]
def test_coords_x_y():
na_value = np.nan
result = P.x
expected = [p.x if p is not None else na_value for p in points]
np.testing.assert_allclose(result, expected)
result = P.y
expected = [p.y if p is not None else na_value for p in points]
np.testing.assert_allclose(result, expected)
def test_bounds():
result = T.bounds
expected = [
t.bounds if not (t is None or t.is_empty) else [np.nan] * 4 for t in triangles
]
np.testing.assert_allclose(result, expected)
# additional check for one empty / missing
for geom in [None, shapely.geometry.Polygon()]:
E = from_shapely([geom])
result = E.bounds
assert result.ndim == 2
assert result.dtype == "float64"
np.testing.assert_allclose(result, np.array([[np.nan] * 4]))
# empty array (https://github.com/geopandas/geopandas/issues/1195)
E = from_shapely([])
result = E.bounds
assert result.shape == (0, 4)
assert result.dtype == "float64"
def test_total_bounds():
result = T.total_bounds
bounds = np.array(
[t.bounds if not (t is None or t.is_empty) else [np.nan] * 4 for t in triangles]
)
expected = np.array(
[
np.nanmin(bounds[:, 0]), # minx
np.nanmin(bounds[:, 1]), # miny
np.nanmax(bounds[:, 2]), # maxx
np.nanmax(bounds[:, 3]), # maxy
]
)
np.testing.assert_allclose(result, expected)
# additional check for empty array or one empty / missing
for geoms in [[], [None], [shapely.geometry.Polygon()]]:
E = from_shapely(geoms)
result = E.total_bounds
assert result.ndim == 1
assert result.dtype == "float64"
np.testing.assert_allclose(result, np.array([np.nan] * 4))
def test_getitem():
points = [shapely.geometry.Point(i, i) for i in range(10)]
P = from_shapely(points)
P2 = P[P.area > 0.3]
assert isinstance(P2, GeometryArray)
P3 = P[[1, 3, 5]]
assert len(P3) == 3
assert isinstance(P3, GeometryArray)
assert [p.x for p in P3] == [1, 3, 5]
P4 = P[1::2]
assert len(P4) == 5
assert isinstance(P3, GeometryArray)
assert [p.x for p in P4] == [1, 3, 5, 7, 9]
P5 = P[1]
assert isinstance(P5, shapely.geometry.Point)
assert P5.equals(points[1])
@pytest.mark.parametrize(
"item",
[
geopandas.GeoDataFrame(
geometry=[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]
),
geopandas.GeoSeries(
[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]
),
np.array([shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])]),
[shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)])],
shapely.geometry.Polygon([(0, 0), (2, 0), (2, 2), (0, 2)]),
],
)
def test_setitem(item):
points = [shapely.geometry.Point(i, i) for i in range(10)]
P = from_shapely(points)
P[[0]] = item
assert isinstance(P[0], shapely.geometry.Polygon)
def test_equality_ops():
with pytest.raises(ValueError):
_ = P[:5] == P[:7]
a1 = from_shapely([points[1], points[2], points[3]])
a2 = from_shapely([points[1], points[0], points[3]])
res = a1 == a2
assert res.tolist() == [True, False, True]
res = a1 != a2
assert res.tolist() == [False, True, False]
# check the correct expansion of list-like geometry
multi_poly = shapely.geometry.MultiPolygon(
[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(3, 3, 4, 4)]
)
a3 = from_shapely([points[1], points[2], points[3], multi_poly])
res = a3 == multi_poly
assert res.tolist() == [False, False, False, True]
def test_dir():
assert "contains" in dir(P)
assert "to_numpy" in dir(P)
def test_chaining():
# contains will give False for empty / missing
T = from_shapely(triangle_no_missing)
assert T.contains(T.centroid).all()
def test_pickle():
import pickle
T2 = pickle.loads(pickle.dumps(T))
# assert (T.data != T2.data).all()
assert T2[-1] is None
assert T2[-2].is_empty
assert T[:-2].geom_equals(T2[:-2]).all()
def test_raise_on_bad_sizes():
with pytest.raises(ValueError) as info:
T.contains(P)
assert "lengths" in str(info.value).lower()
assert "12" in str(info.value)
assert "21" in str(info.value)
def test_buffer_single_multipolygon():
# https://github.com/geopandas/geopandas/issues/1130
multi_poly = shapely.geometry.MultiPolygon(
[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(3, 3, 4, 4)]
)
arr = from_shapely([multi_poly])
result = arr.buffer(1)
expected = [multi_poly.buffer(1)]
equal_geometries(result, expected)
result = arr.buffer(np.array([1]))
equal_geometries(result, expected)
def test_astype_multipolygon():
# https://github.com/geopandas/geopandas/issues/1145
multi_poly = shapely.geometry.MultiPolygon(
[shapely.geometry.box(0, 0, 1, 1), shapely.geometry.box(3, 3, 4, 4)]
)
arr = from_shapely([multi_poly])
result = arr.astype(str)
assert isinstance(result[0], str)
assert result[0] == multi_poly.wkt
# astype(object) does not convert to string
result = arr.astype(object)
assert isinstance(result[0], shapely.geometry.base.BaseGeometry)
# astype(np_dtype) honors the dtype
result = arr.astype(np.dtype("U10"))
assert result.dtype == np.dtype("U10")
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
assert _check_crs(t1, T) is False
assert _check_crs(t1, t1) is True
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()
t1.crs = 4326
t2.crs = 3857
# two different CRS
with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
_crs_mismatch_warn(t1, t2)
# left None
with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
_crs_mismatch_warn(T, t2)
# right None
with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
_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()
t1[0] = NA
assert t1[0] is None
def test_isna_pdNA():
t1 = T.copy()
t1[0] = pd.NA
assert t1[0] is None
def test_shift_has_crs():
t = T.copy()
t.crs = 4326
assert t.shift(1).crs == t.crs
assert t.shift(0).crs == t.crs
assert t.shift(-1).crs == t.crs
def test_unique_has_crs():
t = T.copy()
t.crs = 4326
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)
self.sol = shapely.geometry.Point(-74.0446, 40.6893)
self.landmarks = from_shapely([self.esb, self.sol], crs="epsg:4326")
def test_estimate_utm_crs__geographic(self):
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):
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),
shapely.geometry.Point(4624385.494808555, 8692574.544944234),
],
crs="EPSG:3851",
)
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)])]
).estimate_utm_crs()