diff --git a/requirements.txt b/requirements.txt index 1f2a8398..cceba64b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -66,4 +66,3 @@ werkzeug==3.1.8 # flask # flask-cors -opengeodeweb-microservice==1.*,>=1.2.2 diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index 4c02b6b5..aaa18e19 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -257,23 +257,42 @@ def extract_valid_attribute_values( attribute_name: str, item_index: int, ) -> tuple[list[float], bool]: - attribute_ids_matching_name = attribute_manager.attribute_ids_matching_name( - attribute_name - ) - if not isinstance(attribute_ids_matching_name, list): - return [], False - component_attribute = attribute_manager.find_generic_attribute( - attribute_ids_matching_name[0] - ) - if component_attribute is None: + attribute_ids = attribute_manager.attribute_ids_matching_name(attribute_name) + if not isinstance(attribute_ids, list): return [], False - if not component_attribute.is_genericable(): + attribute = attribute_manager.find_generic_attribute(attribute_ids[0]) + if ( + attribute is None + or not attribute.is_genericable() + or not attribute.properties().transferable + ): return [], False + + nb_items = attribute.nb_items() + default_values = getattr(attribute, "default_values", None) + no_value = default_values().no_value if default_values else None + if no_value is None and ( + "Point" in attribute.type() or "Vector" in attribute.type() + ): + no_value = [0.0] * nb_items + + value_getter = getattr( + attribute, + "value", + lambda element_index: [ + attribute.generic_item_value(element_index, i) for i in range(nb_items) + ], + ) + valid_values: list[float] = [] has_nan = False for element_index in range(attribute_manager.nb_elements()): - value = component_attribute.generic_item_value(element_index, item_index) - if value is None or math.isnan(value): + value = attribute.generic_item_value(element_index, item_index) + if ( + value is None + or math.isnan(value) + or (no_value is not None and value_getter(element_index) == no_value) + ): has_nan = True else: valid_values.append(value) @@ -293,7 +312,7 @@ def attributes_metadata( attribute_name = attribute.name() if attribute_name is None: continue - if not attribute.is_genericable(): + if not attribute.is_genericable() or not attribute.properties().transferable: continue nb_items = attribute.nb_items() min_values, max_values = [], [] diff --git a/tests/test_routes.py b/tests/test_routes.py index 7178dd15..fc04c25e 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -2,6 +2,7 @@ import os # Third party imports +import opengeode as og from werkzeug.datastructures import FileStorage from flask.testing import FlaskClient from werkzeug.test import TestResponse @@ -13,6 +14,7 @@ from opengeodeweb_microservice.database.data import Data from opengeodeweb_microservice.database.connection import get_session from opengeodeweb_back import geode_functions, test_utils +from opengeodeweb_back.routes.blueprint_routes import extract_valid_attribute_values from opengeodeweb_back.geode_objects.geode_polygonal_surface3d import ( GeodePolygonalSurface3D, ) @@ -744,3 +746,47 @@ def get_full_data() -> test_utils.JsonData: return {"id": model_id, "component_ids": block_ids} test_utils.test_route_wrong_params(client, route, get_full_data) + + +def test_extract_valid_attribute_values_with_sentinel_no_value() -> None: + mesh = og.TriangulatedSurface3D.create() + builder = og.TriangulatedSurfaceBuilder3D.create(mesh) + vertex_0 = builder.create_point(og.Point3D([0, 0, 0])) + vertex_1 = builder.create_point(og.Point3D([1, 0, 0])) + builder.create_triangle([vertex_0, vertex_1, vertex_0]) + + attribute_manager = mesh.vertex_attribute_manager() + values_config = og.AttributeValuesDouble() + values_config.no_value = -999.0 + attribute_id = attribute_manager.create_attribute_variable_double( + "variable_double", values_config, og.AttributeProperties() + ) + attribute = attribute_manager.find_attribute_variable_double(attribute_id) + attribute.set_value(vertex_0, -999.0) + attribute.set_value(vertex_1, 42.0) + + valid_values, has_nan = extract_valid_attribute_values( + attribute_manager, "variable_double", 0 + ) + assert has_nan is True + assert valid_values == [42.0] + + +def test_extract_valid_attribute_values_with_non_transferable_attribute() -> None: + mesh = og.TriangulatedSurface3D.create() + builder = og.TriangulatedSurfaceBuilder3D.create(mesh) + vertex_0 = builder.create_point(og.Point3D([0, 0, 0])) + builder.create_triangle([vertex_0, vertex_0, vertex_0]) + + attribute_manager = mesh.vertex_attribute_manager() + properties = og.AttributeProperties() + properties.transferable = False + attribute_manager.create_attribute_variable_double( + "internal_attribute", og.AttributeValuesDouble(), properties + ) + + valid_values, has_nan = extract_valid_attribute_values( + attribute_manager, "internal_attribute", 0 + ) + assert has_nan is False + assert valid_values == []