feat(assets): publish reusable area packages

This commit is contained in:
2026-08-11 16:07:07 +08:00
parent b4a81331e8
commit f385009043
18 changed files with 292 additions and 108 deletions

View File

@@ -579,7 +579,8 @@ def export_glb(filepath, meshes):
def semantic_asset_specs(glb_path, meshes):
"""Derive optional inspection assets from the scene's named collections."""
stem, extension = os.path.splitext(glb_path)
model_dir, filename = os.path.split(glb_path)
_, extension = os.path.splitext(filename)
by_collection = {}
for collection in bpy.context.scene.collection.children:
by_collection[collection.name] = set(collection.all_objects)
@@ -592,7 +593,7 @@ def semantic_asset_specs(glb_path, meshes):
subset = [obj for obj in meshes if obj in objects]
if not subset:
continue
path = stem + "-" + asset_id + extension
path = os.path.join(model_dir, asset_id + extension)
specs.append({
"id": asset_id,
"label": label,
@@ -673,42 +674,19 @@ def export(args):
center_lat = (bounds["min_lat"] + bounds["max_lat"]) / 2.0
else:
center_lon = center_lat = 0.0
area_id = os.path.splitext(os.path.basename(args["glb"]))[0]
metadata = {
"asset": os.path.basename(args["glb"]),
"assets": [{
"id": "main",
"label": "Scene",
"type": "model",
"url": os.path.basename(args["glb"]),
"enabled": True,
}, {
"id": "traffic-dynamic",
"label": "Traffic signals dynamic",
"type": "model",
"url": os.path.basename(args["dynamic_glb"]) if args.get("dynamic_glb") and dynamic_meshes else "",
"enabled": True,
"category": "dynamic",
}, {
"id": "traffic-countdown-0", "label": "Traffic countdown group 0", "type": "model",
"url": os.path.basename(args["countdown_0_glb"]), "enabled": True, "category": "countdown", "phaseGroup": 0,
}, {
"id": "traffic-countdown-1", "label": "Traffic countdown group 1", "type": "model",
"url": os.path.basename(args["countdown_1_glb"]), "enabled": True, "category": "countdown", "phaseGroup": 1,
}] + [{
"id": asset["id"],
"label": asset["label"],
"type": "model",
"url": os.path.basename(asset["path"]),
"enabled": False,
"category": "semantic",
"schema": "osm-asset-package/v1",
"packageVersion": "1.0.0",
"areaId": area_id,
"coordinateSystem": {"axes": "ENU", "units": "meters", "x": "east", "y": "north", "z": "up"},
"placement": {"longitude": center_lon, "latitude": center_lat, "height": 0.35, "headingCorrectionDegrees": -90.0},
"bounds": {"minLon": bounds.get("min_lon", center_lon), "minLat": bounds.get("min_lat", center_lat), "maxLon": bounds.get("max_lon", center_lon), "maxLat": bounds.get("max_lat", center_lat)},
"assets": [{"id": "main", "role": "scene", "category": "scene", "uri": "models/" + os.path.basename(args["glb"]), "defaultLoad": True}] + [{
"id": asset["id"], "role": "layer", "category": asset["id"],
"uri": "models/" + os.path.basename(asset["path"]), "defaultLoad": False,
} for asset in semantic_assets],
"coordinate_system": "local ENU meters (X east, Y north, Z up)",
"heading_correction_degrees": -90.0,
"anchor": {"longitude": center_lon, "latitude": center_lat, "height": 0.35},
"bounds": bounds,
"source_osm": scene.get("source_osm", ""),
"source_geojson": scene.get("source_geojson", ""),
"scene_stats": {
"sceneStats": {
"buildings": scene.get("building_count", 0),
"industrial_buildings": scene.get("industrial_building_count", 0),
"lake_polygons": scene.get("lake_count", 0),
@@ -717,15 +695,6 @@ def export(args):
"scrub_polygons": scene.get("scrub_count", 0),
"fountains": scene.get("fountain_count", 0),
},
"cesium_js": (
"const p = Cesium.Cartesian3.fromDegrees(" +
f"{center_lon:.8f}, {center_lat:.8f}, 0.35);\n" +
"const enu = Cesium.Transforms.eastNorthUpToFixedFrame(p);\n" +
"const correction = Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(-90.0));\n" +
"const modelMatrix = Cesium.Matrix4.multiplyByMatrix3(enu, correction, new Cesium.Matrix4());\n" +
"Cesium.Model.fromGltfAsync({ url: '" + os.path.basename(args["glb"]) +
"', modelMatrix }).then(model => viewer.scene.primitives.add(model));"
),
}
os.makedirs(os.path.dirname(args["metadata"]), exist_ok=True)
with open(args["metadata"], "w", encoding="utf-8") as handle: