134 lines
5.3 KiB
Python
134 lines
5.3 KiB
Python
|
import os
|
|||
|
import json
|
|||
|
import numpy as np
|
|||
|
import requests
|
|||
|
from urllib.parse import urlparse, urljoin
|
|||
|
from pathlib import Path
|
|||
|
from pyproj import Transformer
|
|||
|
from py3dtiles.tileset.content import B3dm
|
|||
|
from py3dtiles.tilers.b3dm.wkb_utils import TriangleSoup
|
|||
|
|
|||
|
CACHE_DIR = "./_cache_tiles"
|
|||
|
os.makedirs(CACHE_DIR, exist_ok=True)
|
|||
|
|
|||
|
def download_file(url, cache_dir=CACHE_DIR):
|
|||
|
os.makedirs(cache_dir, exist_ok=True)
|
|||
|
filename = os.path.basename(urlparse(url).path)
|
|||
|
local_path = os.path.join(cache_dir, filename)
|
|||
|
if not os.path.exists(local_path):
|
|||
|
print(f"📥 Downloading {url}")
|
|||
|
r = requests.get(url)
|
|||
|
r.raise_for_status()
|
|||
|
with open(local_path, "wb") as f:
|
|||
|
f.write(r.content)
|
|||
|
return local_path
|
|||
|
|
|||
|
def apply_transform(points, transform):
|
|||
|
n = points.shape[0]
|
|||
|
homo = np.hstack([points, np.ones((n,1))])
|
|||
|
transformed = (transform @ homo.T).T[:, :3]
|
|||
|
return transformed
|
|||
|
|
|||
|
def extract_positions_from_b3dm(b3dm_bytes, transform=None):
|
|||
|
soup = TriangleSoup.from_glb_bytes(b3dm_bytes)
|
|||
|
positions = soup.get_positions() # Nx3 numpy
|
|||
|
if transform is not None:
|
|||
|
positions = apply_transform(positions, transform)
|
|||
|
return positions
|
|||
|
|
|||
|
def load_tileset_json(path_or_url):
|
|||
|
if path_or_url.startswith("http"):
|
|||
|
local_path = download_file(path_or_url)
|
|||
|
else:
|
|||
|
local_path = path_or_url
|
|||
|
with open(local_path, "r", encoding="utf-8") as f:
|
|||
|
js = json.load(f)
|
|||
|
if "geometricError" not in js:
|
|||
|
print("⚠️ tileset.json缺少geometricError,自动补 1000.0")
|
|||
|
js["geometricError"] = 1000.0
|
|||
|
return js, os.path.dirname(local_path), path_or_url if path_or_url.startswith("http") else None
|
|||
|
|
|||
|
def parse_transform(tile_json):
|
|||
|
# 返回 4x4 np.array
|
|||
|
if "transform" in tile_json:
|
|||
|
return np.array(tile_json["transform"]).reshape(4,4)
|
|||
|
else:
|
|||
|
return np.eye(4)
|
|||
|
|
|||
|
def recursive_load(tile_json, base_dir, base_url, bbox, transformer, transform_accum=None):
|
|||
|
if transform_accum is None:
|
|||
|
transform_accum = np.eye(4)
|
|||
|
|
|||
|
points_list = []
|
|||
|
tile_transform = parse_transform(tile_json)
|
|||
|
transform_total = transform_accum @ tile_transform
|
|||
|
|
|||
|
print(f"\n处理 Tile,当前累计 transform 矩阵:\n{transform_total}")
|
|||
|
print(f"Tile 的 bbox (输入): {bbox}")
|
|||
|
|
|||
|
content = tile_json.get("content")
|
|||
|
if content:
|
|||
|
uri = content.get("uri")
|
|||
|
if uri and uri.endswith(".b3dm"):
|
|||
|
if base_url is not None:
|
|||
|
b3dm_url = urljoin(base_url + "/", uri)
|
|||
|
b3dm_path = download_file(b3dm_url)
|
|||
|
else:
|
|||
|
b3dm_path = os.path.join(base_dir, uri)
|
|||
|
if os.path.exists(b3dm_path):
|
|||
|
with open(b3dm_path, "rb") as f:
|
|||
|
b3dm = B3dm.from_array(f.read())
|
|||
|
positions = extract_positions_from_b3dm(b3dm.glb_bytes, transform_total)
|
|||
|
print(f"加载 {b3dm_path},原始顶点数量: {len(positions)}")
|
|||
|
|
|||
|
lon, lat, elev = transformer.transform(
|
|||
|
positions[:,0], positions[:,1], positions[:,2]
|
|||
|
)
|
|||
|
print(f"转换后经度范围: {lon.min():.6f} ~ {lon.max():.6f}")
|
|||
|
print(f"转换后纬度范围: {lat.min():.6f} ~ {lat.max():.6f}")
|
|||
|
|
|||
|
geo_pts = np.vstack([lon, lat, elev]).T
|
|||
|
mask = (
|
|||
|
(geo_pts[:,0] >= bbox[0]) & (geo_pts[:,0] <= bbox[2]) &
|
|||
|
(geo_pts[:,1] >= bbox[1]) & (geo_pts[:,1] <= bbox[3])
|
|||
|
)
|
|||
|
in_bbox_count = np.sum(mask)
|
|||
|
print(f"bbox内点数量: {in_bbox_count}")
|
|||
|
|
|||
|
points_list.append(geo_pts[mask])
|
|||
|
|
|||
|
elif uri and uri.endswith(".json"):
|
|||
|
if base_url is not None:
|
|||
|
child_url = urljoin(base_url + "/", uri)
|
|||
|
child_json, child_base_dir, child_base_url = load_tileset_json(child_url)
|
|||
|
else:
|
|||
|
child_path = os.path.join(base_dir, uri)
|
|||
|
child_json, child_base_dir, child_base_url = load_tileset_json(child_path)
|
|||
|
pts = recursive_load(child_json, child_base_dir, child_base_url, bbox, transformer, transform_total)
|
|||
|
if pts.size > 0:
|
|||
|
points_list.append(pts)
|
|||
|
|
|||
|
for child in tile_json.get("children", []):
|
|||
|
pts = recursive_load(child, base_dir, base_url, bbox, transformer, transform_total)
|
|||
|
if pts.size > 0:
|
|||
|
points_list.append(pts)
|
|||
|
|
|||
|
if points_list:
|
|||
|
return np.vstack(points_list)
|
|||
|
else:
|
|||
|
return np.empty((0,3))
|
|||
|
|
|||
|
def load_tileset_all_points(path_or_url, bbox):
|
|||
|
transformer = Transformer.from_crs("EPSG:4979", "EPSG:4326", always_xy=True)
|
|||
|
tileset_json, base_dir, base_url = load_tileset_json(path_or_url)
|
|||
|
points = recursive_load(tileset_json, base_dir, base_url, bbox, transformer)
|
|||
|
print(f"\n总计提取 {len(points)} 个点在区域内")
|
|||
|
return points
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
bbox = [116.391, 39.905, 116.405, 39.915]
|
|||
|
model1_url = "http://8.137.54.85:9000/300bdf2b-a150-406e-be63-d28bd29b409f/dszh/1748398014403562192_OUT/B3DM/tileset.json"
|
|||
|
model2_url = "http://8.137.54.85:9000/300bdf2b-a150-406e-be63-d28bd29b409f/dszh/1748325943733189898_OUT/B3DM/tileset.json"
|
|||
|
|
|||
|
pts1 = load_tileset_all_points(model1_url, bbox)
|
|||
|
pts2 = load_tileset_all_points(model2_url, bbox)
|