"""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