Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature処理器に表示名を与える、外観読み込み処理のアタリ付け、など #24

Merged
merged 1 commit into from
Jul 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions plateau_plugin/algorithms/load_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@

def _convert_to_qt_value(v: Any):
if isinstance(v, list):
# list
if not v:
return []

Expand Down Expand Up @@ -113,7 +112,7 @@ def _get_layer_name(self, cityobj: CityObject) -> str:
co: Optional[CityObject] = cityobj
s = []
while co:
s.append(co.processor.id)
s.append(co.processor.name)
co = co.parent
name = " / ".join(reversed(s))
if cityobj.lod is not None:
Expand Down Expand Up @@ -198,7 +197,7 @@ class PlateauVectorLoaderAlrogithm(QgsProcessingAlgorithm):
"""Processing algorithm to load PLATEAU 3D City models as vector layers"""

INPUT = "INPUT"
ONLY_HIGHEST_LOD = "ONLY_HIGHEST_LoD"
ONLY_HIGHEST_LOD = "ONLY_HIGHEST_LOD"
LOAD_SEMANTIC_PARTS = "LOAD_SEMANTIC_PARTS"
FORCE_2D = "FORCE_2D"

Expand Down
3 changes: 2 additions & 1 deletion plateau_plugin/plateau/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
processors.validate_processors()

