2025-07-28 10:56:04 +08:00

134 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)