81 lines
3.3 KiB
Python
81 lines
3.3 KiB
Python
"""tests/test_analysis.py —— 空间分析单元测试。"""
|
||
|
||
import pytest
|
||
import geopandas as gpd
|
||
from shapely.geometry import Point, Polygon
|
||
|
||
from geo_tools.analysis.spatial_ops import overlay, select_by_location
|
||
from geo_tools.analysis.stats import area_weighted_mean, count_by_polygon, summarize_attributes
|
||
|
||
|
||
class TestOverlay:
|
||
def test_intersection(self, sample_multi_polygon_gdf):
|
||
poly_a = sample_multi_polygon_gdf.iloc[[0]].copy()
|
||
poly_b = sample_multi_polygon_gdf.iloc[[1]].copy()
|
||
result = overlay(poly_a, poly_b, how="intersection")
|
||
assert len(result) >= 1
|
||
assert result.geometry.is_valid.all()
|
||
|
||
def test_union(self, sample_multi_polygon_gdf):
|
||
poly_a = sample_multi_polygon_gdf.iloc[[0]].copy()
|
||
poly_b = sample_multi_polygon_gdf.iloc[[1]].copy()
|
||
result = overlay(poly_a, poly_b, how="union", keep_geom_type=False)
|
||
assert result.geometry.is_valid.all()
|
||
|
||
|
||
class TestSelectByLocation:
|
||
def test_select_points_in_polygon(self, sample_points_gdf, sample_polygon_gdf):
|
||
# polygon 覆盖华北区,应选中 北京 点
|
||
result = select_by_location(sample_points_gdf, sample_polygon_gdf, predicate="intersects")
|
||
assert len(result) >= 1
|
||
|
||
def test_select_within(self, sample_points_gdf, sample_polygon_gdf):
|
||
result = select_by_location(sample_points_gdf, sample_polygon_gdf, predicate="within")
|
||
assert len(result) >= 0 # 可能有点在边界上
|
||
|
||
|
||
class TestAreaWeightedMean:
|
||
def test_global_weighted_mean(self, sample_multi_polygon_gdf):
|
||
result = area_weighted_mean(sample_multi_polygon_gdf, value_col="value")
|
||
assert "area_weighted_mean" in result.index
|
||
assert result["area_weighted_mean"] > 0
|
||
|
||
def test_grouped_weighted_mean(self, sample_multi_polygon_gdf):
|
||
gdf = sample_multi_polygon_gdf.copy()
|
||
gdf["group"] = ["A", "B"]
|
||
result = area_weighted_mean(gdf, value_col="value", group_col="group")
|
||
assert "area_weighted_mean" in result.columns
|
||
assert len(result) == 2
|
||
|
||
|
||
class TestSummarizeAttributes:
|
||
def test_basic_summary(self, sample_points_gdf):
|
||
result = summarize_attributes(sample_points_gdf, columns=["value"])
|
||
assert "column" in result.columns
|
||
assert "mean" in result.columns
|
||
|
||
def test_grouped_summary(self, sample_points_gdf):
|
||
gdf = sample_points_gdf.copy()
|
||
gdf["group"] = ["北方", "东部", "南方"]
|
||
result = summarize_attributes(gdf, columns=["value"], group_col="group")
|
||
# 每组一行
|
||
assert len(result) == 3
|
||
|
||
|
||
class TestCountByPolygon:
|
||
def test_count_points_in_polygons(self, sample_points_gdf, sample_polygon_gdf):
|
||
result = count_by_polygon(sample_points_gdf, sample_polygon_gdf)
|
||
assert "point_count" in result.columns
|
||
assert result["point_count"].dtype.kind == "i" # 整数
|
||
|
||
def test_polygon_with_no_points(self):
|
||
# 南海中的 polygon,不含任何点
|
||
poly = Polygon([(115, 10), (120, 10), (120, 15), (115, 15)])
|
||
polygons = gpd.GeoDataFrame({"id": [1]}, geometry=[poly], crs="EPSG:4326")
|
||
points = gpd.GeoDataFrame(
|
||
geometry=[Point(116.4, 39.9)], # 北京,不在 polygon 内
|
||
crs="EPSG:4326",
|
||
)
|
||
result = count_by_polygon(points, polygons)
|
||
assert result["point_count"].iloc[0] == 0
|