# settings = ParseSettings(load_semantic_parts=True)
settings = ParseSettings(only_highest_lod=True, load_semantic_parts=False)
settings = ParseSettings(only_highest_lod=False, load_semantic_parts=False)
parser = FileParser(sys.argv[1], settings)
parser.load_apperance()
for count, cityobj in parser.iter_cityobjs():
print(
f"{count} [{cityobj.processor.id}] {cityobj.type}, {cityobj.name}, LoD={cityobj.lod}, {cityobj.attributes}"
Expand Down
68 changes: 68 additions & 0 deletions plateau_plugin/plateau/appearance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from dataclasses import dataclass
from typing import Iterable, Optional

import lxml.etree as et
import numpy as np

from .namespaces import BASE_NS as _NS


@dataclass
class Material:
__slots__ = ("diffuse_color", "specular_color", "shininess")
diffuse_color: Optional[tuple[float, ...]]
specular_color: Optional[tuple[float, ...]]
shininess: Optional[float]


@dataclass
class Texture:
__slots__ = ("image_uri",)
image_uri: str


@dataclass
class Appearance:
target_material: dict[str, Material]
ring_texture: dict[str, tuple[Texture, np.ndarray]]


def parse_appearances(doc: et._Element) -> Iterable[Appearance]:
target_to_material: dict[str, Material] = {}
ring_to_texture: dict[str, tuple[Texture, np.ndarray]] = {}

for appearance in doc.iterfind(".//app:appearanceMember/app:Appearance", _NS):
for material in appearance.iterfind(
".//app:surfaceDataMember/app:X3DMaterial", _NS
):
m = Material(
diffuse_color=None,
specular_color=None,
shininess=None,
)
if (diffuse := material.find("./app:diffuseColor", _NS)) is not None:
m.diffuse_color = tuple(float(v) for v in diffuse.text.split())
if (specular := material.find("./app:specularColor", _NS)) is not None:
m.specular_color = tuple(float(v) for v in specular.text.split())
if (shininess := material.find("./app:shininess", _NS)) is not None:
m.shininess = float(shininess.text)

for target in material.iterfind("./app:target", _NS):
assert target.text.startswith("#")
target_id = target.text[1:]
target_to_material[target_id] = m

for texture in appearance.iterfind(
".//app:surfaceDataMember/app:ParameterizedTexture", _NS
):
image_uri = texture.find("./app:imageURI", _NS).text
t = Texture(image_uri=image_uri)
for target in texture.iterfind("./app:target", _NS):
coords = target.find(".//app:textureCoordinates", _NS)
ring_id = coords.get("ring")
assert ring_id.startswith("#")
ring_id = ring_id[1:]
uv = np.fromstring(coords.text, dtype=np.float32, sep=" ")
ring_to_texture[ring_id] = (t, uv.reshape(-1, 2))

yield Appearance(target_to_material, ring_to_texture)
112 changes: 89 additions & 23 deletions plateau_plugin/plateau/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,117 @@
import lxml.etree as et
import numpy as np

from .appearance import Appearance, Material, Texture
from .types import Geometry, LineStringCollection, PointCollection, PolygonCollection


def parse_geometry( # noqa: C901 (TODO)
element: et._Element, geometry_paths: Iterable[str], nsmap: dict[str, str]
element: et._Element,
geometry_paths: Iterable[str],
nsmap: dict[str, str],
appearance: Optional[Appearance],
) -> Optional[Geometry]:
"""指定された GML のジオメトリへのパスをもとにマルチパートのジオメトリを構成して返す"""
polygon_geoms = []
line_geoms = []
point_geoms = []
# TODO: refactoring

mpoly_geoms = []
mpoly_materials: list[Optional[Material]] = []
mpoly_textures: list[Optional[Texture]] = []
mpoly_uvs: list[Optional[list[np.ndarray]]] = []
mline_geoms = []
mpoint_geoms = []

for geometry_path in geometry_paths:
if geometry_path.endswith(("/gml:Polygon", "/gml:Triangle")):
for polygon in element.iterfind(geometry_path, nsmap):
pos_list = polygon.find("./gml:exterior//gml:posList", nsmap)
vertices = np.fromstring(pos_list.text, dtype=np.float64, sep=" ")
exterior = vertices.reshape(-1, 3)
rings = []
rings.append(exterior)
# TODO: refactoring
poly_rings = []
poly_uvs = []

# exterior ring
ring_elem = polygon.find("./gml:exterior/gml:LinearRing", nsmap)
poslist = ring_elem.find("./gml:posList", nsmap)
vertices = np.fromstring(poslist.text, dtype=np.float64, sep=" ")
ring = vertices.reshape(-1, 3)
poly_rings.append(ring)
if appearance:
poly_id = polygon.get("{http://www.opengis.net/gml}id")
ring_id = ring_elem.get("{http://www.opengis.net/gml}id")

# TODO: refactoring
mat = None
if poly_id and (m := appearance.target_material.get(poly_id)):
mat = m
else:
sur = polygon.getparent().getparent()
sur_id = sur.get("{http://www.opengis.net/gml}id")
if sur_id and (m := appearance.target_material.get(sur_id)):
mat = m
elif sur.tag == "{http://www.opengis.net/gml}CompositeSurface":
sur2 = sur.getparent().getparent()
sur2_id = sur2.get("{http://www.opengis.net/gml}id")
if sur2_id and (
m := appearance.target_material.get(sur2_id)
):
mat = m

mpoly_materials.append(mat)

if tex_uv := appearance.ring_texture.get(ring_id):
tex, uv = tex_uv
assert ring.shape[0] == uv.shape[0]
mpoly_textures.append(tex)
poly_uvs.append(uv)
else:
mpoly_textures.append(None)
poly_uvs.append(None)

for pos_list in polygon.iterfind("./gml:interior//gml:posList", nsmap):
vertices = np.fromstring(pos_list.text, dtype=np.float64, sep=" ")
interior = vertices.reshape(-1, 3)
rings.append(interior)
# interior rings
for ring_elem in polygon.iterfind(
"./gml:interior/gml:LinearRing", nsmap
):
poslist = ring_elem.find("./gml:posList", nsmap)
vertices = np.fromstring(poslist.text, dtype=np.float64, sep=" ")
ring = vertices.reshape(-1, 3)
poly_rings.append(ring)
if appearance:
uv = None
ring_id = ring_elem.get("{http://www.opengis.net/gml}id")
if tex_uv := appearance.ring_texture.get(ring_id):
_, uv = tex_uv
poly_uvs.append(uv)

polygon_geoms.append(rings)
if appearance:
mpoly_uvs.append(poly_uvs)
mpoly_geoms.append(poly_rings)

elif geometry_path.endswith("/gml:LineString"):
for linestring in element.iterfind(geometry_path, nsmap):
pos_list = linestring.find("./gml:posList", nsmap)
vertices = np.fromstring(pos_list.text, dtype=np.float64, sep=" ")
poslist = linestring.find("./gml:posList", nsmap)
vertices = np.fromstring(poslist.text, dtype=np.float64, sep=" ")
line = vertices.reshape(-1, 3)
line_geoms.append(line)
mline_geoms.append(line)

elif geometry_path.endswith("/gml:Point"):
pass

else:
raise NotImplementedError(f"Unsupported geometry path: {geometry_path}")

if polygon_geoms:
return PolygonCollection(polygons=polygon_geoms)
elif line_geoms:
return LineStringCollection(lines=line_geoms)
elif point_geoms:
return PointCollection(points=np.vstack(point_geoms))
if mpoly_geoms:
if appearance:
assert len(mpoly_geoms) == len(mpoly_textures)
assert len(mpoly_geoms) == len(mpoly_uvs)
assert len(mpoly_geoms) == len(mpoly_materials)
return PolygonCollection(
polygons=mpoly_geoms,
materials=None,
textures=mpoly_textures if appearance else None,
uvs=mpoly_uvs if appearance else None,
)
elif mline_geoms:
return LineStringCollection(lines=mline_geoms)
elif mpoint_geoms:
return PointCollection(points=np.vstack(mpoint_geoms))

return None
15 changes: 10 additions & 5 deletions plateau_plugin/plateau/models/attr_disaster_risk.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
)

