Files
geo_tools/TUTORIAL.md
missum db51d41aef refactor: 重构项目结构,将geo_tools重命名为app并更新相关引用
- 将主包名从geo_tools改为app
- 更新所有模块中的引用路径
- 迁移并更新测试用例
- 添加项目规则文档
- 保持原有功能不变,仅进行结构调整
2026-04-12 19:49:56 +08:00

12 KiB
Raw Permalink Blame History

Geo-Tools 使用教程

1. 安装与环境准备

1.1 前提条件

在安装 Geo-Tools 之前,您需要确保已经安装了以下依赖:

  • Python 3.8 或更高版本
  • pipPython 包管理工具)

1.2 安装方法

  1. 克隆项目仓库

    git clone <项目地址>
    cd geo_tools
    
  2. 安装依赖

    pip install -r requirements.txt
    
  3. 安装 Geo-Tools

    pip install -e .
    

1.3 验证安装

运行以下命令验证 Geo-Tools 是否安装成功:

from app.io.readers import read_vector
print("Geo-Tools 安装成功!")

2. 5分钟体验

2.1 读取 Shapefile

from app.io.readers import read_vector
from pathlib import Path

# 读取 Shapefile 文件
# 这里使用示例数据,您可以替换为自己的文件路径
data_path = Path("data/sample/sample_points.geojson")
gdf = read_vector(data_path)

# 查看数据基本信息
print(f"数据形状:{gdf.shape}")
print(f"数据列名:{list(gdf.columns)}")
print(f"坐标系:{gdf.crs}")

2.2 预览数据

# 预览前5行数据
print("\n数据预览:")
print(gdf.head())

# 或者使用 rows 参数直接只读取前5行
print("\n使用 rows 参数预览:")
gdf_preview = read_vector(data_path, rows=5)
print(gdf_preview)

2.3 制作缓冲区

from app.core.geometry import buffer_geometry

# 为每个点创建缓冲区
gdf["buffer"] = gdf.geometry.apply(lambda geom: buffer_geometry(geom, distance=0.1))

# 查看缓冲区结果
print("\n缓冲区创建完成!")
print(f"原始几何类型:{gdf.geometry.geom_type.unique()}")
print(f"缓冲区几何类型:{gdf['buffer'].geom_type.unique()}")

2.4 保存结果

from app.io.writers import write_vector

# 保存为 GeoJSON 文件
output_path = Path("output/buffered_points.geojson")
write_vector(gdf, output_path)
print(f"\n结果已保存至:{output_path}")

3. 进阶功能详解

3.1 大文件处理

3.1.1 使用 rows 参数预览数据

当处理大文件时,您可以使用 rows 参数只读取前几行数据,快速了解数据结构,而不需要加载整个文件:

from app.io.readers import read_vector
from pathlib import Path

# 只读取前10行数据进行预览
large_file_path = Path("path/to/large_file.shp")
gdf_preview = read_vector(large_file_path, rows=10)

print(f"预览数据包含 {len(gdf_preview)} 条记录")
print(gdf_preview.head())
print(gdf_preview.columns)

3.1.2 使用 chunk_size 分块读取

对于非常大的文件,您可以使用 chunk_size 参数进行分块读取,逐块处理数据,避免内存溢出:

from app.io.readers import read_vector
from pathlib import Path

# 分块读取每块10000条数据
large_file_path = Path("path/to/large_file.shp")

# 使用 for 循环处理每个数据块
for i, chunk in enumerate(read_vector(large_file_path, chunk_size=10000)):
    print(f"处理第 {i+1} 块数据,包含 {len(chunk)} 条记录")
    
    # 在这里进行您的处理操作
    # 例如:计算缓冲区
    chunk["buffer"] = chunk.geometry.apply(lambda geom: buffer_geometry(geom, distance=0.1))
    
    # 保存当前块的结果
    output_path = Path(f"output/chunk_{i+1}.geojson")
    write_vector(chunk, output_path)
    print(f"第 {i+1} 块结果已保存")

3.2 坐标系转换

