Skip to content

Commit

Permalink
shapely and geopandas improvements (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
hamdanal committed Feb 11, 2024
1 parent 151d547 commit f552cf0
Show file tree
Hide file tree
Showing 50 changed files with 1,580 additions and 330 deletions.
16 changes: 16 additions & 0 deletions .github/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Setup
description: Setup Python and install dependencies

runs:
using: composite
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: pip
cache-dependency-path: requirements-tests.txt
- name: Install dependencies
shell: bash
run: pip install -r requirements-tests.txt
60 changes: 40 additions & 20 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,51 @@ on:
- ".vscode/**"
- ".pre-commit-config.yaml"
- "*.md"
workflow_dispatch:

permissions:
contents: read

env:
PIP_DISABLE_PIP_VERSION_CHECK: 1
FORCE_COLOR: 1

jobs:
build:
strategy:
matrix:
os: [ubuntu-latest]
python-version: ["3.10"]
runs-on: ${{ matrix.os }}
ruff:
name: Run ruff check and ruff format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: pip install -r requirements-tests.txt
- uses: ./.github/setup
- name: Lint with ruff
run: python run.py ruff-check
- name: Format with ruff
run: python run.py ruff-format
- name: Run the test suite
run: python run.py pytest
- name: Run mypy on the tests and on the stubs
run: python run.py mypy
- name: Run pyright on the tests and on the stubs
run: python run.py pyright
- name: Run stubtest on the the stubs
run: python run.py stubtest
pytest:
name: Run the test suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/setup
- run: python run.py pytest
mypy:
name: Run mypy on the tests and on the stubs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/setup
- run: python run.py mypy
stubtest:
name: Run stubtest on the the stubs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/setup
- run: python run.py stubtest
pyright:
name: Run pyright on the tests and on the stubs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/setup
- run: python run.py pyright
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
__pycache__/
/build/
/dist/
/.*_cache/
/.?v?env/
.*_cache/
/.venv/
/.env/
/venv/
/env/

# files
.coverage
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
- id: mixed-line-ending
- id: check-case-conflict
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.14 # must match requirements-tests.txt
rev: v0.2.1 # must match requirements-tests.txt
hooks:
- id: ruff
- id: ruff-format
10 changes: 7 additions & 3 deletions requirements-tests.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Tools
ruff==0.1.14 # must match .pre-commit-config.yaml
pytest>=7.0
ruff==0.2.1 # must match .pre-commit-config.yaml
pytest>=8.0
mypy==1.8.0
pyright==1.1.347
pyright==1.1.350

# Runtime
shapely>=2.0,<2.1
geopandas>=0.14.3,<1.0

# Transient dependencies
matplotlib>=3.8.0
pyproj>=3.6.1
folium>=0.15.1
rtree>=1.1.0
pandas-stubs>=2.1.4.231227
6 changes: 3 additions & 3 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import sys

default_args = {
"mypy": ["tests", "stubs/shapely-stubs"],
"pyright": ["tests", "stubs/shapely-stubs"],
"stubtest": ["--allowlist=stubtest_allowlist.txt", "shapely"],
"mypy": ["tests", "stubs/shapely-stubs", "stubs/geopandas-stubs"],
"pyright": ["tests", "stubs/shapely-stubs", "stubs/geopandas-stubs"],
"stubtest": ["--allowlist=stubtest_allowlist.txt", "shapely", "geopandas"],
"ruff-check": ["tests", "stubs"],
"ruff-format": ["tests", "stubs"],
"pytest": [],
Expand Down
2 changes: 2 additions & 0 deletions stubs/geopandas-stubs/.ruff.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
extend = "../../pyproject.toml"

[lint]
isort.known-first-party = ["geopandas"]
43 changes: 27 additions & 16 deletions stubs/geopandas-stubs/array.pyi
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import builtins
from _typeshed import Incomplete, Unused
from collections.abc import Sequence
from typing import Any, Literal, NoReturn, overload
from typing import Any, ClassVar, Literal, NoReturn, SupportsIndex, overload
from typing_extensions import Self, TypeAlias, deprecated

import numpy as np
import pandas as pd
from numpy.typing import ArrayLike, DTypeLike, NDArray
from pandas.api.extensions import ExtensionArray, ExtensionDtype
from pyproj import CRS, Transformer
from shapely import Point
from shapely import Geometry, Point
from shapely.geometry.base import BaseGeometry

from geopandas.base import _ConvertibleToCRS
from geopandas.sindex import PyGEOSSTRTreeIndex

_ArrayOrGeom: TypeAlias = GeometryArray | ArrayLike | BaseGeometry
_ArrayOrGeom: TypeAlias = GeometryArray | ArrayLike | Geometry
_Origin: TypeAlias = Literal["center", "centroid"] | Point | tuple[float, float] | tuple[float, float, float]

TransformerFromCRS = Transformer.from_crs

class GeometryDtype(ExtensionDtype):
type = BaseGeometry
name: str
type: ClassVar[type[BaseGeometry]]
name: ClassVar[str]
na_value: float
@classmethod
def construct_from_string(cls, string: str) -> Self: ...
Expand All @@ -47,19 +48,26 @@ class GeometryArray(ExtensionArray):
@deprecated("Attribute `.data` is deprecated. Use method `to_numpy()` instead.")
def data(self) -> NDArray[np.object_]: ...
@property
def sindex(self): ...
def sindex(self) -> PyGEOSSTRTreeIndex: ...
@property
def has_sindex(self) -> bool: ...
@property
def crs(self) -> CRS | None: ...
@crs.setter
def crs(self, value: _ConvertibleToCRS) -> None: ...
def crs(self, value: _ConvertibleToCRS | None) -> None: ...
def check_geographic_crs(self, stacklevel: int) -> None: ...
@property
def dtype(self) -> GeometryDtype: ...
def __len__(self) -> int: ...
def __getitem__(self, idx): ...
def __setitem__(self, key, value) -> None: ...
@overload
def __getitem__(self, idx: int | np.integer[Any]) -> BaseGeometry: ... # Always 1-D, doesn't accept tuple
@overload
def __getitem__(self, idx: slice | Sequence[int] | NDArray[np.bool_] | NDArray[np.integer[Any]]) -> GeometryArray: ...
@overload
def __getitem__(
self, idx: int | np.integer[Any] | slice | Sequence[int] | NDArray[np.bool_] | NDArray[np.integer[Any]]
) -> BaseGeometry | GeometryArray: ...
def __setitem__(self, key, value: _ArrayOrGeom | pd.DataFrame | pd.Series[Any]) -> None: ...
@property
def is_valid(self) -> NDArray[np.bool_]: ...
@property
Expand All @@ -82,7 +90,7 @@ class GeometryArray(ExtensionArray):
def boundary(self) -> GeometryArray: ...
@property
def centroid(self) -> GeometryArray: ...
def concave_hull(self, ratio: float, allow_holes: bool) -> GeometryArray: ...
def concave_hull(self, ratio: float, allow_holes: bool) -> NDArray[np.object_]: ...
@property
def convex_hull(self) -> GeometryArray: ...
def delaunay_triangles(self, tolerance: float, only_edges: bool) -> GeometryArray: ...
Expand Down Expand Up @@ -134,7 +142,7 @@ class GeometryArray(ExtensionArray):
def buffer(self, distance: float | ArrayLike, resolution: int = 16, **kwargs) -> GeometryArray: ...
def interpolate(self, distance, normalized: bool = False) -> GeometryArray: ...
def simplify(self, tolerance: float | ArrayLike, preserve_topology: bool = True) -> GeometryArray: ...
def project(self, other: _ArrayOrGeom, normalized: bool = False) -> GeometryArray: ...
def project(self, other: _ArrayOrGeom, normalized: bool = False) -> NDArray[np.float64]: ...
def relate(self, other: _ArrayOrGeom) -> NDArray[np.str_]: ...
def unary_union(self) -> BaseGeometry: ...
def affine_transform(self, matrix: Incomplete) -> GeometryArray: ...
Expand All @@ -157,16 +165,19 @@ class GeometryArray(ExtensionArray):
@property
def size(self) -> int: ...
@property
def shape(self) -> tuple[int]: ...
def shape(self) -> tuple[int]: ... # Always 1-D, this is not a mistake
@property
def ndim(self) -> int: ...
def ndim(self) -> Literal[1]: ...
def copy(self, *args: Unused, **kwargs: Unused) -> GeometryArray: ...
def take(
self, indices: Sequence[int] | NDArray[np.int_], allow_fill: bool = False, fill_value: BaseGeometry | None = None
self,
indices: Sequence[SupportsIndex] | NDArray[np.integer[Any]],
allow_fill: bool = False,
fill_value: Geometry | None = None,
) -> GeometryArray: ...
def fillna(
self,
value: BaseGeometry | GeometryArray | None = None,
value: Geometry | GeometryArray | None = None,
method: Literal["backfill", "bfill", "pad", "ffill"] | None = None,
limit: int | None = None,
copy: bool = True,
Expand All @@ -177,7 +188,7 @@ class GeometryArray(ExtensionArray):
def unique(self) -> GeometryArray: ...
@property
def nbytes(self) -> int: ...
def shift(self, periods: int = 1, fill_value: BaseGeometry | None = None) -> GeometryArray: ...
def shift(self, periods: int = 1, fill_value: Geometry | None = None) -> GeometryArray: ... # type: ignore[override]
def argmin(self, skipna: bool = True) -> NoReturn: ...
def argmax(self, skipna: bool = True) -> NoReturn: ...
def __array__(self, dtype: Unused | None = None) -> NDArray[np.object_]: ...
Expand Down
45 changes: 23 additions & 22 deletions stubs/geopandas-stubs/base.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import pandas as pd
from numpy.random import BitGenerator, Generator as RandomGenerator, SeedSequence
from numpy.typing import ArrayLike, NDArray
from pyproj import CRS
from shapely import Geometry
from shapely.geometry.base import BaseGeometry

from geopandas.array import _Origin
Expand Down Expand Up @@ -80,34 +81,34 @@ class GeoPandasBase:
def cascaded_union(self) -> BaseGeometry: ...
@property
def unary_union(self) -> BaseGeometry: ...
def contains(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def geom_equals(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def contains(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def geom_equals(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
@deprecated("Method `geom_almost_equals` is deprecated. Use `geom_equals_exact` instead.")
def geom_almost_equals(self, other: GeoSeries | BaseGeometry, decimal: int = 6, align: bool = True) -> pd.Series[bool]: ...
def geom_almost_equals(self, other: GeoSeries | Geometry, decimal: int = 6, align: bool = True) -> pd.Series[bool]: ...
def geom_equals_exact(
self, other: GeoSeries | BaseGeometry, tolerance: float | ArrayLike, align: bool = True
self, other: GeoSeries | Geometry, tolerance: float | ArrayLike, align: bool = True
) -> pd.Series[bool]: ...
def crosses(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def disjoint(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def intersects(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def overlaps(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def touches(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def within(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def covers(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def covered_by(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[bool]: ...
def distance(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[float]: ...
def crosses(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def disjoint(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def intersects(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def overlaps(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def touches(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def within(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def covers(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def covered_by(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[bool]: ...
def distance(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[float]: ...
def hausdorff_distance(
self, other: GeoSeries | BaseGeometry, align: bool = True, densify: float | ArrayLike | None = None
self, other: GeoSeries | Geometry, align: bool = True, densify: float | ArrayLike | None = None
) -> pd.Series[float]: ...
def frechet_distance(
self, other: GeoSeries | BaseGeometry, align: bool = True, densify: float | ArrayLike | None = None
self, other: GeoSeries | Geometry, align: bool = True, densify: float | ArrayLike | None = None
) -> pd.Series[float]: ...
def difference(self, other: GeoSeries | BaseGeometry, align: bool = True) -> GeoSeries: ...
def symmetric_difference(self, other: GeoSeries | BaseGeometry, align: bool = True) -> GeoSeries: ...
def union(self, other: GeoSeries | BaseGeometry, align: bool = True) -> GeoSeries: ...
def intersection(self, other: GeoSeries | BaseGeometry, align: bool = True) -> GeoSeries: ...
def difference(self, other: GeoSeries | Geometry, align: bool = True) -> GeoSeries: ...
def symmetric_difference(self, other: GeoSeries | Geometry, align: bool = True) -> GeoSeries: ...
def union(self, other: GeoSeries | Geometry, align: bool = True) -> GeoSeries: ...
def intersection(self, other: GeoSeries | Geometry, align: bool = True) -> GeoSeries: ...
def clip_by_rect(self, xmin: float, ymin: float, xmax: float, ymax: float) -> GeoSeries: ...
def shortest_line(self, other: GeoSeries | BaseGeometry, align: bool = True) -> GeoSeries: ...
def shortest_line(self, other: GeoSeries | Geometry, align: bool = True) -> GeoSeries: ...
@property
def bounds(self) -> pd.DataFrame: ...
@property
Expand All @@ -118,8 +119,8 @@ class GeoPandasBase:
def has_sindex(self) -> bool: ...
def buffer(self, distance: float | ArrayLike, resolution: int = 16, **kwargs) -> GeoSeries: ...
def simplify(self, tolerance: float | ArrayLike, preserve_topology: bool = True) -> GeoSeries: ...
def relate(self, other: GeoSeries | BaseGeometry, align: bool = True) -> pd.Series[str]: ...
def project(self, other: GeoSeries | BaseGeometry, normalized: bool = False, align: bool = True) -> pd.Series[float]: ...
def relate(self, other: GeoSeries | Geometry, align: bool = True) -> pd.Series[str]: ...
def project(self, other: GeoSeries | Geometry, normalized: bool = False, align: bool = True) -> pd.Series[float]: ...
def interpolate(self, distance: float | ArrayLike, normalized: bool = False) -> GeoSeries: ...
def affine_transform(self, matrix: Incomplete) -> GeoSeries: ...
def translate(self, xoff: float = 0.0, yoff: float = 0.0, zoff: float = 0.0) -> GeoSeries: ...
Expand Down
6 changes: 3 additions & 3 deletions stubs/geopandas-stubs/explore.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from collections.abc import Callable, Hashable, MutableMapping, Sequence
from typing import Any

import branca
import folium
import branca # type: ignore[import-untyped]
import folium # type: ignore[import-untyped]
import pandas as pd
import xyzservices
import xyzservices # type: ignore[import-untyped]
from matplotlib.colors import Colormap
from numpy.typing import ArrayLike, NDArray

Expand Down
Loading

0 comments on commit f552cf0

Please sign in to comment.