diff --git a/src/opengeodeweb_viewer/rpc/viewer/schemas/picked_ids.json b/src/opengeodeweb_viewer/rpc/viewer/schemas/picked_ids.json new file mode 100644 index 0000000..012afcc --- /dev/null +++ b/src/opengeodeweb_viewer/rpc/viewer/schemas/picked_ids.json @@ -0,0 +1,25 @@ +{ + "rpc": "picked_ids", + "schema": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "x", + "y", + "ids" + ] + } +} \ No newline at end of file diff --git a/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py b/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py index 32051e7..98c355a 100644 --- a/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py +++ b/src/opengeodeweb_viewer/rpc/viewer/viewer_protocols.py @@ -1,5 +1,6 @@ # Standard library imports import json +import math import os from pathlib import Path @@ -14,6 +15,7 @@ from opengeodeweb_viewer.utils_functions import get_schemas_dict, validate_schema from opengeodeweb_viewer.vtk_protocol import VtkView + class VtkViewerView(VtkView): prefix = "opengeodeweb_viewer.viewer." schemas_dict = get_schemas_dict(os.path.join(os.path.dirname(__file__), "schemas")) @@ -23,7 +25,9 @@ def __init__(self): @exportRpc(prefix + schemas_dict["create_visualization"]["rpc"]) def createVisualization(self, params): - print(self.schemas_dict["create_visualization"]["rpc"], f"{params=}", flush=True) + print( + self.schemas_dict["create_visualization"]["rpc"], f"{params=}", flush=True + ) validate_schema(params, self.schemas_dict["create_visualization"]) renderWindow = self.getView("-1") renderer = renderWindow.GetRenderers().GetFirstRenderer() @@ -34,7 +38,9 @@ def createVisualization(self, params): @exportRpc(prefix + schemas_dict["set_background_color"]["rpc"]) def setBackgroundColor(self, params): - print(self.schemas_dict["set_background_color"]["rpc"], f"{params=}", flush=True) + print( + self.schemas_dict["set_background_color"]["rpc"], f"{params=}", flush=True + ) validate_schema(params, self.schemas_dict["set_background_color"]) renderWindow = self.getView("-1") renderer = renderWindow.GetRenderers().GetFirstRenderer() @@ -90,7 +96,7 @@ def takeScreenshot(self, params): else: raise Exception("output_extension not supported") - new_filename = filename + '.' + output_extension + new_filename = filename + "." + output_extension file_path = os.path.join(self.DATA_FOLDER_PATH, new_filename) writer.SetFileName(file_path) writer.SetInputConnection(w2if.GetOutputPort()) @@ -101,7 +107,6 @@ def takeScreenshot(self, params): return {"blob": self.addAttachment(file_content)} - @exportRpc(prefix + schemas_dict["update_data"]["rpc"]) def updateData(self, params): print(self.schemas_dict["update_data"]["rpc"], f"{params=}", flush=True) @@ -142,3 +147,44 @@ def reset(self, params): validate_schema(params, self.schemas_dict["reset"]) renderWindow = self.getView("-1") renderWindow.GetRenderers().GetFirstRenderer().RemoveAllViewProps() + + def computeEpsilon(self, renderer, z): + renderer.SetDisplayPoint(0, 0, z) + renderer.DisplayToWorld() + windowLowerLeft = renderer.GetWorldPoint() + size = renderer.GetRenderWindow().GetSize() + renderer.SetDisplayPoint(size[0], size[1], z) + renderer.DisplayToWorld() + windowUpperRight = renderer.GetWorldPoint() + epsilon = 0 + for i in range(3): + epsilon += (windowUpperRight[i] - windowLowerLeft[i]) * ( + windowUpperRight[i] - windowLowerLeft[i] + ) + return math.sqrt(epsilon) * 0.0125 + + @exportRpc(prefix + schemas_dict["picked_ids"]["rpc"]) + def pickedIds(self, params): + print(self.schemas_dict["picked_ids"]["rpc"], f"{params=}", flush=True) + validate_schema(params, self.schemas_dict["picked_ids"]) + x = params["x"] + y = params["y"] + ids = params["ids"] + + renderWindow = self.getView("-1") + renderer = renderWindow.GetRenderers().GetFirstRenderer() + picker = vtk.vtkWorldPointPicker() + picker.Pick([x, y, 0], renderer) + point = picker.GetPickPosition() + epsilon = self.computeEpsilon(renderer, point[2]) + bbox = vtk.vtkBoundingBox() + bbox.AddPoint(point[0] + epsilon, point[1] + epsilon, point[2] + epsilon) + bbox.AddPoint(point[0] - epsilon, point[1] - epsilon, point[2] - epsilon) + + array_ids = [] + for id in ids: + bounds = self.get_object(id)["actor"].GetBounds() + if bbox.Intersects(bounds): + array_ids.append(id) + + return {"array_ids": array_ids} diff --git a/src/tests/test_viewer_protocols.py b/src/tests/test_viewer_protocols.py index d60b01e..bef7ac1 100644 --- a/src/tests/test_viewer_protocols.py +++ b/src/tests/test_viewer_protocols.py @@ -7,23 +7,37 @@ # Local application imports from .test_mesh_protocols import test_register_mesh + def test_create_visualization(server): - server.call(VtkViewerView.prefix + VtkViewerView.schemas_dict["create_visualization"]["rpc"]) + server.call( + VtkViewerView.prefix + VtkViewerView.schemas_dict["create_visualization"]["rpc"] + ) assert server.compare_image(3, "viewer/create_visualization.jpeg") == True + def test_reset_camera(server): - server.call(VtkViewerView.prefix + VtkViewerView.schemas_dict["reset_camera"]["rpc"]) + server.call( + VtkViewerView.prefix + VtkViewerView.schemas_dict["reset_camera"]["rpc"] + ) assert server.compare_image(3, "viewer/reset_camera.jpeg") == True + def test_set_viewer_background_color(server): - server.call(VtkViewerView.prefix + VtkViewerView.schemas_dict["set_background_color"]["rpc"], [{"red": 0, "green": 0, "blue": 255}]) + server.call( + VtkViewerView.prefix + + VtkViewerView.schemas_dict["set_background_color"]["rpc"], + [{"red": 0, "green": 0, "blue": 255}], + ) assert server.compare_image(3, "viewer/set_background_color.jpeg") == True -def test_get_point_position(server): +def test_get_point_position(server): test_register_mesh(server) - server.call(VtkViewerView.prefix + VtkViewerView.schemas_dict["get_point_position"]["rpc"], [{"x": 0, "y": 0}]) + server.call( + VtkViewerView.prefix + VtkViewerView.schemas_dict["get_point_position"]["rpc"], + [{"x": 0, "y": 0}], + ) response = server.get_response() assert "x" in response["result"] assert "y" in response["result"] @@ -43,7 +57,13 @@ def test_take_screenshot(server): # Take a screenshot with background jpg server.call( VtkViewerView.prefix + VtkViewerView.schemas_dict["take_screenshot"]["rpc"], - [{"filename": "take_screenshot_with_background", "output_extension": "jpg", "include_background": True}], + [ + { + "filename": "take_screenshot_with_background", + "output_extension": "jpg", + "include_background": True, + } + ], ) response = server.get_response() @@ -54,16 +74,24 @@ def test_take_screenshot(server): f.write(blob) f.close() first_image_path = os.path.join(server.test_output_dir, "test.jpg") - second_image_path = os.path.join(server.images_dir_path, "viewer/take_screenshot_with_background.jpg") + second_image_path = os.path.join( + server.images_dir_path, "viewer/take_screenshot_with_background.jpg" + ) assert server.images_diff(first_image_path, second_image_path) == 0.0 # Take a screenshot without background png server.call( VtkViewerView.prefix + VtkViewerView.schemas_dict["take_screenshot"]["rpc"], - [{"filename": "take_screenshot_without_background", "output_extension": "png", "include_background": True}], + [ + { + "filename": "take_screenshot_without_background", + "output_extension": "png", + "include_background": True, + } + ], ) - + response = server.get_response() response = server.get_response() blob = server.get_response() @@ -74,16 +102,24 @@ def test_take_screenshot(server): f.write(blob) f.close() first_image_path = os.path.join(server.test_output_dir, "test.png") - second_image_path = os.path.join(server.images_dir_path, "viewer/take_screenshot_without_background.png") + second_image_path = os.path.join( + server.images_dir_path, "viewer/take_screenshot_without_background.png" + ) assert server.images_diff(first_image_path, second_image_path) == 0.0 # Take a screenshot with background png server.call( VtkViewerView.prefix + VtkViewerView.schemas_dict["take_screenshot"]["rpc"], - [{"filename": "take_screenshot_with_background", "output_extension": "png", "include_background": True}], + [ + { + "filename": "take_screenshot_with_background", + "output_extension": "png", + "include_background": True, + } + ], ) - + response = server.get_response() response = server.get_response() blob = server.get_response() @@ -94,6 +130,32 @@ def test_take_screenshot(server): f.write(blob) f.close() first_image_path = os.path.join(server.test_output_dir, "test.png") - second_image_path = os.path.join(server.images_dir_path, "viewer/take_screenshot_with_background.png") + second_image_path = os.path.join( + server.images_dir_path, "viewer/take_screenshot_with_background.png" + ) assert server.images_diff(first_image_path, second_image_path) == 0.0 + + +def test_picked_ids(server): + + test_register_mesh(server) + + server.call( + VtkViewerView.prefix + VtkViewerView.schemas_dict["picked_ids"]["rpc"], + [{"x": 100, "y": 200, "ids": ["123456789"]}], + ) + response = server.get_response() + + print(f"Response: {response}", flush=True) + + assert "result" in response, f"Key 'result' not found in response: {response}" + + assert ( + "array_ids" in response["result"] + ), f"Key 'array_ids' not found in response['result']: {response['result']}" + + array_ids = response["result"]["array_ids"] + assert isinstance(array_ids, list), f"Expected a list, but got {type(array_ids)}" + assert all(isinstance(id, str) for id in array_ids), "All IDs should be strings" + assert len(array_ids) > 0, "The list of array_ids should not be empty" diff --git a/src/tests/tests_output/test.jpeg b/src/tests/tests_output/test.jpeg index ee7e056..6b2509f 100644 Binary files a/src/tests/tests_output/test.jpeg and b/src/tests/tests_output/test.jpeg differ