Skip to content
Open
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
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,3 @@ wslink==1.12.4
yarl>=1
# via aiohttp

opengeodeweb-microservice==1.*,>=1.1.3
8 changes: 3 additions & 5 deletions src/opengeodeweb_viewer/object/object_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def _prune_hidden_blocks(
dataset: vtkMultiBlockDataSet,
visibility_attributes: vtkCompositeDataDisplayAttributes,
) -> vtkMultiBlockDataSet:
# Recursively construct a new multi-block dataset excluding hidden blocks.
pruned = vtkMultiBlockDataSet()
pruned.SetNumberOfBlocks(dataset.GetNumberOfBlocks())
for index in range(dataset.GetNumberOfBlocks()):
Expand Down Expand Up @@ -165,13 +166,10 @@ def SetBlocksVisibility(
visibility_attributes = mapper.GetCompositeDataDisplayAttributes()
for block_id in block_ids:
visibility_attributes.SetBlockVisibility(blocks[block_id], visibility)
dataset = (
pipeline.filter.GetOutputDataObject(0)
if pipeline.filter
else pipeline.reader.GetOutputDataObject(0)
)
dataset = (pipeline.filter or pipeline.reader).GetOutputDataObject(0)
if not isinstance(dataset, vtkMultiBlockDataSet):
return
# Re-build a pruned dataset for the dedicated pick mapper
if pipeline.pick_mapper is None:
pipeline.pick_mapper = vtkCompositePolyDataMapper()
pipeline.pick_mapper.SetInputDataObject(
Expand Down
141 changes: 26 additions & 115 deletions src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
vtkWorldPointPicker,
vtkCellPicker,
vtkPropPicker,
vtkCompositePolyDataMapper,
vtkDataSetMapper,
vtkActor,
)
Expand Down Expand Up @@ -262,52 +261,26 @@ def pickedIds(self, rpc_params: RpcParams) -> dict[str, list[str] | int | None]:
rpc_params, self.viewer_schemas_dict["picked_ids"], self.viewer_prefix
)
params = schemas.PickedIDS.from_dict(rpc_params)
renderer = self.getView("-1").GetRenderers().GetFirstRenderer()

pipelines_to_restore = [
pipeline
for pipeline_id in params.ids
if (pipeline := self.get_vtk_pipeline(pipeline_id)).pick_mapper is not None
]
for pipeline in pipelines_to_restore:
pick_mapper = pipeline.pick_mapper
if pick_mapper is not None:
pipeline.actor.SetMapper(pick_mapper)

actors = []
picker = vtkCellPicker(tolerance=0.005)
picker.Pick(params.x, params.y, 0, renderer)
actor = picker.GetActor()
viewer_id = picker.GetFlatBlockIndex()

while actor:
actors.append(actor)
actor.SetPickable(False)
picker.Pick(params.x, params.y, 0, renderer)
actor = picker.GetActor()

for actor in actors:
actor.SetPickable(True)
for pipeline in pipelines_to_restore:
pipeline.actor.SetMapper(pipeline.mapper)

# Retrieve all actors under the clicked coordinates
actors, flat_index = self.pick_actors_under_coordinate(
params.ids, params.x, params.y, picker
)
# Filter pipeline IDs whose actors are in the picked list
array_ids = [
id for id in params.ids if self.get_vtk_pipeline(id).actor in actors
data_id
for data_id in params.ids
if self.get_vtk_pipeline(data_id).actor in actors
]
if not array_ids:
return {"array_ids": [], "viewer_id": None}
if array_ids and viewer_id != -1:
viewer_id = flat_index if flat_index != -1 else None
if viewer_id is not None:
pipeline = self.get_vtk_pipeline(array_ids[0])
mapper = pipeline.mapper
if isinstance(mapper, vtkCompositePolyDataMapper):
attr = mapper.GetCompositeDataDisplayAttributes()
if attr and not attr.GetBlockVisibility(
pipeline.blockDataSets[viewer_id]
):
array_ids, viewer_id = [], -1
dataset, geode_id = self.get_composite_block_info(pipeline, picker)
return {
"array_ids": array_ids,
"viewer_id": viewer_id if viewer_id != -1 else None,
"viewer_id": viewer_id,
}

