diff --git a/.gitignore b/.gitignore index 2448ce8..1a1fb6b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ env/ # ── IDE ───────────────────────────────────────────────────────────────────── .idea/ .vscode/ +.trae/ *.swp *.swo .DS_Store @@ -37,6 +38,7 @@ output/* !output/.gitkeep # ── GIS 真实数据(保留示例数据,忽略用户数据)──────────────────────────────── +wiki/ data/* !data/sample/ !data/sample/** diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md deleted file mode 100644 index 07639a8..0000000 --- a/.trae/rules/project_rules.md +++ /dev/null @@ -1,8 +0,0 @@ -# 给 AI 助手看的规则 - 小白专用版 - -## 关于内存(最重要) -1. 凡是读文件的函数,只要没加 `rows` 限制或 `chunk_size` 分块,都要在注释里用【警告】两个字提醒用户注意内存风险。 -2. 不要用 `for` 循环一行行修改 GeoDataFrame,要用 `apply` 或者向量化操作。 - -## 关于报错 -3. 禁止直接把底层库(fiona, shapely)的英文报错抛给用户看。必须包一层 try-except,翻译成中文提示,例如:“文件打不开,检查一下路径是不是有中文?”或者“数据里有烂掉的图形,已自动修复/跳过”。 \ No newline at end of file diff --git a/app/io/readers.py b/app/io/readers.py index 9201804..7da3e55 100644 --- a/app/io/readers.py +++ b/app/io/readers.py @@ -35,7 +35,6 @@ def read_vector( 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, ): @@ -51,9 +50,6 @@ def read_vector( 读取后强制重投影到目标 CRS(不传则保留原始 CRS)。 encoding: 属性表编码,Shapefile 中文路径常需指定 ``"gbk"``。 - chunk_size: - 分块大小,默认 None(一次性读取全部数据)。 - 【警告】:若不设置 chunk_size,大文件可能会占用大量内存。 rows: 限制读取的行数,默认 None(读取全部数据)。 用于快速预览数据,避免读取大文件的全部内容。 @@ -62,22 +58,15 @@ def read_vector( Returns ------- - gpd.GeoDataFrame 或生成器 - 如果 chunk_size 为 None,返回完整的 GeoDataFrame; - 如果设置了 chunk_size,返回一个生成器,每次 yield 一个 GeoDataFrame 块。 + gpd.GeoDataFrame + 读取的矢量数据。 示例 ----- - # 全量读取(老方法) + ### 全量读取 gdf = read_vector("data.shp") - # 分块读取(新方法) - for chunk in read_vector("large_data.shp", chunk_size=10000): - # 处理每个数据块 - print(f"处理了 {len(chunk)} 条数据") - # 在这里做你的操作,比如计算、过滤等 - - # 只读取前 5 行数据(预览模式) + ### 只读取前 5 行数据(预览模式) gdf_preview = read_vector("large_data.shp", rows=5) print(gdf_preview.head()) """ @@ -88,70 +77,27 @@ def read_vector( 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 + try: + if rows is not None: + read_kwargs["rows"] = rows + logger.info("限制读取行数:%d", rows) - if crs is not None: - logger.debug("重投影到 %s", crs) - gdf = gdf.to_crs(crs) # type: ignore + gdf = gpd.read_file(str(path), **read_kwargs) + except Exception as exc: + raise RuntimeError(f"无法读取矢量数据:{exc}") from None - logger.info("读取完成:共 %d 条要素,CRS=%s", len(gdf), gdf.crs) - return gdf # type: ignore + 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 # ── GDB 专用 ─────────────────────────────────────────────────────────────────── diff --git a/tests/test.py b/tests/test.py new file mode 100644 index 0000000..dab2cc7 --- /dev/null +++ b/tests/test.py @@ -0,0 +1,13 @@ +import sys +from pathlib import Path + +# 将项目根目录添加到系统路径中,以便导入模块 +sys.path.append(str(Path(__file__).parent.parent)) +from app.io.readers import read_vector +# 读取 GeoJSON 文件 +data_path = Path(r"D:\工作\三普成果编制\出图数据\永德\制表\制表数据.gdb\AK") + +# 或者使用 rows 参数直接只读取前5行 +print("\n使用 rows 参数预览:") +gdf_preview = read_vector(data_path, rows=5) +print(gdf_preview) \ No newline at end of file