Metadata-Version: 2.2
Name: alobj2shp
Version: 0.1.9
Summary: High-performance building footprint extractor from OBJ to GPKG/SHP
Requires-Python: >=3.11
Requires-Dist: ninja
Requires-Dist: numpy
Requires-Dist: moderngl
Requires-Dist: opencv-python-headless
Requires-Dist: geopandas
Requires-Dist: tqdm
Description-Content-Type: text/markdown

# alobj2shp


```python
import alobj2shp

alobj2shp.extract_footprints(
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_YUp",
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_YUp_v2.gpkg",
    y_is_up=True
)
```

## 功能概述
`alobj2shp` 用于**批量从 OBJ 中提取建筑底面轮廓（footprints）**，并把结果直接写出为 GIS 矢量文件（推荐 `.gpkg`）。

处理流程（实现层面的概念）大致为：
1. 递归读取 `input_dir` 下所有 `*.obj`
2. 将每个 OBJ 的三角网格进行投影/栅格化生成二维轮廓
3. 使用 GEOS 做布尔运算、拓扑构建/简化
4. 输出每个 OBJ 对应一条（Polygon 或 MultiPolygon）的 `geometry`，并写出到 `output_file`

## 安装与依赖（简要）
- Python >= 3.11
- 依赖（来自 `pyproject.toml`）：`ninja`, `numpy`, `moderngl`, `opencv-python-headless`, `geopandas`, `tqdm`

## Python API

### 主入口
`alobj2shp.extract_footprints(...)`

签名（类型声明）：
```python
extract_footprints(
    input_dir: str,
    output_file: str,
    gsd: float = 0.05,
    simplify_tolerance: float = 0.1,
    target_crs: str | None = "EPSG:3857",
    max_workers: int | None = None,
    y_is_up: bool = False
) -> geopandas.GeoDataFrame
```

### 参数说明
- `input_dir`：OBJ 模型目录。会对该目录进行递归查找 `*.obj`。
- `output_file`：输出路径。建议使用 `.gpkg`。
  - 当后缀为 `.gpkg` 时，以 `driver="GPKG"` 写出
  - 其它后缀由 `geopandas.GeoDataFrame.to_file` 按后缀/默认规则写出
- `gsd`：地面采样距离（单位与 OBJ 坐标一致）。
  - `gsd` 越小，栅格越细，几何精度通常更高，但会显著增加计算/显存开销
- `simplify_tolerance`：简化容差（与坐标单位一致），内部对应 GEOS 的拓扑保持简化。
- `target_crs`：结果写入/标注的 CRS。
  - 默认 `"EPSG:3857"`
  - 当 `target_crs=None` 时，不会设置 CRS
- `max_workers`：并发线程数（线程池）。
  - 默认 `None`：使用 `os.cpu_count() or 4`
  - 每个 OBJ 会在独立任务中解析/渲染/计算；线程过多可能引发 GPU/系统资源竞争，必要时可调小
- `y_is_up`：输入 OBJ 是否为 **Y-up 坐标系**。
  - `y_is_up=False`（默认）：使用 `x=val1, y=val2, z=val3`
  - `y_is_up=True`：启用轴变换以适配 GIS 的 Z-up 规范，内部为 `x=val1, z=val2, y=-val3`

### 返回值与输出字段
- 正常情况下返回 `geopandas.GeoDataFrame`，包含列：
  - `old_code`：OBJ 文件相对于 `input_dir` 的相对路径（字符串）
  - `geometry`：由 WKB 解析得到的 shapely 几何（通常为 Polygon 或 MultiPolygon）
- 当在 `input_dir` 中找不到 `*.obj`，或所有 OBJ 都处理失败时，运行时可能返回 `None`。

## 坐标系与坐标平面约定
- `y_is_up` 决定 OBJ 顶点在进入后续“投影/轮廓生成”之前的轴变换方式（见参数说明）。
- `gsd` 与 `simplify_tolerance` 都依赖“OBJ 坐标单位”：
  - 如果你的 OBJ 单位不是米，建议相应缩放 `gsd` / `simplify_tolerance`，避免几何过度简化或细节丢失。

## 调试/高级用法（可选）
如果你想对**单个 OBJ**直接得到几何的 WKB，可以使用内部接口（注意它不是顶层公开 API）：

```python
from shapely import wkb
from alobj2shp._core import process_single_obj_to_wkb

obj_wkb = process_single_obj_to_wkb(
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_ZUp\East_Block_1\xxx.obj",
    gsd=0.05,
    tolerance=0.1,   # 对应 simplify_tolerance
    y_is_up=False
)

geom = wkb.loads(obj_wkb) if obj_wkb else None
print(geom)
```

## 调用样例
下面样例中，建议使用 `r"..."` 书写 Windows 路径，避免 Python 把反斜杠当作转义字符。

### 1. Z-Up（默认）
```python
import alobj2shp

gdf = alobj2shp.extract_footprints(
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_ZUp\East_Block_1",
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_ZUp\East_Block_1_v2.gpkg"
)

print(gdf.shape)
print(gdf[["old_code"]].head())
```

### 2. Y-Up
```python
import alobj2shp

gdf = alobj2shp.extract_footprints(
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000\East_Block_1",
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000\East_Block_1_v2.gpkg",
    y_is_up=True
)

print(gdf.shape)
```

### 3. 参数调优（更细栅格）
```python
import alobj2shp

gdf = alobj2shp.extract_footprints(
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_ZUp\East_Block_1",
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_ZUp\East_Block_1_tuned.gpkg",
    gsd=0.02,
    simplify_tolerance=0.2,
    max_workers=8
)
```

### 4. 不写入 CRS
```python
import alobj2shp

gdf = alobj2shp.extract_footprints(
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_ZUp\East_Block_1",
    r"H:\Work\PT\普陀东部\Data_OBJ_SH2000_ZUp\East_Block_1_no_crs.shp",
    target_crs=None
)
print(gdf.crs)
```