Skip to content

Commit

Permalink
Merge pull request #24 from MIERUNE/feature/misc-improvements
Browse files Browse the repository at this point in the history
Feature処理器の表示名、外観読み込みのアタリ付け
  • Loading branch information
ciscorn committed Jul 10, 2023
2 parents 24b30cf + edde44f commit cab5714
Show file tree
Hide file tree
Showing 25 changed files with 320 additions and 110 deletions.
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

0 comments on commit cab5714

Please sign in to comment.