@exportRpc(viewer_prefix + viewer_schemas_dict["grid_scale"]["rpc"])
Expand Down Expand Up @@ -362,89 +335,27 @@ def setHighlight(
rpc_params, self.viewer_schemas_dict["highlight"], self.viewer_prefix
)
params = schemas.Highlight.from_dict(rpc_params)
picker = vtkCellPicker(tolerance=0.005)

pipelines_to_restore = [
pipeline
for pipeline_id in params.ids
if (pipeline := self.get_vtk_pipeline(pipeline_id)).pick_mapper is not None
]
for pipeline in pipelines_to_restore:
pick_mapper = pipeline.pick_mapper
if pick_mapper is not None:
pipeline.actor.SetMapper(pick_mapper)
try:
picker.Pick(params.x, params.y, 0, self.get_renderer())
finally:
for pipeline in pipelines_to_restore:
pipeline.actor.SetMapper(pipeline.mapper)

# Clear previous highlights
self.clear_highlights(params.ids)
actor = picker.GetActor()
pipeline_id = next(
(id for id in params.ids if self.get_vtk_pipeline(id).actor == actor), None
)
id_to_select = (
picker.GetCellId()
if params.field_type == schemas.FieldType.CELL
else picker.GetPointId()
picker = vtkCellPicker(tolerance=0.005)
# Perform pick operation to identify clicked pipeline and primitive ID
data_id, id_to_select = self.pick_cell_or_point(
params.ids, params.x, params.y, params.field_type.value, picker
)

if not pipeline_id or id_to_select == -1:
if not data_id or id_to_select == -1:
self.render(-1)
return {}

pipeline = self.get_vtk_pipeline(pipeline_id)
dataset = None
geode_id = None
if isinstance(pipeline.mapper, vtkCompositePolyDataMapper):
flat_index = picker.GetFlatBlockIndex()
dataset = (
pipeline.blockDataSets[flat_index]
if 0 <= flat_index < len(pipeline.blockDataSets)
else None
)
if dataset:
attr = pipeline.mapper.GetCompositeDataDisplayAttributes()
if attr and not attr.GetBlockVisibility(dataset):
self.render(-1)
return {}
geode_id = (
pipeline.blockGeodeIds[flat_index]
if 0 <= flat_index < len(pipeline.blockGeodeIds)
else None
)

# Retrieve picked composite block information
pipeline = self.get_vtk_pipeline(data_id)
dataset, geode_id = self.get_composite_block_info(pipeline, picker)
# Update highlight visibility and extract attributes from the picked element
self.update_highlight(pipeline, id_to_select, params.field_type.value, dataset)
self.render(-1)

data_obj = dataset or pipeline.reader.GetOutputDataObject(0)
data_attributes = {}
if isinstance(data_obj, vtkDataSet):
field_data = (
data_obj.GetCellData()
if params.field_type == schemas.FieldType.CELL
else data_obj.GetPointData()
)
for array_index in range(field_data.GetNumberOfArrays()):
array = field_data.GetArray(array_index)
if array and array.GetName():
num_comps = array.GetNumberOfComponents()
component_values = [
array.GetComponent(id_to_select, component_index)
for component_index in range(num_comps)
]
data_attributes[array.GetName()] = (
component_values[0] if num_comps == 1 else component_values
)

if params.field_type == schemas.FieldType.POINT:
point_coordinates = data_obj.GetPoint(id_to_select)
if point_coordinates:
data_attributes["coordinates"] = list(point_coordinates)

data_attributes = self.extract_picked_attributes(
pipeline, id_to_select, params.field_type.value, dataset
)
return {
"id": pipeline_id,
"id": data_id,
"picked_id": id_to_select,
"field_type": params.field_type.value,
"geode_id": geode_id,
Expand Down
110 changes: 108 additions & 2 deletions src/opengeodeweb_viewer/vtk_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
vtkRenderer,
vtkRenderWindow,
vtkDataSetMapper,
vtkCompositePolyDataMapper,
vtkCellPicker,
vtkHardwarePicker,
)
from vtkmodules.vtkCommonDataModel import (
vtkDataObject,
Expand Down Expand Up @@ -189,11 +192,114 @@ def update_highlight(
pipeline.highlight.extractSelection.Update()
pipeline.highlight.actor.VisibilityOn()

def clear_highlights(self, ids: list[str]) -> None:
for data_id in ids:
def clear_highlights(self, data_ids: list[str]) -> None:
for data_id in data_ids:
pipeline = self.get_vtk_pipeline(data_id)
pipeline.highlight.actor.VisibilityOff()

def swap_pick_mappers(self, data_ids: list[str], use_pick_mapper: bool) -> None:
# Swap actor mappers between the default and the pick_mapper (where hidden blocks are pruned).
for data_id in data_ids:
pipeline = self.get_vtk_pipeline(data_id)
if pipeline.pick_mapper:
mapper = pipeline.pick_mapper if use_pick_mapper else pipeline.mapper
pipeline.actor.SetMapper(mapper)

def pick_cell_or_point(
self,
data_ids: list[str],
x: float,
y: float,
field_type: str,
picker: vtkCellPicker,
) -> tuple[str | None, int]:
self.swap_pick_mappers(data_ids, use_pick_mapper=True)
try:
picker.Pick(x, y, 0, self.get_renderer())
finally:
self.swap_pick_mappers(data_ids, use_pick_mapper=False)
actor = picker.GetActor()
# Find which pipeline owns the picked actor
data_id = next(
(
current_data_id
for current_data_id in data_ids
if self.get_vtk_pipeline(current_data_id).actor == actor
),
None,
)
id_to_select = (
picker.GetCellId() if field_type == "CELL" else picker.GetPointId()
)
return data_id, id_to_select

def pick_actors_under_coordinate(
self, data_ids: list[str], x: float, y: float, picker: vtkCellPicker
) -> tuple[list[vtkActor], int]:
renderer = self.get_renderer()
self.swap_pick_mappers(data_ids, use_pick_mapper=True)
actors = []
viewer_id = -1
try:
picker.Pick(x, y, 0, renderer)
viewer_id = picker.GetFlatBlockIndex()
while actor := picker.GetActor():
actors.append(actor)
actor.SetPickable(False)
picker.Pick(x, y, 0, renderer)
finally:
for actor in actors:
actor.SetPickable(True)
self.swap_pick_mappers(data_ids, use_pick_mapper=False)
return actors, viewer_id

def get_composite_block_info(
self, pipeline: VtkPipeline, picker: vtkCellPicker
) -> tuple[vtkDataObject | None, str | None]:
# Extract the specific block dataset and metadata from a picked composite flat index
if not isinstance(pipeline.mapper, vtkCompositePolyDataMapper):
return None, None
flat_index = picker.GetFlatBlockIndex()
if not (0 <= flat_index < len(pipeline.blockDataSets)):
return None, None
dataset = pipeline.blockDataSets[flat_index]
geode_id = (
pipeline.blockGeodeIds[flat_index]
if flat_index < len(pipeline.blockGeodeIds)
else None
)
return dataset, geode_id

def get_array_values(self, array: Any, id_to_select: int) -> list[float] | float:
components = array.GetNumberOfComponents()
if components == 1:
return float(array.GetComponent(id_to_select, 0))
return [float(array.GetComponent(id_to_select, i)) for i in range(components)]

def extract_picked_attributes(
self,
pipeline: VtkPipeline,
id_to_select: int,
field_type: str,
dataset: vtkDataObject | None,
) -> dict[str, list[float] | float]:
data_object = dataset or pipeline.reader.GetOutputDataObject(0)
if not isinstance(data_object, vtkDataSet):
return {}
field_data = (
data_object.GetCellData()
if field_type == "CELL"
else data_object.GetPointData()
)
attributes = {}
for i in range(field_data.GetNumberOfArrays()):
array = field_data.GetArray(i)
if array and array.GetName():
attributes[array.GetName()] = self.get_array_values(array, id_to_select)
if field_type == "POINT" and (coords := data_object.GetPoint(id_to_select)):
attributes["coordinates"] = list(coords)
return attributes

def update_grid_scale_and_clipping_range(self) -> None:
grid_scale = self.get_grid_scale()
if grid_scale is not None:
Expand Down
Loading