必须用geopandas读取shapefile或geojson才能处理地理坐标,因pandas不识别空间结构;常见错误是用read_csv读经纬度csv后误用.between()框选,忽略地球曲率和跨经度边界问题。

用 geopandas 读取 Shapefile 或 GeoJSON 才能真正处理地理坐标
纯 pandas 本身不识别地理空间结构,它把 geometry 列当普通字符串或对象处理,无法做范围筛选、相交判断。必须换成 geopandas——它是 pandas 的地理扩展,底层依赖 shapely 和 fiona。
常见错误是直接用 pandas.read_csv() 读带经纬度的 CSV,然后试图用 .between() 粗暴框选,结果漏掉跨经度边界(如东经179°到西经179°)或忽略地球曲率导致的偏差。
- Shapefile 要用
gpd.read_file("data.shp"),不是pd.read_csv() - CSV 含
lat/lon列?先转成Point:import geopandas as gpd from shapely.geometry import Point df = pd.read_csv("coords.csv") gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.lon, df.lat), crs="EPSG:4326") -
crs不写或写错(如误用"WGS84")会导致后续空间运算失效
用 cx 切片快速筛选矩形范围内的点
gdf.cx[xmin:xmax, ymin:ymax] 是最轻量、最快捷的矩形筛选方式,底层调用 R-tree 索引,比手动写布尔索引快一个数量级。
注意坐标顺序:cx 是 [经度范围, 纬度范围],不是 [纬度, 经度],和地图习惯相反,容易搞反。
- 北京五环内粗筛(经度116.0–116.6,纬度39.6–40.0):
gdf.cx[116.0:116.6, 39.6:40.0] - 跨国际日期变更线?
cx不支持自动绕球,得拆成两段:例如筛选东经170°到西经170°,需分别取cx[170:180, ...]和cx[-180:-170, ...]再拼接 - 若
gdf.crs是投影坐标系(如 EPSG:3857),cx的数值单位是米,不是度——务必确认 CRS 单位
用 within() 或 intersects() 做多边形精确筛选
城市行政边界、湖泊、道路缓冲区这类不规则区域,必须用几何关系判断,不能靠矩形框。
典型坑:原始数据和多边形使用不同 CRS,直接调用 within() 会返回全 False 或报错 TopologyException。
- 确保二者 CRS 一致:
poly_gdf = poly_gdf.to_crs(gdf.crs) - 筛选落在某个多边形内的点:
gdf[gdf.within(poly_gdf.unary_union)](unary_union合并多个面避免循环) - 想查“靠近某条河 500 米内”?先用
river_line.buffer(500)生成面,再用within();注意 buffer 单位取决于 CRS——地理坐标系(EPSG:4326)下 500 是度,必须先转投影坐标系
避免 geometry 列为空或无效导致筛选失败
gdf.is_valid 返回 False 或 gdf.geometry.is_empty.any() 为 True 时,cx 和空间谓词可能静默跳过这些行,或抛出 GEOSException。
- 加载后立刻检查:
gdf = gdf[~gdf.geometry.is_empty & gdf.is_valid] - 从 CSV 构造
Point时,若lat/lon有NaN,对应 geometry 会是None,需提前清洗:df = df.dropna(subset=["lat", "lon"]) - 某些 Shapefile 的 geometry 类型混杂(比如含
MultiPoint和Point),within()对MultiPoint行为不稳定,建议统一转为Point:gdf = gdf.explode(index_parts=False).drop_duplicates(subset=["geometry"])
cx 加基础 CRS 校验就够了;一旦涉及行政边界、缓冲区、拓扑关系,就必须上 geopandas 的空间谓词,且 CRS 对齐和几何有效性是绕不开的两道坎。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











