Skip to content

Commit c01e731

Browse files
authored
Add geopandas stubs (#12990)
1 parent 31bff4a commit c01e731

30 files changed

+1911
-0
lines changed

pyrightconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// Stubs that don't work in all Python versions
1010
"stubs/seaborn",
1111
"stubs/shapely",
12+
"stubs/geopandas",
1213
// test cases use a custom config file
1314
"**/@tests/test_cases",
1415
],

pyrightconfig.stricter.json

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"stubs/Flask-SocketIO",
5050
"stubs/fpdf2",
5151
"stubs/gdb",
52+
"stubs/geopandas",
5253
"stubs/google-cloud-ndb",
5354
"stubs/hdbcli/hdbcli/dbapi.pyi",
5455
"stubs/html5lib",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Stub missing OK
2+
geopandas\.conftest
3+
geopandas\.(.*\.)?tests.*
4+
geopandas\.array\.(type_mapping|geometry_type_ids|geometry_type_values)
5+
geopandas\.io\.file\.(FIONA|PYOGRIO)_GE_.*
6+
geopandas\.io\.file\.(fiona|pyogrio)(_env|_import_error)?
7+
geopandas\.io\.util
8+
geopandas\.datasets\.*
9+
10+
# Inconsistent OK
11+
geopandas\.(geodataframe\.)?GeoDataFrame\.plot
12+
13+
# Failed to import OK (require extra dependencies)
14+
geopandas\.io\._geoarrow
15+
geopandas\._exports
16+
17+
# Not present in stub OK (do not work at runtime, to be removed from geopandas)
18+
geopandas\.(geoseries\.)?GeoSeries\.append # https://github.com/geopandas/geopandas/pull/3460
19+
geopandas\.(geoseries\.)?GeoSeries\.select # https://github.com/geopandas/geopandas/pull/3394
20+
21+
# Inconsistent (TODO)
22+
geopandas\.(geoseries\.)?GeoSeries\.apply
23+
geopandas\.(geoseries\.)?GeoSeries\.fillna
24+
geopandas\.(geoseries\.)?GeoSeries\.sort_index

stubs/geopandas/METADATA.toml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
version = "1.0.1"
2+
# Requires a version of numpy with a `py.typed` file
3+
requires = ["numpy>=1.20", "pandas-stubs", "types-shapely", "pyproj"]
4+
upstream_repository = "https://github.com/geopandas/geopandas"
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import Final
2+
3+
from ._config import options as options
4+
from ._exports import (
5+
gpd as gpd,
6+
list_layers as list_layers,
7+
np as np,
8+
pd as pd,
9+
read_feather as read_feather,
10+
read_file as read_file,
11+
read_parquet as read_parquet,
12+
read_postgis as read_postgis,
13+
)
14+
from .array import points_from_xy as points_from_xy
15+
from .geodataframe import GeoDataFrame as GeoDataFrame
16+
from .geoseries import GeoSeries as GeoSeries
17+
from .tools import clip as clip, overlay as overlay, sjoin as sjoin, sjoin_nearest as sjoin_nearest
18+
from .tools._show_versions import show_versions as show_versions
19+
20+
__version__: Final[str]