3.2.1 什么时候需要转换坐标系?

  • 当您需要进行距离、面积计算时,地理坐标系(如 EPSG:4326的单位是度不适合直接计算
  • 当您需要将数据与其他不同坐标系的数据集叠加时
  • 当您需要使用特定坐标系的工具或服务时

3.2.2 如何转换坐标系

from app.io.readers import read_vector
from app.core.projection import reproject
from pathlib import Path

# 读取数据
data_path = Path("data/sample/sample_points.geojson")
gdf = read_vector(data_path)
print(f"原始坐标系:{gdf.crs}")

# 转换到 Web Mercator 坐标系EPSG:3857
gdf_3857 = reproject(gdf, "EPSG:3857")
print(f"转换后坐标系:{gdf_3857.crs}")

# 或者在读取时直接指定目标坐标系
gdf_direct = read_vector(data_path, crs="EPSG:3857")
print(f"直接指定坐标系:{gdf_direct.crs}")

3.3 空间分析

3.3.1 缓冲区分析

from app.io.readers import read_vector
from app.core.geometry import buffer_geometry
from pathlib import Path

# 读取数据
data_path = Path("data/sample/sample_points.geojson")
gdf = read_vector(data_path)

# 创建不同距离的缓冲区
gdf["buffer_05"] = gdf.geometry.apply(lambda geom: buffer_geometry(geom, distance=0.05))
gdf["buffer_10"] = gdf.geometry.apply(lambda geom: buffer_geometry(geom, distance=0.1))

# 保存结果
output_path = Path("output/buffers.geojson")
write_vector(gdf, output_path)
print(f"缓冲区分析结果已保存至:{output_path}")

3.3.2 空间叠加分析

from app.io.readers import read_vector
from app.analysis.spatial_ops import overlay
from pathlib import Path

# 读取两个数据集
points_path = Path("data/sample/sample_points.geojson")
regions_path = Path("data/sample/sample_regions.geojson")

points = read_vector(points_path)
regions = read_vector(regions_path)

# 执行空间叠加(交集)
overlay_result = overlay(points, regions, how="intersection")
print(f"叠加结果包含 {len(overlay_result)} 条记录")

# 保存结果
output_path = Path("output/overlay_result.geojson")
write_vector(overlay_result, output_path)
print(f"叠加分析结果已保存至:{output_path}")

3.3.3 最近邻查找

from app.core.geometry import distance_between
from app.io.readers import read_vector
from pathlib import Path

# 读取点数据集
points_path = Path("data/sample/sample_points.geojson")
gdf = read_vector(points_path)

# 定义目标点
target_point = gdf.geometry.iloc[0]
print(f"目标点:{target_point}")

# 计算每个点到目标点的距离
gdf["distance"] = gdf.geometry.apply(lambda geom: distance_between(geom, target_point))

# 找到最近的点
nearest_point = gdf.loc[gdf["distance"].idxmin()]
print(f"最近的点:{nearest_point.geometry}")
print(f"距离:{nearest_point['distance']}")

4. 常见问题排查

4.1 报错“文件打不开”怎么办?

可能原因:

  • 文件路径不存在
  • 文件路径包含中文或特殊字符
  • 文件格式不支持
  • 文件损坏

解决方案:

  1. 检查文件路径是否正确,使用绝对路径
  2. 确保文件路径不包含中文或特殊字符
  3. 确认文件格式是否被支持GeoJSON、Shapefile、GPKG、GDB等
  4. 尝试使用其他软件打开文件,确认文件是否损坏

示例代码:

from app.io.readers import read_vector
from pathlib import Path

# 使用绝对路径
try:
    file_path = Path("c:/data/my_shapefile.shp")
    gdf = read_vector(file_path)
    print("文件读取成功!")
except Exception as e:
    print(f"文件读取失败:{e}")
    # 检查路径是否存在
    if not file_path.exists():
        print("错误:文件路径不存在")
    # 检查文件扩展名
    if file_path.suffix not in [".shp", ".geojson", ".gpkg"]:
        print("错误:文件格式可能不支持")

4.2 报错“几何无效”怎么办?

可能原因:

  • 几何数据损坏
  • 几何自相交
  • 几何为空

解决方案:

  1. 使用 fix_geometry 函数尝试修复无效几何
  2. 使用 is_valid_geometry 函数检查几何有效性
  3. 过滤掉无效几何

示例代码:

from app.core.geometry import fix_geometry, is_valid_geometry
from app.io.readers import read_vector
from pathlib import Path

# 读取数据
data_path = Path("data/sample/sample_points.geojson")
gdf = read_vector(data_path)

# 检查并修复几何
print("检查几何有效性...")
valid_count = 0
fixed_count = 0

for i, geom in enumerate(gdf.geometry):
    if is_valid_geometry(geom):
        valid_count += 1
    else:
        # 尝试修复
        fixed_geom = fix_geometry(geom)
        if fixed_geom is not None:
            gdf.geometry.iloc[i] = fixed_geom
            fixed_count += 1

print(f"有效几何:{valid_count}")
print(f"修复几何:{fixed_count}")
print(f"无效几何:{len(gdf) - valid_count - fixed_count}")

# 过滤掉仍然无效的几何
gdf_valid = gdf[gdf.geometry.apply(is_valid_geometry)]
print(f"过滤后剩余几何:{len(gdf_valid)}")

4.3 内存爆了怎么办?

可能原因:

  • 文件太大,一次性加载到内存
  • 处理过程中创建了过多临时对象

解决方案:

  1. 使用 rows 参数预览数据
  2. 使用 chunk_size 参数分块读取
  3. 处理完数据后及时释放内存
  4. 考虑使用更高效的数据结构和算法

示例代码:

from app.io.readers import read_vector
from app.io.writers import write_vector
from pathlib import Path

# 分块处理大文件
large_file = Path("path/to/large_file.shp")
output_file = Path("output/processed_file.geojson")

# 分块读取并处理
chunks = []
for i, chunk in enumerate(read_vector(large_file, chunk_size=10000)):
    print(f"处理第 {i+1} 块...")
    
    # 在这里进行处理
    # 例如:添加面积列
    chunk["area"] = chunk.geometry.area
    
    chunks.append(chunk)

# 合并所有块
import geopandas as gpd
result = gpd.GeoDataFrame(pd.concat(chunks, ignore_index=True))

# 保存结果
write_vector(result, output_file)
print(f"处理完成,结果已保存至:{output_file}")

# 释放内存
import gc
gc.collect()

5. 完整案例

5.1 土地利用数据分析

场景: 读取土地利用数据,筛选出耕地,计算耕地总面积,然后导出为 GeoJSON 文件。

步骤:

  1. 读取数据

    from app.io.readers import read_vector
    from pathlib import Path
    
    # 读取土地利用数据
    landuse_path = Path("data/landuse.shp")
    # 先预览数据结构
    landuse_preview = read_vector(landuse_path, rows=10)
    print("数据列名:", list(landuse_preview.columns))
    print("土地利用类型:", landuse_preview["type"].unique())
    
    # 读取完整数据
    landuse = read_vector(landuse_path)
    print(f"总数据量:{len(landuse)}")
    
  2. 筛选耕地

    # 假设耕地的类型代码是 "1" 或 "耕地"
    # 根据实际数据结构调整条件
    farmland = landuse[landuse["type"] == "耕地"]
    print(f"耕地数量:{len(farmland)}")
    
  3. 计算耕地总面积

    from app.core.projection import reproject
    
    # 检查坐标系
    print(f"原始坐标系:{farmland.crs}")
    
    # 如果是地理坐标系,转换到投影坐标系以获得准确的面积
    if farmland.crs and farmland.crs.to_epsg() == 4326:
        # 转换到 UTM 坐标系(根据数据所在区域选择合适的 EPSG 代码)
        farmland_proj = reproject(farmland, "EPSG:32649")  # 示例UTM 49N
        print(f"转换后坐标系:{farmland_proj.crs}")
    else:
        farmland_proj = farmland
    
    # 计算面积(单位:平方米)
    farmland_proj["area"] = farmland_proj.geometry.area
    total_area = farmland_proj["area"].sum()
    print(f"耕地总面积:{total_area:.2f} 平方米")
    print(f"耕地总面积:{total_area/10000:.2f} 公顷")
    
  4. 导出结果

    from app.io.writers import write_vector
    
    # 导出为 GeoJSON
    output_path = Path("output/farmland.geojson")
    write_vector(farmland_proj, output_path)
    print(f"耕地数据已导出至:{output_path}")
    
    # 导出面积统计
    import pandas as pd
    stats = pd.DataFrame({
        "总耕地数量": [len(farmland)],
        "总面积(平方米)": [total_area],
        "总面积(公顷)": [total_area/10000]
    })
    stats_path = Path("output/farmland_stats.csv")
    stats.to_csv(stats_path, index=False, encoding="utf-8-sig")
    print(f"统计数据已导出至:{stats_path}")
    

6. 总结

Geo-Tools 是一个功能强大的地理数据处理库,提供了丰富的空间分析工具和便捷的文件 I/O 功能。通过本教程,您应该已经掌握了:

  • 基本的文件读取和写入操作
  • 大文件的分块处理技巧
  • 坐标系转换的方法
  • 常见的空间分析操作
  • 常见问题的排查方法

如果您在使用过程中遇到任何问题,可以参考本教程的常见问题排查部分,或者查看项目的详细文档。

祝您使用愉快!