refactor: 重构项目结构,将geo_tools重命名为app并更新相关引用
- 将主包名从geo_tools改为app - 更新所有模块中的引用路径 - 迁移并更新测试用例 - 添加项目规则文档 - 保持原有功能不变,仅进行结构调整
This commit is contained in:
1
app/io/__init__.py
Normal file
1
app/io/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""geo_tools.io 包 —— 数据读写层。"""
|
||||
374
app/io/readers.py
Normal file
374
app/io/readers.py
Normal file
@@ -0,0 +1,374 @@
|
||||
"""
|
||||
geo_tools.io.readers
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
统一的矢量数据读取接口,支持:
|
||||
- Shapefile (.shp)
|
||||
- GeoJSON (.geojson / .json)
|
||||
- GeoPackage (.gpkg)
|
||||
- File Geodatabase (.gdb) ← 通过 fiona OpenFileGDB / ESRI FileGDB 驱动
|
||||
- KML / KMZ
|
||||
- FlatGeobuf (.fgb)
|
||||
- CSV(含 WKT 或 经纬度列)
|
||||
|
||||
所有函数均返回 ``geopandas.GeoDataFrame``。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Generator
|
||||
|
||||
import fiona
|
||||
import geopandas as gpd
|
||||
|
||||
from app.utils.logger import get_logger
|
||||
from app.utils.validators import validate_vector_path
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
# ── 主入口 ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
def read_vector(
|
||||
path: str | Path,
|
||||
layer: str | int | None = None,
|
||||
crs: str | int | None = None,
|
||||
encoding: str = "utf-8",
|
||||
chunk_size: int | None = None,
|
||||
rows: int | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""统一的矢量数据读取入口,自动识别文件格式。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path:
|
||||
数据路径。支持文件或目录(FileGDB ``*.gdb``)。
|
||||
layer:
|
||||
图层名或索引(多图层格式如 GPKG、GDB 必填;单图层可省略)。
|
||||
crs:
|
||||
读取后强制重投影到目标 CRS(不传则保留原始 CRS)。
|
||||
encoding:
|
||||
属性表编码,Shapefile 中文路径常需指定 ``"gbk"``。
|
||||
chunk_size:
|
||||
分块大小,默认 None(一次性读取全部数据)。
|
||||
【警告】:若不设置 chunk_size,大文件可能会占用大量内存。
|
||||
rows:
|
||||
限制读取的行数,默认 None(读取全部数据)。
|
||||
用于快速预览数据,避免读取大文件的全部内容。
|
||||
**kwargs:
|
||||
透传给 ``geopandas.read_file`` 的额外参数。
|
||||
|
||||
Returns
|
||||
-------
|
||||
gpd.GeoDataFrame 或生成器
|
||||
如果 chunk_size 为 None,返回完整的 GeoDataFrame;
|
||||
如果设置了 chunk_size,返回一个生成器,每次 yield 一个 GeoDataFrame 块。
|
||||
|
||||
示例
|
||||
-----
|
||||
# 全量读取(老方法)
|
||||
gdf = read_vector("data.shp")
|
||||
|
||||
# 分块读取(新方法)
|
||||
for chunk in read_vector("large_data.shp", chunk_size=10000):
|
||||
# 处理每个数据块
|
||||
print(f"处理了 {len(chunk)} 条数据")
|
||||
# 在这里做你的操作,比如计算、过滤等
|
||||
|
||||
# 只读取前 5 行数据(预览模式)
|
||||
gdf_preview = read_vector("large_data.shp", rows=5)
|
||||
print(gdf_preview.head())
|
||||
"""
|
||||
path, layer = _split_gdb_layer(path)
|
||||
path = validate_vector_path(path)
|
||||
suffix = path.suffix.lower()
|
||||
|
||||
logger.info("读取矢量数据:%s(格式:%s,图层:%s)", path, suffix or "目录", layer)
|
||||
|
||||
if suffix == ".csv":
|
||||
# CSV 文件暂时不支持分块读取
|
||||
if chunk_size is not None:
|
||||
logger.warning("CSV 文件暂不支持分块读取,将一次性读取全部数据")
|
||||
return _read_csv_vector(path, crs=crs, **kwargs)
|
||||
|
||||
# fiona / geopandas 通用读取
|
||||
read_kwargs: dict[str, Any] = {"encoding": encoding, **kwargs}
|
||||
if layer is not None:
|
||||
read_kwargs["layer"] = layer
|
||||
|
||||
# 分块读取模式
|
||||
if chunk_size is not None:
|
||||
def _chunk_generator():
|
||||
logger.info("启用分块读取模式,每块 %d 条数据", chunk_size)
|
||||
try:
|
||||
# 使用 fiona 打开文件
|
||||
with fiona.open(str(path), **read_kwargs) as src:
|
||||
# 获取坐标系信息
|
||||
crs_info = src.crs
|
||||
# 分块读取
|
||||
features = []
|
||||
for i, feature in enumerate(src):
|
||||
# 检查是否达到行数限制
|
||||
if rows is not None and i >= rows:
|
||||
break
|
||||
|
||||
features.append(feature)
|
||||
if (i + 1) % chunk_size == 0:
|
||||
# 创建 GeoDataFrame 并设置 CRS
|
||||
gdf = gpd.GeoDataFrame.from_features(features, crs=crs_info)
|
||||
# 重投影
|
||||
if crs is not None:
|
||||
gdf = gdf.to_crs(crs) # type: ignore
|
||||
logger.debug("读取并处理第 %d 块数据,共 %d 条", (i + 1) // chunk_size, len(gdf))
|
||||
yield gdf
|
||||
features = []
|
||||
# 处理最后一块
|
||||
if features:
|
||||
gdf = gpd.GeoDataFrame.from_features(features, crs=crs_info)
|
||||
if crs is not None:
|
||||
gdf = gdf.to_crs(crs) # type: ignore
|
||||
logger.debug("读取并处理最后一块数据,共 %d 条", len(gdf))
|
||||
yield gdf
|
||||
except Exception as exc:
|
||||
raise RuntimeError(f"无法分块读取矢量数据:{exc}") from None
|
||||
return _chunk_generator()
|
||||
else:
|
||||
# 一次性读取模式
|
||||
try:
|
||||
# 添加 rows 参数到读取参数中
|
||||
if rows is not None:
|
||||
read_kwargs["rows"] = rows
|
||||
logger.info("限制读取行数:%d", rows)
|
||||
|
||||
gdf = gpd.read_file(str(path), **read_kwargs)
|
||||
except Exception as exc:
|
||||
raise RuntimeError(f"无法读取矢量数据:{exc}") from None
|
||||
|
||||
if crs is not None:
|
||||
logger.debug("重投影到 %s", crs)
|
||||
gdf = gdf.to_crs(crs) # type: ignore
|
||||
|
||||
logger.info("读取完成:共 %d 条要素,CRS=%s", len(gdf), gdf.crs)
|
||||
return gdf # type: ignore
|
||||
|
||||
|
||||
# ── GDB 专用 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
def read_gdb(
|
||||
gdb_path: str | Path,
|
||||
layer: str | int | None = None,
|
||||
crs: str | int | None = None,
|
||||
encoding: str = "utf-8",
|
||||
**kwargs: Any,
|
||||
) -> gpd.GeoDataFrame:
|
||||
"""读取 Esri File Geodatabase(.gdb)中的图层。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
gdb_path:
|
||||
``.gdb`` 目录路径。
|
||||
layer:
|
||||
图层名称或索引。若不指定且 GDB 仅有一个图层,则自动选取第一层;
|
||||
多图层时必须指定。
|
||||
crs:
|
||||
读取后目标 CRS,``None`` 则保留原始坐标系。
|
||||
encoding:
|
||||
属性表字段编码。
|
||||
"""
|
||||
gdb_path = Path(gdb_path)
|
||||
if not gdb_path.exists():
|
||||
raise FileNotFoundError(f"GDB 路径不存在:{gdb_path}")
|
||||
if gdb_path.suffix.lower() != ".gdb":
|
||||
raise ValueError(f"期望 .gdb 目录,收到:{gdb_path.suffix!r}")
|
||||
|
||||
available_layers = list_gdb_layers(gdb_path)
|
||||
logger.debug("GDB 可用图层:%s", available_layers)
|
||||
|
||||
if layer is None:
|
||||
if not available_layers:
|
||||
raise ValueError(f"GDB 中没有可用图层:{gdb_path}")
|
||||
layer = available_layers[0]
|
||||
if len(available_layers) > 1:
|
||||
logger.warning(
|
||||
"GDB 包含多个图层 %s,默认读取第一层 %r。请显式传入 layer=... 以指定图层。",
|
||||
available_layers,
|
||||
layer,
|
||||
)
|
||||
|
||||
logger.info("读取 GDB 图层:%s >> %s", gdb_path.name, layer)
|
||||
gdf = gpd.read_file(str(gdb_path), layer=layer, encoding=encoding, **kwargs)
|
||||
|
||||
if crs is not None:
|
||||
gdf = gdf.to_crs(crs) # type: ignore
|
||||
|
||||
logger.info("GDB 读取完成:%d 条要素,CRS=%s", len(gdf), gdf.crs)
|
||||
return gdf # type: ignore
|
||||
|
||||
|
||||
def list_gdb_layers(gdb_path: str | Path) -> list[str]:
|
||||
"""列出 FileGDB 中所有图层名称。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
gdb_path:
|
||||
``.gdb`` 目录路径。
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[str]
|
||||
图层名称列表。
|
||||
"""
|
||||
gdb_path = Path(gdb_path)
|
||||
try:
|
||||
return fiona.listlayers(str(gdb_path))
|
||||
except Exception as exc:
|
||||
raise RuntimeError(
|
||||
f"无法列出 GDB 图层:{gdb_path}。\n"
|
||||
"请确认 fiona 已安装 OpenFileGDB 驱动(通常随 conda/wheels 自带)。\n"
|
||||
f"原始错误:{exc}"
|
||||
) from exc
|
||||
|
||||
def _split_gdb_layer(path: str | Path) -> tuple[Path, str | None]:
|
||||
"""从完整路径中分离 GDB 数据库路径和图层名。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path:
|
||||
完整路径,可以是字符串或 Path 对象。
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple[Path, str | None]
|
||||
(gdb_path, layer_name),其中 gdb_path 是 GDB 目录路径,layer_name 是图层名,若没有图层名则为 None。
|
||||
"""
|
||||
path_obj = Path(path)
|
||||
str_path = str(path_obj)
|
||||
|
||||
# 查找 .gdb 的位置
|
||||
gdb_pos = str_path.find('.gdb')
|
||||
|
||||
if gdb_pos == -1:
|
||||
# 如果没有 .gdb,整个路径作为 GDB 路径,没有图层
|
||||
return path_obj, None
|
||||
|
||||
# 提取 GDB 路径(包含 .gdb)
|
||||
gdb_path = str_path[:gdb_pos + 4]
|
||||
|
||||
# 提取图层名(.gdb 之后的部分)
|
||||
layer_part = str_path[gdb_pos + 4:]
|
||||
# 去除开头的路径分隔符
|
||||
layer_name = layer_part.lstrip(os.sep).lstrip('/').lstrip('\\')
|
||||
|
||||
# 如果没有图层名,返回 None
|
||||
if not layer_name:
|
||||
layer_name = None
|
||||
|
||||
return Path(gdb_path), layer_name
|
||||
|
||||
# ── GPKG 专用 ──────────────────────────────────────────────────────────────────
|
||||
|
||||
def read_gpkg(
|
||||
gpkg_path: str | Path,
|
||||
layer: str | int | None = None,
|
||||
crs: str | int | None = None,
|
||||
**kwargs: Any,
|
||||
) -> gpd.GeoDataFrame:
|
||||
"""读取 GeoPackage (.gpkg) 文件。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
gpkg_path:
|
||||
``.gpkg`` 文件路径。
|
||||
layer:
|
||||
图层名或索引;多图层时必须指定。
|
||||
"""
|
||||
gpkg_path = Path(gpkg_path)
|
||||
if not gpkg_path.exists():
|
||||
raise FileNotFoundError(f"GPKG 文件不存在:{gpkg_path}")
|
||||
|
||||
available = fiona.listlayers(str(gpkg_path))
|
||||
if layer is None:
|
||||
if not available:
|
||||
raise ValueError(f"GPKG 中没有可用图层:{gpkg_path}")
|
||||
layer = available[0]
|
||||
if len(available) > 1:
|
||||
logger.warning(
|
||||
"GPKG 包含多个图层 %s,默认读取第一层 %r。", available, layer
|
||||
)
|
||||
|
||||
gdf = gpd.read_file(str(gpkg_path), layer=layer, **kwargs)
|
||||
if crs is not None:
|
||||
gdf = gdf.to_crs(crs) # type: ignore
|
||||
return gdf # type: ignore
|
||||
|
||||
|
||||
def list_gpkg_layers(gpkg_path: str | Path) -> list[str]:
|
||||
"""列出 GeoPackage 中所有图层名称。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
gpkg_path:
|
||||
GeoPackage 文件路径,可以是字符串或 Path 对象。
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[str]
|
||||
图层名称列表。
|
||||
"""
|
||||
return fiona.listlayers(str(gpkg_path))
|
||||
|
||||
|
||||
# ── CSV 矢量读取 ────────────────────────────────────────────────────────────────
|
||||
|
||||
def _read_csv_vector(
|
||||
path: Path,
|
||||
lon_col: str = "longitude",
|
||||
lat_col: str = "latitude",
|
||||
wkt_col: str | None = None,
|
||||
crs: str | int | None = None,
|
||||
**kwargs: Any,
|
||||
) -> gpd.GeoDataFrame:
|
||||
"""从 CSV 读取空间数据,支持 WKT 列或经纬度列。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path:
|
||||
CSV 文件路径。
|
||||
lon_col:
|
||||
经度列名(WKT 模式时忽略)。
|
||||
lat_col:
|
||||
纬度列名(WKT 模式时忽略)。
|
||||
wkt_col:
|
||||
WKT 几何列名;若指定则优先使用。
|
||||
"""
|
||||
import pandas as pd
|
||||
from shapely import wkt as shapely_wkt
|
||||
|
||||
df = pd.read_csv(path, **kwargs)
|
||||
|
||||
if wkt_col and wkt_col in df.columns:
|
||||
geometry = df[wkt_col].apply(shapely_wkt.loads)
|
||||
elif lon_col in df.columns and lat_col in df.columns:
|
||||
from shapely.geometry import Point
|
||||
geometry = [Point(lon, lat) for lon, lat in zip(df[lon_col], df[lat_col])]
|
||||
else:
|
||||
raise ValueError(
|
||||
f"CSV 中未找到 WKT 列 {wkt_col!r} 或经纬度列 ({lon_col!r}, {lat_col!r})。"
|
||||
)
|
||||
|
||||
gdf = gpd.GeoDataFrame(df, geometry=geometry, crs=crs or "EPSG:4326")
|
||||
return gdf
|
||||
|
||||
|
||||
def read_csv_points(
|
||||
path: str | Path,
|
||||
lon_col: str = "longitude",
|
||||
lat_col: str = "latitude",
|
||||
crs: str | int = "EPSG:4326",
|
||||
**kwargs: Any,
|
||||
) -> gpd.GeoDataFrame:
|
||||
"""从含经纬度列的 CSV 文件创建点 GeoDataFrame(公开接口)。"""
|
||||
path = Path(path)
|
||||
return _read_csv_vector(path, lon_col=lon_col, lat_col=lat_col, crs=crs, **kwargs)
|
||||
207
app/io/writers.py
Normal file
207
app/io/writers.py
Normal file
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
geo_tools.io.writers
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
统一的矢量数据写出接口,支持:
|
||||
- Shapefile (.shp)
|
||||
- GeoJSON (.geojson / .json)
|
||||
- GeoPackage (.gpkg) ← 支持追加图层
|
||||
- File Geodatabase (.gdb) ← 通过 fiona OpenFileGDB 驱动
|
||||
- FlatGeobuf (.fgb)
|
||||
- CSV(含 WKT 列)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal
|
||||
|
||||
import geopandas as gpd
|
||||
|
||||
from app.utils.logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
# ── 主入口 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
def write_vector(
|
||||
gdf: gpd.GeoDataFrame,
|
||||
path: str | Path,
|
||||
layer: str | None = None,
|
||||
driver: str | None = None,
|
||||
encoding: str = "utf-8",
|
||||
mode: Literal["w", "a"] = "w",
|
||||
**kwargs: Any,
|
||||
) -> Path:
|
||||
"""统一的矢量数据写出入口,自动识别格式。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
gdf:
|
||||
待写出的 GeoDataFrame。
|
||||
path:
|
||||
目标路径(文件或 `.gdb` 目录)。
|
||||
layer:
|
||||
图层名(GPKG、GDB 多图层格式时使用)。
|
||||
driver:
|
||||
强制指定 fiona 驱动名(通常不需要,自动推断)。
|
||||
encoding:
|
||||
字段编码,Shapefile 导出中文时常需 ``"gbk"``。
|
||||
mode:
|
||||
``"w"`` 覆盖写出,``"a"`` 追加图层(GPKG / GDB 支持)。
|
||||
|
||||
Returns
|
||||
-------
|
||||
Path
|
||||
实际写出的路径。
|
||||
"""
|
||||
path = Path(path)
|
||||
suffix = path.suffix.lower()
|
||||
|
||||
if suffix == ".csv":
|
||||
return _write_csv_vector(gdf, path)
|
||||
|
||||
# 自动推断驱动
|
||||
if driver is None:
|
||||
driver = _infer_driver(path)
|
||||
|
||||
# 确保父目录存在(GDB 是目录,其父目录要存在)
|
||||
if suffix == ".gdb":
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
else:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
write_kwargs: dict[str, Any] = {
|
||||
"driver": driver,
|
||||
"encoding": encoding,
|
||||
"mode": mode,
|
||||
**kwargs,
|
||||
}
|
||||
if layer is not None:
|
||||
write_kwargs["layer"] = layer
|
||||
|
||||
logger.info(
|
||||
"写出矢量数据:%s(驱动:%s,图层:%s,模式:%s,要素数:%d)",
|
||||
path, driver, layer, mode, len(gdf),
|
||||
)
|
||||
gdf.to_file(str(path), **write_kwargs)
|
||||
logger.info("写出完成:%s", path)
|
||||
return path
|
||||
|
||||
|
||||
# ── GDB 专用 ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def write_gdb(
|
||||
gdf: gpd.GeoDataFrame,
|
||||
gdb_path: str | Path,
|
||||
layer: str,
|
||||
mode: Literal["w", "a"] = "w",
|
||||
encoding: str = "utf-8",
|
||||
**kwargs: Any,
|
||||
) -> Path:
|
||||
"""将 GeoDataFrame 写出到 Esri File Geodatabase(.gdb)中。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
gdf:
|
||||
待写出的 GeoDataFrame。
|
||||
gdb_path:
|
||||
目标 ``.gdb`` 目录路径(不存在时自动创建)。
|
||||
layer:
|
||||
图层名称(必填)。
|
||||
mode:
|
||||
``"w"`` 覆盖图层;``"a"`` 向已有 GDB 追加图层。
|
||||
|
||||
Notes
|
||||
-----
|
||||
写出 GDB 依赖 fiona 的 ``OpenFileGDB``(写)或 ``FileGDB``(需 ESRI 驱动)支持。
|
||||
当前 fiona >= 1.9 的 ``OpenFileGDB`` 驱动已支持创建和写出,无需额外安装。
|
||||
"""
|
||||
gdb_path = Path(gdb_path)
|
||||
if not layer:
|
||||
raise ValueError("写出 GDB 必须指定 layer 参数。")
|
||||
|
||||
gdb_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
logger.info("写出到 GDB:%s >> 图层 %r(模式:%s)", gdb_path.name, layer, mode)
|
||||
gdf.to_file(
|
||||
str(gdb_path),
|
||||
layer=layer,
|
||||
driver="OpenFileGDB",
|
||||
mode=mode,
|
||||
encoding=encoding,
|
||||
**kwargs,
|
||||
)
|
||||
logger.info("GDB 写出完成:%s >> %s", gdb_path, layer)
|
||||
return gdb_path
|
||||
|
||||
|
||||
# ── GPKG 专用 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
def write_gpkg(
|
||||
gdf: gpd.GeoDataFrame,
|
||||
gpkg_path: str | Path,
|
||||
layer: str,
|
||||
mode: Literal["w", "a"] = "w",
|
||||
**kwargs: Any,
|
||||
) -> Path:
|
||||
"""将 GeoDataFrame 写出为 GeoPackage 中的一个图层。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
gpkg_path:
|
||||
目标 ``.gpkg`` 文件路径(不存在时自动创建)。
|
||||
layer:
|
||||
图层名称(必填)。
|
||||
mode:
|
||||
``"w"`` 覆盖;``"a"`` 向已有 GPKG 追加图层。
|
||||
"""
|
||||
gpkg_path = Path(gpkg_path)
|
||||
gpkg_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
gdf.to_file(str(gpkg_path), layer=layer, driver="GPKG", mode=mode, **kwargs)
|
||||
logger.info("GPKG 写出完成:%s >> %s", gpkg_path, layer)
|
||||
return gpkg_path
|
||||
|
||||
|
||||
# ── CSV 写出 ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _write_csv_vector(gdf: gpd.GeoDataFrame, path: Path, **kwargs: Any) -> Path:
|
||||
"""将 GeoDataFrame 写出为含 WKT 几何列的 CSV。"""
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
df = gdf.copy()
|
||||
df["geometry"] = df["geometry"].apply(lambda g: g.wkt if g is not None else None)
|
||||
df.to_csv(path, index=False, encoding="utf-8-sig", **kwargs) # utf-8-sig 兼容 Excel
|
||||
logger.info("CSV 写出完成:%s", path)
|
||||
return path
|
||||
|
||||
|
||||
def write_csv(gdf: gpd.GeoDataFrame, path: str | Path, **kwargs: Any) -> Path:
|
||||
"""将 GeoDataFrame 写出为含 WKT 几何列的 CSV(公开接口)。"""
|
||||
return _write_csv_vector(gdf, Path(path), **kwargs)
|
||||
|
||||
|
||||
# ── 工具函数 ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _infer_driver(path: Path) -> str:
|
||||
"""根据文件扩展名推断 fiona 驱动。"""
|
||||
_EXT_TO_DRIVER: dict[str, str] = {
|
||||
".shp": "ESRI Shapefile",
|
||||
".geojson": "GeoJSON",
|
||||
".json": "GeoJSON",
|
||||
".gpkg": "GPKG",
|
||||
".gdb": "OpenFileGDB",
|
||||
".kml": "KML",
|
||||
".fgb": "FlatGeobuf",
|
||||
".gml": "GML",
|
||||
".dxf": "DXF",
|
||||
}
|
||||
suffix = path.suffix.lower()
|
||||
if path.is_dir() and suffix == ".gdb":
|
||||
return "OpenFileGDB"
|
||||
driver = _EXT_TO_DRIVER.get(suffix)
|
||||
if driver is None:
|
||||
raise ValueError(
|
||||
f"无法自动推断 fiona 驱动,未知扩展名:{suffix!r}。"
|
||||
f"请显式传入 driver=... 参数。"
|
||||
)
|
||||
return driver
|
||||
Reference in New Issue
Block a user