diff --git a/.vscode/settings.json b/.vscode/settings.json index 006e581e..95f9b2e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,6 @@ "--config-file=mypy.ini" ], "mypy-type-checker.interpreter": [ - "${workspaceFolder}/venv/bin/python" + "${workspaceFolder}/../../.venv/bin/python" ] } \ No newline at end of file diff --git a/opengeodeweb_back_schemas.json b/opengeodeweb_back_schemas.json index 928d482b..939f3206 100644 --- a/opengeodeweb_back_schemas.json +++ b/opengeodeweb_back_schemas.json @@ -213,7 +213,7 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 }, @@ -223,7 +223,7 @@ } }, "required": [ - "input_geode_object", + "geode_object_type", "filename" ], "additionalProperties": false @@ -283,7 +283,7 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 }, @@ -293,7 +293,7 @@ } }, "required": [ - "input_geode_object", + "geode_object_type", "filename" ], "additionalProperties": false @@ -321,14 +321,14 @@ "type": "string", "minLength": 1 }, - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 } }, "required": [ "filename", - "input_geode_object" + "geode_object_type" ], "additionalProperties": false }, @@ -351,13 +351,13 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 } }, "required": [ - "input_geode_object" + "geode_object_type" ], "additionalProperties": false }, @@ -369,7 +369,7 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 }, @@ -379,7 +379,7 @@ } }, "required": [ - "input_geode_object", + "geode_object_type", "filename" ], "additionalProperties": false @@ -406,6 +406,24 @@ ], "additionalProperties": false }, + "cell_attribute_names": { + "$id": "opengeodeweb_back/cell_attribute_names", + "route": "/cell_attribute_names", + "methods": [ + "POST" + ], + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id" + ], + "additionalProperties": false + }, "allowed_objects": { "$id": "opengeodeweb_back/allowed_objects", "route": "/allowed_objects", @@ -417,17 +435,10 @@ "filename": { "type": "string", "minLength": 1 - }, - "supported_feature": { - "type": [ - "string", - "null" - ] } }, "required": [ - "filename", - "supported_feature" + "filename" ], "additionalProperties": false }, @@ -438,17 +449,8 @@ "POST" ], "type": "object", - "properties": { - "supported_feature": { - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "supported_feature" - ], + "properties": {}, + "required": [], "additionalProperties": false } } diff --git a/requirements.in b/requirements.in index 77597074..1f319112 100644 --- a/requirements.in +++ b/requirements.in @@ -1,7 +1,7 @@ -opengeode-core==15.30.2 +opengeode-core==15.30.4 opengeode-io==7.4.6 opengeode-inspector==6.8.6 -opengeode-geosciences==9.5.4 +opengeode-geosciences==9.5.5 opengeode-geosciencesio==5.8.6 geode-common==33.11.3 geode-viewables==3.3.2 diff --git a/requirements.txt b/requirements.txt index a28132c8..08d64bdf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,7 +31,7 @@ markupsafe>=3 # flask # jinja2 # werkzeug -opengeode-core==15.30.2 +opengeode-core==15.30.4 # via # -r requirements.in # geode-common @@ -40,7 +40,7 @@ opengeode-core==15.30.2 # opengeode-geosciencesio # opengeode-inspector # opengeode-io -opengeode-geosciences==9.5.4 +opengeode-geosciences==9.5.5 # via # -r requirements.in # geode-viewables @@ -60,4 +60,3 @@ werkzeug==3.1.2 # flask # flask-cors -opengeodeweb-microservice==1.*,>=1.0.9 diff --git a/src/opengeodeweb_back/app.py b/src/opengeodeweb_back/app.py index e5175124..bf167398 100644 --- a/src/opengeodeweb_back/app.py +++ b/src/opengeodeweb_back/app.py @@ -2,7 +2,6 @@ import argparse import os -import time from typing import Any import flask import flask_cors # type: ignore @@ -36,8 +35,13 @@ @app.before_request -def before_request() -> None: +def before_request() -> flask.Response | None: + if flask.request.method == "OPTIONS": + response = flask.make_response() + response.headers["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE,OPTIONS" + return response utils_functions.before_request(flask.current_app) + return None @app.teardown_request @@ -68,13 +72,14 @@ def teardown_request(exception: BaseException | None) -> None: @app.errorhandler(HTTPException) -def errorhandler(e: HTTPException) -> tuple[dict[str, Any], int] | Response: - return utils_functions.handle_exception(e) +def errorhandler(exception: HTTPException) -> tuple[dict[str, Any], int] | Response: + return utils_functions.handle_exception(exception) @app.errorhandler(Exception) -def handle_generic_exception(e: Exception) -> Response: - return flask.make_response({"error": str(e)}, 500) +def handle_generic_exception(exception: Exception) -> Response: + print("\033[91mError:\033[0m \033[91m" + str(exception) + "\033[0m", flush=True) + return flask.make_response({"description": str(exception)}, 500) @app.route( diff --git a/src/opengeodeweb_back/geode_functions.py b/src/opengeodeweb_back/geode_functions.py index c9e8fbbd..afcd2e1c 100644 --- a/src/opengeodeweb_back/geode_functions.py +++ b/src/opengeodeweb_back/geode_functions.py @@ -2,75 +2,47 @@ import os # Third party imports -import opengeode_geosciences as og_gs # type: ignore -import opengeode as og # type: ignore import werkzeug import flask -from typing import Any # Local application imports -from .geode_objects import geode_objects_dict -from . import utils_functions +from .geode_objects import geode_objects +from .geode_objects.types import ( + GeodeObjectType, + geode_object_type, +) +from .geode_objects.geode_object import GeodeObject from opengeodeweb_microservice.database.data import Data -from opengeodeweb_microservice.database.connection import get_session -def geode_object_value(geode_object: str): - return geode_objects_dict()[geode_object] - - -def geode_object_class(geode_object: str): - return geode_object_value(geode_object)["class"] - - -def input_factory(geode_object: str): - return geode_object_value(geode_object)["input_factory"] - - -def output_factory(geode_object: str): - return geode_object_value(geode_object)["output_factory"] - - -def additional_files(geode_object: str, file_absolute_path: str): - return geode_object_value(geode_object)["additional_files"](file_absolute_path) - - -def is_loadable(geode_object: str, file_absolute_path: str) -> float: - percentage = geode_object_value(geode_object)["is_loadable"](file_absolute_path) - return percentage.value() - - -def object_priority(geode_object: str, file_absolute_path: str) -> int: - return geode_object_value(geode_object)["object_priority"](file_absolute_path) - +def data_file_path(data_id: str, filename: str | None = None) -> str: + data_folder_path = flask.current_app.config["DATA_FOLDER_PATH"] + data_path = os.path.join(data_folder_path, data_id) + if filename is not None: + return os.path.join(data_path, filename) + return data_path -def load(geode_object: str, file_absolute_path: str): - return geode_object_value(geode_object)["load"](file_absolute_path) +def geode_object_from_string(value: str) -> type[GeodeObject]: + return geode_objects[geode_object_type(value)] -def data_file_path(data_id: str, filename: str = "") -> str: - data_folder_path = flask.current_app.config["DATA_FOLDER_PATH"] - if filename: - return os.path.join(data_folder_path, data_id, filename) - return os.path.join(data_folder_path, data_id) - -def load_data(data_id: str) -> Any: - data_entry = Data.get(data_id) - if not data_entry: +def load_geode_object(data_id: str) -> GeodeObject: + data = Data.get(data_id) + if not data: flask.abort(404, f"Data with id {data_id} not found") - file_absolute_path = data_file_path(data_id, data_entry.native_file_name) + file_absolute_path = data_file_path(data_id, data.native_file) print("Loading file: ", file_absolute_path) print("File exists: ", os.path.exists(file_absolute_path)) - return load(data_entry.geode_object, file_absolute_path) + return geode_object_from_string(data.geode_object).load(file_absolute_path) def get_data_info(data_id: str) -> Data: - data_entry = Data.get(data_id) - if not data_entry: + data = Data.get(data_id) + if not data: flask.abort(404, f"Data with id {data_id} not found") - return data_entry + return data def upload_file_path(filename: str) -> str: @@ -79,237 +51,26 @@ def upload_file_path(filename: str) -> str: return os.path.abspath(os.path.join(upload_folder, secure_filename)) -def is_saveable(geode_object: str, data, filename: str): - return geode_object_value(geode_object)["is_saveable"](data, filename) - - -def save(geode_object: str, data, folder_absolute_path: str, filename: str): - return geode_object_value(geode_object)["save"]( - data, os.path.join(folder_absolute_path, filename) - ) - - -def create_builder(geode_object: str, data): - return geode_object_value(geode_object)["builder"](data) - - -def assign_crs(geode_object: str, data, crs_name: str, info): - builder = create_builder(geode_object, data) - geode_object_value(geode_object)["crs"]["assign"](data, builder, crs_name, info) - - -def convert_crs(geode_object: str, data, crs_name: str, info): - builder = create_builder(geode_object, data) - geode_object_value(geode_object)["crs"]["convert"](data, builder, crs_name, info) - - -def create_crs( - geode_object: str, - data, - name: str, - input_coordiante_system, - output_coordiante_system, -): - builder = create_builder(geode_object, data) - geode_object_value(geode_object)["crs"]["create"]( - data, builder, name, input_coordiante_system, output_coordiante_system - ) - - -def get_object_type(geode_object: str): - return geode_object_value(geode_object)["object_type"] - - -def get_elements(geode_object: str): - return geode_object_value(geode_object)["elements"] - - -def is_3D(geode_object: str): - return geode_object_value(geode_object)["is_3D"] - - -def is_viewable(geode_object: str): - return geode_object_value(geode_object)["is_viewable"] - - -def inspect(geode_object: str, data): - return geode_object_value(geode_object)["inspector"](data) - - -def save_viewable(geode_object: str, data, folder_absolute_path: str, id: str): - return geode_object_value(geode_object)["save_viewable"]( - data, os.path.join(folder_absolute_path, id) - ) - - -def save_light_viewable(geode_object: str, data, folder_absolute_path: str, id: str): - return geode_object_value(geode_object)["save_light_viewable"]( - data, os.path.join(folder_absolute_path, id) - ) - - -def geode_object_input_extensions(geode_object: str): - geode_object_input_list_creators = input_factory(geode_object).list_creators() - geode_object_input_list_creators.sort() - return geode_object_input_list_creators - - -def geode_object_output_extensions(geode_object: str): - geode_object_output_list_creators = output_factory(geode_object).list_creators() - geode_object_output_list_creators.sort() - return geode_object_output_list_creators - - -def filter_geode_objects(key: str = None): - geode_objects_filtered_list = [] - for geode_object, value in geode_objects_dict().items(): - if key != None and key != "": - if key in value: - if type(value[key]) == bool: - geode_objects_filtered_list.append(geode_object) - else: - geode_objects_filtered_list.append(geode_object) - else: - geode_objects_filtered_list.append(geode_object) - geode_objects_filtered_list.sort() - return geode_objects_filtered_list - - -def list_input_extensions(key: str | None = None) -> list[str]: - extensions_list = [] - geode_objects_filtered_list = filter_geode_objects(key) - for geode_object in geode_objects_filtered_list: - extensions_list += geode_object_input_extensions(geode_object) - - extensions_list = list(set(extensions_list)) - extensions_list.sort() - return extensions_list - - -def has_creator(geode_object: str, extension: str): - return input_factory(geode_object).has_creator(extension) - - -def list_geode_objects( - file_absolute_path: str, - key: str | None = None, -): - return_dict = {} - file_extension = utils_functions.extension_from_filename( - os.path.basename(file_absolute_path) - ) - geode_objects_filtered_list = filter_geode_objects(key) - for geode_object in geode_objects_filtered_list: - if has_creator(geode_object, file_extension): - loadability_score = is_loadable(geode_object, file_absolute_path) - priority_score = object_priority(geode_object, file_absolute_path) - return_dict[geode_object] = { - "is_loadable": loadability_score, - "object_priority": priority_score, - } - return return_dict - - -def geode_objects_output_extensions(geode_object: str, data): - geode_objects_output_extensions_dict = {} - output_extensions = geode_object_output_extensions(geode_object) - extensions_dict = {} - for output_extension in output_extensions: - bool_is_saveable = is_saveable(geode_object, data, f"test.{output_extension}") - extensions_dict[output_extension] = {"is_saveable": bool_is_saveable} - geode_objects_output_extensions_dict[geode_object] = extensions_dict - - if "parent" in geode_object_value(geode_object).keys(): - parent_geode_object = geode_object_value(geode_object)["parent"] - geode_objects_output_extensions_dict.update( - geode_objects_output_extensions(parent_geode_object, data) - ) - return geode_objects_output_extensions_dict - - -def get_inspector_children(obj): - new_object = {} - - if "inspection_type" in dir(obj): - new_object["title"] = obj.inspection_type() - new_object["nb_issues"] = 0 - new_object["children"] = [] - for child in dir(obj): - if not child.startswith("__") and not child in [ - "inspection_type", - "string", - ]: - child_instance = obj.__getattribute__(child) - child_object = get_inspector_children(child_instance) - new_object["children"].append(child_object) - new_object["nb_issues"] += child_object["nb_issues"] - else: - new_object["title"] = obj.description() - nb_issues = obj.nb_issues() - new_object["nb_issues"] = nb_issues - if nb_issues > 0: - issues = obj.string().split("\n") - new_object["issues"] = issues - return new_object - - -def geographic_coordinate_systems(geode_object: str): - if is_3D(geode_object): - return og_gs.GeographicCoordinateSystem3D.geographic_coordinate_systems() - else: - return og_gs.GeographicCoordinateSystem2D.geographic_coordinate_systems() - - -def geographic_coordinate_systems_info(geode_object: str, crs): - if is_3D(geode_object): - return og_gs.GeographicCoordinateSystemInfo3D( - crs["authority"], crs["code"], crs["name"] +def geode_object_output_extensions( + geode_object: GeodeObject, +) -> dict[GeodeObjectType, dict[str, bool]]: + results: dict[GeodeObjectType, dict[str, bool]] = {} + for mixin_geode_object in geode_objects[geode_object.geode_object_type()].__mro__: + output_extensions_method = getattr( + mixin_geode_object, "output_extensions", None ) - else: - return og_gs.GeographicCoordinateSystemInfo2D( - crs["authority"], crs["code"], crs["name"] - ) - - -def coordinate_system(geode_object: str, coordinate_system): - return og.CoordinateSystem2D( - [ - og.Vector2D( - og.Point2D( - [coordinate_system["origin_x"], coordinate_system["origin_y"]] - ), - og.Point2D( - [coordinate_system["point_1_x"], coordinate_system["point_1_y"]] - ), - ), - og.Vector2D( - og.Point2D( - [coordinate_system["origin_x"], coordinate_system["origin_y"]] - ), - og.Point2D( - [coordinate_system["point_2_x"], coordinate_system["point_2_y"]] - ), - ), - ], - og.Point2D([coordinate_system["origin_x"], coordinate_system["origin_y"]]), - ) - - -def assign_geographic_coordinate_system_info(geode_object: str, data, input_crs): - info = geographic_coordinate_systems_info(geode_object, input_crs) - assign_crs(geode_object, data, input_crs["name"], info) - - -def convert_geographic_coordinate_system_info(geode_object: str, data, output_crs): - info = geographic_coordinate_systems_info(geode_object, output_crs) - convert_crs(geode_object, data, output_crs["name"], info) - - -def create_coordinate_system( - geode_object: str, data, name, input_coordinate_points, output_coordinate_points -): - input_coordiante_system = coordinate_system(geode_object, input_coordinate_points) - output_coordiante_system = coordinate_system(geode_object, output_coordinate_points) - create_crs( - geode_object, data, name, input_coordiante_system, output_coordiante_system - ) + if output_extensions_method is None: + continue + output_extensions = output_extensions_method.__func__(mixin_geode_object) + if output_extensions is None: + continue + object_output_extensions: dict[str, bool] = {} + is_saveable_method = getattr(mixin_geode_object, "is_saveable") + for output_extension in output_extensions: + bool_is_saveable = is_saveable_method( + geode_object, f"test.{output_extension}" + ) + object_output_extensions[output_extension] = bool_is_saveable + if hasattr(mixin_geode_object, "geode_object_type"): + results[mixin_geode_object.geode_object_type()] = object_output_extensions + return results diff --git a/src/opengeodeweb_back/geode_objects.py b/src/opengeodeweb_back/geode_objects.py deleted file mode 100644 index 596011b3..00000000 --- a/src/opengeodeweb_back/geode_objects.py +++ /dev/null @@ -1,570 +0,0 @@ -# Standard library imports - -# Third party imports -import opengeode as og # type: ignore -import opengeode_io as og_io # type: ignore -import opengeode_inspector as og_inspector # type: ignore -import opengeode_geosciences as og_gs # type: ignore -import opengeode_geosciencesio as og_gs_io # type: ignore -import geode_viewables as g_v # type: ignore - -# Local application imports - -mesh = "mesh" -model = "model" - -points = "points" -edges = "edges" -polygons = "polygons" -polyhedrons = "polyhedrons" - - -def geode_objects_dict(): - return { - "BRep": { - "class": og.BRep, - "input_factory": og.BRepInputFactory, - "output_factory": og.BRepOutputFactory, - "additional_files": og.brep_additional_files, - "is_loadable": og.is_brep_loadable, - "object_priority": og.brep_object_priority, - "load": og.load_brep, - "is_saveable": og.is_brep_saveable, - "save": og.save_brep, - "builder": og.BRepBuilder, - "crs": { - "assign": og_gs.assign_brep_geographic_coordinate_system_info, - "convert": og_gs.convert_brep_coordinate_reference_system, - "create": og.create_brep_coordinate_system, - }, - "object_type": model, - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_brep, - "save_light_viewable": g_v.save_light_viewable_brep, - "inspector": og_inspector.inspect_brep, - }, - "CrossSection": { - "parent": "Section", - "class": og_gs.CrossSection, - "input_factory": og_gs.CrossSectionInputFactory, - "output_factory": og_gs.CrossSectionOutputFactory, - "additional_files": og_gs.cross_section_additional_files, - "is_loadable": og_gs.is_cross_section_loadable, - "object_priority": og_gs.cross_section_object_priority, - "load": og_gs.load_cross_section, - "is_saveable": og_gs.is_cross_section_saveable, - "save": og_gs.save_cross_section, - "builder": og_gs.CrossSectionBuilder, - "crs": { - "assign": og_gs.assign_section_geographic_coordinate_system_info, - "convert": og_gs.convert_section_coordinate_reference_system, - "create": og.create_section_coordinate_system, - }, - "object_type": model, - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_cross_section, - "save_light_viewable": g_v.save_light_viewable_cross_section, - "inspector": og_inspector.inspect_section, - }, - "EdgedCurve2D": { - "class": og.EdgedCurve2D, - "input_factory": og.EdgedCurveInputFactory2D, - "output_factory": og.EdgedCurveOutputFactory2D, - "additional_files": og.edged_curve_additional_files2D, - "is_loadable": og.is_edged_curve_loadable2D, - "object_priority": og.edged_curve_object_priority2D, - "load": og.load_edged_curve2D, - "is_saveable": og.is_edged_curve_saveable2D, - "save": og.save_edged_curve2D, - "builder": og.EdgedCurveBuilder2D.create, - "crs": { - "assign": og_gs.assign_edged_curve_geographic_coordinate_system_info2D, - "convert": og_gs.convert_edged_curve_coordinate_reference_system2D, - "create": og.create_edged_curve_coordinate_system2D, - }, - "object_type": mesh, - "elements": [points, edges], - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_edged_curve2D, - "save_light_viewable": g_v.save_light_viewable_edged_curve2D, - "inspector": og_inspector.inspect_edged_curve2D, - }, - "EdgedCurve3D": { - "class": og.EdgedCurve3D, - "input_factory": og.EdgedCurveInputFactory3D, - "output_factory": og.EdgedCurveOutputFactory3D, - "additional_files": og.edged_curve_additional_files3D, - "is_loadable": og.is_edged_curve_loadable3D, - "object_priority": og.edged_curve_object_priority3D, - "load": og.load_edged_curve3D, - "is_saveable": og.is_edged_curve_saveable3D, - "save": og.save_edged_curve3D, - "builder": og.EdgedCurveBuilder3D.create, - "crs": { - "assign": og_gs.assign_edged_curve_geographic_coordinate_system_info3D, - "convert": og_gs.convert_edged_curve_coordinate_reference_system3D, - "create": og.create_edged_curve_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points, edges], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_edged_curve3D, - "save_light_viewable": g_v.save_light_viewable_edged_curve3D, - "inspector": og_inspector.inspect_edged_curve3D, - }, - "Graph": { - "class": og.Graph, - "input_factory": og.GraphInputFactory, - "output_factory": og.GraphOutputFactory, - "additional_files": og.graph_additional_files, - "is_loadable": og.is_graph_loadable, - "object_priority": og.graph_object_priority, - "load": og.load_graph, - "is_saveable": og.is_graph_saveable, - "save": og.save_graph, - "builder": og.GraphBuilder.create, - "object_type": mesh, - "is_3D": False, - "is_viewable": False, - }, - "HybridSolid3D": { - "class": og.HybridSolid3D, - "input_factory": og.HybridSolidInputFactory3D, - "output_factory": og.HybridSolidOutputFactory3D, - "additional_files": og.hybrid_solid_additional_files3D, - "is_loadable": og.is_hybrid_solid_loadable3D, - "object_priority": og.hybrid_solid_object_priority3D, - "load": og.load_hybrid_solid3D, - "is_saveable": og.is_hybrid_solid_saveable3D, - "save": og.save_hybrid_solid3D, - "builder": og.HybridSolidBuilder3D.create, - "crs": { - "assign": og_gs.assign_solid_mesh_geographic_coordinate_system_info3D, - "convert": og_gs.convert_solid_mesh_coordinate_reference_system3D, - "create": og.create_solid_mesh_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points, polyhedrons], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_hybrid_solid3D, - "save_light_viewable": g_v.save_light_viewable_hybrid_solid3D, - "inspector": og_inspector.inspect_solid3D, - }, - "ImplicitCrossSection": { - "parent": "CrossSection", - "class": og_gs.ImplicitCrossSection, - "input_factory": og_gs.ImplicitCrossSectionInputFactory, - "output_factory": og_gs.ImplicitCrossSectionOutputFactory, - "additional_files": og_gs.implicit_cross_section_additional_files, - "is_loadable": og_gs.is_implicit_cross_section_loadable, - "object_priority": og_gs.implicit_cross_section_object_priority, - "load": og_gs.load_implicit_cross_section, - "is_saveable": og_gs.is_implicit_cross_section_saveable, - "save": og_gs.save_implicit_cross_section, - "builder": og_gs.ImplicitCrossSectionBuilder, - "crs": { - "assign": og_gs.assign_section_geographic_coordinate_system_info, - "convert": og_gs.convert_section_coordinate_reference_system, - "create": og.create_section_coordinate_system, - }, - "object_type": model, - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_implicit_cross_section, - "save_light_viewable": g_v.save_light_viewable_implicit_cross_section, - "inspector": og_inspector.inspect_section, - }, - "ImplicitStructuralModel": { - "parent": "StructuralModel", - "class": og_gs.ImplicitStructuralModel, - "input_factory": og_gs.ImplicitStructuralModelInputFactory, - "output_factory": og_gs.ImplicitStructuralModelOutputFactory, - "additional_files": og_gs.implicit_structural_model_additional_files, - "is_loadable": og_gs.is_implicit_structural_model_loadable, - "object_priority": og_gs.implicit_structural_model_object_priority, - "load": og_gs.load_implicit_structural_model, - "is_saveable": og_gs.is_implicit_structural_model_saveable, - "save": og_gs.save_implicit_structural_model, - "builder": og_gs.ImplicitStructuralModelBuilder, - "crs": { - "assign": og_gs.assign_brep_geographic_coordinate_system_info, - "convert": og_gs.convert_brep_coordinate_reference_system, - "create": og.create_brep_coordinate_system, - }, - "object_type": model, - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_implicit_structural_model, - "save_light_viewable": g_v.save_light_viewable_implicit_structural_model, - "inspector": og_inspector.inspect_brep, - }, - "LightRegularGrid2D": { - "class": og.LightRegularGrid2D, - "input_factory": og.LightRegularGridInputFactory2D, - "output_factory": og.LightRegularGridOutputFactory2D, - "additional_files": og.light_regular_grid_additional_files2D, - "is_loadable": og.is_light_regular_grid_loadable2D, - "object_priority": og.light_regular_grid_object_priority2D, - "load": og.load_light_regular_grid2D, - "is_saveable": og.is_light_regular_grid_saveable2D, - "save": og.save_light_regular_grid2D, - "object_type": mesh, - "elements": [points, polygons], - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_light_regular_grid2D, - "save_light_viewable": g_v.save_light_viewable_light_regular_grid2D, - }, - "LightRegularGrid3D": { - "class": og.LightRegularGrid3D, - "input_factory": og.LightRegularGridInputFactory3D, - "output_factory": og.LightRegularGridOutputFactory3D, - "additional_files": og.light_regular_grid_additional_files3D, - "is_loadable": og.is_light_regular_grid_loadable3D, - "object_priority": og.light_regular_grid_object_priority3D, - "load": og.load_light_regular_grid3D, - "is_saveable": og.is_light_regular_grid_saveable3D, - "save": og.save_light_regular_grid3D, - "object_type": mesh, - "elements": [points, polyhedrons], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_light_regular_grid3D, - "save_light_viewable": g_v.save_light_viewable_light_regular_grid3D, - }, - "PointSet2D": { - "class": og.PointSet2D, - "input_factory": og.PointSetInputFactory2D, - "output_factory": og.PointSetOutputFactory2D, - "additional_files": og.point_set_additional_files2D, - "is_loadable": og.is_point_set_loadable2D, - "object_priority": og.point_set_object_priority2D, - "load": og.load_point_set2D, - "is_saveable": og.is_point_set_saveable2D, - "save": og.save_point_set2D, - "builder": og.PointSetBuilder2D.create, - "crs": { - "assign": og_gs.assign_point_set_geographic_coordinate_system_info2D, - "convert": og_gs.convert_point_set_coordinate_reference_system2D, - "create": og.create_point_set_coordinate_system2D, - }, - "object_type": mesh, - "elements": [points], - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_point_set2D, - "save_light_viewable": g_v.save_light_viewable_point_set2D, - "inspector": og_inspector.inspect_point_set2D, - }, - "PointSet3D": { - "class": og.PointSet3D, - "input_factory": og.PointSetInputFactory3D, - "output_factory": og.PointSetOutputFactory3D, - "additional_files": og.point_set_additional_files3D, - "is_loadable": og.is_point_set_loadable3D, - "object_priority": og.point_set_object_priority3D, - "load": og.load_point_set3D, - "is_saveable": og.is_point_set_saveable3D, - "save": og.save_point_set3D, - "builder": og.PointSetBuilder3D.create, - "crs": { - "assign": og_gs.assign_point_set_geographic_coordinate_system_info3D, - "convert": og_gs.convert_point_set_coordinate_reference_system3D, - "create": og.create_point_set_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_point_set3D, - "save_light_viewable": g_v.save_light_viewable_point_set3D, - "inspector": og_inspector.inspect_point_set3D, - }, - "PolygonalSurface2D": { - "class": og.PolygonalSurface2D, - "input_factory": og.PolygonalSurfaceInputFactory2D, - "output_factory": og.PolygonalSurfaceOutputFactory2D, - "additional_files": og.polygonal_surface_additional_files2D, - "is_loadable": og.is_polygonal_surface_loadable2D, - "object_priority": og.polygonal_surface_object_priority2D, - "load": og.load_polygonal_surface2D, - "is_saveable": og.is_polygonal_surface_saveable2D, - "save": og.save_polygonal_surface2D, - "builder": og.PolygonalSurfaceBuilder2D.create, - "crs": { - "assign": og_gs.assign_surface_mesh_geographic_coordinate_system_info2D, - "convert": og_gs.convert_surface_mesh_coordinate_reference_system2D, - "create": og.create_surface_mesh_coordinate_system2D, - }, - "object_type": mesh, - "elements": [points, polygons], - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_polygonal_surface2D, - "save_light_viewable": g_v.save_light_viewable_polygonal_surface2D, - "inspector": og_inspector.inspect_surface2D, - }, - "PolygonalSurface3D": { - "class": og.PolygonalSurface3D, - "input_factory": og.PolygonalSurfaceInputFactory3D, - "output_factory": og.PolygonalSurfaceOutputFactory3D, - "additional_files": og.polygonal_surface_additional_files3D, - "is_loadable": og.is_polygonal_surface_loadable3D, - "object_priority": og.polygonal_surface_object_priority3D, - "load": og.load_polygonal_surface3D, - "is_saveable": og.is_polygonal_surface_saveable3D, - "save": og.save_polygonal_surface3D, - "builder": og.PolygonalSurfaceBuilder3D.create, - "crs": { - "assign": og_gs.assign_surface_mesh_geographic_coordinate_system_info3D, - "convert": og_gs.convert_surface_mesh_coordinate_reference_system3D, - "create": og.create_surface_mesh_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points, polygons], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_polygonal_surface3D, - "save_light_viewable": g_v.save_light_viewable_polygonal_surface3D, - "inspector": og_inspector.inspect_surface3D, - }, - "PolyhedralSolid3D": { - "class": og.PolyhedralSolid3D, - "input_factory": og.PolyhedralSolidInputFactory3D, - "output_factory": og.PolyhedralSolidOutputFactory3D, - "additional_files": og.polyhedral_solid_additional_files3D, - "is_loadable": og.is_polyhedral_solid_loadable3D, - "object_priority": og.polyhedral_solid_object_priority3D, - "load": og.load_polyhedral_solid3D, - "is_saveable": og.is_polyhedral_solid_saveable3D, - "save": og.save_polyhedral_solid3D, - "builder": og.PolyhedralSolidBuilder3D.create, - "crs": { - "assign": og_gs.assign_solid_mesh_geographic_coordinate_system_info3D, - "convert": og_gs.convert_solid_mesh_coordinate_reference_system3D, - "create": og.create_solid_mesh_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points, polyhedrons], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_polyhedral_solid3D, - "save_light_viewable": g_v.save_light_viewable_polyhedral_solid3D, - "inspector": og_inspector.inspect_solid3D, - }, - "RasterImage2D": { - "class": og.RasterImage2D, - "input_factory": og.RasterImageInputFactory2D, - "output_factory": og.RasterImageOutputFactory2D, - "additional_files": og.raster_image_additional_files2D, - "is_loadable": og.is_raster_image_loadable2D, - "object_priority": og.raster_image_object_priority2D, - "load": og.load_raster_image2D, - "is_saveable": og.is_raster_image_saveable2D, - "save": og.save_raster_image2D, - "object_type": mesh, - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_raster_image2D, - }, - "RasterImage3D": { - "class": og.RasterImage3D, - "input_factory": og.RasterImageInputFactory3D, - "output_factory": og.RasterImageOutputFactory3D, - "additional_files": og.raster_image_additional_files3D, - "is_loadable": og.is_raster_image_loadable3D, - "object_priority": og.raster_image_object_priority3D, - "load": og.load_raster_image3D, - "is_saveable": og.is_raster_image_saveable3D, - "save": og.save_raster_image3D, - "object_type": mesh, - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_raster_image3D, - }, - "RegularGrid2D": { - "class": og.RegularGrid2D, - "input_factory": og.RegularGridInputFactory2D, - "output_factory": og.RegularGridOutputFactory2D, - "additional_files": og.regular_grid_additional_files2D, - "is_loadable": og.is_regular_grid_loadable2D, - "object_priority": og.regular_grid_object_priority2D, - "load": og.load_regular_grid2D, - "is_saveable": og.is_regular_grid_saveable2D, - "save": og.save_regular_grid2D, - "builder": og.RegularGridBuilder2D.create, - "crs": { - "assign": og_gs.assign_surface_mesh_geographic_coordinate_system_info2D, - "convert": og_gs.convert_surface_mesh_coordinate_reference_system2D, - "create": og.create_surface_mesh_coordinate_system2D, - }, - "object_type": mesh, - "elements": [points, polygons], - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_regular_grid2D, - "save_light_viewable": g_v.save_light_viewable_regular_grid2D, - }, - "RegularGrid3D": { - "class": og.RegularGrid3D, - "input_factory": og.RegularGridInputFactory3D, - "output_factory": og.RegularGridOutputFactory3D, - "additional_files": og.regular_grid_additional_files3D, - "is_loadable": og.is_regular_grid_loadable3D, - "object_priority": og.regular_grid_object_priority3D, - "load": og.load_regular_grid3D, - "is_saveable": og.is_regular_grid_saveable3D, - "save": og.save_regular_grid3D, - "builder": og.RegularGridBuilder3D.create, - "crs": { - "assign": og_gs.assign_solid_mesh_geographic_coordinate_system_info3D, - "convert": og_gs.convert_solid_mesh_coordinate_reference_system3D, - "create": og.create_surface_mesh_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points, polyhedrons], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_regular_grid3D, - "save_light_viewable": g_v.save_light_viewable_regular_grid3D, - }, - "Section": { - "class": og.Section, - "input_factory": og.SectionInputFactory, - "output_factory": og.SectionOutputFactory, - "additional_files": og.section_additional_files, - "is_loadable": og.is_section_loadable, - "object_priority": og.section_object_priority, - "load": og.load_section, - "is_saveable": og.is_section_saveable, - "save": og.save_section, - "builder": og.SectionBuilder, - "crs": { - "assign": og_gs.assign_section_geographic_coordinate_system_info, - "convert": og_gs.convert_section_coordinate_reference_system, - "create": og.create_section_coordinate_system, - }, - "object_type": model, - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_section, - "save_light_viewable": g_v.save_light_viewable_section, - "inspector": og_inspector.inspect_section, - }, - "StructuralModel": { - "parent": "BRep", - "class": og_gs.StructuralModel, - "input_factory": og_gs.StructuralModelInputFactory, - "output_factory": og_gs.StructuralModelOutputFactory, - "additional_files": og_gs.structural_model_additional_files, - "is_loadable": og_gs.is_structural_model_loadable, - "object_priority": og_gs.structural_model_object_priority, - "load": og_gs.load_structural_model, - "is_saveable": og_gs.is_structural_model_saveable, - "save": og_gs.save_structural_model, - "builder": og_gs.StructuralModelBuilder, - "crs": { - "assign": og_gs.assign_brep_geographic_coordinate_system_info, - "convert": og_gs.convert_brep_coordinate_reference_system, - "create": og.create_brep_coordinate_system, - }, - "object_type": model, - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_structural_model, - "save_light_viewable": g_v.save_light_viewable_structural_model, - "inspector": og_inspector.inspect_brep, - }, - "TetrahedralSolid3D": { - "class": og.TetrahedralSolid3D, - "input_factory": og.TetrahedralSolidInputFactory3D, - "output_factory": og.TetrahedralSolidOutputFactory3D, - "additional_files": og.tetrahedral_solid_additional_files3D, - "is_loadable": og.is_tetrahedral_solid_loadable3D, - "object_priority": og.tetrahedral_solid_object_priority3D, - "load": og.load_tetrahedral_solid3D, - "is_saveable": og.is_tetrahedral_solid_saveable3D, - "save": og.save_tetrahedral_solid3D, - "builder": og.TetrahedralSolidBuilder3D.create, - "crs": { - "assign": og_gs.assign_solid_mesh_geographic_coordinate_system_info3D, - "convert": og_gs.convert_solid_mesh_coordinate_reference_system3D, - "create": og.create_solid_mesh_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points, polyhedrons], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_tetrahedral_solid3D, - "save_light_viewable": g_v.save_light_viewable_tetrahedral_solid3D, - "inspector": og_inspector.inspect_solid3D, - }, - "TriangulatedSurface2D": { - "class": og.TriangulatedSurface2D, - "input_factory": og.TriangulatedSurfaceInputFactory2D, - "output_factory": og.TriangulatedSurfaceOutputFactory2D, - "additional_files": og.triangulated_surface_additional_files2D, - "is_loadable": og.is_triangulated_surface_loadable2D, - "object_priority": og.triangulated_surface_object_priority2D, - "load": og.load_triangulated_surface2D, - "is_saveable": og.is_triangulated_surface_saveable2D, - "save": og.save_triangulated_surface2D, - "builder": og.TriangulatedSurfaceBuilder2D.create, - "crs": { - "assign": og_gs.assign_surface_mesh_geographic_coordinate_system_info2D, - "convert": og_gs.convert_surface_mesh_coordinate_reference_system2D, - "create": og.create_surface_mesh_coordinate_system2D, - }, - "object_type": mesh, - "elements": [points, polygons], - "is_3D": False, - "is_viewable": True, - "save_viewable": g_v.save_viewable_triangulated_surface2D, - "save_light_viewable": g_v.save_light_viewable_triangulated_surface2D, - "inspector": og_inspector.inspect_surface2D, - }, - "TriangulatedSurface3D": { - "class": og.TriangulatedSurface3D, - "input_factory": og.TriangulatedSurfaceInputFactory3D, - "output_factory": og.TriangulatedSurfaceOutputFactory3D, - "additional_files": og.triangulated_surface_additional_files3D, - "is_loadable": og.is_triangulated_surface_loadable3D, - "object_priority": og.triangulated_surface_object_priority3D, - "load": og.load_triangulated_surface3D, - "is_saveable": og.is_triangulated_surface_saveable3D, - "save": og.save_triangulated_surface3D, - "builder": og.TriangulatedSurfaceBuilder3D.create, - "crs": { - "assign": og_gs.assign_surface_mesh_geographic_coordinate_system_info3D, - "convert": og_gs.convert_surface_mesh_coordinate_reference_system3D, - "create": og.create_surface_mesh_coordinate_system3D, - }, - "object_type": mesh, - "elements": [points, polygons], - "is_3D": True, - "is_viewable": True, - "save_viewable": g_v.save_viewable_triangulated_surface3D, - "save_light_viewable": g_v.save_light_viewable_triangulated_surface3D, - "inspector": og_inspector.inspect_surface3D, - }, - "VertexSet": { - "class": og.VertexSet, - "input_factory": og.VertexSetInputFactory, - "output_factory": og.VertexSetOutputFactory, - "additional_files": og.vertex_set_additional_files, - "is_loadable": og.is_vertex_set_loadable, - "object_priority": og.vertex_set_object_priority, - "load": og.load_vertex_set, - "is_saveable": og.is_vertex_set_saveable, - "save": og.save_vertex_set, - "builder": og.VertexSetBuilder.create, - "object_type": mesh, - "is_3D": False, - "is_viewable": False, - }, - } diff --git a/src/opengeodeweb_back/geode_objects/__init__.py b/src/opengeodeweb_back/geode_objects/__init__.py new file mode 100644 index 00000000..bb32b69d --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/__init__.py @@ -0,0 +1,61 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports + +# Local application imports +from .types import GeodeObjectType +from .geode_object import GeodeObject +from .geode_brep import GeodeBRep +from .geode_vertex_set import GeodeVertexSet +from .geode_graph import GeodeGraph +from .geode_point_set2d import GeodePointSet2D +from .geode_point_set3d import GeodePointSet3D +from .geode_edged_curve2d import GeodeEdgedCurve2D +from .geode_edged_curve3d import GeodeEdgedCurve3D +from .geode_raster_image2d import GeodeRasterImage2D +from .geode_raster_image3d import GeodeRasterImage3D +from .geode_polygonal_surface2d import GeodePolygonalSurface2D +from .geode_polygonal_surface3d import GeodePolygonalSurface3D +from .geode_triangulated_surface2d import GeodeTriangulatedSurface2D +from .geode_triangulated_surface3d import GeodeTriangulatedSurface3D +from .geode_regular_grid2d import GeodeRegularGrid2D +from .geode_polyhedral_solid3d import GeodePolyhedralSolid3D +from .geode_tetrahedral_solid3d import GeodeTetrahedralSolid3D +from .geode_hybrid_solid3d import GeodeHybridSolid3D +from .geode_regular_grid3d import GeodeRegularGrid3D +from .geode_light_regular_grid2d import GeodeLightRegularGrid2D +from .geode_light_regular_grid3d import GeodeLightRegularGrid3D +from .geode_section import GeodeSection +from .geode_structural_model import GeodeStructuralModel +from .geode_cross_section import GeodeCrossSection +from .geode_implicit_structural_model import GeodeImplicitStructuralModel +from .geode_implicit_cross_section import GeodeImplicitCrossSection + +geode_objects: dict[GeodeObjectType, type[GeodeObject]] = { + "VertexSet": GeodeVertexSet, + "Graph": GeodeGraph, + "PointSet2D": GeodePointSet2D, + "PointSet3D": GeodePointSet3D, + "EdgedCurve2D": GeodeEdgedCurve2D, + "EdgedCurve3D": GeodeEdgedCurve3D, + "RasterImage2D": GeodeRasterImage2D, + "RasterImage3D": GeodeRasterImage3D, + "PolygonalSurface2D": GeodePolygonalSurface2D, + "PolygonalSurface3D": GeodePolygonalSurface3D, + "TriangulatedSurface2D": GeodeTriangulatedSurface2D, + "TriangulatedSurface3D": GeodeTriangulatedSurface3D, + "RegularGrid2D": GeodeRegularGrid2D, + "PolyhedralSolid3D": GeodePolyhedralSolid3D, + "TetrahedralSolid3D": GeodeTetrahedralSolid3D, + "HybridSolid3D": GeodeHybridSolid3D, + "RegularGrid3D": GeodeRegularGrid3D, + "LightRegularGrid2D": GeodeLightRegularGrid2D, + "LightRegularGrid3D": GeodeLightRegularGrid3D, + "BRep": GeodeBRep, + "Section": GeodeSection, + "StructuralModel": GeodeStructuralModel, + "CrossSection": GeodeCrossSection, + "ImplicitStructuralModel": GeodeImplicitStructuralModel, + "ImplicitCrossSection": GeodeImplicitCrossSection, +} diff --git a/src/opengeodeweb_back/geode_objects/geode_brep.py b/src/opengeodeweb_back/geode_objects/geode_brep.py new file mode 100644 index 00000000..6637fb9a --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_brep.py @@ -0,0 +1,102 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeModelType +from .geode_model import GeodeModel, ComponentRegistry + + +class GeodeBRep(GeodeModel): + brep: og.BRep + + def __init__(self, brep: og.BRep | None = None) -> None: + self.brep = brep if brep is not None else og.BRep() + super().__init__(self.brep) + + @classmethod + def geode_object_type(cls) -> GeodeModelType: + return "BRep" + + def native_extension(self) -> str: + return self.brep.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return True + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.BRepBuilder: + return og.BRepBuilder(self.brep) + + @classmethod + def load(cls, filename: str) -> GeodeBRep: + return GeodeBRep(og.load_brep(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.brep_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_brep_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.BRepInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.BRepOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.brep_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_brep_saveable(self.brep, filename) + + def save(self, filename: str) -> list[str]: + return og.save_brep(self.brep, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_brep(self.brep, filename_without_extension) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_brep(self.brep, filename_without_extension) + + def mesh_components(self) -> ComponentRegistry: + return self.brep.mesh_components() + + def inspect(self) -> og_inspector.BRepInspectionResult: + return og_inspector.inspect_brep(self.brep) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_brep_geographic_coordinate_system_info( + self.brep, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_brep_coordinate_reference_system( + self.brep, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_brep_coordinate_system(self.brep, builder, crs_name, input, output) diff --git a/src/opengeodeweb_back/geode_objects/geode_cross_section.py b/src/opengeodeweb_back/geode_objects/geode_cross_section.py new file mode 100644 index 00000000..bfeda2c6 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_cross_section.py @@ -0,0 +1,75 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import geode_viewables as viewables + +# Local application imports +from .types import GeodeModelType +from .geode_section import GeodeSection + + +class GeodeCrossSection(GeodeSection): + cross_section: og_geosciences.CrossSection + + def __init__( + self, cross_section: og_geosciences.CrossSection | None = None + ) -> None: + self.cross_section = ( + cross_section + if cross_section is not None + else og_geosciences.CrossSection() + ) + super().__init__(self.cross_section) + + @classmethod + def geode_object_type(cls) -> GeodeModelType: + return "CrossSection" + + def native_extension(self) -> str: + return self.cross_section.native_extension() + + def builder(self) -> og_geosciences.CrossSectionBuilder: + return og_geosciences.CrossSectionBuilder(self.cross_section) + + @classmethod + def load(cls, filename: str) -> GeodeCrossSection: + return GeodeCrossSection(og_geosciences.load_cross_section(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og_geosciences.cross_section_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og_geosciences.is_cross_section_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og_geosciences.CrossSectionInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og_geosciences.CrossSectionOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og_geosciences.cross_section_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og_geosciences.is_cross_section_saveable(self.cross_section, filename) + + def save(self, filename: str) -> list[str]: + return og_geosciences.save_cross_section(self.cross_section, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_cross_section( + self.cross_section, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_cross_section( + self.cross_section, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py b/src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py new file mode 100644 index 00000000..a0124882 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_edged_curve2d.py @@ -0,0 +1,107 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_graph import GeodeGraph + + +class GeodeEdgedCurve2D(GeodeGraph): + edged_curve: og.EdgedCurve2D + + def __init__(self, edged_curve: og.EdgedCurve2D | None = None) -> None: + self.edged_curve = ( + edged_curve if edged_curve is not None else og.EdgedCurve2D.create() + ) + super().__init__(self.edged_curve) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "EdgedCurve2D" + + def native_extension(self) -> str: + return self.edged_curve.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.EdgedCurveBuilder2D: + return og.EdgedCurveBuilder2D.create(self.edged_curve) + + @classmethod + def load(cls, filename: str) -> GeodeEdgedCurve2D: + return GeodeEdgedCurve2D(og.load_edged_curve2D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.edged_curve_additional_files2D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_edged_curve_loadable2D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.EdgedCurveInputFactory2D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.EdgedCurveOutputFactory2D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.edged_curve_object_priority2D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_edged_curve_saveable2D(self.edged_curve, filename) + + def save(self, filename: str) -> list[str]: + return og.save_edged_curve2D(self.edged_curve, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_edged_curve2D( + self.edged_curve, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_edged_curve2D( + self.edged_curve, filename_without_extension + ) + + def inspect(self) -> og_inspector.EdgedCurveInspectionResult: + return og_inspector.inspect_edged_curve2D(self.edged_curve) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_edged_curve_geographic_coordinate_system_info2D( + self.edged_curve, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_edged_curve_coordinate_reference_system2D( + self.edged_curve, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_edged_curve_coordinate_system2D( + self.edged_curve, builder, crs_name, input, output + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_edged_curve3d.py b/src/opengeodeweb_back/geode_objects/geode_edged_curve3d.py new file mode 100644 index 00000000..632da9a7 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_edged_curve3d.py @@ -0,0 +1,107 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_graph import GeodeGraph + + +class GeodeEdgedCurve3D(GeodeGraph): + edged_curve: og.EdgedCurve3D + + def __init__(self, edged_curve: og.EdgedCurve3D | None = None) -> None: + self.edged_curve = ( + edged_curve if edged_curve is not None else og.EdgedCurve3D.create() + ) + super().__init__(self.edged_curve) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "EdgedCurve3D" + + def native_extension(self) -> str: + return self.edged_curve.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return True + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.EdgedCurveBuilder3D: + return og.EdgedCurveBuilder3D.create(self.edged_curve) + + @classmethod + def load(cls, filename: str) -> GeodeEdgedCurve3D: + return GeodeEdgedCurve3D(og.load_edged_curve3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.edged_curve_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_edged_curve_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.EdgedCurveInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.EdgedCurveOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.edged_curve_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_edged_curve_saveable3D(self.edged_curve, filename) + + def save(self, filename: str) -> list[str]: + return og.save_edged_curve3D(self.edged_curve, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_edged_curve3D( + self.edged_curve, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_edged_curve3D( + self.edged_curve, filename_without_extension + ) + + def inspect(self) -> og_inspector.EdgedCurveInspectionResult: + return og_inspector.inspect_edged_curve3D(self.edged_curve) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_edged_curve_geographic_coordinate_system_info3D( + self.edged_curve, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_edged_curve_coordinate_reference_system3D( + self.edged_curve, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_edged_curve_coordinate_system3D( + self.edged_curve, builder, crs_name, input, output + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_graph.py b/src/opengeodeweb_back/geode_objects/geode_graph.py new file mode 100644 index 00000000..cd12775c --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_graph.py @@ -0,0 +1,76 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_vertex_set import GeodeVertexSet + + +class GeodeGraph(GeodeVertexSet): + graph: og.Graph + + def __init__(self, graph: og.Graph | None = None) -> None: + self.graph = graph if graph is not None else og.Graph.create() + super().__init__(self.graph) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "Graph" + + def native_extension(self) -> str: + return self.graph.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return False + + def builder(self) -> og.GraphBuilder: + return og.GraphBuilder.create(self.graph) + + @classmethod + def load(cls, filename: str) -> GeodeGraph: + return GeodeGraph(og.load_graph(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.graph_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_graph_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.GraphInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.GraphOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.graph_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_graph_saveable(self.graph, filename) + + def save(self, filename: str) -> list[str]: + return og.save_graph(self.graph, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return "" + + def save_light_viewable(self, filename_without_extension: str) -> str: + return "" + + def inspect(self) -> object: + return None diff --git a/src/opengeodeweb_back/geode_objects/geode_grid2d.py b/src/opengeodeweb_back/geode_objects/geode_grid2d.py new file mode 100644 index 00000000..75cb1a0d --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_grid2d.py @@ -0,0 +1,30 @@ +# Standard library imports +from __future__ import annotations +from abc import abstractmethod + +# Third party imports +import opengeode as og +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_mesh import GeodeMesh + + +class GeodeGrid2D(GeodeMesh): + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> object: + return None + + def inspect(self) -> object: + return None + + @abstractmethod + def cell_attribute_manager(self) -> og.AttributeManager: ... diff --git a/src/opengeodeweb_back/geode_objects/geode_grid3d.py b/src/opengeodeweb_back/geode_objects/geode_grid3d.py new file mode 100644 index 00000000..bf4a441a --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_grid3d.py @@ -0,0 +1,30 @@ +# Standard library imports +from __future__ import annotations +from abc import abstractmethod + +# Third party imports +import opengeode as og +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_mesh import GeodeMesh + + +class GeodeGrid3D(GeodeMesh): + @classmethod + def is_3D(cls) -> bool: + return True + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> object: + return None + + def inspect(self) -> object: + return None + + @abstractmethod + def cell_attribute_manager(self) -> og.AttributeManager: ... diff --git a/src/opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py b/src/opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py new file mode 100644 index 00000000..8a22d9be --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_hybrid_solid3d.py @@ -0,0 +1,71 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_solid_mesh3d import GeodeSolidMesh3D + + +class GeodeHybridSolid3D(GeodeSolidMesh3D): + hybrid_solid: og.HybridSolid3D + + def __init__(self, hybrid_solid: og.HybridSolid3D | None = None) -> None: + self.hybrid_solid = ( + hybrid_solid if hybrid_solid is not None else og.HybridSolid3D.create() + ) + super().__init__(self.hybrid_solid) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "HybridSolid3D" + + def native_extension(self) -> str: + return self.hybrid_solid.native_extension() + + def builder(self) -> og.HybridSolidBuilder3D: + return og.HybridSolidBuilder3D.create(self.hybrid_solid) + + @classmethod + def load(cls, filename: str) -> GeodeHybridSolid3D: + return GeodeHybridSolid3D(og.load_hybrid_solid3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.hybrid_solid_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_hybrid_solid_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.HybridSolidInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.HybridSolidOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.hybrid_solid_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_hybrid_solid_saveable3D(self.hybrid_solid, filename) + + def save(self, filename: str) -> list[str]: + return og.save_hybrid_solid3D(self.hybrid_solid, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_hybrid_solid3D( + self.hybrid_solid, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_hybrid_solid3D( + self.hybrid_solid, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_implicit_cross_section.py b/src/opengeodeweb_back/geode_objects/geode_implicit_cross_section.py new file mode 100644 index 00000000..8f9a4e41 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_implicit_cross_section.py @@ -0,0 +1,81 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import geode_viewables as viewables + +# Local application imports +from .types import GeodeModelType +from .geode_cross_section import GeodeCrossSection + + +class GeodeImplicitCrossSection(GeodeCrossSection): + implicit_cross_section: og_geosciences.ImplicitCrossSection + + def __init__( + self, implicit_cross_section: og_geosciences.ImplicitCrossSection | None = None + ) -> None: + self.implicit_cross_section = ( + implicit_cross_section + if implicit_cross_section is not None + else og_geosciences.ImplicitCrossSection() + ) + super().__init__(self.implicit_cross_section) + + @classmethod + def geode_object_type(cls) -> GeodeModelType: + return "ImplicitCrossSection" + + def native_extension(self) -> str: + return self.implicit_cross_section.native_extension() + + def builder(self) -> og_geosciences.ImplicitCrossSectionBuilder: + return og_geosciences.ImplicitCrossSectionBuilder(self.implicit_cross_section) + + @classmethod + def load(cls, filename: str) -> GeodeImplicitCrossSection: + return GeodeImplicitCrossSection( + og_geosciences.load_implicit_cross_section(filename) + ) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og_geosciences.implicit_cross_section_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og_geosciences.is_implicit_cross_section_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og_geosciences.ImplicitCrossSectionInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og_geosciences.ImplicitCrossSectionOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og_geosciences.implicit_cross_section_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og_geosciences.is_implicit_cross_section_saveable( + self.implicit_cross_section, filename + ) + + def save(self, filename: str) -> list[str]: + return og_geosciences.save_implicit_cross_section( + self.implicit_cross_section, filename + ) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_implicit_cross_section( + self.implicit_cross_section, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_implicit_cross_section( + self.implicit_cross_section, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_implicit_structural_model.py b/src/opengeodeweb_back/geode_objects/geode_implicit_structural_model.py new file mode 100644 index 00000000..0df5a5b6 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_implicit_structural_model.py @@ -0,0 +1,85 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeModelType +from .geode_structural_model import GeodeStructuralModel + + +class GeodeImplicitStructuralModel(GeodeStructuralModel): + implicit_structural_model: og_geosciences.ImplicitStructuralModel + + def __init__( + self, + implicit_structural_model: og_geosciences.ImplicitStructuralModel | None = None, + ) -> None: + self.implicit_structural_model = ( + implicit_structural_model + if implicit_structural_model is not None + else og_geosciences.ImplicitStructuralModel() + ) + super().__init__(self.implicit_structural_model) + + @classmethod + def geode_object_type(cls) -> GeodeModelType: + return "ImplicitStructuralModel" + + def native_extension(self) -> str: + return self.implicit_structural_model.native_extension() + + def builder(self) -> og_geosciences.ImplicitStructuralModelBuilder: + return og_geosciences.ImplicitStructuralModelBuilder( + self.implicit_structural_model + ) + + @classmethod + def load(cls, filename: str) -> GeodeImplicitStructuralModel: + return GeodeImplicitStructuralModel( + og_geosciences.load_implicit_structural_model(filename) + ) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og_geosciences.implicit_structural_model_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og_geosciences.is_implicit_structural_model_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og_geosciences.ImplicitStructuralModelInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og_geosciences.ImplicitStructuralModelOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og_geosciences.implicit_structural_model_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og_geosciences.is_implicit_structural_model_saveable( + self.implicit_structural_model, filename + ) + + def save(self, filename: str) -> list[str]: + return og_geosciences.save_implicit_structural_model( + self.implicit_structural_model, filename + ) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_implicit_structural_model( + self.implicit_structural_model, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_implicit_structural_model( + self.implicit_structural_model, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py b/src/opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py new file mode 100644 index 00000000..5a345707 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_light_regular_grid2d.py @@ -0,0 +1,72 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_grid2d import GeodeGrid2D + + +class GeodeLightRegularGrid2D(GeodeGrid2D): + light_regular_grid: og.LightRegularGrid2D + + def __init__(self, light_regular_grid: og.LightRegularGrid2D) -> None: + self.light_regular_grid = light_regular_grid + super().__init__(self.light_regular_grid) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "LightRegularGrid2D" + + def native_extension(self) -> str: + return self.light_regular_grid.native_extension() + + @classmethod + def load(cls, filename: str) -> GeodeLightRegularGrid2D: + return GeodeLightRegularGrid2D(og.load_light_regular_grid2D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.light_regular_grid_additional_files2D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_light_regular_grid_loadable2D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.LightRegularGridInputFactory2D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.LightRegularGridOutputFactory2D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.light_regular_grid_object_priority2D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_light_regular_grid_saveable2D(self.light_regular_grid, filename) + + def save(self, filename: str) -> list[str]: + return og.save_light_regular_grid2D(self.light_regular_grid, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_light_regular_grid2D( + self.light_regular_grid, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_light_regular_grid2D( + self.light_regular_grid, filename_without_extension + ) + + def vertex_attribute_manager(self) -> og.AttributeManager: + return self.light_regular_grid.grid_vertex_attribute_manager() + + def cell_attribute_manager(self) -> og.AttributeManager: + return self.light_regular_grid.cell_attribute_manager() diff --git a/src/opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py b/src/opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py new file mode 100644 index 00000000..e342907b --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_light_regular_grid3d.py @@ -0,0 +1,72 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_grid3d import GeodeGrid3D + + +class GeodeLightRegularGrid3D(GeodeGrid3D): + light_regular_grid: og.LightRegularGrid3D + + def __init__(self, light_regular_grid: og.LightRegularGrid3D) -> None: + self.light_regular_grid = light_regular_grid + super().__init__(self.light_regular_grid) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "LightRegularGrid3D" + + def native_extension(self) -> str: + return self.light_regular_grid.native_extension() + + @classmethod + def load(cls, filename: str) -> GeodeLightRegularGrid3D: + return GeodeLightRegularGrid3D(og.load_light_regular_grid3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.light_regular_grid_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_light_regular_grid_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.LightRegularGridInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.LightRegularGridOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.light_regular_grid_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_light_regular_grid_saveable3D(self.light_regular_grid, filename) + + def save(self, filename: str) -> list[str]: + return og.save_light_regular_grid3D(self.light_regular_grid, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_light_regular_grid3D( + self.light_regular_grid, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_light_regular_grid3D( + self.light_regular_grid, filename_without_extension + ) + + def vertex_attribute_manager(self) -> og.AttributeManager: + return self.light_regular_grid.grid_vertex_attribute_manager() + + def cell_attribute_manager(self) -> og.AttributeManager: + return self.light_regular_grid.cell_attribute_manager() diff --git a/src/opengeodeweb_back/geode_objects/geode_mesh.py b/src/opengeodeweb_back/geode_objects/geode_mesh.py new file mode 100644 index 00000000..6680eca4 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_mesh.py @@ -0,0 +1,19 @@ +# Standard library imports +from __future__ import annotations +from abc import abstractmethod + +# Third party imports +import opengeode as og + +# Local application imports +from .types import GeodeObjectType, ViewerType +from .geode_object import GeodeObject + + +class GeodeMesh(GeodeObject): + @classmethod + def viewer_type(cls) -> ViewerType: + return "mesh" + + @abstractmethod + def vertex_attribute_manager(self) -> og.AttributeManager: ... diff --git a/src/opengeodeweb_back/geode_objects/geode_model.py b/src/opengeodeweb_back/geode_objects/geode_model.py new file mode 100644 index 00000000..8715c8f3 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_model.py @@ -0,0 +1,22 @@ +# Standard library imports +from __future__ import annotations +from abc import ABC, abstractmethod +from typing import Literal, Any, get_args, cast + +# Third party imports +import opengeode as og + +# Local application imports +from .types import GeodeObjectType, GeodeModelType, ViewerType +from .geode_object import GeodeObject + +ComponentRegistry = dict[og.ComponentType, list[og.uuid]] + + +class GeodeModel(GeodeObject): + @classmethod + def viewer_type(cls) -> ViewerType: + return "model" + + @abstractmethod + def mesh_components(self) -> ComponentRegistry: ... diff --git a/src/opengeodeweb_back/geode_objects/geode_object.py b/src/opengeodeweb_back/geode_objects/geode_object.py new file mode 100644 index 00000000..8c44ef45 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_object.py @@ -0,0 +1,78 @@ +# Standard library imports +from __future__ import annotations +from abc import ABC, abstractmethod +from typing import Literal, Any, get_args, cast + +# Third party imports +import opengeode as og + +# Local application imports +from .types import GeodeObjectType, ViewerType + + +class GeodeObject(ABC): + identifier: og.Identifier + + def __init__(self, identifier: og.Identifier | None = None) -> None: + self.identifier = identifier if identifier is not None else og.Identifier() + + @classmethod + @abstractmethod + def geode_object_type(cls) -> GeodeObjectType: ... + + @classmethod + @abstractmethod + def viewer_type(cls) -> ViewerType: ... + + @classmethod + @abstractmethod + def is_3D(cls) -> bool: ... + + @classmethod + @abstractmethod + def is_viewable(cls) -> bool: ... + + @abstractmethod + def builder(self) -> Any: ... + + @classmethod + @abstractmethod + def is_loadable(cls, filename: str) -> og.Percentage: ... + + @classmethod + @abstractmethod + def load(cls, filename: str) -> GeodeObject: ... + + @classmethod + @abstractmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: ... + + @abstractmethod + def native_extension(cls) -> str: ... + + @classmethod + @abstractmethod + def input_extensions(cls) -> list[str]: ... + + @classmethod + @abstractmethod + def object_priority(cls, filename: str) -> int: ... + + @classmethod + @abstractmethod + def output_extensions(cls) -> list[str]: ... + + @abstractmethod + def is_saveable(self, filename: str) -> bool: ... + + @abstractmethod + def save(self, filename: str) -> list[str]: ... + + @abstractmethod + def save_viewable(self, filename_without_extension: str) -> str: ... + + @abstractmethod + def save_light_viewable(self, filename_without_extension: str) -> str: ... + + @abstractmethod + def inspect(self) -> Any: ... diff --git a/src/opengeodeweb_back/geode_objects/geode_point_set2d.py b/src/opengeodeweb_back/geode_objects/geode_point_set2d.py new file mode 100644 index 00000000..2a35b97a --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_point_set2d.py @@ -0,0 +1,105 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_vertex_set import GeodeVertexSet + + +class GeodePointSet2D(GeodeVertexSet): + point_set: og.PointSet2D + + def __init__(self, point_set: og.PointSet2D | None = None) -> None: + self.point_set = point_set if point_set is not None else og.PointSet2D.create() + super().__init__(self.point_set) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "PointSet2D" + + def native_extension(self) -> str: + return self.point_set.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.PointSetBuilder2D: + return og.PointSetBuilder2D.create(self.point_set) + + @classmethod + def load(cls, filename: str) -> GeodePointSet2D: + return GeodePointSet2D(og.load_point_set2D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.point_set_additional_files2D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_point_set_loadable2D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.PointSetInputFactory2D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.PointSetOutputFactory2D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.point_set_object_priority2D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_point_set_saveable2D(self.point_set, filename) + + def save(self, filename: str) -> list[str]: + return og.save_point_set2D(self.point_set, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_point_set2D( + self.point_set, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_point_set2D( + self.point_set, filename_without_extension + ) + + def inspect(self) -> og_inspector.PointSetInspectionResult: + return og_inspector.inspect_point_set2D(self.point_set) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_point_set_geographic_coordinate_system_info2D( + self.point_set, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_point_set_coordinate_reference_system2D( + self.point_set, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_point_set_coordinate_system2D( + self.point_set, builder, crs_name, input, output + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_point_set3d.py b/src/opengeodeweb_back/geode_objects/geode_point_set3d.py new file mode 100644 index 00000000..1c490b59 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_point_set3d.py @@ -0,0 +1,105 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_vertex_set import GeodeVertexSet + + +class GeodePointSet3D(GeodeVertexSet): + point_set: og.PointSet3D + + def __init__(self, point_set: og.PointSet3D | None = None) -> None: + self.point_set = point_set if point_set is not None else og.PointSet3D.create() + super().__init__(self.point_set) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "PointSet3D" + + def native_extension(self) -> str: + return self.point_set.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return True + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.PointSetBuilder3D: + return og.PointSetBuilder3D.create(self.point_set) + + @classmethod + def load(cls, filename: str) -> GeodePointSet3D: + return GeodePointSet3D(og.load_point_set3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.point_set_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_point_set_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.PointSetInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.PointSetOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.point_set_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_point_set_saveable3D(self.point_set, filename) + + def save(self, filename: str) -> list[str]: + return og.save_point_set3D(self.point_set, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_point_set3D( + self.point_set, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_point_set3D( + self.point_set, filename_without_extension + ) + + def inspect(self) -> og_inspector.PointSetInspectionResult: + return og_inspector.inspect_point_set3D(self.point_set) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_point_set_geographic_coordinate_system_info3D( + self.point_set, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_point_set_coordinate_reference_system3D( + self.point_set, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_point_set_coordinate_system3D( + self.point_set, builder, crs_name, input, output + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py b/src/opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py new file mode 100644 index 00000000..6ff30f5a --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_polygonal_surface2d.py @@ -0,0 +1,73 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_surface_mesh2d import GeodeSurfaceMesh2D + + +class GeodePolygonalSurface2D(GeodeSurfaceMesh2D): + polygonal_surface: og.PolygonalSurface2D + + def __init__(self, polygonal_surface: og.PolygonalSurface2D | None = None) -> None: + self.polygonal_surface = ( + polygonal_surface + if polygonal_surface is not None + else og.PolygonalSurface2D.create() + ) + super().__init__(self.polygonal_surface) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "PolygonalSurface2D" + + def native_extension(self) -> str: + return self.polygonal_surface.native_extension() + + def builder(self) -> og.PolygonalSurfaceBuilder2D: + return og.PolygonalSurfaceBuilder2D.create(self.polygonal_surface) + + @classmethod + def load(cls, filename: str) -> GeodePolygonalSurface2D: + return GeodePolygonalSurface2D(og.load_polygonal_surface2D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.polygonal_surface_additional_files2D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_polygonal_surface_loadable2D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.PolygonalSurfaceInputFactory2D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.PolygonalSurfaceOutputFactory2D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.polygonal_surface_object_priority2D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_polygonal_surface_saveable2D(self.polygonal_surface, filename) + + def save(self, filename: str) -> list[str]: + return og.save_polygonal_surface2D(self.polygonal_surface, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_polygonal_surface2D( + self.polygonal_surface, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_polygonal_surface2D( + self.polygonal_surface, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py b/src/opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py new file mode 100644 index 00000000..48438d53 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_polygonal_surface3d.py @@ -0,0 +1,73 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_surface_mesh3d import GeodeSurfaceMesh3D + + +class GeodePolygonalSurface3D(GeodeSurfaceMesh3D): + polygonal_surface: og.PolygonalSurface3D + + def __init__(self, polygonal_surface: og.PolygonalSurface3D | None = None) -> None: + self.polygonal_surface = ( + polygonal_surface + if polygonal_surface is not None + else og.PolygonalSurface3D.create() + ) + super().__init__(self.polygonal_surface) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "PolygonalSurface3D" + + def native_extension(self) -> str: + return self.polygonal_surface.native_extension() + + def builder(self) -> og.PolygonalSurfaceBuilder3D: + return og.PolygonalSurfaceBuilder3D.create(self.polygonal_surface) + + @classmethod + def load(cls, filename: str) -> GeodePolygonalSurface3D: + return GeodePolygonalSurface3D(og.load_polygonal_surface3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.polygonal_surface_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_polygonal_surface_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.PolygonalSurfaceInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.PolygonalSurfaceOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.polygonal_surface_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_polygonal_surface_saveable3D(self.polygonal_surface, filename) + + def save(self, filename: str) -> list[str]: + return og.save_polygonal_surface3D(self.polygonal_surface, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_polygonal_surface3D( + self.polygonal_surface, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_polygonal_surface3D( + self.polygonal_surface, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py b/src/opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py new file mode 100644 index 00000000..140dd324 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_polyhedral_solid3d.py @@ -0,0 +1,73 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_solid_mesh3d import GeodeSolidMesh3D + + +class GeodePolyhedralSolid3D(GeodeSolidMesh3D): + polyhedral_solid: og.PolyhedralSolid3D + + def __init__(self, polyhedral_solid: og.PolyhedralSolid3D | None = None) -> None: + self.polyhedral_solid = ( + polyhedral_solid + if polyhedral_solid is not None + else og.PolyhedralSolid3D.create() + ) + super().__init__(self.polyhedral_solid) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "PolyhedralSolid3D" + + def native_extension(self) -> str: + return self.polyhedral_solid.native_extension() + + def builder(self) -> og.PolyhedralSolidBuilder3D: + return og.PolyhedralSolidBuilder3D.create(self.polyhedral_solid) + + @classmethod + def load(cls, filename: str) -> GeodePolyhedralSolid3D: + return GeodePolyhedralSolid3D(og.load_polyhedral_solid3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.polyhedral_solid_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_polyhedral_solid_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.PolyhedralSolidInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.PolyhedralSolidOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.polyhedral_solid_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_polyhedral_solid_saveable3D(self.polyhedral_solid, filename) + + def save(self, filename: str) -> list[str]: + return og.save_polyhedral_solid3D(self.polyhedral_solid, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_polyhedral_solid3D( + self.polyhedral_solid, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_polyhedral_solid3D( + self.polyhedral_solid, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_raster_image2d.py b/src/opengeodeweb_back/geode_objects/geode_raster_image2d.py new file mode 100644 index 00000000..1cd24e52 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_raster_image2d.py @@ -0,0 +1,83 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_mesh import GeodeMesh + + +class GeodeRasterImage2D(GeodeMesh): + raster_image: og.RasterImage2D + + def __init__(self, raster_image: og.RasterImage2D) -> None: + self.raster_image = raster_image + super().__init__(self.raster_image) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "RasterImage2D" + + def native_extension(self) -> str: + return self.raster_image.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> None: + return None + + @classmethod + def load(cls, filename: str) -> GeodeRasterImage2D: + return GeodeRasterImage2D(og.load_raster_image2D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.raster_image_additional_files2D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_raster_image_loadable2D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.RasterImageInputFactory2D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.RasterImageOutputFactory2D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.raster_image_object_priority2D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_raster_image_saveable2D(self.raster_image, filename) + + def save(self, filename: str) -> list[str]: + return og.save_raster_image2D(self.raster_image, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_raster_image2D( + self.raster_image, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_raster_image2D( + self.raster_image, filename_without_extension + ) + + def inspect(self) -> None: + return None + + def vertex_attribute_manager(self) -> og.AttributeManager: + return og.AttributeManager() diff --git a/src/opengeodeweb_back/geode_objects/geode_raster_image3d.py b/src/opengeodeweb_back/geode_objects/geode_raster_image3d.py new file mode 100644 index 00000000..6aef8db7 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_raster_image3d.py @@ -0,0 +1,83 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_mesh import GeodeMesh + + +class GeodeRasterImage3D(GeodeMesh): + raster_image: og.RasterImage3D + + def __init__(self, raster_image: og.RasterImage3D) -> None: + self.raster_image = raster_image + super().__init__(self.raster_image) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "RasterImage3D" + + def native_extension(self) -> str: + return self.raster_image.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return True + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> None: + return None + + @classmethod + def load(cls, filename: str) -> GeodeRasterImage3D: + return GeodeRasterImage3D(og.load_raster_image3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.raster_image_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_raster_image_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.RasterImageInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.RasterImageOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.raster_image_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_raster_image_saveable3D(self.raster_image, filename) + + def save(self, filename: str) -> list[str]: + return og.save_raster_image3D(self.raster_image, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_raster_image3D( + self.raster_image, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_raster_image3D( + self.raster_image, filename_without_extension + ) + + def inspect(self) -> None: + return None + + def vertex_attribute_manager(self) -> og.AttributeManager: + return og.AttributeManager() diff --git a/src/opengeodeweb_back/geode_objects/geode_regular_grid2d.py b/src/opengeodeweb_back/geode_objects/geode_regular_grid2d.py new file mode 100644 index 00000000..7fa45e07 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_regular_grid2d.py @@ -0,0 +1,78 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_surface_mesh2d import GeodeSurfaceMesh2D +from .geode_grid2d import GeodeGrid2D + + +class GeodeRegularGrid2D(GeodeSurfaceMesh2D, GeodeGrid2D): + regular_grid: og.RegularGrid2D + + def __init__(self, regular_grid: og.RegularGrid2D | None = None) -> None: + self.regular_grid = ( + regular_grid if regular_grid is not None else og.RegularGrid2D.create() + ) + super().__init__(self.regular_grid) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "RegularGrid2D" + + def inspect(self) -> og_inspector.SurfaceInspectionResult: + return super().inspect() + + def native_extension(self) -> str: + return self.regular_grid.native_extension() + + def builder(self) -> og.RegularGridBuilder2D: + return og.RegularGridBuilder2D.create(self.regular_grid) + + @classmethod + def load(cls, filename: str) -> GeodeRegularGrid2D: + return GeodeRegularGrid2D(og.load_regular_grid2D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.regular_grid_additional_files2D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_regular_grid_loadable2D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.RegularGridInputFactory2D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.RegularGridOutputFactory2D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.regular_grid_object_priority2D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_regular_grid_saveable2D(self.regular_grid, filename) + + def save(self, filename: str) -> list[str]: + return og.save_regular_grid2D(self.regular_grid, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_regular_grid2D( + self.regular_grid, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_regular_grid2D( + self.regular_grid, filename_without_extension + ) + + def cell_attribute_manager(self) -> og.AttributeManager: + return self.regular_grid.cell_attribute_manager() diff --git a/src/opengeodeweb_back/geode_objects/geode_regular_grid3d.py b/src/opengeodeweb_back/geode_objects/geode_regular_grid3d.py new file mode 100644 index 00000000..8f158ca4 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_regular_grid3d.py @@ -0,0 +1,78 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_solid_mesh3d import GeodeSolidMesh3D +from .geode_grid3d import GeodeGrid3D + + +class GeodeRegularGrid3D(GeodeSolidMesh3D, GeodeGrid3D): + regular_grid: og.RegularGrid3D + + def __init__(self, regular_grid: og.RegularGrid3D | None = None) -> None: + self.regular_grid = ( + regular_grid if regular_grid is not None else og.RegularGrid3D.create() + ) + super().__init__(self.regular_grid) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "RegularGrid3D" + + def native_extension(self) -> str: + return self.regular_grid.native_extension() + + def builder(self) -> og.RegularGridBuilder3D: + return og.RegularGridBuilder3D.create(self.regular_grid) + + @classmethod + def load(cls, filename: str) -> GeodeRegularGrid3D: + return GeodeRegularGrid3D(og.load_regular_grid3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.regular_grid_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_regular_grid_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.RegularGridInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.RegularGridOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.regular_grid_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_regular_grid_saveable3D(self.regular_grid, filename) + + def save(self, filename: str) -> list[str]: + return og.save_regular_grid3D(self.regular_grid, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_regular_grid3D( + self.regular_grid, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_regular_grid3D( + self.regular_grid, filename_without_extension + ) + + def cell_attribute_manager(self) -> og.AttributeManager: + return self.regular_grid.cell_attribute_manager() + + def inspect(self) -> og_inspector.SolidInspectionResult: + return super().inspect() diff --git a/src/opengeodeweb_back/geode_objects/geode_section.py b/src/opengeodeweb_back/geode_objects/geode_section.py new file mode 100644 index 00000000..a848673b --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_section.py @@ -0,0 +1,106 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeModelType, ViewerType +from .geode_model import GeodeModel, ComponentRegistry + + +class GeodeSection(GeodeModel): + section: og.Section + + def __init__(self, section: og.Section | None = None) -> None: + self.section = section if section is not None else og.Section() + super().__init__(self.section) + + @classmethod + def geode_object_type(cls) -> GeodeModelType: + return "Section" + + def native_extension(self) -> str: + return self.section.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.SectionBuilder: + return og.SectionBuilder(self.section) + + @classmethod + def load(cls, filename: str) -> GeodeSection: + return GeodeSection(og.load_section(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.section_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_section_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.SectionInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.SectionOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.section_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_section_saveable(self.section, filename) + + def save(self, filename: str) -> list[str]: + return og.save_section(self.section, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_section(self.section, filename_without_extension) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_section( + self.section, filename_without_extension + ) + + def mesh_components(self) -> ComponentRegistry: + return self.section.mesh_components() + + def inspect(self) -> og_inspector.SectionInspectionResult: + return og_inspector.inspect_section(self.section) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_section_geographic_coordinate_system_info( + self.section, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_section_coordinate_reference_system( + self.section, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_section_coordinate_system( + self.section, builder, crs_name, input, output + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_solid_mesh3d.py b/src/opengeodeweb_back/geode_objects/geode_solid_mesh3d.py new file mode 100644 index 00000000..7842bcc8 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_solid_mesh3d.py @@ -0,0 +1,61 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_vertex_set import GeodeVertexSet + + +class GeodeSolidMesh3D(GeodeVertexSet): + solid_mesh: og.SolidMesh3D + + def __init__(self, solid_mesh: og.SolidMesh3D | None = None) -> None: + self.solid_mesh = solid_mesh if solid_mesh is not None else og.SolidMesh3D() + super().__init__(self.solid_mesh) + + @classmethod + def is_3D(cls) -> bool: + return True + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.SolidMeshBuilder3D: + return og.SolidMeshBuilder3D.create(self.solid_mesh) + + def inspect(self) -> og_inspector.SolidInspectionResult: + return og_inspector.inspect_solid3D(self.solid_mesh) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_solid_mesh_geographic_coordinate_system_info3D( + self.solid_mesh, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_solid_mesh_coordinate_reference_system3D( + self.solid_mesh, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_solid_mesh_coordinate_system3D( + self.solid_mesh, builder, crs_name, input, output + ) + + def polyhedron_attribute_manager(self) -> og.AttributeManager: + return self.solid_mesh.polyhedron_attribute_manager() diff --git a/src/opengeodeweb_back/geode_objects/geode_structural_model.py b/src/opengeodeweb_back/geode_objects/geode_structural_model.py new file mode 100644 index 00000000..74b28fc8 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_structural_model.py @@ -0,0 +1,78 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeModelType +from .geode_brep import GeodeBRep + + +class GeodeStructuralModel(GeodeBRep): + structural_model: og_geosciences.StructuralModel + + def __init__( + self, structural_model: og_geosciences.StructuralModel | None = None + ) -> None: + self.structural_model = ( + structural_model + if structural_model is not None + else og_geosciences.StructuralModel() + ) + super().__init__(self.structural_model) + + @classmethod + def geode_object_type(cls) -> GeodeModelType: + return "StructuralModel" + + def native_extension(self) -> str: + return self.structural_model.native_extension() + + def builder(self) -> og_geosciences.StructuralModelBuilder: + return og_geosciences.StructuralModelBuilder(self.structural_model) + + @classmethod + def load(cls, filename: str) -> GeodeStructuralModel: + return GeodeStructuralModel(og_geosciences.load_structural_model(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og_geosciences.structural_model_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og_geosciences.is_structural_model_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og_geosciences.StructuralModelInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og_geosciences.StructuralModelOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og_geosciences.structural_model_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og_geosciences.is_structural_model_saveable( + self.structural_model, filename + ) + + def save(self, filename: str) -> list[str]: + return og_geosciences.save_structural_model(self.structural_model, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_structural_model( + self.structural_model, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_structural_model( + self.structural_model, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_surface_mesh2d.py b/src/opengeodeweb_back/geode_objects/geode_surface_mesh2d.py new file mode 100644 index 00000000..dd49135e --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_surface_mesh2d.py @@ -0,0 +1,66 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_vertex_set import GeodeVertexSet + + +class GeodeSurfaceMesh2D(GeodeVertexSet): + surface_mesh: og.SurfaceMesh2D + + def __init__(self, surface_mesh: og.SurfaceMesh2D | None = None) -> None: + self.surface_mesh = ( + surface_mesh if surface_mesh is not None else og.SurfaceMesh2D.create() + ) + super().__init__(self.surface_mesh) + + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.SurfaceMeshBuilder2D: + return og.SurfaceMeshBuilder2D.create(self.surface_mesh) + + def inspect(self) -> og_inspector.SurfaceInspectionResult: + return og_inspector.inspect_surface2D(self.surface_mesh) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_surface_mesh_geographic_coordinate_system_info2D( + self.surface_mesh, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_surface_mesh_coordinate_reference_system2D( + self.surface_mesh, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_surface_mesh_coordinate_system2D( + self.surface_mesh, builder, crs_name, input, output + ) + + def polygon_attribute_manager(self) -> og.AttributeManager: + return self.surface_mesh.polygon_attribute_manager() + + def texture_manager(self) -> og.TextureManager2D: + return self.surface_mesh.texture_manager() diff --git a/src/opengeodeweb_back/geode_objects/geode_surface_mesh3d.py b/src/opengeodeweb_back/geode_objects/geode_surface_mesh3d.py new file mode 100644 index 00000000..d064f885 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_surface_mesh3d.py @@ -0,0 +1,66 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_geosciences as og_geosciences +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_vertex_set import GeodeVertexSet + + +class GeodeSurfaceMesh3D(GeodeVertexSet): + surface_mesh: og.SurfaceMesh3D + + def __init__(self, surface_mesh: og.SurfaceMesh3D | None = None) -> None: + self.surface_mesh = ( + surface_mesh if surface_mesh is not None else og.SurfaceMesh3D.create() + ) + super().__init__(self.surface_mesh) + + @classmethod + def is_3D(cls) -> bool: + return True + + @classmethod + def is_viewable(cls) -> bool: + return True + + def builder(self) -> og.SurfaceMeshBuilder3D: + return og.SurfaceMeshBuilder3D.create(self.surface_mesh) + + def inspect(self) -> og_inspector.SurfaceInspectionResult: + return og_inspector.inspect_surface3D(self.surface_mesh) + + def assign_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.assign_surface_mesh_geographic_coordinate_system_info3D( + self.surface_mesh, builder, crs_name, info + ) + + def convert_crs( + self, crs_name: str, info: og_geosciences.GeographicCoordinateSystemInfo + ) -> None: + builder = self.builder() + og_geosciences.convert_surface_mesh_coordinate_reference_system3D( + self.surface_mesh, builder, crs_name, info + ) + + def create_crs( + self, crs_name: str, input: og.CoordinateSystem2D, output: og.CoordinateSystem2D + ) -> None: + builder = self.builder() + og.create_surface_mesh_coordinate_system3D( + self.surface_mesh, builder, crs_name, input, output + ) + + def polygon_attribute_manager(self) -> og.AttributeManager: + return self.surface_mesh.polygon_attribute_manager() + + def texture_manager(self) -> og.TextureManager2D: + return self.surface_mesh.texture_manager() diff --git a/src/opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py b/src/opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py new file mode 100644 index 00000000..d1b95f02 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_tetrahedral_solid3d.py @@ -0,0 +1,73 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_solid_mesh3d import GeodeSolidMesh3D + + +class GeodeTetrahedralSolid3D(GeodeSolidMesh3D): + tetrahedral_solid: og.TetrahedralSolid3D + + def __init__(self, tetrahedral_solid: og.TetrahedralSolid3D | None = None) -> None: + self.tetrahedral_solid = ( + tetrahedral_solid + if tetrahedral_solid is not None + else og.TetrahedralSolid3D.create() + ) + super().__init__(self.tetrahedral_solid) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "TetrahedralSolid3D" + + def native_extension(self) -> str: + return self.tetrahedral_solid.native_extension() + + def builder(self) -> og.TetrahedralSolidBuilder3D: + return og.TetrahedralSolidBuilder3D.create(self.tetrahedral_solid) + + @classmethod + def load(cls, filename: str) -> GeodeTetrahedralSolid3D: + return GeodeTetrahedralSolid3D(og.load_tetrahedral_solid3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.tetrahedral_solid_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_tetrahedral_solid_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.TetrahedralSolidInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.TetrahedralSolidOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.tetrahedral_solid_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_tetrahedral_solid_saveable3D(self.tetrahedral_solid, filename) + + def save(self, filename: str) -> list[str]: + return og.save_tetrahedral_solid3D(self.tetrahedral_solid, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_tetrahedral_solid3D( + self.tetrahedral_solid, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_tetrahedral_solid3D( + self.tetrahedral_solid, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py b/src/opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py new file mode 100644 index 00000000..8363cd38 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_triangulated_surface2d.py @@ -0,0 +1,77 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_surface_mesh2d import GeodeSurfaceMesh2D + + +class GeodeTriangulatedSurface2D(GeodeSurfaceMesh2D): + triangulated_surface: og.TriangulatedSurface2D + + def __init__( + self, triangulated_surface: og.TriangulatedSurface2D | None = None + ) -> None: + self.triangulated_surface = ( + triangulated_surface + if triangulated_surface is not None + else og.TriangulatedSurface2D.create() + ) + super().__init__(self.triangulated_surface) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "TriangulatedSurface2D" + + def native_extension(self) -> str: + return self.triangulated_surface.native_extension() + + def builder(self) -> og.TriangulatedSurfaceBuilder2D: + return og.TriangulatedSurfaceBuilder2D.create(self.triangulated_surface) + + @classmethod + def load(cls, filename: str) -> GeodeTriangulatedSurface2D: + return GeodeTriangulatedSurface2D(og.load_triangulated_surface2D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.triangulated_surface_additional_files2D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_triangulated_surface_loadable2D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.TriangulatedSurfaceInputFactory2D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.TriangulatedSurfaceOutputFactory2D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.triangulated_surface_object_priority2D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_triangulated_surface_saveable2D( + self.triangulated_surface, filename + ) + + def save(self, filename: str) -> list[str]: + return og.save_triangulated_surface2D(self.triangulated_surface, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_triangulated_surface2D( + self.triangulated_surface, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_triangulated_surface2D( + self.triangulated_surface, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py b/src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py new file mode 100644 index 00000000..b34d6487 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_triangulated_surface3d.py @@ -0,0 +1,77 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType +from .geode_surface_mesh3d import GeodeSurfaceMesh3D + + +class GeodeTriangulatedSurface3D(GeodeSurfaceMesh3D): + triangulated_surface: og.TriangulatedSurface3D + + def __init__( + self, triangulated_surface: og.TriangulatedSurface3D | None = None + ) -> None: + self.triangulated_surface = ( + triangulated_surface + if triangulated_surface is not None + else og.TriangulatedSurface3D.create() + ) + super().__init__(self.triangulated_surface) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "TriangulatedSurface3D" + + def native_extension(self) -> str: + return self.triangulated_surface.native_extension() + + def builder(self) -> og.TriangulatedSurfaceBuilder3D: + return og.TriangulatedSurfaceBuilder3D.create(self.triangulated_surface) + + @classmethod + def load(cls, filename: str) -> GeodeTriangulatedSurface3D: + return GeodeTriangulatedSurface3D(og.load_triangulated_surface3D(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.triangulated_surface_additional_files3D(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_triangulated_surface_loadable3D(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.TriangulatedSurfaceInputFactory3D.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.TriangulatedSurfaceOutputFactory3D.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.triangulated_surface_object_priority3D(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_triangulated_surface_saveable3D( + self.triangulated_surface, filename + ) + + def save(self, filename: str) -> list[str]: + return og.save_triangulated_surface3D(self.triangulated_surface, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return viewables.save_viewable_triangulated_surface3D( + self.triangulated_surface, filename_without_extension + ) + + def save_light_viewable(self, filename_without_extension: str) -> str: + return viewables.save_light_viewable_triangulated_surface3D( + self.triangulated_surface, filename_without_extension + ) diff --git a/src/opengeodeweb_back/geode_objects/geode_vertex_set.py b/src/opengeodeweb_back/geode_objects/geode_vertex_set.py new file mode 100644 index 00000000..0cf9028f --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/geode_vertex_set.py @@ -0,0 +1,81 @@ +# Standard library imports +from __future__ import annotations + +# Third party imports +import opengeode as og +import opengeode_inspector as og_inspector +import geode_viewables as viewables + +# Local application imports +from .types import GeodeMeshType, ViewerType +from .geode_mesh import GeodeMesh + + +class GeodeVertexSet(GeodeMesh): + vertex_set: og.VertexSet + + def __init__(self, vertex_set: og.VertexSet | None = None) -> None: + self.vertex_set = ( + vertex_set if vertex_set is not None else og.VertexSet.create() + ) + super().__init__(self.vertex_set) + + @classmethod + def geode_object_type(cls) -> GeodeMeshType: + return "VertexSet" + + def native_extension(self) -> str: + return self.vertex_set.native_extension() + + @classmethod + def is_3D(cls) -> bool: + return False + + @classmethod + def is_viewable(cls) -> bool: + return False + + def builder(self) -> og.VertexSetBuilder: + return og.VertexSetBuilder.create(self.vertex_set) + + @classmethod + def load(cls, filename: str) -> GeodeVertexSet: + return GeodeVertexSet(og.load_vertex_set(filename)) + + @classmethod + def additional_files(cls, filename: str) -> og.AdditionalFiles: + return og.vertex_set_additional_files(filename) + + @classmethod + def is_loadable(cls, filename: str) -> og.Percentage: + return og.is_vertex_set_loadable(filename) + + @classmethod + def input_extensions(cls) -> list[str]: + return og.VertexSetInputFactory.list_creators() + + @classmethod + def output_extensions(cls) -> list[str]: + return og.VertexSetOutputFactory.list_creators() + + @classmethod + def object_priority(cls, filename: str) -> int: + return og.vertex_set_object_priority(filename) + + def is_saveable(self, filename: str) -> bool: + return og.is_vertex_set_saveable(self.vertex_set, filename) + + def save(self, filename: str) -> list[str]: + return og.save_vertex_set(self.vertex_set, filename) + + def save_viewable(self, filename_without_extension: str) -> str: + return "" + + def save_light_viewable(self, filename_without_extension: str) -> str: + return "" + + def inspect(self) -> object: + return None + + def vertex_attribute_manager(self) -> og.AttributeManager: + return self.vertex_set.vertex_attribute_manager() diff --git a/src/opengeodeweb_back/geode_objects/types.py b/src/opengeodeweb_back/geode_objects/types.py new file mode 100644 index 00000000..57fbccf6 --- /dev/null +++ b/src/opengeodeweb_back/geode_objects/types.py @@ -0,0 +1,75 @@ +# Standard library imports +from typing import Literal, get_args, cast + +# Third party imports + +# Local application imports + +GeodePointMeshType = Literal[ + "PointSet2D", + "PointSet3D", +] +GeodeEdgeMeshType = Literal[ + "EdgedCurve2D", + "EdgedCurve3D", +] +GeodePolygonMeshType = Literal[ + "RasterImage2D", + "PolygonalSurface2D", + "PolygonalSurface3D", + "TriangulatedSurface2D", + "TriangulatedSurface3D", + "RegularGrid2D", + "LightRegularGrid2D", +] +GeodePolyhedronMeshType = Literal[ + "RasterImage3D", + "PolyhedralSolid3D", + "TetrahedralSolid3D", + "HybridSolid3D", + "RegularGrid3D", + "LightRegularGrid3D", +] +GeodeMeshType = ( + Literal[ + "VertexSet", + "Graph", + ] + | GeodePointMeshType + | GeodeEdgeMeshType + | GeodePolygonMeshType + | GeodePolyhedronMeshType +) +GeodeModelType = Literal[ + "BRep", + "Section", + "StructuralModel", + "CrossSection", + "ImplicitStructuralModel", + "ImplicitCrossSection", +] +GeodeObjectType = GeodeMeshType | GeodeModelType + + +def _flatten_literal_args(literal: object) -> tuple[str, ...]: + flattened: list[str] = [] + for arg in get_args(literal): + if isinstance(arg, str): + flattened.append(arg) + else: + flattened.extend(_flatten_literal_args(arg)) + return tuple(flattened) + + +GeodeObjectType_values = _flatten_literal_args(GeodeObjectType) + + +def geode_object_type(value: str) -> GeodeObjectType: + if value not in GeodeObjectType_values: + raise ValueError( + f"Invalid GeodeObjectType: {value!r}. Must be one of {GeodeObjectType_values}" + ) + return cast(GeodeObjectType, value) + + +ViewerType = Literal["mesh", "model"] diff --git a/src/opengeodeweb_back/routes/blueprint_routes.py b/src/opengeodeweb_back/routes/blueprint_routes.py index f44788f5..654c1116 100644 --- a/src/opengeodeweb_back/routes/blueprint_routes.py +++ b/src/opengeodeweb_back/routes/blueprint_routes.py @@ -2,20 +2,30 @@ import os import time import shutil +from typing import Any # Third party imports import flask import werkzeug import zipfile +import opengeode_geosciences as og_geosciences from opengeodeweb_microservice.schemas import get_schemas_dict +from opengeodeweb_microservice.database.data import Data +from opengeodeweb_microservice.database.connection import get_session +from opengeodeweb_microservice.database import connection # Local application imports -from opengeodeweb_back import geode_functions, utils_functions from .models import blueprint_models from . import schemas -from opengeodeweb_microservice.database.data import Data -from opengeodeweb_microservice.database.connection import get_session -from opengeodeweb_microservice.database import connection +from opengeodeweb_back import geode_functions, utils_functions +from opengeodeweb_back.geode_objects import geode_objects +from opengeodeweb_back.geode_objects.types import geode_object_type +from opengeodeweb_back.geode_objects.geode_mesh import GeodeMesh +from opengeodeweb_back.geode_objects.geode_grid2d import GeodeGrid2D +from opengeodeweb_back.geode_objects.geode_grid3d import GeodeGrid3D +from opengeodeweb_back.geode_objects.geode_surface_mesh2d import GeodeSurfaceMesh2D +from opengeodeweb_back.geode_objects.geode_surface_mesh3d import GeodeSurfaceMesh3D +from opengeodeweb_back.geode_objects.geode_solid_mesh3d import GeodeSolidMesh3D routes = flask.Blueprint("routes", __name__, url_prefix="/opengeodeweb_back") @@ -35,9 +45,11 @@ ) def allowed_files() -> flask.Response: utils_functions.validate_request(flask.request, schemas_dict["allowed_files"]) - params = schemas.AllowedFiles.from_dict(flask.request.get_json()) - extensions = geode_functions.list_input_extensions(params.supported_feature) - return flask.make_response({"extensions": extensions}, 200) + extensions: set[str] = set() + for geode_object in geode_objects.values(): + for extension in geode_object.input_extensions(): + extensions.add(extension) + return flask.make_response({"extensions": list(extensions)}, 200) @routes.route( @@ -45,9 +57,6 @@ def allowed_files() -> flask.Response: methods=schemas_dict["upload_file"]["methods"], ) def upload_file() -> flask.Response: - if flask.request.method == "OPTIONS": - return flask.make_response({}, 200) - UPLOAD_FOLDER = flask.current_app.config["UPLOAD_FOLDER"] if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER, exist_ok=True) @@ -63,15 +72,24 @@ def upload_file() -> flask.Response: methods=schemas_dict["allowed_objects"]["methods"], ) def allowed_objects() -> flask.Response: - if flask.request.method == "OPTIONS": - return flask.make_response({}, 200) - - utils_functions.validate_request(flask.request, schemas_dict["allowed_objects"]) - params = schemas.AllowedObjects.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["allowed_objects"] + ) + params = schemas.AllowedObjects.from_dict(json_data) file_absolute_path = geode_functions.upload_file_path(params.filename) - allowed_objects = geode_functions.list_geode_objects( - file_absolute_path, params.supported_feature + file_extension = utils_functions.extension_from_filename( + os.path.basename(file_absolute_path) ) + allowed_objects = {} + for geode_object_type, geode_object in geode_objects.items(): + if file_extension not in geode_object.input_extensions(): + continue + loadability_score = geode_object.is_loadable(file_absolute_path) + priority_score = geode_object.object_priority(file_absolute_path) + allowed_objects[geode_object_type] = { + "is_loadable": loadability_score.value(), + "object_priority": priority_score, + } return flask.make_response({"allowed_objects": allowed_objects}, 200) @@ -80,26 +98,25 @@ def allowed_objects() -> flask.Response: methods=schemas_dict["missing_files"]["methods"], ) def missing_files() -> flask.Response: - utils_functions.validate_request(flask.request, schemas_dict["missing_files"]) - params = schemas.MissingFiles.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["missing_files"] + ) + params = schemas.MissingFiles.from_dict(json_data) file_path = geode_functions.upload_file_path(params.filename) - - additional_files = geode_functions.additional_files( - params.input_geode_object, + geode_object = geode_functions.geode_object_from_string(params.geode_object_type) + additional_files = geode_object.additional_files( file_path, ) - has_missing_files = any( file.is_missing for file in additional_files.mandatory_files + additional_files.optional_files ) - mandatory_files = [ os.path.basename(file.filename) for file in additional_files.mandatory_files if file.is_missing ] - additional_files = [ + additional_files_array = [ os.path.basename(file.filename) for file in additional_files.optional_files if file.is_missing @@ -109,7 +126,7 @@ def missing_files() -> flask.Response: { "has_missing_files": has_missing_files, "mandatory_files": mandatory_files, - "additional_files": additional_files, + "additional_files": additional_files_array, }, 200, ) @@ -120,11 +137,16 @@ def missing_files() -> flask.Response: methods=schemas_dict["geographic_coordinate_systems"]["methods"], ) def crs_converter_geographic_coordinate_systems() -> flask.Response: - utils_functions.validate_request( + json_data = utils_functions.validate_request( flask.request, schemas_dict["geographic_coordinate_systems"] ) - params = schemas.GeographicCoordinateSystems.from_dict(flask.request.get_json()) - infos = geode_functions.geographic_coordinate_systems(params.input_geode_object) + params = schemas.GeographicCoordinateSystems.from_dict(json_data) + geode_object = geode_functions.geode_object_from_string(params.geode_object_type) + infos = ( + og_geosciences.GeographicCoordinateSystem3D.geographic_coordinate_systems() + if geode_object.is_3D() + else og_geosciences.GeographicCoordinateSystem2D.geographic_coordinate_systems() + ) crs_list = [] for info in infos: crs = {} @@ -132,7 +154,6 @@ def crs_converter_geographic_coordinate_systems() -> flask.Response: crs["code"] = info.code crs["authority"] = info.authority crs_list.append(crs) - return flask.make_response({"crs_list": crs_list}, 200) @@ -141,31 +162,62 @@ def crs_converter_geographic_coordinate_systems() -> flask.Response: methods=schemas_dict["inspect_file"]["methods"], ) def inspect_file() -> flask.Response: - utils_functions.validate_request(flask.request, schemas_dict["inspect_file"]) - params = schemas.InspectFile.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["inspect_file"] + ) + params = schemas.InspectFile.from_dict(json_data) file_path = geode_functions.upload_file_path(params.filename) - data = geode_functions.load(params.input_geode_object, file_path) - class_inspector = geode_functions.inspect(params.input_geode_object, data) - inspection_result = geode_functions.get_inspector_children(class_inspector) + geode_object = geode_functions.geode_object_from_string( + params.geode_object_type + ).load(file_path) + inspection_data = geode_object.inspect() + inspection_result = extract_inspector_result(inspection_data) return flask.make_response({"inspection_result": inspection_result}, 200) +def extract_inspector_result(inspection_data: Any) -> object: + new_object = {} + + if hasattr(inspection_data, "inspection_type"): + new_object["title"] = inspection_data.inspection_type() + new_object["nb_issues"] = 0 + new_object["children"] = [] + for child in dir(inspection_data): + if child.startswith("__") or child in [ + "inspection_type", + "string", + ]: + continue + child_instance = getattr(inspection_data, child) + child_object = extract_inspector_result(child_instance) + new_object["children"].append(child_object) + if hasattr(child_object, "nb_issues"): + new_object["nb_issues"] += child_object.nb_issues() + else: + new_object["title"] = inspection_data.description() + nb_issues = inspection_data.nb_issues() + new_object["nb_issues"] = nb_issues + if nb_issues > 0: + issues = inspection_data.string().split("\n") + new_object["issues"] = issues + return new_object + + @routes.route( schemas_dict["geode_objects_and_output_extensions"]["route"], methods=schemas_dict["geode_objects_and_output_extensions"]["methods"], ) def geode_objects_and_output_extensions() -> flask.Response: - utils_functions.validate_request( + json_data = utils_functions.validate_request( flask.request, schemas_dict["geode_objects_and_output_extensions"] ) - params = schemas.GeodeObjectsAndOutputExtensions.from_dict(flask.request.get_json()) + params = schemas.GeodeObjectsAndOutputExtensions.from_dict(json_data) file_path = geode_functions.upload_file_path(params.filename) - data = geode_functions.load( - params.input_geode_object, - file_path, - ) + geode_object = geode_functions.geode_object_from_string( + params.geode_object_type + ).load(file_path) geode_objects_and_output_extensions = ( - geode_functions.geode_objects_output_extensions(params.input_geode_object, data) + geode_functions.geode_object_output_extensions(geode_object) ) return flask.make_response( {"geode_objects_and_output_extensions": geode_objects_and_output_extensions}, @@ -178,12 +230,14 @@ def geode_objects_and_output_extensions() -> flask.Response: methods=schemas_dict["save_viewable_file"]["methods"], ) def save_viewable_file() -> flask.Response: - utils_functions.validate_request(flask.request, schemas_dict["save_viewable_file"]) - params = schemas.SaveViewableFile.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["save_viewable_file"] + ) + params = schemas.SaveViewableFile.from_dict(json_data) return flask.make_response( utils_functions.generate_native_viewable_and_light_viewable_from_file( - geode_object=params.input_geode_object, - input_filename=params.filename, + geode_object_type=geode_object_type(params.geode_object_type), + input_file=params.filename, ), 200, ) @@ -194,10 +248,14 @@ def save_viewable_file() -> flask.Response: methods=schemas_dict["texture_coordinates"]["methods"], ) def texture_coordinates() -> flask.Response: - utils_functions.validate_request(flask.request, schemas_dict["texture_coordinates"]) - params = schemas.TextureCoordinates.from_dict(flask.request.get_json()) - data = geode_functions.load_data(params.id) - texture_coordinates = data.texture_manager().texture_names() + json_data = utils_functions.validate_request( + flask.request, schemas_dict["texture_coordinates"] + ) + params = schemas.TextureCoordinates.from_dict(json_data) + geode_object = geode_functions.load_geode_object(params.id) + if not isinstance(geode_object, GeodeSurfaceMesh2D | GeodeSurfaceMesh3D): + flask.abort(400, f"{params.id} is not a GeodeSurfaceMesh") + texture_coordinates = geode_object.texture_manager().texture_names() return flask.make_response({"texture_coordinates": texture_coordinates}, 200) @@ -206,12 +264,14 @@ def texture_coordinates() -> flask.Response: methods=schemas_dict["vertex_attribute_names"]["methods"], ) def vertex_attribute_names() -> flask.Response: - utils_functions.validate_request( + json_data = utils_functions.validate_request( flask.request, schemas_dict["vertex_attribute_names"] ) - params = schemas.VertexAttributeNames.from_dict(flask.request.get_json()) - data = geode_functions.load_data(params.id) - vertex_attribute_names = data.vertex_attribute_manager().attribute_names() + params = schemas.VertexAttributeNames.from_dict(json_data) + geode_object = geode_functions.load_geode_object(params.id) + if not isinstance(geode_object, GeodeMesh): + flask.abort(400, f"{params.id} is not a GeodeMesh") + vertex_attribute_names = geode_object.vertex_attribute_manager().attribute_names() return flask.make_response( { "vertex_attribute_names": vertex_attribute_names, @@ -220,17 +280,40 @@ def vertex_attribute_names() -> flask.Response: ) +@routes.route( + schemas_dict["cell_attribute_names"]["route"], + methods=schemas_dict["cell_attribute_names"]["methods"], +) +def cell_attribute_names() -> flask.Response: + json_data = utils_functions.validate_request( + flask.request, schemas_dict["cell_attribute_names"] + ) + params = schemas.PolygonAttributeNames.from_dict(json_data) + geode_object = geode_functions.load_geode_object(params.id) + if not isinstance(geode_object, GeodeGrid2D | GeodeGrid3D): + flask.abort(400, f"{params.id} is not a GeodeGrid") + cell_attribute_names = geode_object.cell_attribute_manager().attribute_names() + return flask.make_response( + { + "cell_attribute_names": cell_attribute_names, + }, + 200, + ) + + @routes.route( schemas_dict["polygon_attribute_names"]["route"], methods=schemas_dict["polygon_attribute_names"]["methods"], ) def polygon_attribute_names() -> flask.Response: - utils_functions.validate_request( + json_data = utils_functions.validate_request( flask.request, schemas_dict["polygon_attribute_names"] ) - params = schemas.PolygonAttributeNames.from_dict(flask.request.get_json()) - data = geode_functions.load_data(params.id) - polygon_attribute_names = data.polygon_attribute_manager().attribute_names() + params = schemas.PolygonAttributeNames.from_dict(json_data) + geode_object = geode_functions.load_geode_object(params.id) + if not isinstance(geode_object, GeodeSurfaceMesh2D | GeodeSurfaceMesh3D): + flask.abort(400, f"{params.id} is not a GeodeSurfaceMesh") + polygon_attribute_names = geode_object.polygon_attribute_manager().attribute_names() return flask.make_response( { "polygon_attribute_names": polygon_attribute_names, @@ -244,12 +327,16 @@ def polygon_attribute_names() -> flask.Response: methods=schemas_dict["polyhedron_attribute_names"]["methods"], ) def polyhedron_attribute_names() -> flask.Response: - utils_functions.validate_request( + json_data = utils_functions.validate_request( flask.request, schemas_dict["polyhedron_attribute_names"] ) - params = schemas.PolyhedronAttributeNames.from_dict(flask.request.get_json()) - data = geode_functions.load_data(params.id) - polyhedron_attribute_names = data.polyhedron_attribute_manager().attribute_names() + params = schemas.PolyhedronAttributeNames.from_dict(json_data) + geode_object = geode_functions.load_geode_object(params.id) + if not isinstance(geode_object, GeodeSolidMesh3D): + flask.abort(400, f"{params.id} is not a GeodeSolidMesh") + polyhedron_attribute_names = ( + geode_object.polyhedron_attribute_manager().attribute_names() + ) return flask.make_response( { "polyhedron_attribute_names": polyhedron_attribute_names, @@ -280,8 +367,10 @@ def kill() -> flask.Response: methods=schemas_dict["export_project"]["methods"], ) def export_project() -> flask.Response: - utils_functions.validate_request(flask.request, schemas_dict["export_project"]) - params = schemas.ExportProject.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["export_project"] + ) + params = schemas.ExportProject.from_dict(json_data) project_folder: str = flask.current_app.config["DATA_FOLDER_PATH"] os.makedirs(project_folder, exist_ok=True) @@ -382,25 +471,27 @@ def import_project() -> flask.Response: rows = session.query(Data).all() with get_session() as session: - for data_entry in rows: - data_path = geode_functions.data_file_path(data_entry.id) - viewable_name = data_entry.viewable_file_name + for data in rows: + data_path = geode_functions.data_file_path(data.id) + viewable_name = data.viewable_file if viewable_name: - vpath = geode_functions.data_file_path(data_entry.id, viewable_name) + vpath = geode_functions.data_file_path(data.id, viewable_name) if os.path.isfile(vpath): continue - input_file = str(data_entry.input_file or "") + input_file = str(data.input_file or "") if not input_file: continue - input_full = geode_functions.data_file_path(data_entry.id, input_file) + input_full = geode_functions.data_file_path(data.id, input_file) if not os.path.isfile(input_full): continue - data_object = geode_functions.load(data_entry.geode_object, input_full) + geode_object = geode_functions.geode_object_from_string( + data.geode_object + ).load(input_full) utils_functions.save_all_viewables_and_return_info( - data_entry.geode_object, data_object, data_entry, data_path + geode_object, data, data_path ) session.commit() diff --git a/src/opengeodeweb_back/routes/create/blueprint_create.py b/src/opengeodeweb_back/routes/create/blueprint_create.py index 10766664..b788f261 100644 --- a/src/opengeodeweb_back/routes/create/blueprint_create.py +++ b/src/opengeodeweb_back/routes/create/blueprint_create.py @@ -9,6 +9,8 @@ # Local application imports from opengeodeweb_back import geode_functions, utils_functions from . import schemas +from opengeodeweb_back.geode_objects.geode_point_set3d import GeodePointSet3D +from opengeodeweb_back.geode_objects.geode_edged_curve3d import GeodeEdgedCurve3D routes = flask.Blueprint("create", __name__, url_prefix="/create") schemas_dict = get_schemas_dict(os.path.join(os.path.dirname(__file__), "schemas")) @@ -20,19 +22,20 @@ ) def create_point() -> flask.Response: """Endpoint to create a single point in 3D space.""" - utils_functions.validate_request(flask.request, schemas_dict["create_point"]) - params = schemas.CreatePoint.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["create_point"] + ) + params = schemas.CreatePoint.from_dict(json_data) # Create the point - pointset = geode_functions.geode_object_class("PointSet3D").create() - builder = geode_functions.create_builder("PointSet3D", pointset) + pointset = GeodePointSet3D() + builder = pointset.builder() builder.set_name(params.name) builder.create_point(opengeode.Point3D([params.x, params.y, params.z])) # Save and get info result = utils_functions.generate_native_viewable_and_light_viewable_from_object( - geode_object="PointSet3D", - data=pointset, + pointset ) return flask.make_response(result, 200) @@ -42,12 +45,14 @@ def create_point() -> flask.Response: ) def create_aoi() -> flask.Response: """Endpoint to create an Area of Interest (AOI) as an EdgedCurve3D.""" - utils_functions.validate_request(flask.request, schemas_dict["create_aoi"]) - params = schemas.CreateAoi.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["create_aoi"] + ) + params = schemas.CreateAoi.from_dict(json_data) # Create the edged curve - edged_curve = geode_functions.geode_object_class("EdgedCurve3D").create() - builder = geode_functions.create_builder("EdgedCurve3D", edged_curve) + edged_curve = GeodeEdgedCurve3D() + builder = edged_curve.builder() builder.set_name(params.name) # Create vertices first @@ -62,8 +67,7 @@ def create_aoi() -> flask.Response: # Save and get info result = utils_functions.generate_native_viewable_and_light_viewable_from_object( - geode_object="EdgedCurve3D", - data=edged_curve, + edged_curve ) return flask.make_response(result, 200) @@ -73,29 +77,34 @@ def create_aoi() -> flask.Response: ) def create_voi() -> flask.Response: """Endpoint to create a Volume of Interest (VOI) as an EdgedCurve3D (a bounding box/prism).""" - utils_functions.validate_request(flask.request, schemas_dict["create_voi"]) - params = schemas.CreateVoi.from_dict(flask.request.get_json()) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["create_voi"] + ) + params = schemas.CreateVoi.from_dict(json_data) aoi_data = geode_functions.get_data_info(params.aoi_id) if not aoi_data: flask.abort(404, f"AOI with id {params.aoi_id} not found") - aoi_object = geode_functions.load_data(params.aoi_id) + aoi_object = geode_functions.load_geode_object(params.aoi_id) + if not isinstance(aoi_object, GeodeEdgedCurve3D): + flask.abort(400, f"AOI with id {params.aoi_id} is not a GeodeEdgedCurve3D") - nb_points = aoi_object.nb_vertices() + aoi_curve = aoi_object.edged_curve + nb_points = aoi_curve.nb_vertices() - edged_curve = geode_functions.geode_object_class("EdgedCurve3D").create() - builder = geode_functions.create_builder("EdgedCurve3D", edged_curve) + edged_curve = GeodeEdgedCurve3D() + builder = edged_curve.builder() builder.set_name(params.name) for point_id in range(nb_points): - aoi_point = aoi_object.point(point_id) + aoi_point = aoi_curve.point(point_id) builder.create_point( opengeode.Point3D([aoi_point.value(0), aoi_point.value(1), params.z_min]) ) for point_id in range(nb_points): - aoi_point = aoi_object.point(point_id) + aoi_point = aoi_curve.point(point_id) builder.create_point( opengeode.Point3D([aoi_point.value(0), aoi_point.value(1), params.z_max]) ) @@ -107,7 +116,6 @@ def create_voi() -> flask.Response: builder.create_edge_with_vertices(point_id, point_id + nb_points) result = utils_functions.generate_native_viewable_and_light_viewable_from_object( - geode_object="EdgedCurve3D", - data=edged_curve, + edged_curve ) return flask.make_response(result, 200) diff --git a/src/opengeodeweb_back/routes/models/blueprint_models.py b/src/opengeodeweb_back/routes/models/blueprint_models.py index ecc5e88a..ab052745 100644 --- a/src/opengeodeweb_back/routes/models/blueprint_models.py +++ b/src/opengeodeweb_back/routes/models/blueprint_models.py @@ -4,6 +4,7 @@ from opengeodeweb_microservice.schemas import get_schemas_dict from opengeodeweb_back import geode_functions, utils_functions +from opengeodeweb_back.geode_objects.geode_model import GeodeModel from . import schemas routes = flask.Blueprint("models", __name__, url_prefix="/models") @@ -15,15 +16,15 @@ methods=schemas_dict["vtm_component_indices"]["methods"], ) def uuid_to_flat_index() -> flask.Response: - utils_functions.validate_request( + json_data = utils_functions.validate_request( flask.request, schemas_dict["vtm_component_indices"] ) - params = schemas.VtmComponentIndices.from_dict(flask.request.get_json()) + params = schemas.VtmComponentIndices.from_dict(json_data) vtm_file_path = geode_functions.data_file_path(params.id, "viewable.vtm") tree = ET.parse(vtm_file_path) root = tree.find("vtkMultiBlockDataSet") if root is None: - raise Exception("Failed to read viewable file") + flask.abort(500, "Failed to read viewable file") uuid_to_flat_index = {} current_index = 0 for elem in root.iter(): @@ -38,9 +39,13 @@ def uuid_to_flat_index() -> flask.Response: methods=schemas_dict["mesh_components"]["methods"], ) def extract_uuids_endpoint() -> flask.Response: - utils_functions.validate_request(flask.request, schemas_dict["mesh_components"]) - params = schemas.MeshComponents.from_dict(flask.request.get_json()) - model = geode_functions.load_data(params.id) + json_data = utils_functions.validate_request( + flask.request, schemas_dict["mesh_components"] + ) + params = schemas.MeshComponents.from_dict(json_data) + model = geode_functions.load_geode_object(params.id) + if not isinstance(model, GeodeModel): + flask.abort(400, f"{params.id} is not a GeodeModel") mesh_components = model.mesh_components() uuid_dict = {} for mesh_component, ids in mesh_components.items(): diff --git a/src/opengeodeweb_back/routes/schemas/__init__.py b/src/opengeodeweb_back/routes/schemas/__init__.py index 339f8961..4ad76b82 100644 --- a/src/opengeodeweb_back/routes/schemas/__init__.py +++ b/src/opengeodeweb_back/routes/schemas/__init__.py @@ -12,5 +12,6 @@ from .geographic_coordinate_systems import * from .geode_objects_and_output_extensions import * from .export_project import * +from .cell_attribute_names import * from .allowed_objects import * from .allowed_files import * diff --git a/src/opengeodeweb_back/routes/schemas/allowed_files.json b/src/opengeodeweb_back/routes/schemas/allowed_files.json index 32f5d394..0734d40f 100644 --- a/src/opengeodeweb_back/routes/schemas/allowed_files.json +++ b/src/opengeodeweb_back/routes/schemas/allowed_files.json @@ -1,12 +1,10 @@ { "route": "/allowed_files", - "methods": ["POST"], + "methods": [ + "POST" + ], "type": "object", - "properties": { - "supported_feature": { - "type": ["string", "null"] - } - }, - "required": ["supported_feature"], + "properties": {}, + "required": [], "additionalProperties": false -} +} \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/schemas/allowed_files.py b/src/opengeodeweb_back/routes/schemas/allowed_files.py index edebd493..f0227012 100644 --- a/src/opengeodeweb_back/routes/schemas/allowed_files.py +++ b/src/opengeodeweb_back/routes/schemas/allowed_files.py @@ -1,6 +1,5 @@ from dataclasses_json import DataClassJsonMixin from dataclasses import dataclass -from typing import Optional @dataclass @@ -8,4 +7,4 @@ class AllowedFiles(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - supported_feature: Optional[str] = None + pass diff --git a/src/opengeodeweb_back/routes/schemas/allowed_objects.json b/src/opengeodeweb_back/routes/schemas/allowed_objects.json index 7aadc618..7fe189c0 100644 --- a/src/opengeodeweb_back/routes/schemas/allowed_objects.json +++ b/src/opengeodeweb_back/routes/schemas/allowed_objects.json @@ -8,17 +8,10 @@ "filename": { "type": "string", "minLength": 1 - }, - "supported_feature": { - "type": [ - "string", - "null" - ] } }, "required": [ - "filename", - "supported_feature" + "filename" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/schemas/allowed_objects.py b/src/opengeodeweb_back/routes/schemas/allowed_objects.py index 9ee550c7..f0c74b75 100644 --- a/src/opengeodeweb_back/routes/schemas/allowed_objects.py +++ b/src/opengeodeweb_back/routes/schemas/allowed_objects.py @@ -1,6 +1,5 @@ from dataclasses_json import DataClassJsonMixin from dataclasses import dataclass -from typing import Optional @dataclass @@ -9,4 +8,3 @@ def __post_init__(self) -> None: print(self, flush=True) filename: str - supported_feature: Optional[str] = None diff --git a/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json b/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json new file mode 100644 index 00000000..0fe6632b --- /dev/null +++ b/src/opengeodeweb_back/routes/schemas/cell_attribute_names.json @@ -0,0 +1,13 @@ +{ + "route": "/cell_attribute_names", + "methods": ["POST"], + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1 + } + }, + "required": ["id"], + "additionalProperties": false +} diff --git a/src/opengeodeweb_back/routes/schemas/cell_attribute_names.py b/src/opengeodeweb_back/routes/schemas/cell_attribute_names.py new file mode 100644 index 00000000..df5a9996 --- /dev/null +++ b/src/opengeodeweb_back/routes/schemas/cell_attribute_names.py @@ -0,0 +1,10 @@ +from dataclasses_json import DataClassJsonMixin +from dataclasses import dataclass + + +@dataclass +class CellAttributeNames(DataClassJsonMixin): + def __post_init__(self) -> None: + print(self, flush=True) + + id: str diff --git a/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json b/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json index 8002c3ef..5f145f58 100644 --- a/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json +++ b/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.json @@ -5,7 +5,7 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 }, @@ -15,7 +15,7 @@ } }, "required": [ - "input_geode_object", + "geode_object_type", "filename" ], "additionalProperties": false diff --git a/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py b/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py index 888178c5..5dedb454 100644 --- a/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py +++ b/src/opengeodeweb_back/routes/schemas/geode_objects_and_output_extensions.py @@ -8,4 +8,4 @@ def __post_init__(self) -> None: print(self, flush=True) filename: str - input_geode_object: str + geode_object_type: str diff --git a/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json b/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json index 8fcbea82..6472abb8 100644 --- a/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json +++ b/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.json @@ -5,13 +5,13 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 } }, "required": [ - "input_geode_object" + "geode_object_type" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py b/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py index 31353997..a4a307f5 100644 --- a/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py +++ b/src/opengeodeweb_back/routes/schemas/geographic_coordinate_systems.py @@ -7,4 +7,4 @@ class GeographicCoordinateSystems(DataClassJsonMixin): def __post_init__(self) -> None: print(self, flush=True) - input_geode_object: str + geode_object_type: str diff --git a/src/opengeodeweb_back/routes/schemas/inspect_file.json b/src/opengeodeweb_back/routes/schemas/inspect_file.json index 7ea39c23..3059bad0 100644 --- a/src/opengeodeweb_back/routes/schemas/inspect_file.json +++ b/src/opengeodeweb_back/routes/schemas/inspect_file.json @@ -9,14 +9,14 @@ "type": "string", "minLength": 1 }, - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 } }, "required": [ "filename", - "input_geode_object" + "geode_object_type" ], "additionalProperties": false } \ No newline at end of file diff --git a/src/opengeodeweb_back/routes/schemas/inspect_file.py b/src/opengeodeweb_back/routes/schemas/inspect_file.py index 0e0e36ea..2c17ef2e 100644 --- a/src/opengeodeweb_back/routes/schemas/inspect_file.py +++ b/src/opengeodeweb_back/routes/schemas/inspect_file.py @@ -8,4 +8,4 @@ def __post_init__(self) -> None: print(self, flush=True) filename: str - input_geode_object: str + geode_object_type: str diff --git a/src/opengeodeweb_back/routes/schemas/missing_files.json b/src/opengeodeweb_back/routes/schemas/missing_files.json index 825a2d2c..e067d1a1 100644 --- a/src/opengeodeweb_back/routes/schemas/missing_files.json +++ b/src/opengeodeweb_back/routes/schemas/missing_files.json @@ -5,7 +5,7 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 }, @@ -15,7 +15,7 @@ } }, "required": [ - "input_geode_object", + "geode_object_type", "filename" ], "additionalProperties": false diff --git a/src/opengeodeweb_back/routes/schemas/missing_files.py b/src/opengeodeweb_back/routes/schemas/missing_files.py index 05a99f0a..cfdda06a 100644 --- a/src/opengeodeweb_back/routes/schemas/missing_files.py +++ b/src/opengeodeweb_back/routes/schemas/missing_files.py @@ -8,4 +8,4 @@ def __post_init__(self) -> None: print(self, flush=True) filename: str - input_geode_object: str + geode_object_type: str diff --git a/src/opengeodeweb_back/routes/schemas/save_viewable_file.json b/src/opengeodeweb_back/routes/schemas/save_viewable_file.json index 253fbb0e..49786cb4 100644 --- a/src/opengeodeweb_back/routes/schemas/save_viewable_file.json +++ b/src/opengeodeweb_back/routes/schemas/save_viewable_file.json @@ -5,7 +5,7 @@ ], "type": "object", "properties": { - "input_geode_object": { + "geode_object_type": { "type": "string", "minLength": 1 }, @@ -15,7 +15,7 @@ } }, "required": [ - "input_geode_object", + "geode_object_type", "filename" ], "additionalProperties": false diff --git a/src/opengeodeweb_back/routes/schemas/save_viewable_file.py b/src/opengeodeweb_back/routes/schemas/save_viewable_file.py index 754e5b32..be01b3b0 100644 --- a/src/opengeodeweb_back/routes/schemas/save_viewable_file.py +++ b/src/opengeodeweb_back/routes/schemas/save_viewable_file.py @@ -8,4 +8,4 @@ def __post_init__(self) -> None: print(self, flush=True) filename: str - input_geode_object: str + geode_object_type: str diff --git a/src/opengeodeweb_back/test_utils.py b/src/opengeodeweb_back/test_utils.py index 0888a4ad..f890989f 100644 --- a/src/opengeodeweb_back/test_utils.py +++ b/src/opengeodeweb_back/test_utils.py @@ -1,17 +1,23 @@ # Standard library imports +from typing import Callable, Any # Third party imports +from flask.testing import FlaskClient # Local application imports +JsonData = dict[str, Any] -def test_route_wrong_params(client, route, get_full_data): + +def test_route_wrong_params( + client: FlaskClient, route: str, get_full_data: Callable[[], JsonData] +) -> None: for key, value in get_full_data().items(): json = get_full_data() json.pop(key) response = client.post(route, json=json) assert response.status_code == 400 - error_description = response.json["description"] + error_description = response.get_json()["description"] assert "data must contain" in error_description assert f"'{key}'" in error_description @@ -19,6 +25,6 @@ def test_route_wrong_params(client, route, get_full_data): json["dumb_key"] = "dumb_value" response = client.post(route, json=json) assert response.status_code == 400 - error_description = response.json["description"] + error_description = response.get_json()["description"] assert "data must not contain" in error_description assert "'dumb_key'" in error_description diff --git a/src/opengeodeweb_back/utils_functions.py b/src/opengeodeweb_back/utils_functions.py index 1f70ccd8..75c99bf0 100644 --- a/src/opengeodeweb_back/utils_functions.py +++ b/src/opengeodeweb_back/utils_functions.py @@ -5,6 +5,8 @@ import zipfile from collections.abc import Callable from concurrent.futures import ThreadPoolExecutor +from typing import Any + # Third party imports import flask @@ -13,13 +15,16 @@ import shutil from werkzeug.exceptions import HTTPException import werkzeug - -# Local application imports -from . import geode_functions from opengeodeweb_microservice.schemas import SchemaDict from opengeodeweb_microservice.database.data import Data from opengeodeweb_microservice.database.connection import get_session +# Local application imports +from . import geode_functions +from .geode_objects import geode_objects +from .geode_objects.types import GeodeObjectType +from .geode_objects.geode_object import GeodeObject + def increment_request_counter(current_app: flask.Flask) -> None: if "REQUEST_COUNTER" in current_app.config: @@ -98,7 +103,7 @@ def versions(list_packages: list[str]) -> list[dict[str, str]]: return list_with_versions -def validate_request(request: flask.Request, schema: SchemaDict) -> None: +def validate_request(request: flask.Request, schema: SchemaDict) -> dict[str, Any]: json_data = request.get_json(force=True, silent=True) if json_data is None: @@ -108,7 +113,9 @@ def validate_request(request: flask.Request, schema: SchemaDict) -> None: validate(json_data) except fastjsonschema.JsonSchemaException as e: error_msg = str(e) + print("Validation failed:", error_msg, flush=True) flask.abort(400, error_msg) + return json_data def set_interval( @@ -158,15 +165,16 @@ def send_file( def handle_exception(exception: HTTPException) -> flask.Response: - response = exception.get_response() - response.data = flask.json.dumps( + print("\033[91mError:\033[0m \033[91m" + str(exception) + "\033[0m", flush=True) + response = flask.jsonify( { "code": exception.code, "name": exception.name, - "description": exception.description, + "description": exception.description or "An error occurred", } ) response.content_type = "application/json" + response.status_code = exception.code or 500 return response @@ -178,81 +186,79 @@ def create_data_folder_from_id(data_id: str) -> str: def save_all_viewables_and_return_info( - geode_object: str, - data: object, - data_entry: Data, + geode_object: GeodeObject, + data: Data, data_path: str, ) -> dict[str, str | list[str]]: with ThreadPoolExecutor() as executor: - native_future = executor.submit( - geode_functions.save, - geode_object, - data, - data_path, - "native." + data.native_extension(), - ) - viewable_future = executor.submit( - geode_functions.save_viewable, geode_object, data, data_path, "viewable" - ) - light_viewable_future = executor.submit( - geode_functions.save_light_viewable, - geode_object, - data, - data_path, - "light_viewable", + (native_files, viewable_path, light_path) = executor.map( + lambda args: args[0](args[1]), + [ + ( + geode_object.save, + os.path.join( + data_path, "native." + geode_object.native_extension() + ), + ), + (geode_object.save_viewable, os.path.join(data_path, "viewable")), + ( + geode_object.save_light_viewable, + os.path.join(data_path, "light_viewable"), + ), + ], ) - saved_light_viewable_file_path = light_viewable_future.result() - with open(saved_light_viewable_file_path, "rb") as f: + with open(light_path, "rb") as f: binary_light_viewable = f.read() - saved_native_file_path = native_future.result() - saved_viewable_file_path = viewable_future.result() - data_entry.native_file_name = os.path.basename(saved_native_file_path[0]) - data_entry.viewable_file_name = os.path.basename(saved_viewable_file_path) - data_entry.light_viewable = os.path.basename(saved_light_viewable_file_path) - + data.native_file = os.path.basename(native_files[0]) + data.viewable_file = os.path.basename(viewable_path) + data.light_viewable_file = os.path.basename(light_path) + assert data.native_file is not None + assert data.viewable_file is not None + assert data.light_viewable_file is not None return { - "native_file_name": data_entry.native_file_name, - "viewable_file_name": data_entry.viewable_file_name, - "id": data_entry.id, - "name": data.name(), # type: ignore - "object_type": geode_functions.get_object_type(geode_object), + "native_file": data.native_file, + "viewable_file": data.viewable_file, + "id": data.id, + "name": geode_object.identifier.name(), + "viewer_type": data.viewer_object, "binary_light_viewable": binary_light_viewable.decode("utf-8"), - "geode_object": data_entry.geode_object, - "input_file": data_entry.input_file, - "additional_files": data_entry.additional_files, + "geode_object_type": data.geode_object, + "input_file": data.input_file or "", + "additional_files": data.additional_files or [], } def generate_native_viewable_and_light_viewable_from_object( - geode_object: str, data: object + geode_object: GeodeObject, ) -> dict[str, str | list[str]]: - data_entry = Data.create( - geode_object=geode_object, - viewer_object=geode_functions.get_object_type(geode_object), + data = Data.create( + geode_object=geode_object.geode_object_type(), + viewer_object=geode_object.viewer_type(), ) - data_path = create_data_folder_from_id(data_entry.id) - return save_all_viewables_and_return_info(geode_object, data, data_entry, data_path) + data_path = create_data_folder_from_id(data.id) + return save_all_viewables_and_return_info(geode_object, data, data_path) def generate_native_viewable_and_light_viewable_from_file( - geode_object: str, input_filename: str + geode_object_type: GeodeObjectType, input_file: str ) -> dict[str, str | list[str]]: - data_entry = Data.create( - geode_object=geode_object, - viewer_object=geode_functions.get_object_type(geode_object), - input_file=input_filename, + generic_geode_object = geode_objects[geode_object_type] + data = Data.create( + geode_object=geode_object_type, + viewer_object=generic_geode_object.viewer_type(), + input_file=input_file, ) - data_path = create_data_folder_from_id(data_entry.id) + data_path = create_data_folder_from_id(data.id) - full_input_filename = geode_functions.upload_file_path(input_filename) + full_input_filename = geode_functions.upload_file_path(input_file) copied_full_path = os.path.join( - data_path, werkzeug.utils.secure_filename(input_filename) + data_path, werkzeug.utils.secure_filename(input_file) ) shutil.copy2(full_input_filename, copied_full_path) additional_files_copied: list[str] = [] - additional = geode_functions.additional_files(geode_object, full_input_filename) + additional = generic_geode_object.additional_files(full_input_filename) for additional_file in additional.mandatory_files + additional.optional_files: if additional_file.is_missing: continue @@ -266,12 +272,10 @@ def generate_native_viewable_and_light_viewable_from_file( shutil.copy2(source_path, dest_path) additional_files_copied.append(additional_file.filename) - data = geode_functions.load(geode_object, copied_full_path) - - data_entry.additional_files = additional_files_copied + geode_object = generic_geode_object.load(copied_full_path) + data.additional_files = additional_files_copied return save_all_viewables_and_return_info( geode_object, data, - data_entry, data_path, ) diff --git a/tests/conftest.py b/tests/conftest.py index 283d2188..f9815593 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,6 +6,8 @@ from typing import Generator # Third party imports +from flask.ctx import AppContext +from flask.testing import FlaskClient import pytest # Local application imports @@ -47,20 +49,20 @@ def configure_test_environment() -> Generator[None, None, None]: @pytest.fixture -def client(): +def client() -> Generator[FlaskClient, None, None]: app.config["REQUEST_COUNTER"] = 0 app.config["LAST_REQUEST_TIME"] = time.time() client = app.test_client() - client.headers = {"Content-type": "application/json", "Accept": "application/json"} + # client.headers = {"Content-type": "application/json", "Accept": "application/json"} yield client @pytest.fixture -def app_context(): - with app.app_context(): - yield +def app_context() -> Generator[AppContext, None, None]: + with app.app_context() as ctx: + yield ctx @pytest.fixture -def test_id(): +def test_id() -> str: return TEST_ID diff --git a/tests/test_create_routes.py b/tests/test_create_routes.py index 974e996a..61b1eb42 100644 --- a/tests/test_create_routes.py +++ b/tests/test_create_routes.py @@ -1,7 +1,5 @@ # Standard library imports -import os import uuid -from typing import Any, Callable, Dict, List # Third party imports import pytest @@ -12,12 +10,12 @@ @pytest.fixture -def point_data() -> Dict[str, Any]: +def point_data() -> test_utils.JsonData: return {"name": "test_point", "x": 1.0, "y": 2.0, "z": 3.0} @pytest.fixture -def aoi_data() -> Dict[str, Any]: +def aoi_data() -> test_utils.JsonData: return { "name": "test_aoi", "points": [ @@ -31,7 +29,7 @@ def aoi_data() -> Dict[str, Any]: @pytest.fixture -def voi_data() -> Dict[str, Any]: +def voi_data() -> test_utils.JsonData: """Fixture for Volume of Interest (VOI) test data.""" return { "name": "test_voi", @@ -42,7 +40,7 @@ def voi_data() -> Dict[str, Any]: } -def test_create_point(client: FlaskClient, point_data: Dict[str, Any]) -> None: +def test_create_point(client: FlaskClient, point_data: test_utils.JsonData) -> None: """Test the creation of a point with valid data.""" route: str = "/opengeodeweb_back/create/create_point" @@ -51,23 +49,23 @@ def test_create_point(client: FlaskClient, point_data: Dict[str, Any]) -> None: assert response.status_code == 200 # Verify response data - response_data: Any = response.json - assert "viewable_file_name" in response_data + response_data = response.get_json() + assert "viewable_file" in response_data assert "id" in response_data assert "name" in response_data - assert "native_file_name" in response_data - assert "object_type" in response_data - assert "geode_object" in response_data + assert "native_file" in response_data + assert "viewer_type" in response_data + assert "geode_object_type" in response_data assert response_data["name"] == point_data["name"] - assert response_data["object_type"] == "mesh" - assert response_data["geode_object"] == "PointSet3D" + assert response_data["viewer_type"] == "mesh" + assert response_data["geode_object_type"] == "PointSet3D" # Test with missing parameters - test_utils.test_route_wrong_params(client, route, lambda: point_data.copy()) # type: ignore + test_utils.test_route_wrong_params(client, route, lambda: point_data.copy()) -def test_create_aoi(client: FlaskClient, aoi_data: Dict[str, Any]) -> None: +def test_create_aoi(client: FlaskClient, aoi_data: test_utils.JsonData) -> None: """Test the creation of an AOI with valid data.""" route: str = "/opengeodeweb_back/create/create_aoi" @@ -76,30 +74,30 @@ def test_create_aoi(client: FlaskClient, aoi_data: Dict[str, Any]) -> None: assert response.status_code == 200 # Verify response data - response_data: Any = response.json - assert "viewable_file_name" in response_data + response_data = response.get_json() + assert "viewable_file" in response_data assert "id" in response_data assert "name" in response_data - assert "native_file_name" in response_data - assert "object_type" in response_data - assert "geode_object" in response_data + assert "native_file" in response_data + assert "viewer_type" in response_data + assert "geode_object_type" in response_data assert response_data["name"] == aoi_data["name"] - assert response_data["object_type"] == "mesh" - assert response_data["geode_object"] == "EdgedCurve3D" + assert response_data["viewer_type"] == "mesh" + assert response_data["geode_object_type"] == "EdgedCurve3D" # Test with missing parameters - test_utils.test_route_wrong_params(client, route, lambda: aoi_data.copy()) # type: ignore + test_utils.test_route_wrong_params(client, route, lambda: aoi_data.copy()) def test_create_voi( - client: FlaskClient, aoi_data: Dict[str, Any], voi_data: Dict[str, Any] + client: FlaskClient, aoi_data: test_utils.JsonData, voi_data: test_utils.JsonData ) -> None: """Test the creation of a VOI with valid data (including optional id).""" aoi_route = "/opengeodeweb_back/create/create_aoi" aoi_response = client.post(aoi_route, json=aoi_data) assert aoi_response.status_code == 200 - aoi_id = aoi_response.json["id"] + aoi_id = aoi_response.get_json()["id"] voi_data["aoi_id"] = aoi_id @@ -107,12 +105,12 @@ def test_create_voi( response = client.post(voi_route, json=voi_data) assert response.status_code == 200 - response_data = response.json + response_data = response.get_json() assert "id" in response_data assert "name" in response_data assert response_data["name"] == voi_data["name"] - assert response_data["object_type"] == "mesh" - assert response_data["geode_object"] == "EdgedCurve3D" + assert response_data["viewer_type"] == "mesh" + assert response_data["geode_object_type"] == "EdgedCurve3D" def test_create_point_with_invalid_data(client: FlaskClient) -> None: @@ -120,7 +118,7 @@ def test_create_point_with_invalid_data(client: FlaskClient) -> None: route: str = "/opengeodeweb_back/create/create_point" # Test with non-numeric coordinates - invalid_data: Dict[str, Any] = { + invalid_data: test_utils.JsonData = { "name": "invalid_point", "x": "not_a_number", "y": 2.0, @@ -136,12 +134,12 @@ def test_create_point_with_invalid_data(client: FlaskClient) -> None: def test_create_aoi_with_invalid_data( - client: FlaskClient, aoi_data: Dict[str, Any] + client: FlaskClient, aoi_data: test_utils.JsonData ) -> None: """Test the AOI creation endpoint with invalid data.""" route: str = "/opengeodeweb_back/create/create_aoi" - invalid_data: Dict[str, Any] = { + invalid_data: test_utils.JsonData = { **aoi_data, "points": [ {"x": "not_a_number", "y": 0.0}, @@ -163,13 +161,13 @@ def test_create_aoi_with_invalid_data( def test_create_voi_with_invalid_data( - client: FlaskClient, aoi_data: Dict[str, Any], voi_data: Dict[str, Any] + client: FlaskClient, aoi_data: test_utils.JsonData, voi_data: test_utils.JsonData ) -> None: """Test the VOI creation endpoint with invalid data.""" aoi_route = "/opengeodeweb_back/create/create_aoi" aoi_response = client.post(aoi_route, json=aoi_data) assert aoi_response.status_code == 200 - aoi_id = aoi_response.json["id"] + aoi_id = aoi_response.get_json()["id"] route = "/opengeodeweb_back/create/create_aoi" diff --git a/tests/test_geode_functions.py b/tests/test_geode_functions.py index b3c57b1c..dbc8cb70 100644 --- a/tests/test_geode_functions.py +++ b/tests/test_geode_functions.py @@ -5,333 +5,60 @@ # Third party imports # Local application imports -from opengeodeweb_back import geode_functions, geode_objects +from opengeodeweb_back import geode_functions +from opengeodeweb_back.geode_objects import geode_objects +from opengeodeweb_back.geode_objects.types import GeodeObjectType_values data_folder = os.path.join(os.path.dirname(__file__), "data") -def test_geode_object_value(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - geode_object_value = geode_functions.geode_object_value(geode_object) - assert type(geode_object_value) is dict +def test_geode_objects() -> None: + for geode_object_type in GeodeObjectType_values: + assert geode_object_type in geode_objects -def test_input_factory(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - geode_object_input = geode_functions.input_factory(geode_object) - assert type(geode_object_input.list_creators()) is list - - -def test_input_extensions(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - geode_object_input_extensions = geode_functions.geode_object_input_extensions( - geode_object - ) - - assert type(geode_object_input_extensions) is list - for extension in geode_object_input_extensions: - assert type(extension) is str - - -def test_output_factory(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - geode_object_output = geode_functions.output_factory(geode_object) - geode_object_output_list = geode_object_output.list_creators() - assert type(geode_object_output_list) is list - for output in geode_object_output_list: - assert type(output) is str - - -def test_additional_files(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - input_extensions = geode_functions.geode_object_input_extensions(geode_object) - for input_extension in input_extensions: - file_absolute_path = os.path.join(data_folder, f"test.{input_extension}") - additional_files = geode_functions.additional_files( - geode_object, file_absolute_path - ) - mandatory_files = additional_files.mandatory_files - optional_files = additional_files.optional_files - assert type(mandatory_files) is list - assert type(optional_files) is list - - -def test_is_loadable(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - input_extensions = geode_functions.geode_object_input_extensions(geode_object) - for input_extension in input_extensions: - file_absolute_path = os.path.join(data_folder, f"test.{input_extension}") - is_loadable = geode_functions.is_loadable(geode_object, file_absolute_path) - assert isinstance(is_loadable, float) - assert 0.0 <= is_loadable <= 1.0 - - -def test_object_priority(): - for geode_object, _value in geode_objects.geode_objects_dict().items(): - input_extensions = geode_functions.geode_object_input_extensions(geode_object) - for input_extension in input_extensions: - file_absolute_path = os.path.join(data_folder, f"test.{input_extension}") - priority = geode_functions.object_priority(geode_object, file_absolute_path) - assert isinstance( - priority, int - ), f"Priority should be int for {geode_object}" - - -def test_load(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - print(f"\n{geode_object=}") - input_extensions = geode_functions.geode_object_input_extensions(geode_object) - for input_extension in input_extensions: +def test_input_output() -> None: + for generic_geode_object in geode_objects.values(): + print(f"\n{generic_geode_object.geode_object_type()=}") + for input_extension in generic_geode_object.input_extensions(): print(f"\t{input_extension=}") file_absolute_path = os.path.join(data_folder, f"test.{input_extension}") - if geode_functions.is_loadable(geode_object, file_absolute_path) > 0.0: - data = geode_functions.load(geode_object, file_absolute_path) - data_name = data.name() - if "save_viewable" in value: - viewable_file_path = geode_functions.save_viewable( - geode_object, - data, - os.path.abspath(f"./output"), - data_name, - ) - os.remove(viewable_file_path) - - if "save_light_viewable" in value: - light_viewable_file_path = geode_functions.save_light_viewable( - geode_object, - data, - os.path.abspath(f"./output"), - data_name, - ) - os.remove(light_viewable_file_path) - geode_objects_and_output_extensions = ( - geode_functions.geode_objects_output_extensions(geode_object, data) + if generic_geode_object.is_loadable(file_absolute_path).value() == 0.0: + continue + geode_object = generic_geode_object.load(file_absolute_path) + data_name = geode_object.identifier.name() + if geode_object.is_viewable(): + viewable_file_path = geode_object.save_viewable( + os.path.join(os.path.abspath(f"./output"), data_name) ) - assert type(geode_objects_and_output_extensions) is dict - for ( - output_geode_object, - output_geode_object_value, - ) in geode_objects_and_output_extensions.items(): - print(f"\t\t{output_geode_object=}") - for ( - output_extension, - output_extension_value, - ) in output_geode_object_value.items(): - print(f"\t\t\t{output_extension=}") - uu_id = str(uuid.uuid4()).replace("-", "") - filename = f"{uu_id}.{output_extension}" - if geode_functions.is_saveable( - output_geode_object, data, filename - ): - saved_files = geode_functions.save( - output_geode_object, - data, - os.path.abspath(f"./output"), - filename, - ) - assert type(saved_files) is list - for saved_file in saved_files: - os.remove(saved_file) - - -def test_get_object_type(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - object_type = geode_functions.get_object_type(geode_object) - assert type(object_type) is str - assert object_type in ["model", "mesh"] - - -def test_is_3D(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - is_3D = geode_functions.is_3D(geode_object) - assert type(is_3D) is bool - - -def test_is_viewable(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - is_viewable = geode_functions.is_viewable(geode_object) - assert type(is_viewable) is bool - - -def test_geode_object_input_extensions(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - input_extensions = geode_functions.geode_object_input_extensions(geode_object) - assert type(input_extensions) is list - for input_extension in input_extensions: - assert type(input_extension) is str - - -def test_geode_object_output_extensions(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - print(f"\n{geode_object=}") - input_extensions = geode_functions.geode_object_input_extensions(geode_object) - for input_extension in input_extensions: - print(f"\t{input_extension=}") - file_absolute_path = os.path.join(data_folder, f"test.{input_extension}") - additional = geode_functions.additional_files( - geode_object, file_absolute_path - ) - has_missing_files = any( - f.is_missing - for f in additional.mandatory_files + additional.optional_files - ) - if has_missing_files: - print( - f"\t\tMandatory files: {[f.filename for f in additional.mandatory_files]}" + os.remove(viewable_file_path) + if geode_object.is_viewable(): + light_viewable_file_path = geode_object.save_light_viewable( + os.path.join(os.path.abspath(f"./output"), data_name) ) - print( - f"\t\tAdditional files: {[f.filename for f in additional.optional_files]}" - ) - if geode_functions.is_loadable(geode_object, file_absolute_path) > 0.0: - data = geode_functions.load(geode_object, file_absolute_path) - geode_objets_and_output_extensions = ( - geode_functions.geode_objects_output_extensions(geode_object, data) - ) - data_name = data.name() - assert isinstance(geode_objets_and_output_extensions, dict) - for ( - output_geode_object, - output_geode_object_value, - ) in geode_objets_and_output_extensions.items(): - for ( - output_extension, - output_extension_value, - ) in output_geode_object_value.items(): - assert isinstance(output_extension, str) - assert isinstance(output_extension_value["is_saveable"], bool) - - -def test_get_inspector_children(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - if "inspector" in value: - print(f"\n{geode_object=}", flush=True) - input_extensions = geode_functions.geode_object_input_extensions( - geode_object + os.remove(light_viewable_file_path) + geode_objects_output_extensions = ( + geode_functions.geode_object_output_extensions(geode_object) ) - for input_extension in input_extensions: - print(f"\t{input_extension=}", flush=True) - file_absolute_path = os.path.join( - data_folder, f"test.{input_extension}" - ) - additional = geode_functions.additional_files( - geode_object, file_absolute_path - ) - has_missing_files = any( - f.is_missing - for f in additional.mandatory_files + additional.optional_files - ) - if has_missing_files: - mandatory_files = [f.filename for f in additional.mandatory_files] - print(f"\t\t{mandatory_files=}", flush=True) - additional_files = [f.filename for f in additional.optional_files] - print(f"\t\t{additional_files=}", flush=True) - if geode_functions.is_loadable(geode_object, file_absolute_path) > 0.0: - data = geode_functions.load(geode_object, file_absolute_path) - class_inspector = geode_functions.inspect(geode_object, data) - assert "InspectionResult" in class_inspector.__class__.__name__ - inspection_result = geode_functions.get_inspector_children( - class_inspector - ) - - print(f"\t\t{inspection_result=}", flush=True) - assert isinstance(inspection_result, dict) - - -def test_filter_geode_objects(): - filters_list = ["", "crs", "inspector", None] - - for filter in filters_list: - geode_objects_filtered_list = geode_functions.filter_geode_objects(filter) - assert type(geode_objects_filtered_list) is list - for geode_object in geode_objects_filtered_list: - assert type(geode_object) is str - - -def test_list_input_extensions(): - keys_array = ["crs", "inspector", None] - for geode_object, value in geode_objects.geode_objects_dict().items(): - for keys in keys_array: - input_extensions = geode_functions.list_input_extensions(keys) - assert type(input_extensions) is list - - -def test_list_geode_objects(): - test_list = [ - { - "key": "crs", - "invalid_geode_objects": [ - "Graph", - "RasterImage2D", - "RasterImage3D", - "VertexSet", - ], - }, - { - "key": "inspector", - "invalid_geode_objects": [ - "Graph", - "RasterImage2D", - "RasterImage3D", - "VertexSet", - ], - }, - { - "key": None, - "invalid_geode_objects": [], - }, - ] - for test in test_list: - key = test["key"] - invalid_geode_objects = test["invalid_geode_objects"] - - input_extensions = geode_functions.list_input_extensions(key) - for geode_object, value in geode_objects.geode_objects_dict().items(): - for input_extension in input_extensions: - file_absolute_path = os.path.join( - data_folder, f"test.{input_extension}" - ) - return_dict = geode_functions.list_geode_objects( - file_absolute_path, key - ) - assert type(return_dict) is dict - - if key != None: - assert len(return_dict.keys()) > 0 - for invalid_geode_object in invalid_geode_objects: - assert invalid_geode_object not in return_dict.keys() - else: - assert len(return_dict.keys()) >= 1 - for input_geode_object, input_extension_dict in return_dict.items(): - assert type(input_extension_dict) is dict - if "is_loadable" in input_extension_dict: - is_loadable_value = input_extension_dict["is_loadable"] - assert isinstance(is_loadable_value, float) - assert 0.0 <= is_loadable_value <= 1.0 - assert "object_priority" in input_extension_dict - object_priority_value = input_extension_dict[ - "object_priority" - ] - assert isinstance(object_priority_value, int) - - -def test_geode_objects_output_extensions(): - for geode_object, value in geode_objects.geode_objects_dict().items(): - input_extensions = geode_functions.geode_object_input_extensions(geode_object) - for input_extension in input_extensions: - file_absolute_path = os.path.join(data_folder, f"test.{input_extension}") - if geode_functions.is_loadable(geode_object, file_absolute_path) > 0.0: - data = geode_functions.load(geode_object, file_absolute_path) - geode_objects_and_output_extensions = ( - geode_functions.geode_objects_output_extensions(geode_object, data) - ) - assert type(geode_objects_and_output_extensions) is dict + assert type(geode_objects_output_extensions) is dict + for ( + output_geode_object_type, + output_geode_extensions, + ) in geode_objects_output_extensions.items(): + print(f"\t\t{output_geode_object_type=}") for ( - output_geode_object, - output_geode_object_value, - ) in geode_objects_and_output_extensions.items(): - for ( - output_extension, - output_extension_value, - ) in output_geode_object_value.items(): - assert type(output_extension_value["is_saveable"]) is bool + output_extension, + output_is_saveable, + ) in output_geode_extensions.items(): + print(f"\t\t\t{output_extension=}") + uu_id = str(uuid.uuid4()).replace("-", "") + filename = f"{uu_id}.{output_extension}" + if output_is_saveable: + saved_files = geode_objects[output_geode_object_type].save( + geode_object, + os.path.join(os.path.abspath(f"./output"), filename), + ) + assert type(saved_files) is list + for saved_file in saved_files: + os.remove(saved_file) diff --git a/tests/test_models_routes.py b/tests/test_models_routes.py index 9507316c..379e5b16 100644 --- a/tests/test_models_routes.py +++ b/tests/test_models_routes.py @@ -1,18 +1,21 @@ import os import shutil +import zipfile +import json +from flask.testing import FlaskClient +from werkzeug.datastructures import FileStorage +from pathlib import Path -from opengeodeweb_back import geode_functions from opengeodeweb_microservice.database.data import Data from opengeodeweb_microservice.database.connection import get_session -from werkzeug.datastructures import FileStorage -import zipfile -import json +from opengeodeweb_back import geode_functions +from opengeodeweb_back.geode_objects.geode_brep import GeodeBRep base_dir = os.path.abspath(os.path.dirname(__file__)) data_dir = os.path.join(base_dir, "data") -def test_model_mesh_components(client, test_id): +def test_model_mesh_components(client: FlaskClient, test_id: str) -> None: route = "/opengeodeweb_back/models/vtm_component_indices" with client.application.app_context(): @@ -23,7 +26,7 @@ def test_model_mesh_components(client, test_id): response = client.post(route, json={"id": test_id}) assert response.status_code == 200 - uuid_dict = response.json["uuid_to_flat_index"] + uuid_dict = response.get_json()["uuid_to_flat_index"] assert isinstance(uuid_dict, dict) indices = list(uuid_dict.values()) @@ -33,29 +36,29 @@ def test_model_mesh_components(client, test_id): assert isinstance(uuid, str) -def test_extract_brep_uuids(client, test_id): +def test_extract_brep_uuids(client: FlaskClient, test_id: str) -> None: route = "/opengeodeweb_back/models/mesh_components" brep_filename = os.path.join(data_dir, "cube.og_brep") with client.application.app_context(): - data_entry = Data.create( - geode_object="BRep", - viewer_object=geode_functions.get_object_type("BRep"), + data = Data.create( + geode_object=GeodeBRep.geode_object_type(), + viewer_object=GeodeBRep.viewer_type(), input_file=brep_filename, ) - data_entry.native_file_name = brep_filename + data.native_file = brep_filename session = get_session() if session: session.commit() - response = client.post(route, json={"id": data_entry.id}) + response = client.post(route, json={"id": data.id}) assert response.status_code == 200 - assert "uuid_dict" in response.json - uuid_dict = response.json["uuid_dict"] + assert "uuid_dict" in response.get_json() + uuid_dict = response.get_json()["uuid_dict"] assert isinstance(uuid_dict, dict) -def test_export_project_route(client, tmp_path): +def test_export_project_route(client: FlaskClient, tmp_path: Path) -> None: route = "/opengeodeweb_back/export_project" snapshot = { "styles": {"1": {"visibility": True, "opacity": 1.0, "color": [0.2, 0.6, 0.9]}} @@ -86,7 +89,7 @@ def test_export_project_route(client, tmp_path): os.remove(export_path) -def test_import_project_route(client, tmp_path): +def test_import_project_route(client: FlaskClient, tmp_path: Path) -> None: route = "/opengeodeweb_back/import_project" snapshot = { "styles": {"1": {"visibility": True, "opacity": 1.0, "color": [0.2, 0.6, 0.9]}} @@ -103,8 +106,8 @@ def test_import_project_route(client, tmp_path): temp_db = tmp_path / "temp_project.db" conn = sqlite3.connect(str(temp_db)) conn.execute( - "CREATE TABLE datas (id TEXT PRIMARY KEY, geode_object TEXT, viewer_object TEXT, native_file_name TEXT, " - "viewable_file_name TEXT, light_viewable TEXT, input_file TEXT, additional_files TEXT)" + "CREATE TABLE datas (id TEXT PRIMARY KEY, geode_object TEXT, viewer_object TEXT, native_file TEXT, " + "viewable_file TEXT, light_viewable_file TEXT, input_file TEXT, additional_files TEXT)" ) conn.commit() conn.close() @@ -122,7 +125,7 @@ def test_import_project_route(client, tmp_path): ) assert resp.status_code == 200 - assert resp.json.get("snapshot") == snapshot + assert resp.get_json().get("snapshot") == snapshot assert os.path.exists(db_path) from opengeodeweb_microservice.database import connection @@ -135,7 +138,7 @@ def test_import_project_route(client, tmp_path): client.application.config["DATA_FOLDER_PATH"] = original_data_folder -def test_save_viewable_workflow_from_file(client): +def test_save_viewable_workflow_from_file(client: FlaskClient) -> None: file = os.path.join(data_dir, "cube.og_brep") upload_resp = client.put( "/opengeodeweb_back/upload_file", @@ -144,14 +147,14 @@ def test_save_viewable_workflow_from_file(client): assert upload_resp.status_code == 201 route = "/opengeodeweb_back/save_viewable_file" - payload = {"input_geode_object": "BRep", "filename": "cube.og_brep"} + payload = {"geode_object_type": "BRep", "filename": "cube.og_brep"} response = client.post(route, json=payload) assert response.status_code == 200 - data_id = response.json["id"] + data_id = response.get_json()["id"] assert isinstance(data_id, str) and len(data_id) > 0 - assert response.json["viewable_file_name"].endswith(".vtm") + assert response.get_json()["viewable_file"].endswith(".vtm") comp_resp = client.post( "/opengeodeweb_back/models/vtm_component_indices", json={"id": data_id} @@ -162,7 +165,7 @@ def test_save_viewable_workflow_from_file(client): assert refreshed is not None -def test_save_viewable_workflow_from_object(client): +def test_save_viewable_workflow_from_object(client: FlaskClient) -> None: route = "/opengeodeweb_back/create/create_aoi" aoi_data = { "name": "workflow_aoi", @@ -178,13 +181,7 @@ def test_save_viewable_workflow_from_object(client): response = client.post(route, json=aoi_data) assert response.status_code == 200 - data_id = response.json["id"] + data_id = response.get_json()["id"] assert isinstance(data_id, str) and len(data_id) > 0 - assert response.json["geode_object"] == "EdgedCurve3D" - assert response.json["viewable_file_name"].endswith(".vtp") - - attr_resp = client.post( - "/opengeodeweb_back/vertex_attribute_names", json={"id": data_id} - ) - assert attr_resp.status_code == 200 - assert isinstance(attr_resp.json.get("vertex_attribute_names", []), list) + assert response.get_json()["geode_object_type"] == "EdgedCurve3D" + assert response.get_json()["viewable_file"].endswith(".vtp") diff --git a/tests/test_routes.py b/tests/test_routes.py index bd8220e5..69c42c9f 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -3,23 +3,37 @@ # Third party imports from werkzeug.datastructures import FileStorage +from flask.testing import FlaskClient # Local application imports 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.geode_objects.geode_polygonal_surface3d import ( + GeodePolygonalSurface3D, +) +from opengeodeweb_back.geode_objects.geode_polyhedral_solid3d import ( + GeodePolyhedralSolid3D, +) + +from opengeodeweb_back.geode_objects.geode_regular_grid2d import ( + GeodeRegularGrid2D, +) base_dir = os.path.abspath(os.path.dirname(__file__)) data_dir = os.path.join(base_dir, "data") -def test_allowed_files(client): +def test_allowed_files(client: FlaskClient) -> None: route = f"/opengeodeweb_back/allowed_files" - get_full_data = lambda: {"supported_feature": "None"} + + def get_full_data() -> test_utils.JsonData: + return {} + json = get_full_data() response = client.post(route, json=json) assert response.status_code == 200 - extensions = response.json["extensions"] + extensions = response.get_json()["extensions"] assert type(extensions) is list for extension in extensions: assert type(extension) is str @@ -28,19 +42,18 @@ def test_allowed_files(client): test_utils.test_route_wrong_params(client, route, get_full_data) -def test_allowed_objects(client): +def test_allowed_objects(client: FlaskClient) -> None: route = f"/opengeodeweb_back/allowed_objects" - def get_full_data(): + def get_full_data() -> test_utils.JsonData: return { "filename": "corbi.og_brep", - "supported_feature": None, } # Normal test with filename 'corbi.og_brep' response = client.post(route, json=get_full_data()) assert response.status_code == 200 - allowed_objects = response.json["allowed_objects"] + allowed_objects = response.get_json()["allowed_objects"] assert type(allowed_objects) is dict for allowed_object in allowed_objects: assert type(allowed_object) is str @@ -49,7 +62,7 @@ def get_full_data(): test_utils.test_route_wrong_params(client, route, get_full_data) -def test_upload_file(client, filename="test.og_brep"): +def test_upload_file(client: FlaskClient, filename: str = "test.og_brep") -> None: file = os.path.join(data_dir, filename) print(f"{file=}", flush=True) response = client.put( @@ -59,21 +72,21 @@ def test_upload_file(client, filename="test.og_brep"): assert response.status_code == 201 -def test_missing_files(client): +def test_missing_files(client: FlaskClient) -> None: route = f"/opengeodeweb_back/missing_files" - def get_full_data(): + def get_full_data() -> test_utils.JsonData: return { - "input_geode_object": "BRep", + "geode_object_type": "BRep", "filename": "test.og_brep", } json = get_full_data() response = client.post(route, json=json) assert response.status_code == 200 - has_missing_files = response.json["has_missing_files"] - mandatory_files = response.json["mandatory_files"] - additional_files = response.json["additional_files"] + has_missing_files = response.get_json()["has_missing_files"] + mandatory_files = response.get_json()["mandatory_files"] + additional_files = response.get_json()["additional_files"] assert type(has_missing_files) is bool assert type(mandatory_files) is list assert type(additional_files) is list @@ -82,13 +95,17 @@ def get_full_data(): test_utils.test_route_wrong_params(client, route, get_full_data) -def test_geographic_coordinate_systems(client): +def test_geographic_coordinate_systems(client: FlaskClient) -> None: route = f"/opengeodeweb_back/geographic_coordinate_systems" - get_full_data = lambda: {"input_geode_object": "BRep"} - # Normal test with geode_object 'BRep' + + def get_full_data() -> test_utils.JsonData: + return { + "geode_object_type": "BRep", + } + response = client.post(route, json=get_full_data()) assert response.status_code == 200 - crs_list = response.json["crs_list"] + crs_list = response.get_json()["crs_list"] assert type(crs_list) is list for crs in crs_list: assert type(crs) is dict @@ -97,12 +114,12 @@ def test_geographic_coordinate_systems(client): test_utils.test_route_wrong_params(client, route, get_full_data) -def test_inspect_file(client): +def test_inspect_file(client: FlaskClient) -> None: route = f"/opengeodeweb_back/inspect_file" - def get_full_data(): + def get_full_data() -> test_utils.JsonData: return { - "input_geode_object": "BRep", + "geode_object_type": "BRep", "filename": "corbi.og_brep", } @@ -111,174 +128,199 @@ def get_full_data(): # Normal test with geode_object 'BRep' response = client.post(route, json=json) assert response.status_code == 200 - inspection_result = response.json["inspection_result"] + inspection_result = response.get_json()["inspection_result"] assert type(inspection_result) is dict # Test all params test_utils.test_route_wrong_params(client, route, get_full_data) -def test_geode_objects_and_output_extensions(client): +def test_geode_objects_and_output_extensions(client: FlaskClient) -> None: route = "/opengeodeweb_back/geode_objects_and_output_extensions" - def get_full_data(): + def get_full_data() -> test_utils.JsonData: return { - "input_geode_object": "BRep", + "geode_object_type": "BRep", "filename": "corbi.og_brep", } response = client.post(route, json=get_full_data()) assert response.status_code == 200 - geode_objects_and_output_extensions = response.json[ + geode_objects_and_output_extensions = response.get_json()[ "geode_objects_and_output_extensions" ] assert type(geode_objects_and_output_extensions) is dict for geode_object, values in geode_objects_and_output_extensions.items(): assert type(values) is dict for output_extension, value in values.items(): - assert type(value) is dict - assert type(value["is_saveable"]) is bool + assert type(value) is bool # Test all params test_utils.test_route_wrong_params(client, route, get_full_data) -def test_save_viewable_file(client): +def test_save_viewable_file(client: FlaskClient) -> None: test_upload_file(client, filename="corbi.og_brep") route = f"/opengeodeweb_back/save_viewable_file" - def get_full_data(): + def get_full_data() -> test_utils.JsonData: return { - "input_geode_object": "BRep", + "geode_object_type": "BRep", "filename": "corbi.og_brep", } # Normal test with filename 'corbi.og_brep' response = client.post(route, json=get_full_data()) assert response.status_code == 200 - native_file_name = response.json["native_file_name"] - assert type(native_file_name) is str - viewable_file_name = response.json["viewable_file_name"] - assert type(viewable_file_name) is str - id = response.json.get("id") + native_file = response.get_json()["native_file"] + assert type(native_file) is str + viewable_file = response.get_json()["viewable_file"] + assert type(viewable_file) is str + id = response.get_json().get("id") assert type(id) is str - object_type = response.json["object_type"] + object_type = response.get_json()["viewer_type"] assert type(object_type) is str assert object_type in ["model", "mesh"] - binary_light_viewable = response.json["binary_light_viewable"] + binary_light_viewable = response.get_json()["binary_light_viewable"] assert type(binary_light_viewable) is str # Test all params test_utils.test_route_wrong_params(client, route, get_full_data) -def test_texture_coordinates(client, test_id): +def test_texture_coordinates(client: FlaskClient, test_id: str) -> None: with client.application.app_context(): file = os.path.join(data_dir, "hat.vtp") data = Data.create( - geode_object="PolygonalSurface3D", - viewer_object=geode_functions.get_object_type("PolygonalSurface3D"), + geode_object=GeodePolygonalSurface3D.geode_object_type(), + viewer_object=GeodePolygonalSurface3D.viewer_type(), input_file=file, ) - data.native_file_name = file + data.native_file = file session = get_session() if session: session.commit() - data_path = geode_functions.data_file_path(data.id, data.native_file_name) + data_path = geode_functions.data_file_path(data.id, data.native_file) os.makedirs(os.path.dirname(data_path), exist_ok=True) assert os.path.exists(data_path), f"File not found at {data_path}" response = client.post( "/opengeodeweb_back/texture_coordinates", json={"id": data.id} ) assert response.status_code == 200 - texture_coordinates = response.json["texture_coordinates"] + texture_coordinates = response.get_json()["texture_coordinates"] assert type(texture_coordinates) is list for texture_coordinate in texture_coordinates: assert type(texture_coordinate) is str -def test_vertex_attribute_names(client, test_id): +def test_vertex_attribute_names(client: FlaskClient, test_id: str) -> None: route = f"/opengeodeweb_back/vertex_attribute_names" with client.application.app_context(): file = os.path.join(data_dir, "test.vtp") data = Data.create( - geode_object="PolygonalSurface3D", - viewer_object=geode_functions.get_object_type("PolygonalSurface3D"), + geode_object=GeodePolygonalSurface3D.geode_object_type(), + viewer_object=GeodePolygonalSurface3D.viewer_type(), input_file=file, ) - data.native_file_name = file + data.native_file = file session = get_session() if session: session.commit() - data_path = geode_functions.data_file_path(data.id, data.native_file_name) + data_path = geode_functions.data_file_path(data.id, data.native_file) os.makedirs(os.path.dirname(data_path), exist_ok=True) assert os.path.exists(data_path), f"File not found at {data_path}" response = client.post(route, json={"id": data.id}) assert response.status_code == 200 - vertex_attribute_names = response.json["vertex_attribute_names"] + vertex_attribute_names = response.get_json()["vertex_attribute_names"] assert type(vertex_attribute_names) is list for vertex_attribute_name in vertex_attribute_names: assert type(vertex_attribute_name) is str -def test_polygon_attribute_names(client, test_id): +def test_cell_attribute_names(client: FlaskClient, test_id: str) -> None: + route = f"/opengeodeweb_back/cell_attribute_names" + + with client.application.app_context(): + file = os.path.join(data_dir, "test.og_rgd2d") + data = Data.create( + geode_object=GeodeRegularGrid2D.geode_object_type(), + viewer_object=GeodeRegularGrid2D.viewer_type(), + input_file=file, + ) + data.native_file = file + session = get_session() + if session: + session.commit() + + data_path = geode_functions.data_file_path(data.id, data.native_file) + os.makedirs(os.path.dirname(data_path), exist_ok=True) + assert os.path.exists(data_path), f"File not found at {data_path}" + response = client.post(route, json={"id": data.id}) + assert response.status_code == 200 + cell_attribute_names = response.get_json()["cell_attribute_names"] + assert type(cell_attribute_names) is list + for cell_attribute_name in cell_attribute_names: + assert type(cell_attribute_name) is str + + +def test_polygon_attribute_names(client: FlaskClient, test_id: str) -> None: route = f"/opengeodeweb_back/polygon_attribute_names" with client.application.app_context(): file = os.path.join(data_dir, "test.vtp") data = Data.create( - geode_object="PolygonalSurface3D", - viewer_object=geode_functions.get_object_type("PolygonalSurface3D"), + geode_object=GeodePolygonalSurface3D.geode_object_type(), + viewer_object=GeodePolygonalSurface3D.viewer_type(), input_file=file, ) - data.native_file_name = file + data.native_file = file session = get_session() if session: session.commit() - data_path = geode_functions.data_file_path(data.id, data.native_file_name) + data_path = geode_functions.data_file_path(data.id, data.native_file) os.makedirs(os.path.dirname(data_path), exist_ok=True) assert os.path.exists(data_path), f"File not found at {data_path}" response = client.post(route, json={"id": data.id}) assert response.status_code == 200 - polygon_attribute_names = response.json["polygon_attribute_names"] + polygon_attribute_names = response.get_json()["polygon_attribute_names"] assert type(polygon_attribute_names) is list for polygon_attribute_name in polygon_attribute_names: assert type(polygon_attribute_name) is str -def test_polyhedron_attribute_names(client, test_id): +def test_polyhedron_attribute_names(client: FlaskClient, test_id: str) -> None: route = f"/opengeodeweb_back/polyhedron_attribute_names" with client.application.app_context(): file = os.path.join(data_dir, "test.vtu") data = Data.create( - geode_object="PolyhedralSolid3D", - viewer_object=geode_functions.get_object_type("PolyhedralSolid3D"), + geode_object=GeodePolyhedralSolid3D.geode_object_type(), + viewer_object=GeodePolyhedralSolid3D.viewer_type(), input_file=file, ) - data.native_file_name = file + data.native_file = file session = get_session() if session: session.commit() - data_path = geode_functions.data_file_path(data.id, data.native_file_name) + data_path = geode_functions.data_file_path(data.id, data.native_file) os.makedirs(os.path.dirname(data_path), exist_ok=True) assert os.path.exists(data_path), f"File not found at {data_path}" response = client.post(route, json={"id": data.id}) - print(response.json) + print(response.get_json()) assert response.status_code == 200 - polyhedron_attribute_names = response.json["polyhedron_attribute_names"] + polyhedron_attribute_names = response.get_json()["polyhedron_attribute_names"] assert type(polyhedron_attribute_names) is list for polyhedron_attribute_name in polyhedron_attribute_names: assert type(polyhedron_attribute_name) is str -def test_database_uri_path(client): +def test_database_uri_path(client: FlaskClient) -> None: app = client.application with app.app_context(): base_dir = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/test_utils_functions.py b/tests/test_utils_functions.py index ea75794b..905217b1 100644 --- a/tests/test_utils_functions.py +++ b/tests/test_utils_functions.py @@ -4,53 +4,55 @@ # Third party imports import flask +from flask.ctx import AppContext +from flask.testing import FlaskClient import shutil import uuid import zipfile -import io +from pathlib import Path # Local application imports from opengeodeweb_microservice.database.data import Data -from opengeodeweb_microservice.database.connection import get_session -from opengeodeweb_back import geode_functions, utils_functions +from opengeodeweb_back import utils_functions +from opengeodeweb_back.geode_objects.geode_brep import GeodeBRep base_dir = os.path.abspath(os.path.dirname(__file__)) data_dir = os.path.join(base_dir, "data") -def test_increment_request_counter(app_context): +def test_increment_request_counter(app_context: AppContext) -> None: assert flask.current_app.config.get("REQUEST_COUNTER") == 0 utils_functions.increment_request_counter(flask.current_app) assert flask.current_app.config.get("REQUEST_COUNTER") == 1 -def test_decrement_request_counter(app_context): +def test_decrement_request_counter(app_context: AppContext) -> None: assert flask.current_app.config.get("REQUEST_COUNTER") == 1 utils_functions.decrement_request_counter(flask.current_app) assert flask.current_app.config.get("REQUEST_COUNTER") == 0 -def test_update_last_request_time(app_context): +def test_update_last_request_time(app_context: AppContext) -> None: LAST_REQUEST_TIME = flask.current_app.config.get("LAST_REQUEST_TIME") utils_functions.update_last_request_time(flask.current_app) - assert flask.current_app.config.get("LAST_REQUEST_TIME") >= LAST_REQUEST_TIME + assert flask.current_app.config.get("LAST_REQUEST_TIME", 0) >= LAST_REQUEST_TIME -def test_before_request(app_context): +def test_before_request(app_context: AppContext) -> None: assert flask.current_app.config.get("REQUEST_COUNTER") == 0 utils_functions.before_request(flask.current_app) assert flask.current_app.config.get("REQUEST_COUNTER") == 1 -def test_teardown_request(app_context): +def test_teardown_request(app_context: AppContext) -> None: LAST_REQUEST_TIME = flask.current_app.config.get("LAST_REQUEST_TIME") assert flask.current_app.config.get("REQUEST_COUNTER") == 1 utils_functions.teardown_request(flask.current_app) assert flask.current_app.config.get("REQUEST_COUNTER") == 0 - assert flask.current_app.config.get("LAST_REQUEST_TIME") >= LAST_REQUEST_TIME + assert flask.current_app.config.get("LAST_REQUEST_TIME", 0) >= LAST_REQUEST_TIME -def test_versions(): +def test_versions() -> None: list_packages = [ "OpenGeode-core", "OpenGeode-IO", @@ -63,13 +65,13 @@ def test_versions(): assert type(version) is dict -def test_extension_from_filename(): +def test_extension_from_filename() -> None: extension = utils_functions.extension_from_filename("test.toto") assert type(extension) is str assert extension.count(".") == 0 -def test_handle_exception(client): +def test_handle_exception(client: FlaskClient) -> None: route = "/error" response = client.post(route) assert response.status_code == 500 @@ -80,7 +82,7 @@ def test_handle_exception(client): assert type(data["code"]) is int -def test_create_data_folder_from_id(client): +def test_create_data_folder_from_id(client: FlaskClient) -> None: app = client.application with app.app_context(): test_id = str(uuid.uuid4()).replace("-", "") @@ -93,7 +95,7 @@ def test_create_data_folder_from_id(client): assert not os.path.exists(data_path) -def test_save_all_viewables_and_return_info(client): +def test_save_all_viewables_and_return_info(client: FlaskClient) -> None: app = client.application with app.app_context(): expected_db_path = os.path.join(data_dir, "project.db") @@ -102,41 +104,42 @@ def test_save_all_viewables_and_return_info(client): assert app.config["SQLALCHEMY_DATABASE_URI"] == expected_uri assert os.path.exists(expected_db_path) - geode_object = "BRep" - data = geode_functions.load( - geode_object, os.path.join(data_dir, "test.og_brep") - ) + geode_object = GeodeBRep.load(os.path.join(data_dir, "test.og_brep")) input_file = "test.og_brep" additional_files = ["additional_file.txt"] data_entry = Data.create( - geode_object=geode_object, - viewer_object=geode_functions.get_object_type(geode_object), + geode_object=geode_object.geode_object_type(), + viewer_object=geode_object.viewer_type(), input_file=input_file, additional_files=additional_files, ) data_path = utils_functions.create_data_folder_from_id(data_entry.id) result = utils_functions.save_all_viewables_and_return_info( - geode_object, data, data_entry, data_path + geode_object, data_entry, data_path ) assert isinstance(result, dict) - assert result["native_file_name"].startswith("native.") - assert result["viewable_file_name"].endswith(".vtm") + native_file = result["native_file"] + assert isinstance(native_file, str) + assert native_file.startswith("native.") + viewable_file = result["viewable_file"] + assert isinstance(viewable_file, str) + assert viewable_file.endswith(".vtm") assert isinstance(result["id"], str) assert len(result["id"]) == 32 assert re.match(r"[0-9a-f]{32}", result["id"]) - assert isinstance(result["object_type"], str) + assert isinstance(result["viewer_type"], str) assert isinstance(result["binary_light_viewable"], str) - assert result["geode_object"] == geode_object + assert result["geode_object_type"] == geode_object.geode_object_type() assert result["input_file"] == input_file db_entry = Data.get(result["id"]) assert db_entry is not None - assert db_entry.native_file_name == result["native_file_name"] - assert db_entry.viewable_file_name == result["viewable_file_name"] - assert db_entry.geode_object == geode_object + assert db_entry.native_file == result["native_file"] + assert db_entry.viewable_file == result["viewable_file"] + assert db_entry.geode_object == geode_object.geode_object_type() assert db_entry.input_file == input_file assert db_entry.additional_files == additional_files @@ -144,81 +147,76 @@ def test_save_all_viewables_and_return_info(client): assert os.path.exists(expected_data_path) -def test_save_all_viewables_commits_to_db(client): +def test_save_all_viewables_commits_to_db(client: FlaskClient) -> None: app = client.application with app.app_context(): - geode_object = "BRep" - data = geode_functions.load( - geode_object, os.path.join(data_dir, "test.og_brep") - ) + geode_object = GeodeBRep.load(os.path.join(data_dir, "test.og_brep")) input_file = "test.og_brep" - data_entry = Data.create( - geode_object=geode_object, - viewer_object=geode_functions.get_object_type(geode_object), + geode_object=geode_object.geode_object_type(), + viewer_object=geode_object.viewer_type(), input_file=input_file, additional_files=[], ) data_path = utils_functions.create_data_folder_from_id(data_entry.id) result = utils_functions.save_all_viewables_and_return_info( - geode_object, data, data_entry, data_path + geode_object, data_entry, data_path ) data_id = result["id"] + assert isinstance(data_id, str) db_entry_before = Data.get(data_id) assert db_entry_before is not None - assert db_entry_before.native_file_name == result["native_file_name"] + assert db_entry_before.native_file == result["native_file"] -def test_generate_native_viewable_and_light_viewable_from_object(client): +def test_generate_native_viewable_and_light_viewable_from_object( + client: FlaskClient, +) -> None: app = client.application with app.app_context(): - geode_object = "BRep" - data = geode_functions.load( - geode_object, os.path.join(data_dir, "test.og_brep") - ) + geode_object = GeodeBRep.load(os.path.join(data_dir, "test.og_brep")) result = ( utils_functions.generate_native_viewable_and_light_viewable_from_object( - geode_object, data + geode_object ) ) assert isinstance(result, dict) - assert isinstance(result["native_file_name"], str) - assert result["native_file_name"].startswith("native.") - assert isinstance(result["viewable_file_name"], str) - assert result["viewable_file_name"].endswith(".vtm") + assert isinstance(result["native_file"], str) + assert result["native_file"].startswith("native.") + assert isinstance(result["viewable_file"], str) + assert result["viewable_file"].endswith(".vtm") assert isinstance(result["id"], str) assert re.match(r"[0-9a-f]{32}", result["id"]) - assert isinstance(result["object_type"], str) + assert isinstance(result["viewer_type"], str) assert isinstance(result["binary_light_viewable"], str) - assert result["input_file"] == None + assert result["input_file"] == "" -def test_generate_native_viewable_and_light_viewable_from_file(client): +def test_generate_native_viewable_and_light_viewable_from_file( + client: FlaskClient, +) -> None: app = client.application with app.app_context(): - geode_object = "BRep" - input_filename = "test.og_brep" - result = utils_functions.generate_native_viewable_and_light_viewable_from_file( - geode_object, input_filename + GeodeBRep.geode_object_type(), "test.og_brep" ) assert isinstance(result, dict) - assert isinstance(result["native_file_name"], str) - assert result["native_file_name"].startswith("native.") - assert isinstance(result["viewable_file_name"], str) - assert result["viewable_file_name"].endswith(".vtm") + assert isinstance(result["native_file"], str) + assert result["native_file"].startswith("native.") + assert isinstance(result["viewable_file"], str) + assert result["viewable_file"].endswith(".vtm") assert isinstance(result["id"], str) assert re.match(r"[0-9a-f]{32}", result["id"]) - assert isinstance(result["object_type"], str) + assert isinstance(result["viewer_type"], str) assert isinstance(result["binary_light_viewable"], str) assert isinstance(result["input_file"], str) -def test_send_file_multiple_returns_zip(client, tmp_path): +def test_send_file_multiple_returns_zip(client: FlaskClient, tmp_path: Path) -> None: app = client.application with app.app_context(): app.config["UPLOAD_FOLDER"] = str(tmp_path) @@ -243,7 +241,9 @@ def test_send_file_multiple_returns_zip(client, tmp_path): response.close() -def test_send_file_single_returns_octet_binary(client, tmp_path): +def test_send_file_single_returns_octet_binary( + client: FlaskClient, tmp_path: Path +) -> None: app = client.application with app.app_context(): app.config["UPLOAD_FOLDER"] = str(tmp_path)