stubs/geopandas/geopandas/_config.pyi

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from _typeshed import SupportsItems, Unused
2+
from collections.abc import Callable
3+
from typing import Any, NamedTuple
4+
5+
class Option(NamedTuple):
6+
key: str
7+
default_value: Any # Can be "any" type
8+
doc: str
9+
validator: Callable[[object], Unused]
10+
callback: Callable[[str, object], Unused] | None
11+
12+
class Options:
13+
def __init__(self, options: SupportsItems[str, Option]) -> None: ...
14+
# Accept and return arbitrary values
15+
def __setattr__(self, key: str, value: Any) -> None: ...
16+
def __getattr__(self, key: str) -> Any: ...
17+
18+
display_precision: Option
19+
use_pygeos: Option
20+
io_engine: Option
21+
options: Options
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from collections.abc import Callable
2+
from typing import TypeVar, overload
3+
from typing_extensions import TypeAlias
4+
5+
_AnyCallable: TypeAlias = Callable[..., object]
6+
_Func = TypeVar("_Func", bound=_AnyCallable)
7+
8+
# We (ab)use this decorator to also copy the signature of the source function (overload 1)
9+
# The advantages are:
10+
# - avoid copying all parameters and types manually while conserving type safety
11+
# - signature properly handeled in IDEs (at least with Pylance)
12+
# - docstring from the original function properly displayed (at least with Pylance)
13+
# Using the other overloads returns the signature of the decorated function instead
14+
@overload
15+
def doc(func: _Func, /, **params: object) -> Callable[[_AnyCallable], _Func]: ...
16+
@overload
17+
def doc(docstring: str, /, *docstrings: str | _AnyCallable, **params: object) -> Callable[[_Func], _Func]: ...
18+
@overload
19+
def doc(
20+
docstring1: str | _AnyCallable, docstring2: str | _AnyCallable, /, *docstrings: str | _AnyCallable, **params: object
21+
) -> Callable[[_Func], _Func]: ...
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Type checking-only module to export public symbols with a different name in __init__.pyi
2+
3+
import geopandas as gpd
4+
import numpy as np
5+
import pandas as pd
6+
from geopandas.io.arrow import _read_feather as read_feather, _read_parquet as read_parquet
7+
from geopandas.io.file import _list_layers as list_layers, _read_file as read_file
8+
from geopandas.io.sql import _read_postgis as read_postgis
9+
10+
__all__ = [
11+
# IO functions
12+
"read_file",
13+
"read_feather",
14+
"read_parquet",
15+
"read_postgis",
16+
"list_layers",
17+
# Modules for interactive use
18+
"np",
19+
"pd",
20+
"gpd",
21+
]

