Repository navigation
Feat/crs assign - #286
Feat/crs assign#286MaxNumerique wants to merge 17 commits into
Conversation
…OpenGeodeWeb-Back into feat/crs_assign
PyInstaller loads libproj away from opengeode/share/proj, so PROJ cannot find proj.db: the CRS list is empty and conversions fail. TEMPORARY: default PROJ_DATA to the opengeode share folder and bundle it in the spec. To remove once OpenGeode sets PROJ_DATA itself and ships a PyInstaller hook.
CRS support — back-end partPart of the CRS work for Geode-solutions/Vease#798. Overview of the 4 PRs and merge order: see Geode-solutions/Vease#818. This PR goes first. New
|
| Route | Params | Response | Notes |
|---|---|---|---|
geographic_list |
geode_object_type |
crs_list: [{authority, code, name}] |
Replaces /geographic_coordinate_systems (removed, no alias: no project to keep compatible yet). 2D or 3D list depending on the type |
active |
id |
crs_name, crs_info? |
crs_info only when the active CRS is geographic |
assign_geographic |
id, authority, code, name |
crs |
Label only, coordinates unchanged. Only the native file is rewritten |
convert_geographic |
id, authority, code, name |
crs, binary_light_viewable |
400 if no geographic CRS yet, or if PROJ rejects the coordinates (out of domain) |
align |
id, source_points[3], target_points[3], authority, code, name |
crs, binary_light_viewable |
3-point affine transform (create_crs), then assigns the target CRS. 400 if a geographic CRS is already set, or if points are colinear |
Common rules:
- Supported objects:
CRS_GEODE_OBJECTSincrs_functions.py(BRep, Section, PointSet 2D/3D, EdgedCurve 2D/3D, SurfaceMesh 2D/3D, SolidMesh3D and their subclasses). Others → 400. Unknown id → 404. - Only CRS from the PROJ list are accepted.
geographic_coordinate_systems(is_3d)builds anAUTHORITY:CODE → infodict once (functools.cache, ~13k entries), andknown_geographic_infolooks it up. Unknown CRS → 400. Thenamestored comes from that list, not from the request. Before this, an unknown CRS was saved, and the next conversion crashed the whole process (segfault inOGRCoordinateTransformation::Transform). - Assigning or converting to a CRS that is already active → 400. A registered but inactive CRS with the same name is deleted first (
_prepare_crs_name), so it is recomputed instead of reactivating stale coordinates.
Models and meshes — geode_objects/*
- New methods on the CRS-capable wrappers:
crs_manager(),delete_crs,assign_crs,convert_crs,create_crs. Models (BRep, Section) have no CRS manager of their own: they apply the operation to every component mesh, andcrs_manager()reads the first component (ValueErrorif the model has none). geode_objects/crs_coordinates.py→delete_crs_with_coordinates: when a geographic CRS is deleted, its coordinate vertex attribute is deleted too, unless the active CRS still uses it. Without this, each convert round trip left a full coordinate array in the native file (874 → 2604 bytes after 6 Lambert↔UTM round trips on a 1-point PointSet).
CRS in data responses
crs_functions.active_crs(geode_object)returns{authority, code, name}orNone. It catchesValueError/RuntimeErrorand logs a warning: the CRS is extra information and must never fail a data response.utils_functions.save_all_viewables_and_return_infoaddscrswhen present, sosave_viewable_file, thecreate/*routes and project import return it. It is computed from the loaded object, not stored in the database: the native file stays the source of truth.- Optional
crsdeclared in the response schemas:save_viewable_file,create/point_set,create/edged_curve,create/polygonal_surface, and the CRS routes.
⚠️ Temporary PROJ workaround
In packaged apps (PyInstaller), libproj is loaded from _internal/ instead of opengeode/lib64/, and opengeode/share/proj is not bundled. PROJ then cannot find proj.db (ERROR 1: PROJ: proj_get_crs_info_list_from_database: Cannot find proj.db): the CRS list is empty and conversions fail. This is what broke the Vease browser e2e on CI (Linux and Windows).
crs_functions.py:os.environ.setdefault("PROJ_DATA", <opengeode>/share/proj). An existingPROJ_DATAis respected; no effect in a regular venv.opengeodeweb-back.spec:collect_data_files('opengeode', subdir='share').- Both are marked
TEMPORARY. The proper fix belongs in OpenGeode: setPROJ_DATA(orOSRSetPROJSearchPaths) on import, and ship a PyInstaller hook (pyinstaller40entry point) collectingshare/. Until then, every packaged back using CRS needs the spec line (Vease-Back is done in feat(crsAssign): crs support Vease-Back#158; PEGGHy-Back and Vease-Modeling-Back will need it).
Other changes
test_utils.py:check_upload_file/check_save_viewable_filemoved here fromtests/test_routes.pyto be shared.pyproject.toml:crs.schemaspackage data..releaserc: semantic-release success/fail comments disabled (unrelated to CRS).
Tests — tests/test_crs_routes.py
geographic_list; active; assign (and errors: 404, unsupported type, already active); convert (and errors, out of domain); align (and colinear points, already georeferenced); reassign after convert; crs returned by save_viewable_file for a georeferenced native file; unknown CRS on assign/align/convert and name taken from the list; no file growth over round trips; assign leaves viewables untouched; unreadable CRS does not fail active_crs; PROJ_DATA points to an existing proj.db.
Known limitations
- RegularGrid 2D/3D subclass SurfaceMesh2D/SolidMesh3D, so convert/align accept them, but only the vertices move, not the grid origin/spacing. Left as is for now.
- No lock on concurrent writes to the same data (same pattern as
apply_time_series).
…ible assign - Assign keeps the original coordinates as the "points" CRS, so every re-align restarts from them (Align is no longer refused on georeferenced data). - Source points carry a `picked` flag: picked points are mapped from the displayed coordinates back to "points", typed points are original ones. - assign_geographic returns `implausible` when the data lies outside the CRS area of use (read from the PROJ database shipped with opengeode). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1640532 to
dc7b139
Compare
- New `mode` param on crs/align: "similarity" (default) or "affine". - Similarity takes 1 pair (translation) or 2 pairs (translation, rotation and uniform scale). Two frames with perpendicular axes of equal length go through the existing create_*_coordinate_system, then every Z of the aligned CRS is scaled around Z = 0 (meshes and every BRep component). - The response returns the applied `scale` and `rotation` (degrees). - Similarity onto a non projected CRS (degrees) is refused: Z would be flattened and (lat, lon) axes would mirror the data. - Affine keeps the 3 pairs behaviour (X/Y only). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New optional `linked_ids` on crs/align: data sharing the local frame of the aligned one (a model and its wells) are aligned in the same request, with the same frames and Z scale. - The alignment is computed once, on the aligned data (picked points mapped back to its original coordinates); each linked data restarts from its own original coordinates. - Every data is loaded and checked before any file is written: an unknown or unsupported linked id leaves everything untouched. - The response lists the linked data with their CRS and light viewable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- assign_geographic and convert_geographic take optional linked_ids, like align: every data is loaded and changed in memory before any file is written, so an unknown or invalid linked data leaves everything untouched. - The responses list the linked data: their CRS, plus the plausibility for assign and the new light viewable for convert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| "code": { | ||
| "type": "string" | ||
| }, | ||
| "name": { |
| source = _coordinate_system(source_points) | ||
| target = _coordinate_system(target_points) | ||
| except RuntimeError: # OpenGeode cannot build a coordinate system from colinear points | ||
| flask.abort(400, "Points must not be colinear") |
There was a problem hiding this comment.
Et si l'erreur est autre chose ?
| AFFINE_PAIRS = 3 | ||
|
|
||
|
|
||
| def _prepare_crs_name( |
There was a problem hiding this comment.
Pourquoi ne pas mettre tout ça dans crs_functions et ne laisser que le routes dans blueprint ?
assign_geographic takes an optional only_plausible flag. The coordinates of every data are checked before any change; with the flag, a data that does not fit the CRS is neither assigned nor saved, and its response has no crs. The front uses it to give the project CRS to data without CRS automatically. Without the flag, the assignment is unchanged and the plausibility stays a warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No description provided.