ATTR_DISASTER_RISK_RIVER_FLOODING = FeatureProcessingDefinition(
id="RiverFloodingRisk",
id="uro:RiverFloodingRisk",
name="RiverFloodingRisk",
target_elements=[
"uro:BuildingRiverFloodingRiskAttribute",
"uro:RiverFloodingRiskAttribute",
Expand Down Expand Up @@ -72,7 +73,8 @@
)

ATTR_DISASTER_RISK_TSUNAMI = FeatureProcessingDefinition(
id="TsumaniRisk",
id="uro:TsumaniRisk",
name="TsumaniRisk",
target_elements=[
"uro:BuildingTsunamiRiskAttribute",
"uro:TsunamiRiskAttribute",
Expand Down Expand Up @@ -113,7 +115,8 @@
)

ATTR_DISASTER_RISK_HIGH_TIDE = FeatureProcessingDefinition(
id="HighTideRisk",
id="uro:HighTideRisk",
name="HighTideRisk",
target_elements=[
"uro:BuildingHighTideRiskAttribute",
"uro:HighTideRiskAttribute",
Expand Down Expand Up @@ -154,7 +157,8 @@
)

ATTR_DISASTER_RISK_INLAND_FLOODING = FeatureProcessingDefinition(
id="InlandFloodingRisk",
id="uro:InlandFloodingRisk",
name="InlandFloodingRisk",
target_elements=[
"uro:BuildingInlandFloodingRiskAttribute",
"uro:InlandFloodingRiskAttribute",
Expand Down Expand Up @@ -195,7 +199,8 @@
)

ATTR_DISASTER_RISK_LAND_SLIDE = FeatureProcessingDefinition(
id="LandSlideRisk",
id="uro:LandSlideRisk",
name="LandSlideRisk",
target_elements=[
"uro:BuildingLandSlideRiskAttribute",
"uro:LandSlideRiskAttribute",
Expand Down
8 changes: 7 additions & 1 deletion plateau_plugin/plateau/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class FeatureProcessingDefinition:
id: str
"""このProcessorのID"""

name: str
"""このProcessorの表示名"""

target_elements: list[str]
"""処理対象とするFeature要素 (e.g. "tran:Road", "tran:TrafficArea", "bldg:WallSurface")"""

Expand All @@ -104,7 +107,10 @@ class FeatureProcessingDefinition:
"""災害リスク属性 uro:(Building)DisasterRiskAttribute を包含する要素への element path"""

non_geometric: bool = False
"""ジオメトリを持たない Feature かどうか"""
"""ジオメトリを持たない Feature かどうか

Trueの場合は、ジオメトリをもたない場合も地物として出力する
"""

def detect_lods(self, elem: et._Element, nsmap: dict[str, str]) -> tuple[bool, ...]:
"""
Expand Down
Loading