stubs/geopandas/geopandas/array.pyi

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import builtins
2+
from _typeshed import Incomplete, Unused
3+
from collections.abc import Callable, Sequence
4+
from typing import Any, ClassVar, Literal, NoReturn, SupportsIndex, TypeVar, overload
5+
from typing_extensions import Self, TypeAlias, deprecated
6+
7+
import numpy as np
8+
import pandas as pd
9+
from numpy.typing import ArrayLike, DTypeLike, NDArray
10+
from pandas.api.extensions import ExtensionArray, ExtensionDtype
11+
from pyproj import CRS, Transformer
12+
from shapely import Geometry
13+
from shapely.geometry.base import BaseGeometry
14+
15+
from .base import _AffinityOrigin, _ConvertibleToCRS
16+
from .sindex import SpatialIndex
17+
18+
_ScalarType = TypeVar("_ScalarType", bound=np.generic)
19+
_Array1D: TypeAlias = np.ndarray[tuple[int], np.dtype[_ScalarType]]
20+
_Array2D: TypeAlias = np.ndarray[tuple[int, int], np.dtype[_ScalarType]]
21+
_ArrayOrGeom: TypeAlias = GeometryArray | ArrayLike | Geometry
22+
23+
TransformerFromCRS = Transformer.from_crs
24+
25+
class GeometryDtype(ExtensionDtype):
26+
type: ClassVar[type[BaseGeometry]]
27+
name: ClassVar[str]
28+
na_value: float
29+
@classmethod
30+
def construct_from_string(cls, string: str) -> Self: ...
31+
@classmethod
32+
def construct_array_type(cls) -> builtins.type[GeometryArray]: ...
33+
34+
def isna(value: object) -> bool: ...
35+
def from_shapely(data, crs: _ConvertibleToCRS | None = None) -> GeometryArray: ...
36+
def to_shapely(geoms: GeometryArray) -> _Array1D[np.object_]: ...
37+
def from_wkb(
38+
data, crs: _ConvertibleToCRS | None = None, on_invalid: Literal["raise", "warn", "ignore"] = "raise"
39+
) -> GeometryArray: ...
40+
@overload
41+
def to_wkb(geoms: GeometryArray, hex: Literal[False] = False, **kwargs) -> _Array1D[np.bytes_]: ...
42+
@overload
43+
def to_wkb(geoms: GeometryArray, hex: Literal[True], **kwargs) -> _Array1D[np.str_]: ...
44+
def from_wkt(
45+
data, crs: _ConvertibleToCRS | None = None, on_invalid: Literal["raise", "warn", "ignore"] = "raise"
46+
) -> GeometryArray: ...
47+
def to_wkt(geoms: GeometryArray, **kwargs) -> _Array1D[np.str_]: ...
48+
def points_from_xy(
49+
x: ArrayLike, y: ArrayLike, z: ArrayLike | None = None, crs: _ConvertibleToCRS | None = None
50+
) -> GeometryArray: ...
51+
52+
class GeometryArray(ExtensionArray):
53+
def __init__(self, data: GeometryArray | NDArray[np.object_], crs: _ConvertibleToCRS | None = None) -> None: ...
54+
@property
55+
def sindex(self) -> SpatialIndex: ...
56+
@property
57+
def has_sindex(self) -> bool: ...
58+
@property
59+
def crs(self) -> CRS | None: ...
60+
@crs.setter
61+
def crs(self, value: _ConvertibleToCRS | None) -> None: ...
62+
def check_geographic_crs(self, stacklevel: int) -> None: ...
63+
@property
64+
def dtype(self) -> GeometryDtype: ...
65+
def __len__(self) -> int: ...
66+
# np.integer[Any] because precision is not important
67+
@overload
68+
def __getitem__(self, idx: int | np.integer[Any]) -> BaseGeometry: ... # Always 1-D, doesn't accept tuple
69+
@overload
70+
def __getitem__(
71+
self, idx: slice | Sequence[SupportsIndex] | NDArray[np.bool_] | NDArray[np.integer[Any]]
72+
) -> GeometryArray: ...
73+
@overload
74+
def __getitem__(
75+
self, idx: int | np.integer[Any] | slice | Sequence[int] | NDArray[np.bool_] | NDArray[np.integer[Any]]
76+
) -> BaseGeometry | GeometryArray: ...
77+
def __setitem__(
78+
self, key, value: _ArrayOrGeom | pd.DataFrame | pd.Series[Any] # Cannot use pd.Series[BaseGeometry]
79+
) -> None: ...
80+
@property
81+
def is_valid(self) -> _Array1D[np.bool_]: ...
82+
def is_valid_reason(self) -> _Array1D[np.object_]: ...
83+
@property
84+
def is_empty(self) -> _Array1D[np.bool_]: ...
85+
@property
86+
def is_simple(self) -> _Array1D[np.bool_]: ...
87+
@property
88+
def is_ring(self) -> _Array1D[np.bool_]: ...
89+
@property
90+
def is_closed(self) -> _Array1D[np.bool_]: ...
91+
@property
92+
def is_ccw(self) -> _Array1D[np.bool_]: ...
93+
@property
94+
def has_z(self) -> _Array1D[np.bool_]: ...
95+
@property
96+
def geom_type(self) -> _Array1D[np.str_]: ...
97+
@property
98+
def area(self) -> _Array1D[np.float64]: ...
99+
@property
100+
def length(self) -> _Array1D[np.float64]: ...
101+
def count_coordinates(self) -> _Array1D[np.int32]: ...
102+
def count_geometries(self) -> _Array1D[np.int32]: ...
103+
def count_interior_rings(self) -> _Array1D[np.int32]: ...
104+
def get_precision(self) -> _Array1D[np.float64]: ...
105+
def get_geometry(self, index: SupportsIndex | ArrayLike) -> _Array1D[np.object_]: ...
106+
@property
107+
def boundary(self) -> GeometryArray: ...
108+
@property
109+
def centroid(self) -> GeometryArray: ...
110+
def concave_hull(self, ratio: float, allow_holes: bool) -> _Array1D[np.object_]: ...
111+
@property
112+
def convex_hull(self) -> GeometryArray: ...
113+
@property
114+
def envelope(self) -> GeometryArray: ...
115+
def minimum_rotated_rectangle(self) -> GeometryArray: ...
116+
@property
117+
def exterior(self) -> GeometryArray: ...
118+
def extract_unique_points(self) -> GeometryArray: ...
119+
def offset_curve(
120+
self,
121+
distance: float | ArrayLike,
122+
quad_segs: int = 8,
123+
join_style: Literal["round", "bevel", "mitre"] = "round",
124+
mitre_limit: float = 5.0,
125+
) -> GeometryArray: ...
126+
@property
127+
def interiors(self) -> _Array1D[np.object_]: ...
128+
def remove_repeated_points(self, tolerance: float | ArrayLike = 0.0) -> GeometryArray: ...
129+
def representative_point(self) -> GeometryArray: ...
130+
def minimum_bounding_circle(self) -> GeometryArray: ...
131+
def minimum_bounding_radius(self) -> _Array1D[np.float64]: ...
132+
def minimum_clearance(self) -> _Array1D[np.float64]: ...
133+
def normalize(self) -> GeometryArray: ...
134+
def make_valid(self) -> GeometryArray: ...
135+
def reverse(self) -> GeometryArray: ...
136+
def segmentize(self, max_segment_length: float | ArrayLike) -> GeometryArray: ...
137+
def force_2d(self) -> GeometryArray: ...
138+
def force_3d(self, z: float | ArrayLike = 0) -> GeometryArray: ...
139+
def transform(
140+
self, transformation: Callable[[NDArray[np.float64]], NDArray[np.float64]], include_z: bool = False
141+
) -> GeometryArray: ...
142+
def line_merge(self, directed: bool = False) -> GeometryArray: ...
143+
def set_precision(
144+
self, grid_size: float, mode: Literal["valid_output", "pointwise", "keep_collapsed", 0, 1, 2] = "valid_output"
145+
) -> GeometryArray: ...
146+
def covers(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
147+
def covered_by(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
148+
def contains(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
149+
def contains_properly(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
150+
def crosses(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
151+
def disjoint(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
152+
def geom_equals(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
153+
def intersects(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
154+
def overlaps(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
155+
def touches(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
156+
def within(self, other: _ArrayOrGeom) -> _Array1D[np.bool_]: ...
157+
def dwithin(self, other: _ArrayOrGeom, distance: float) -> _Array1D[np.bool_]: ...
158+
def geom_equals_exact(self, other: _ArrayOrGeom, tolerance: float | ArrayLike) -> _Array1D[np.bool_]: ...
159+
@deprecated("Use method `geom_equals_exact` instead.")
160+
def geom_almost_equals(self, other: _ArrayOrGeom, decimal: float) -> _Array1D[np.bool_]: ...
161+
def clip_by_rect(self, xmin: float, ymin: float, xmax: float, ymax: float) -> GeometryArray: ...
162+
def difference(self, other: _ArrayOrGeom) -> GeometryArray: ...
163+
def intersection(self, other: _ArrayOrGeom) -> GeometryArray: ...
164+
def symmetric_difference(self, other: _ArrayOrGeom) -> GeometryArray: ...
165+
def union(self, other: _ArrayOrGeom) -> GeometryArray: ...
166+
def shortest_line(self, other: _ArrayOrGeom) -> GeometryArray: ...
167+
def snap(self, other: _ArrayOrGeom, tolerance: float | ArrayLike) -> GeometryArray: ...
168+
def shared_paths(self, other: _ArrayOrGeom) -> GeometryArray: ...
169+
def distance(self, other: _ArrayOrGeom) -> _Array1D[np.float64]: ...
170+
def hausdorff_distance(self, other: _ArrayOrGeom, **kwargs) -> _Array1D[np.float64]: ...
171+
def frechet_distance(self, other: _ArrayOrGeom, **kwargs) -> _Array1D[np.float64]: ...
172+
def buffer(self, distance: float | ArrayLike, resolution: int = 16, **kwargs) -> GeometryArray: ...
173+
def interpolate(self, distance: float | ArrayLike, normalized: bool = False) -> GeometryArray: ...
174+
def simplify(self, tolerance: float | ArrayLike, preserve_topology: bool = True) -> GeometryArray: ...
175+
def project(self, other: _ArrayOrGeom, normalized: bool = False) -> _Array1D[np.float64]: ...
176+
def relate(self, other: _ArrayOrGeom) -> _Array1D[np.str_]: ...
177+
def relate_pattern(self, other: _ArrayOrGeom, pattern: str) -> _Array1D[np.bool_]: ...
178+
@deprecated("Use method `union_all` instead.")
179+
def unary_union(self) -> BaseGeometry: ...
180+
def union_all(self, method: Literal["coverage", "unary"] = "unary") -> BaseGeometry: ...
181+
def intersection_all(self) -> BaseGeometry: ...
182+
def affine_transform(self, matrix) -> GeometryArray: ...
183+
def translate(self, xoff: float = 0.0, yoff: float = 0.0, zoff: float = 0.0) -> GeometryArray: ...
184+
def rotate(self, angle: float, origin: _AffinityOrigin = "center", use_radians: bool = False) -> GeometryArray: ...
185+
def scale(
186+
self, xfact: float = 1.0, yfact: float = 1.0, zfact: float = 1.0, origin: _AffinityOrigin = "center"
187+
) -> GeometryArray: ...
188+
def skew(
189+
self, xs: float = 0.0, ys: float = 0.0, origin: _AffinityOrigin = "center", use_radians: bool = False
190+
) -> GeometryArray: ...
191+
def to_crs(self, crs: _ConvertibleToCRS | None = None, epsg: int | None = None) -> GeometryArray: ...
192+
def estimate_utm_crs(self, datum_name: str = "WGS 84") -> CRS: ...
193+
@property
194+
def x(self) -> _Array1D[np.float64]: ...
195+
@property
196+
def y(self) -> _Array1D[np.float64]: ...
197+
@property
198+
def z(self) -> _Array1D[np.float64]: ...
199+
@property
200+
def bounds(self) -> _Array2D[np.float64]: ...
201+
@property
202+
def total_bounds(self) -> _Array1D[np.float64]: ...
203+
@property
204+
def size(self) -> int: ...
205+
@property
206+
def shape(self) -> tuple[int]: ... # Always 1-D, this is not a mistake
207+
@property
208+
def ndim(self) -> Literal[1]: ...
209+
def copy(self, *args: Unused, **kwargs: Unused) -> GeometryArray: ...
210+
def take(
211+
self,
212+
indices: Sequence[SupportsIndex] | NDArray[np.integer[Any]], # np.integer[Any] because precision is not important
213+
allow_fill: bool = False,
214+
fill_value: Geometry | None = None,
215+
) -> GeometryArray: ...
216+
def fillna(
217+
self,
218+
value: Geometry | GeometryArray | None = None,
219+
method: Literal["backfill", "bfill", "pad", "ffill"] | None = None,
220+
limit: int | None = None,
221+
copy: bool = True,
222+
) -> GeometryArray: ...
223+
@overload
224+
def astype(self, dtype: GeometryDtype, copy: bool = True) -> GeometryArray: ...
225+
@overload
226+
def astype(self, dtype: ExtensionDtype | Literal["string"], copy: bool = True) -> ExtensionArray: ... # type: ignore[overload-overlap]
227+
@overload
228+
def astype(self, dtype: DTypeLike, copy: bool = True) -> _Array1D[Incomplete]: ...
229+
def isna(self) -> _Array1D[np.bool_]: ...
230+
def value_counts(self, dropna: bool = True) -> pd.Series[int]: ...
231+
def unique(self) -> GeometryArray: ...
232+
@property
233+
def nbytes(self) -> int: ...
234+
def shift(self, periods: int = 1, fill_value: Geometry | None = None) -> GeometryArray: ... # type: ignore[override]
235+
def argmin(self, skipna: bool = True) -> NoReturn: ...
236+
def argmax(self, skipna: bool = True) -> NoReturn: ...
237+
def __array__(self, dtype: DTypeLike | None = None, copy: bool | None = None) -> _Array1D[np.object_]: ...
238+
def __eq__(self, other: object) -> _Array1D[np.bool_]: ... # type: ignore[override]
239+
def __ne__(self, other: object) -> _Array1D[np.bool_]: ... # type: ignore[override]
240+
def __contains__(self, item: object) -> np.bool_: ...
241+
242+
def transform(
243+
data: NDArray[np.object_], func: Callable[[NDArray[np.float64], NDArray[np.float64]], NDArray[np.float64]]
244+
) -> NDArray[np.object_]: ...

0 commit comments

Comments
 (0)