From 719f0c33c52a49c7510ebc338d86ad7ea9d6bc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20Zim=C3=A1nyi?= Date: Sun, 1 Nov 2026 00:46:23 +0100 Subject: [PATCH] Generate the regular method families of the temporal points from the catalog tools/oo_codegen/codegen.py writes the comparisons, the spatial relationships, the distances and the restrictions of TGeomPoint and TGeogPoint as the mixins pymeos/main/_generated/tgeompoint_methods.py and tgeogpoint_methods.py, each the first base of its class. A class answers a member with the overload whose catalog sqlSignatures take its SQL type as first operand, tgeompoint or tgeogpoint, from the tpoint_* and tgeo_* functions of the member, so each class carries the surface MobilityDB declares for its type: a temporal point has no containment and no covering, a temporal geography point has no touches, and its disjoint and intersects take another temporal geography point. A geometry argument of a temporal geography point converts as a geography, an STBox reaches a temporal point through at, minus and nearest_approach_distance, the functions MEOS defines for it, and a temporal point restricts to an elevation span through tpoint_at_elevation and tpoint_minus_elevation. Temporal.at and Temporal.minus restrict to a set through temporal_at_values and temporal_minus_values. The generator passes True for a trailing spheroid parameter, the default of the SQL function, and orders the dispatch so that a point argument reaches atValue before atGeometry. Witness: against PyMEOS master 84dd465, TGeomPointSeq('[Point(1 1 1)@2001-01-01, Point(1 1 3)@2001-01-03]').at(FloatSpan('[1.5, 2]')) raises TypeError and TGeomPointSeq('[Point(1 1)@2001-01-01, Point(2 2)@2001-01-02]').ever_touches(Point(1, 1)) answers False where eTouches(tgeompoint, geometry), the function etouches_tpoint_geo, answers true; against this branch the first answers {[POINT Z (1 1 1.5)@2001-01-01 12:00:00+00, POINT Z (1 1 2)@2001-01-02 00:00:00+00]} and the second True. Measured: on the catalog of MobilityDB a8e931bf50, 5446 functions accounted for; TGeomPoint's mixin holds 24 methods and TGeogPoint's 21, in place of 27 hand-written methods of TPoint, TGeomPoint and TGeogPoint; a second generator run rewrites the six mixins byte for byte; 4859 tests, 8 of them the elevation and set restrictions and the absent containment, covering and geography touches; ruff check and ruff format report no finding. Why: the bindings are projections of the MEOS catalog, so the methods of a temporal point are the functions the catalog declares for its SQL type. --- pymeos/main/_generated/tgeogpoint_methods.py | 367 +++++++++++ pymeos/main/_generated/tgeompoint_methods.py | 409 ++++++++++++ pymeos/main/_generated/trgeometry_methods.py | 12 +- pymeos/main/tpoint.py | 646 +------------------ pymeos/temporal/temporal.py | 8 +- tests/main/tgeogpoint_test.py | 8 +- tests/main/tgeompoint_test.py | 50 +- tests/test_oo_codegen_coverage.py | 4 +- tools/oo_codegen/codegen.py | 91 ++- 9 files changed, 922 insertions(+), 673 deletions(-) create mode 100644 pymeos/main/_generated/tgeogpoint_methods.py create mode 100644 pymeos/main/_generated/tgeompoint_methods.py diff --git a/pymeos/main/_generated/tgeogpoint_methods.py b/pymeos/main/_generated/tgeogpoint_methods.py new file mode 100644 index 00000000..d4b86e4e --- /dev/null +++ b/pymeos/main/_generated/tgeogpoint_methods.py @@ -0,0 +1,367 @@ +# Copyright (c) 2016-2026, Université libre de Bruxelles and PyMEOS +# contributors. Licensed under the PostgreSQL License (see LICENSE). +# +# ============================================================================ +# GENERATED by tools/oo_codegen/codegen.py -- DO NOT EDIT. +# Regenerate: +# python3 tools/oo_codegen/codegen.py --idl --mixin geogpoint \ +# --mixin-out pymeos/main/_generated/tgeogpoint_methods.py +# ============================================================================ +# +# Wired into pymeos.main.TGeogPoint via TGeogPointRegularMixin. Every method +# dispatches by argument type to the EXACT pymeos_cffi backing the +# hand-written method used -- same native call, same transforms, same +# result, never reimplemented: identical by construction. +"""Generated regular OO methods for the TGeogPoint family.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import shapely.geometry as shp +import shapely.geometry.base as shpb +from pymeos_cffi import * + +from ...collections import FloatSpan +from ...temporal import Temporal + +if TYPE_CHECKING: + pass + + +class TGeogPointRegularMixin: + """Generated regular families (comparison, spatial relationship, + distance, restriction) for :class:`TGeogPoint`.""" + + def always_equal(self, other): + """Generated regular ``always_equal``. + + MEOS Functions: + always_eq_geo_tgeo, always_eq_tgeo_geo, always_eq_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = always_eq_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = always_eq_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def always_intersects(self, other): + """Generated regular ``always_intersects``. + + MEOS Functions: + aintersects_geo_tgeo, aintersects_tgeo_geo, aintersects_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = aintersects_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = aintersects_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def always_not_equal(self, other): + """Generated regular ``always_not_equal``. + + MEOS Functions: + always_ne_geo_tgeo, always_ne_tgeo_geo, always_ne_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = always_ne_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = always_ne_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def at(self, other): + """Generated regular ``at``. + + MEOS Functions: + tgeo_at_geom, tgeo_at_stbox, tgeo_at_value, tpoint_at_elevation, tpoint_at_geom, tpoint_at_value + """ + from ...boxes import STBox + + if isinstance(other, shp.Point): + result = tgeo_at_value(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, shpb.BaseGeometry): + result = tgeo_at_geom(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, FloatSpan): + result = tpoint_at_elevation(self._inner, other._inner) + elif isinstance(other, STBox): + result = tgeo_at_stbox(self._inner, other._inner, True) + else: + return super().at(other) + return Temporal._factory(result) + + def disjoint(self, other): + """Generated regular ``disjoint``. + + MEOS Functions: + tdisjoint_geo_tgeo, tdisjoint_tgeo_geo, tdisjoint_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, TGeogPoint): + result = tdisjoint_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def distance(self, other): + """Generated regular ``distance``. + + MEOS Functions: + tdistance_tgeo_geo, tdistance_tgeo_tgeo, tdistance_tposechain_geo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = tdistance_tgeo_geo(self._inner, geo_to_gserialized(other, True), True) + elif isinstance(other, TGeogPoint): + result = tdistance_tgeo_tgeo(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def ever_equal(self, other): + """Generated regular ``ever_equal``. + + MEOS Functions: + ever_eq_geo_tgeo, ever_eq_tgeo_geo, ever_eq_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = ever_eq_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = ever_eq_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def ever_intersects(self, other): + """Generated regular ``ever_intersects``. + + MEOS Functions: + eintersects_geo_tgeo, eintersects_tgeo_geo, eintersects_tgeo_tgeo, eintersects_tpcpoint_geo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = eintersects_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = eintersects_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def ever_not_equal(self, other): + """Generated regular ``ever_not_equal``. + + MEOS Functions: + ever_ne_geo_tgeo, ever_ne_tgeo_geo, ever_ne_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = ever_ne_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = ever_ne_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def intersects(self, other): + """Generated regular ``intersects``. + + MEOS Functions: + tintersects_geo_tgeo, tintersects_tgeo_geo, tintersects_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, TGeogPoint): + result = tintersects_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def is_always_disjoint(self, other): + """Generated regular ``is_always_disjoint``. + + MEOS Functions: + adisjoint_geo_tgeo, adisjoint_tgeo_geo, adisjoint_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = adisjoint_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = adisjoint_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def is_always_within_distance(self, other, distance): + """Generated regular ``is_always_within_distance``. + + MEOS Functions: + adwithin_geo_tgeo, adwithin_tgeo_geo, adwithin_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, TGeogPoint): + result = adwithin_tgeo_tgeo(self._inner, other._inner, distance, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def is_ever_disjoint(self, other): + """Generated regular ``is_ever_disjoint``. + + MEOS Functions: + edisjoint_geo_tgeo, edisjoint_tgeo_geo, edisjoint_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = edisjoint_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, TGeogPoint): + result = edisjoint_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def is_ever_within_distance(self, other, distance): + """Generated regular ``is_ever_within_distance``. + + MEOS Functions: + edwithin_geo_tgeo, edwithin_tgeo_geo, edwithin_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = edwithin_tgeo_geo(self._inner, geo_to_gserialized(other, True), distance, True) + elif isinstance(other, TGeogPoint): + result = edwithin_tgeo_tgeo(self._inner, other._inner, distance, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def minus(self, other): + """Generated regular ``minus``. + + MEOS Functions: + tgeo_minus_geom, tgeo_minus_stbox, tgeo_minus_value, tpoint_minus_elevation, tpoint_minus_geom, tpoint_minus_value + """ + from ...boxes import STBox + + if isinstance(other, shp.Point): + result = tgeo_minus_value(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, shpb.BaseGeometry): + result = tgeo_minus_geom(self._inner, geo_to_gserialized(other, True)) + elif isinstance(other, FloatSpan): + result = tpoint_minus_elevation(self._inner, other._inner) + elif isinstance(other, STBox): + result = tgeo_minus_stbox(self._inner, other._inner, True) + else: + return super().minus(other) + return Temporal._factory(result) + + def nearest_approach_distance(self, other): + """Generated regular ``nearest_approach_distance``. + + MEOS Functions: + nad_stbox_geo, nad_tgeo_geo, nad_tgeo_stbox, nad_tgeo_tgeo, nad_tpcpoint_geo, nad_tposechain_geo + """ + from ...boxes import STBox + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = nad_tgeo_geo(self._inner, geo_to_gserialized(other, True), True) + elif isinstance(other, TGeogPoint): + result = nad_tgeo_tgeo(self._inner, other._inner, True) + elif isinstance(other, STBox): + result = nad_tgeo_stbox(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result + + def nearest_approach_instant(self, other): + """Generated regular ``nearest_approach_instant``. + + MEOS Functions: + nai_tgeo_geo, nai_tgeo_tgeo, nai_tposechain_geo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = nai_tgeo_geo(self._inner, geo_to_gserialized(other, True), True) + elif isinstance(other, TGeogPoint): + result = nai_tgeo_tgeo(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def shortest_line(self, other): + """Generated regular ``shortest_line``. + + MEOS Functions: + shortestline_tgeo_geo, shortestline_tgeo_tgeo, shortestline_tposechain_geo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = shortestline_tgeo_geo(self._inner, geo_to_gserialized(other, True), True) + elif isinstance(other, TGeogPoint): + result = shortestline_tgeo_tgeo(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return gserialized_to_shapely_geometry(result, 10) + + def temporal_equal(self, other): + """Generated regular ``temporal_equal``. + + MEOS Functions: + teq_geo_tgeo, teq_tgeo_geo + """ + if isinstance(other, shpb.BaseGeometry): + result = teq_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + else: + return super().temporal_equal(other) + return Temporal._factory(result) + + def temporal_not_equal(self, other): + """Generated regular ``temporal_not_equal``. + + MEOS Functions: + tne_geo_tgeo, tne_tgeo_geo + """ + if isinstance(other, shpb.BaseGeometry): + result = tne_tgeo_geo(self._inner, geo_to_gserialized(other, True)) + else: + return super().temporal_not_equal(other) + return Temporal._factory(result) + + def within_distance(self, other, distance): + """Generated regular ``within_distance``. + + MEOS Functions: + tdwithin_geo_tgeo, tdwithin_tgeo_geo, tdwithin_tgeo_tgeo + """ + from ..tpoint import TGeogPoint + + if isinstance(other, shpb.BaseGeometry): + result = tdwithin_tgeo_geo(self._inner, geo_to_gserialized(other, True), distance, True) + elif isinstance(other, TGeogPoint): + result = tdwithin_tgeo_tgeo(self._inner, other._inner, distance, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) diff --git a/pymeos/main/_generated/tgeompoint_methods.py b/pymeos/main/_generated/tgeompoint_methods.py new file mode 100644 index 00000000..d997e851 --- /dev/null +++ b/pymeos/main/_generated/tgeompoint_methods.py @@ -0,0 +1,409 @@ +# Copyright (c) 2016-2026, Université libre de Bruxelles and PyMEOS +# contributors. Licensed under the PostgreSQL License (see LICENSE). +# +# ============================================================================ +# GENERATED by tools/oo_codegen/codegen.py -- DO NOT EDIT. +# Regenerate: +# python3 tools/oo_codegen/codegen.py --idl --mixin geompoint \ +# --mixin-out pymeos/main/_generated/tgeompoint_methods.py +# ============================================================================ +# +# Wired into pymeos.main.TGeomPoint via TGeomPointRegularMixin. Every method +# dispatches by argument type to the EXACT pymeos_cffi backing the +# hand-written method used -- same native call, same transforms, same +# result, never reimplemented: identical by construction. +"""Generated regular OO methods for the TGeomPoint family.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import shapely.geometry as shp +import shapely.geometry.base as shpb +from pymeos_cffi import * + +from ...collections import FloatSpan +from ...temporal import Temporal + +if TYPE_CHECKING: + pass + + +class TGeomPointRegularMixin: + """Generated regular families (comparison, spatial relationship, + distance, restriction) for :class:`TGeomPoint`.""" + + def always_equal(self, other): + """Generated regular ``always_equal``. + + MEOS Functions: + always_eq_geo_tgeo, always_eq_tgeo_geo, always_eq_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = always_eq_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = always_eq_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def always_intersects(self, other): + """Generated regular ``always_intersects``. + + MEOS Functions: + aintersects_geo_tgeo, aintersects_tgeo_geo, aintersects_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = aintersects_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = aintersects_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def always_not_equal(self, other): + """Generated regular ``always_not_equal``. + + MEOS Functions: + always_ne_geo_tgeo, always_ne_tgeo_geo, always_ne_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = always_ne_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = always_ne_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def always_touches(self, other): + """Generated regular ``always_touches``. + + MEOS Functions: + atouches_geo_tgeo, atouches_geo_tpoint, atouches_tgeo_geo, atouches_tgeo_tgeo, atouches_tpoint_geo + """ + if isinstance(other, shpb.BaseGeometry): + result = atouches_tpoint_geo(self._inner, geo_to_gserialized(other, False)) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def at(self, other): + """Generated regular ``at``. + + MEOS Functions: + tgeo_at_geom, tgeo_at_stbox, tgeo_at_value, tpoint_at_elevation, tpoint_at_geom, tpoint_at_value + """ + from ...boxes import STBox + + if isinstance(other, shp.Point): + result = tgeo_at_value(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, shpb.BaseGeometry): + result = tgeo_at_geom(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, FloatSpan): + result = tpoint_at_elevation(self._inner, other._inner) + elif isinstance(other, STBox): + result = tgeo_at_stbox(self._inner, other._inner, True) + else: + return super().at(other) + return Temporal._factory(result) + + def disjoint(self, other): + """Generated regular ``disjoint``. + + MEOS Functions: + tdisjoint_geo_tgeo, tdisjoint_tgeo_geo, tdisjoint_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = tdisjoint_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = tdisjoint_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def distance(self, other): + """Generated regular ``distance``. + + MEOS Functions: + tdistance_tgeo_geo, tdistance_tgeo_tgeo, tdistance_tposechain_geo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = tdistance_tgeo_geo(self._inner, geo_to_gserialized(other, False), True) + elif isinstance(other, TGeomPoint): + result = tdistance_tgeo_tgeo(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def ever_equal(self, other): + """Generated regular ``ever_equal``. + + MEOS Functions: + ever_eq_geo_tgeo, ever_eq_tgeo_geo, ever_eq_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = ever_eq_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = ever_eq_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def ever_intersects(self, other): + """Generated regular ``ever_intersects``. + + MEOS Functions: + eintersects_geo_tgeo, eintersects_tgeo_geo, eintersects_tgeo_tgeo, eintersects_tpcpoint_geo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = eintersects_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = eintersects_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def ever_not_equal(self, other): + """Generated regular ``ever_not_equal``. + + MEOS Functions: + ever_ne_geo_tgeo, ever_ne_tgeo_geo, ever_ne_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = ever_ne_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = ever_ne_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result > 0 + + def ever_touches(self, other): + """Generated regular ``ever_touches``. + + MEOS Functions: + etouches_geo_tgeo, etouches_geo_tpoint, etouches_tgeo_geo, etouches_tgeo_tgeo, etouches_tpoint_geo + """ + if isinstance(other, shpb.BaseGeometry): + result = etouches_tpoint_geo(self._inner, geo_to_gserialized(other, False)) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def intersects(self, other): + """Generated regular ``intersects``. + + MEOS Functions: + tintersects_geo_tgeo, tintersects_tgeo_geo, tintersects_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = tintersects_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = tintersects_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def is_always_disjoint(self, other): + """Generated regular ``is_always_disjoint``. + + MEOS Functions: + adisjoint_geo_tgeo, adisjoint_tgeo_geo, adisjoint_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = adisjoint_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = adisjoint_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def is_always_within_distance(self, other, distance): + """Generated regular ``is_always_within_distance``. + + MEOS Functions: + adwithin_geo_tgeo, adwithin_tgeo_geo, adwithin_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = adwithin_tgeo_geo(self._inner, geo_to_gserialized(other, False), distance, True) + elif isinstance(other, TGeomPoint): + result = adwithin_tgeo_tgeo(self._inner, other._inner, distance, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def is_ever_disjoint(self, other): + """Generated regular ``is_ever_disjoint``. + + MEOS Functions: + edisjoint_geo_tgeo, edisjoint_tgeo_geo, edisjoint_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = edisjoint_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, TGeomPoint): + result = edisjoint_tgeo_tgeo(self._inner, other._inner) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def is_ever_within_distance(self, other, distance): + """Generated regular ``is_ever_within_distance``. + + MEOS Functions: + edwithin_geo_tgeo, edwithin_tgeo_geo, edwithin_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = edwithin_tgeo_geo(self._inner, geo_to_gserialized(other, False), distance, True) + elif isinstance(other, TGeomPoint): + result = edwithin_tgeo_tgeo(self._inner, other._inner, distance, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result == 1 + + def minus(self, other): + """Generated regular ``minus``. + + MEOS Functions: + tgeo_minus_geom, tgeo_minus_stbox, tgeo_minus_value, tpoint_minus_elevation, tpoint_minus_geom, tpoint_minus_value + """ + from ...boxes import STBox + + if isinstance(other, shp.Point): + result = tgeo_minus_value(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, shpb.BaseGeometry): + result = tgeo_minus_geom(self._inner, geo_to_gserialized(other, False)) + elif isinstance(other, FloatSpan): + result = tpoint_minus_elevation(self._inner, other._inner) + elif isinstance(other, STBox): + result = tgeo_minus_stbox(self._inner, other._inner, True) + else: + return super().minus(other) + return Temporal._factory(result) + + def nearest_approach_distance(self, other): + """Generated regular ``nearest_approach_distance``. + + MEOS Functions: + nad_stbox_geo, nad_tgeo_geo, nad_tgeo_stbox, nad_tgeo_tgeo, nad_tpcpoint_geo, nad_tposechain_geo + """ + from ...boxes import STBox + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = nad_tgeo_geo(self._inner, geo_to_gserialized(other, False), True) + elif isinstance(other, TGeomPoint): + result = nad_tgeo_tgeo(self._inner, other._inner, True) + elif isinstance(other, STBox): + result = nad_tgeo_stbox(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return result + + def nearest_approach_instant(self, other): + """Generated regular ``nearest_approach_instant``. + + MEOS Functions: + nai_tgeo_geo, nai_tgeo_tgeo, nai_tposechain_geo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = nai_tgeo_geo(self._inner, geo_to_gserialized(other, False), True) + elif isinstance(other, TGeomPoint): + result = nai_tgeo_tgeo(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def shortest_line(self, other): + """Generated regular ``shortest_line``. + + MEOS Functions: + shortestline_tgeo_geo, shortestline_tgeo_tgeo, shortestline_tposechain_geo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = shortestline_tgeo_geo(self._inner, geo_to_gserialized(other, False), True) + elif isinstance(other, TGeomPoint): + result = shortestline_tgeo_tgeo(self._inner, other._inner, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return gserialized_to_shapely_geometry(result, 10) + + def temporal_equal(self, other): + """Generated regular ``temporal_equal``. + + MEOS Functions: + teq_geo_tgeo, teq_tgeo_geo + """ + if isinstance(other, shpb.BaseGeometry): + result = teq_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + else: + return super().temporal_equal(other) + return Temporal._factory(result) + + def temporal_not_equal(self, other): + """Generated regular ``temporal_not_equal``. + + MEOS Functions: + tne_geo_tgeo, tne_tgeo_geo + """ + if isinstance(other, shpb.BaseGeometry): + result = tne_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + else: + return super().temporal_not_equal(other) + return Temporal._factory(result) + + def touches(self, other): + """Generated regular ``touches``. + + MEOS Functions: + ttouches_geo_tgeo, ttouches_tgeo_geo, ttouches_tgeo_tgeo + """ + if isinstance(other, shpb.BaseGeometry): + result = ttouches_tgeo_geo(self._inner, geo_to_gserialized(other, False)) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) + + def within_distance(self, other, distance): + """Generated regular ``within_distance``. + + MEOS Functions: + tdwithin_geo_tgeo, tdwithin_tgeo_geo, tdwithin_tgeo_tgeo + """ + from ..tpoint import TGeomPoint + + if isinstance(other, shpb.BaseGeometry): + result = tdwithin_tgeo_geo(self._inner, geo_to_gserialized(other, False), distance, True) + elif isinstance(other, TGeomPoint): + result = tdwithin_tgeo_tgeo(self._inner, other._inner, distance, True) + else: + raise TypeError(f"Operation not supported with type {other.__class__}") + return Temporal._factory(result) diff --git a/pymeos/main/_generated/trgeometry_methods.py b/pymeos/main/_generated/trgeometry_methods.py index 8a7e45da..416d6c24 100644 --- a/pymeos/main/_generated/trgeometry_methods.py +++ b/pymeos/main/_generated/trgeometry_methods.py @@ -103,10 +103,10 @@ def at(self, other): """ from ...boxes import STBox - if isinstance(other, shpb.BaseGeometry): - result = trgeometry_at_geom(self._inner, geometry_to_gserialized(other)) - elif isinstance(other, Pose): + if isinstance(other, Pose): result = trgeometry_at_value(self._inner, other._inner) + elif isinstance(other, shpb.BaseGeometry): + result = trgeometry_at_geom(self._inner, geometry_to_gserialized(other)) elif isinstance(other, PoseSet): result = trgeometry_at_values(self._inner, other._inner) elif isinstance(other, STBox): @@ -297,10 +297,10 @@ def minus(self, other): """ from ...boxes import STBox - if isinstance(other, shpb.BaseGeometry): - result = trgeometry_minus_geom(self._inner, geometry_to_gserialized(other)) - elif isinstance(other, Pose): + if isinstance(other, Pose): result = trgeometry_minus_value(self._inner, other._inner) + elif isinstance(other, shpb.BaseGeometry): + result = trgeometry_minus_geom(self._inner, geometry_to_gserialized(other)) elif isinstance(other, PoseSet): result = trgeometry_minus_values(self._inner, other._inner) elif isinstance(other, STBox): diff --git a/pymeos/main/tpoint.py b/pymeos/main/tpoint.py index c528f30e..4b080da9 100644 --- a/pymeos/main/tpoint.py +++ b/pymeos/main/tpoint.py @@ -14,6 +14,8 @@ from ..collections import * from ..mixins import TSimplifiable, TSpatialIO from ..temporal import Temporal, TInstant, TInterpolation, TSequence, TSequenceSet +from ._generated.tgeogpoint_methods import TGeogPointRegularMixin +from ._generated.tgeompoint_methods import TGeomPointRegularMixin from .tbool import TBool from .tfloat import TFloat, TFloatInst, TFloatSeq, TFloatSeqSet @@ -487,66 +489,6 @@ def transform(self: Self, srid: int) -> Self: return Temporal._factory(result) # ------------------------- Restrictions ---------------------------------- - def at(self, other: shpb.BaseGeometry | GeoSet | STBox | Time) -> TG: - """ - Returns a new temporal object with the values of `self` restricted to `other`. - - Args: - other: An object to restrict the values of `self` to. - - Returns: - A new :TPoint: with the values of `self` restricted to `other`. - - MEOS Functions: - tpoint_at_value, tpoint_at_stbox, temporal_at_values, - temporal_at_timestamp, temporal_at_tstzset, temporal_at_tstzspan, temporal_at_tstzspanset - """ - from ..boxes import STBox - - if isinstance(other, shp.Point): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tpoint_at_value(self._inner, gs) - elif isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tgeo_at_geom(self._inner, gs) - elif isinstance(other, GeoSet): - result = temporal_at_values(self._inner, other._inner) - elif isinstance(other, STBox): - result = tgeo_at_stbox(self._inner, other._inner, True) - else: - return super().at(other) - return Temporal._factory(result) - - def minus(self, other: shpb.BaseGeometry | GeoSet | STBox | Time) -> TG: - """ - Returns a new temporal object with the values of `self` restricted to the complement of `other`. - - Args: - other: An object to restrict the values of `self` to the complement of. - - Returns: - A new :TPoint: with the values of `self` restricted to the complement of `other`. - - MEOS Functions: - tpoint_minus_value, tpoint_minus_stbox, temporal_minus_values, - temporal_minus_timestamp, temporal_minus_tstzset, temporal_minus_tstzspan, temporal_minus_tstzspanset - """ - from ..boxes import STBox - - if isinstance(other, shp.Point): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tpoint_minus_value(self._inner, gs) - elif isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tgeo_minus_geom(self._inner, gs) - elif isinstance(other, GeoSet): - result = temporal_minus_values(self._inner, other._inner) - elif isinstance(other, STBox): - result = tgeo_minus_stbox(self._inner, other._inner, True) - else: - return super().minus(other) - return Temporal._factory(result) - # ------------------------- Position Operations --------------------------- def is_left(self, other: Temporal | Box) -> bool: """ @@ -753,107 +695,6 @@ def is_ever_contained_in(self, container: shpb.BaseGeometry | STBox) -> bool: raise TypeError(f"Operation not supported with type {container.__class__}") return result == 1 - def is_ever_disjoint(self, other: shpb.BaseGeometry | TPoint) -> bool: - """ - Returns whether the temporal point is ever disjoint from `other`. - - Args: - other: An object to check for disjointness with. - - Returns: - A :class:`bool` indicating whether the temporal point is ever disjoint from `other`. - - MEOS Functions: - edisjoint_tgeo_geo, edisjoint_tgeo_tgeo - """ - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = edisjoint_tgeo_geo(self._inner, gs) - elif isinstance(other, TPoint): - result = edisjoint_tgeo_tgeo(self._inner, other._inner) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return result == 1 - - def is_ever_within_distance(self, other: shpb.BaseGeometry | TPoint | STBox, distance: float) -> bool: - """ - Returns whether the temporal point is ever within `distance` of `other`. - - Args: - other: An object to check the distance to. - distance: The distance to check in units of the spatial reference system. - - Returns: - A :class:`bool` indicating whether the temporal point is ever within `distance` of `other`. - - MEOS Functions: - edwithin_tgeo_geo, edwithin_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = edwithin_tgeo_geo(self._inner, gs, distance, spheroid=True) - elif isinstance(other, STBox): - result = edwithin_tgeo_geo(self._inner, stbox_to_geo(other._inner), distance, spheroid=True) - elif isinstance(other, TPoint): - result = edwithin_tgeo_tgeo(self._inner, other._inner, distance, spheroid=True) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return result == 1 - - def ever_intersects(self, other: shpb.BaseGeometry | TPoint | STBox) -> bool: - """ - Returns whether the temporal point ever intersects `other`. - - Args: - other: An object to check for intersection with. - - Returns: - A :class:`bool` indicating whether the temporal point ever intersects `other`. - - MEOS Functions: - eintersects_tgeo_geo, eintersects_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = eintersects_tgeo_geo(self._inner, gs) - elif isinstance(other, STBox): - result = eintersects_tgeo_geo(self._inner, stbox_to_geo(other._inner)) - elif isinstance(other, TPoint): - result = eintersects_tgeo_tgeo(self._inner, other._inner) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return result == 1 - - def ever_touches(self, other: shpb.BaseGeometry | STBox | TPoint) -> bool: - """ - Returns whether the temporal point ever touches `other`. - - Args: - other: An object to check for touching with. - - Returns: - A :class:`bool` indicating whether the temporal point ever touches `other`. - - MEOS Functions: - etouches_tgeo_geo, etouches_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = etouches_tgeo_geo(self._inner, gs) - elif isinstance(other, STBox): - result = etouches_tgeo_geo(self._inner, stbox_to_geo(other._inner)) - elif isinstance(other, TPoint): - result = etouches_tgeo_tgeo(self._inner, other._inner) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return result == 1 - # ------------------------- Temporal Spatial Relationships ---------------- def is_spatially_contained_in(self, container: shpb.BaseGeometry | STBox) -> TBool: """ @@ -881,102 +722,6 @@ def is_spatially_contained_in(self, container: shpb.BaseGeometry | STBox) -> TBo return Temporal._factory(result) # ------------------------- Distance Operations --------------------------- - def distance(self, other: shpb.BaseGeometry | TPoint | STBox) -> TFloat: - """ - Returns the temporal distance between the temporal point and `other`. - - Args: - other: An object to check the distance to. - - Returns: - A new :class:`TFloat` indicating the temporal distance between the temporal point and `other`. - - MEOS Functions: - tdistance_tgeo_geo, tdistance_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tdistance_tgeo_geo(self._inner, gs, spheroid=True) - elif isinstance(other, STBox): - result = tdistance_tgeo_geo(self._inner, stbox_to_geo(other._inner), spheroid=True) - elif isinstance(other, TPoint): - result = tdistance_tgeo_tgeo(self._inner, other._inner, spheroid=True) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return Temporal._factory(result) - - def nearest_approach_distance(self, other: shpb.BaseGeometry | STBox | TPoint) -> float: - """ - Returns the nearest approach distance between the temporal point and `other`. - - Args: - other: An object to check the nearest approach distance to. - - Returns: - A :class:`float` indicating the nearest approach distance between the temporal point and `other`. - - MEOS Functions: - nad_tgeo_geo, nad_tgeo_stbox, nad_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - return nad_tgeo_geo(self._inner, gs, spheroid=True) - elif isinstance(other, STBox): - return nad_tgeo_stbox(self._inner, other._inner, spheroid=True) - elif isinstance(other, TPoint): - return nad_tgeo_tgeo(self._inner, other._inner, spheroid=True) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - - def nearest_approach_instant(self, other: shpb.BaseGeometry | TPoint) -> TI: - """ - Returns the nearest approach instant between the temporal point and `other`. - - Args: - other: An object to check the nearest approach instant to. - - Returns: - A new temporal instant indicating the nearest approach instant between the temporal point and `other`. - - MEOS Functions: - nai_tgeo_geo, nai_tgeo_tgeo - """ - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = nai_tgeo_geo(self._inner, gs, spheroid=True) - elif isinstance(other, TPoint): - result = nai_tgeo_tgeo(self._inner, other._inner, spheroid=True) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return Temporal._factory(result) - - def shortest_line(self, other: shpb.BaseGeometry | TPoint) -> shpb.BaseGeometry: - """ - Returns the shortest line between the temporal point and `other`. - - Args: - other: An object to check the shortest line to. - - Returns: - A new :class:`~shapely.geometry.base.BaseGeometry` indicating the shortest line between the temporal point - and `other`. - - MEOS Functions: - shortestline_tgeo_geo, shortestline_tgeo_tgeo - """ - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = shortestline_tgeo_geo(self._inner, gs, spheroid=True) - elif isinstance(other, TPoint): - result = shortestline_tgeo_tgeo(self._inner, other._inner, spheroid=True) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return gserialized_to_shapely_geometry(result, 10) - # ------------------------- Split Operations ------------------------------ def space_split( self, @@ -1184,7 +929,9 @@ def plot(self, *args, **kwargs): return TemporalPointSequenceSetPlotter.plot_xy(self, *args, **kwargs) -class TGeomPoint(TPoint["TGeomPoint", "TGeomPointInst", "TGeomPointSeq", "TGeomPointSeqSet"], ABC): +class TGeomPoint( + TGeomPointRegularMixin, TPoint["TGeomPoint", "TGeomPointInst", "TGeomPointSeq", "TGeomPointSeqSet"], ABC +): """ Abstract class for temporal geometric points. """ @@ -1313,196 +1060,7 @@ def to_dataframe(self) -> GeoDataFrame: # --------------------- Temporal Spatial Relationships -------------------- - def disjoint(self, other: shpb.BaseGeometry | STBox | TPoint) -> TBool: - """ - Returns a new temporal boolean indicating whether the temporal point intersects `other`. - - Args: - other: An object to check for intersection with. - - Returns: - A new :TBool: indicating whether the temporal point intersects `other`. - - MEOS Functions: - tdisjoint_tgeo_geo, tdisjoint_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tdisjoint_tgeo_geo(self._inner, gs) - elif isinstance(other, STBox): - result = tdisjoint_tgeo_geo(self._inner, stbox_to_geo(other._inner)) - elif isinstance(other, TPoint): - result = tdisjoint_tgeo_tgeo(self._inner, other._inner) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return Temporal._factory(result) - - def intersects(self, other: shpb.BaseGeometry | STBox | TPoint) -> TBool: - """ - Returns a new temporal boolean indicating whether the temporal point intersects `other`. - - Args: - other: An object to check for intersection with. - - Returns: - A new :TBool: indicating whether the temporal point intersects `other`. - - MEOS Functions: - tintersects_tgeo_geo, tintersects_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tintersects_tgeo_geo(self._inner, gs) - elif isinstance(other, STBox): - result = tintersects_tgeo_geo(self._inner, stbox_to_geo(other._inner)) - elif isinstance(other, TPoint): - result = tintersects_tgeo_tgeo(self._inner, other._inner) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return Temporal._factory(result) - - def touches(self, other: shpb.BaseGeometry | STBox | TGeomPoint) -> TBool: - """ - Returns a new temporal boolean indicating whether the temporal point touches `other`. - - Args: - other: An object to check for touching with. - - Returns: - A new :TBool: indicating whether the temporal point touches `other`. - - MEOS Functions: - ttouches_tgeo_geo, ttouches_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = ttouches_tgeo_geo(self._inner, gs) - elif isinstance(other, STBox): - result = ttouches_tgeo_geo(self._inner, stbox_to_geo(other._inner)) - elif isinstance(other, TGeomPoint): - result = ttouches_tgeo_tgeo(self._inner, other._inner) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return Temporal._factory(result) - - def within_distance(self, other: shpb.BaseGeometry | TGeomPoint | STBox, distance: float) -> TBool: - """ - Returns a new temporal boolean indicating whether the temporal point is within `distance` of `other`. - - Args: - other: An object to check the distance to. - distance: The distance to check in units of the spatial reference system. - - Returns: - A new :TBool: indicating whether the temporal point is within `distance` of `other`. - - MEOS Functions: - tdwithin_tgeo_geo, tdwithin_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = tdwithin_tgeo_geo(self._inner, gs, distance, spheroid=True) - elif isinstance(other, STBox): - result = tdwithin_tgeo_geo(self._inner, stbox_to_geo(other._inner), distance, spheroid=True) - elif isinstance(other, TGeomPoint): - result = tdwithin_tgeo_tgeo(self._inner, other._inner, distance, spheroid=True) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return Temporal._factory(result) - # ------------------------- Ever and Always Comparisons ------------------- - def always_equal(self, value: shpb.BaseGeometry | TGeomPoint) -> bool: - """ - Returns whether `self` is always equal to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is always equal to `value`, False otherwise. - - MEOS Functions: - always_eq_tgeo_geo, always_eq_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geometry_to_gserialized(value) - return always_eq_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeomPoint): - return always_eq_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - - def always_not_equal(self, value: shpb.BaseGeometry | TGeomPoint) -> bool: - """ - Returns whether `self` is always different to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is always different to `value`, False otherwise. - - MEOS Functions: - always_ne_tgeo_geo, always_ne_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geometry_to_gserialized(value) - return always_ne_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeomPoint): - return always_ne_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - - def ever_equal(self, value: shpb.BaseGeometry | TGeomPoint) -> bool: - """ - Returns whether `self` is ever equal to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is ever equal to `value`, False otherwise. - - MEOS Functions: - ever_eq_tgeo_geo, ever_eq_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geometry_to_gserialized(value) - return ever_eq_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeomPoint): - return ever_eq_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - - def ever_not_equal(self, value: shpb.BaseGeometry | TGeomPoint) -> bool: - """ - Returns whether `self` is ever different to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is ever different to `value`, False otherwise. - - MEOS Functions: - ever_ne_tgeo_geo, ever_ne_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geometry_to_gserialized(value) - return ever_ne_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeomPoint): - return ever_ne_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - def never_equal(self, value: shpb.BaseGeometry | TGeomPoint) -> bool: """ Returns whether `self` is never equal to `value`. @@ -1533,73 +1091,7 @@ def never_not_equal(self, value: shpb.BaseGeometry | TGeomPoint) -> bool: """ return not self.ever_not_equal(value) - def is_ever_disjoint(self, other: shpb.BaseGeometry | TGeomPoint | STBox) -> bool: - """ - Returns whether the temporal point is ever disjoint from `other`. - - Args: - other: An object to check for disjointness with. - - Returns: - A :class:`bool` indicating whether the temporal point is ever disjoint from `other`. - - MEOS Functions: - edisjoint_tgeo_geo, edisjoint_tgeo_tgeo - """ - from ..boxes import STBox - - if isinstance(other, shpb.BaseGeometry): - gs = geo_to_gserialized(other, isinstance(self, TGeogPoint)) - result = edisjoint_tgeo_geo(self._inner, gs) - elif isinstance(other, STBox): - result = edisjoint_tgeo_geo(self._inner, stbox_to_geo(other._inner)) - elif isinstance(other, TGeomPoint): - result = edisjoint_tgeo_tgeo(self._inner, other._inner) - else: - raise TypeError(f"Operation not supported with type {other.__class__}") - return result == 1 - # ------------------------- Temporal Comparisons -------------------------- - def temporal_equal(self, other: shp.Point | TGeomPoint) -> TBool: - """ - Returns the temporal equality relation between `self` and `other`. - - Args: - other: A temporal object to compare to `self`. - - Returns: - A :class:`TBool` with the result of the temporal equality relation. - - MEOS Functions: - teq_tgeo_geo, teq_tgeo_geo - """ - if isinstance(other, shp.Point): - gs = geometry_to_gserialized(other) - result = teq_tgeo_geo(self._inner, gs) - else: - return super().temporal_equal(other) - return Temporal._factory(result) - - def temporal_not_equal(self, other: shp.Point | TGeomPoint) -> TBool: - """ - Returns the temporal inequality relation between `self` and `other`. - - Args: - other: A temporal object to compare to `self`. - - Returns: - A :class:`TBool` with the result of the temporal inequality relation. - - MEOS Functions: - tne_tgeo_geo, tne_tgeo_geo - """ - if isinstance(other, shp.Point): - gs = geometry_to_gserialized(other) - result = tne_tgeo_geo(self._inner, gs) - else: - return super().temporal_not_equal(other) - return Temporal._factory(result) - # ------------------------- Database Operations --------------------------- @staticmethod def read_from_cursor(value, _=None): @@ -1628,7 +1120,9 @@ def read_from_cursor(value, _=None): raise Exception("ERROR: Could not parse temporal point value") -class TGeogPoint(TPoint["TGeogPoint", "TGeogPointInst", "TGeogPointSeq", "TGeogPointSeqSet"], ABC): +class TGeogPoint( + TGeogPointRegularMixin, TPoint["TGeogPoint", "TGeogPointInst", "TGeogPointSeq", "TGeogPointSeqSet"], ABC +): """ Abstract class for representing temporal geographic points. """ @@ -1746,90 +1240,6 @@ def to_geometric(self) -> TGeomPoint: return Temporal._factory(result) # ------------------------- Ever and Always Comparisons ------------------- - def always_equal(self, value: shpb.BaseGeometry | TGeogPoint) -> bool: - """ - Returns whether `self` is always equal to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is always equal to `value`, False otherwise. - - MEOS Functions: - always_eq_tgeo_geo, always_eq_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geography_to_gserialized(value) - return always_eq_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeogPoint): - return always_eq_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - - def always_not_equal(self, value: shpb.BaseGeometry | TGeogPoint) -> bool: - """ - Returns whether `self` is always different to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is always different to `value`, False otherwise. - - MEOS Functions: - always_ne_tgeo_geo, always_ne_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geography_to_gserialized(value) - return always_ne_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeogPoint): - return always_ne_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - - def ever_equal(self, value: shpb.BaseGeometry | TGeogPoint) -> bool: - """ - Returns whether `self` is ever equal to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is ever equal to `value`, False otherwise. - - MEOS Functions: - ever_eq_tgeo_geo, ever_eq_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geography_to_gserialized(value) - return ever_eq_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeogPoint): - return ever_eq_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - - def ever_not_equal(self, value: shpb.BaseGeometry | TGeogPoint) -> bool: - """ - Returns whether `self` is ever different to `value`. - - Args: - value: The geometry to compare with. - - Returns: - True if `self` is ever different to `value`, False otherwise. - - MEOS Functions: - ever_ne_tgeo_geo, ever_ne_tgeo_tgeo - """ - if isinstance(value, shpb.BaseGeometry): - gs = geography_to_gserialized(value) - return ever_ne_tgeo_geo(self._inner, gs) > 0 - elif isinstance(value, TGeogPoint): - return ever_ne_tgeo_tgeo(self._inner, value._inner) > 0 - else: - raise TypeError(f"Operation not supported with type {value.__class__}") - def never_equal(self, value: shpb.BaseGeometry | TGeogPoint) -> bool: """ Returns whether `self` is never equal to `value`. @@ -1861,46 +1271,6 @@ def never_not_equal(self, value: shpb.BaseGeometry | TGeogPoint) -> bool: return not self.ever_not_equal(value) # ------------------------- Temporal Comparisons -------------------------- - def temporal_equal(self, other: shp.Point | TGeogPoint) -> TBool: - """ - Returns the temporal equality relation between `self` and `other`. - - Args: - other: A temporal object to compare to `self`. - - Returns: - A :class:`TBool` with the result of the temporal equality relation. - - MEOS Functions: - teq_tgeo_geo, teq_temporal_temporal - """ - if isinstance(other, shp.Point): - gs = geography_to_gserialized(other) - result = teq_tgeo_geo(self._inner, gs) - else: - return super().temporal_equal(other) - return Temporal._factory(result) - - def temporal_not_equal(self, other: shp.Point | TGeogPoint) -> TBool: - """ - Returns the temporal inequality relation between `self` and `other`. - - Args: - other: A temporal object to compare to `self`. - - Returns: - A :class:`TBool` with the result of the temporal inequality relation. - - MEOS Functions: - tne_tgeo_geo, tne_temporal_temporal - """ - if isinstance(other, shp.Point): - gs = geography_to_gserialized(other) - result = tne_tgeo_geo(self._inner, gs) - else: - return super().temporal_not_equal(other) - return Temporal._factory(result) - # ------------------------- Database Operations --------------------------- @staticmethod def read_from_cursor(value, _=None): diff --git a/pymeos/temporal/temporal.py b/pymeos/temporal/temporal.py index 49d4050e..fa8d3f46 100644 --- a/pymeos/temporal/temporal.py +++ b/pymeos/temporal/temporal.py @@ -856,7 +856,7 @@ def at(self, other: Time) -> TG: MEOS Functions: temporal_at_timestamp, temporal_at_tstzset, - temporal_at_tstzspan, temporal_at_tstzspanset + temporal_at_tstzspan, temporal_at_tstzspanset, temporal_at_values """ if isinstance(other, datetime): result = temporal_at_timestamptz(self._inner, datetime_to_timestamptz(other)) @@ -866,6 +866,8 @@ def at(self, other: Time) -> TG: result = temporal_at_tstzspan(self._inner, other._inner) elif isinstance(other, TsTzSpanSet): result = temporal_at_tstzspanset(self._inner, other._inner) + elif isinstance(other, Set): + result = temporal_at_values(self._inner, other._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) @@ -911,7 +913,7 @@ def minus(self, other: Time) -> TG: MEOS Functions: temporal_minus_timestamp, temporal_minus_tstzset, - temporal_minus_tstzspan, temporal_minus_tstzspanset + temporal_minus_tstzspan, temporal_minus_tstzspanset, temporal_minus_values """ if isinstance(other, TsTzSpan): result = temporal_minus_tstzspan(self._inner, other._inner) @@ -921,6 +923,8 @@ def minus(self, other: Time) -> TG: result = temporal_minus_timestamptz(self._inner, datetime_to_timestamptz(other)) elif isinstance(other, TsTzSet): result = temporal_minus_tstzset(self._inner, other._inner) + elif isinstance(other, Set): + result = temporal_minus_values(self._inner, other._inner) else: raise TypeError(f"Operation not supported with type {other.__class__}") return Temporal._factory(result) diff --git a/tests/main/tgeogpoint_test.py b/tests/main/tgeogpoint_test.py index 276d84cf..8785ab3c 100644 --- a/tests/main/tgeogpoint_test.py +++ b/tests/main/tgeogpoint_test.py @@ -2456,20 +2456,20 @@ class TestTGeogPointDistanceOperations(TestTGeogPoint): Point(1, 1), TFloatSeqSet("{[0@2019-09-01, 156876.149@2019-09-02],[0@2019-09-03, 0@2019-09-05]}"), ), - (tpi, STBox("GEODSTBOX X((1,1),(1,1))"), TFloatInst("0@2019-09-01")), + (tpi, STBox("GEODSTBOX X((1,1),(1,1))").to_geometry(), TFloatInst("0@2019-09-01")), ( tpds, - STBox("GEODSTBOX X((1,1),(1,1))"), + STBox("GEODSTBOX X((1,1),(1,1))").to_geometry(), TFloatSeq("{0@2019-09-01, 156876.149@2019-09-02}"), ), ( tps, - STBox("GEODSTBOX X((1,1),(1,1))"), + STBox("GEODSTBOX X((1,1),(1,1))").to_geometry(), TFloatSeq("[0@2019-09-01, 156876.149@2019-09-02]"), ), ( tpss, - STBox("GEODSTBOX X((1,1),(1,1))"), + STBox("GEODSTBOX X((1,1),(1,1))").to_geometry(), TFloatSeqSet("{[0@2019-09-01, 156876.149@2019-09-02],[0@2019-09-03, 0@2019-09-05]}"), ), (tpi, TGeogPointInst("Point(1 1)@2019-09-01"), TFloatInst("0@2019-09-01")), diff --git a/tests/main/tgeompoint_test.py b/tests/main/tgeompoint_test.py index 04b73c17..ab0ddd85 100644 --- a/tests/main/tgeompoint_test.py +++ b/tests/main/tgeompoint_test.py @@ -9,6 +9,8 @@ from shapely import GeometryCollection, LineString, MultiPoint, Point, Polygon from pymeos import ( + FloatSpan, + GeometrySet, STBox, TBoolInst, TBoolSeq, @@ -16,6 +18,7 @@ TFloatInst, TFloatSeq, TFloatSeqSet, + TGeogPoint, TGeogPointInst, TGeogPointSeq, TGeogPointSeqSet, @@ -2922,14 +2925,16 @@ def test_temporal_ever_contained_withindist_intersects(self, temporal, expected) assert temporal.ever_intersects(Point(1, 1)) == expected assert temporal.ever_intersects(TGeomPointInst("Point(1 1)@2019-09-01")) == expected - # Verify that these results are correct wrt lifting the 9DEM definition of touches + # etouches_tpoint_geo, the function of eTouches(tgeompoint, geometry): the + # boundary of the trajectory of a sequence starting at Point(1 1) holds + # Point(1 1), and a trajectory of points has an empty boundary @pytest.mark.parametrize( "temporal, expected", [ (tpi, False), (tpds, False), - (tps, False), - (tpss, False), + (tps, True), + (tpss, True), ], ids=["Instant", "Discrete Sequence", "Sequence", "SequenceSet"], ) @@ -3083,20 +3088,20 @@ class TestTGeomPointDistanceOperations(TestTGeomPoint): Point(1, 1), TFloatSeqSet("{[0@2019-09-01, 1.414@2019-09-02],[0@2019-09-03, 0@2019-09-05]}"), ), - (tpi, STBox("STBOX X((1,1),(1,1))"), TFloatInst("0@2019-09-01")), + (tpi, STBox("STBOX X((1,1),(1,1))").to_geometry(), TFloatInst("0@2019-09-01")), ( tpds, - STBox("STBOX X((1,1),(1,1))"), + STBox("STBOX X((1,1),(1,1))").to_geometry(), TFloatSeq("{0@2019-09-01, 1.414@2019-09-02}"), ), ( tps, - STBox("STBOX X((1,1),(1,1))"), + STBox("STBOX X((1,1),(1,1))").to_geometry(), TFloatSeq("[0@2019-09-01, 1.414@2019-09-02]"), ), ( tpss, - STBox("STBOX X((1,1),(1,1))"), + STBox("STBOX X((1,1),(1,1))").to_geometry(), TFloatSeqSet("{[0@2019-09-01, 1.414@2019-09-02],[0@2019-09-03, 0@2019-09-05]}"), ), (tpi, TGeomPointInst("Point(1 1)@2019-09-01"), TFloatInst("0@2019-09-01")), @@ -3493,3 +3498,34 @@ def test_temporal_equal_temporal(self, temporal, expected): def test_temporal_equal_point(self, temporal, argument, expected): assert temporal.temporal_equal(argument) == expected assert temporal.temporal_not_equal(argument) == expected.temporal_not() + + +class TestTGeomPointGeneratedSurface(TestTGeomPoint): + tps3d = TGeomPointSeq("[Point(1 1 1)@2019-09-01, Point(1 1 3)@2019-09-03]") + + def test_at_minus_elevation(self): + assert self.tps3d.at(FloatSpan("[1.5, 2]")) == TGeomPointSeqSet( + "{[Point(1 1 1.5)@2019-09-01 12:00:00+00, Point(1 1 2)@2019-09-02]}" + ) + assert self.tps3d.minus(FloatSpan("[1.5, 2]")) == TGeomPointSeqSet( + "{[Point(1 1 1)@2019-09-01, Point(1 1 1.5)@2019-09-01 12:00:00+00)," + "(Point(1 1 2)@2019-09-02, Point(1 1 3)@2019-09-03]}" + ) + + def test_at_minus_geometry_set(self): + assert self.tps3d.at(GeometrySet("{Point(1 1 2)}")) == TGeomPointSeqSet("{[Point(1 1 2)@2019-09-02]}") + assert self.tps3d.minus(GeometrySet("{Point(1 1 2)}")) == TGeomPointSeqSet( + "{[Point(1 1 1)@2019-09-01, Point(1 1 2)@2019-09-02), (Point(1 1 2)@2019-09-02, Point(1 1 3)@2019-09-03]}" + ) + + @pytest.mark.parametrize( + "method", + ["is_ever_contains", "is_always_contains", "is_ever_covers", "is_always_covers", "covers"], + ) + def test_no_containment_or_covering_on_points(self, method): + assert not hasattr(TGeomPoint, method) + assert not hasattr(TGeogPoint, method) + + def test_geography_point_has_no_touches(self): + assert not hasattr(TGeogPoint, "touches") + assert not hasattr(TGeogPoint, "ever_touches") diff --git a/tests/test_oo_codegen_coverage.py b/tests/test_oo_codegen_coverage.py index b9a7dbf7..8cd29e24 100644 --- a/tests/test_oo_codegen_coverage.py +++ b/tests/test_oo_codegen_coverage.py @@ -72,7 +72,9 @@ def test_committed_matches_generator(self): for family in cg.FAMILY_MODEL: path = cg.mixin_path(family) with self.subTest(family=family): - generated = cg.format_mixin(cg.emit_faithful_mixin(family, fams[family]), path) + generated = cg.format_mixin( + cg.emit_faithful_mixin(family, fams[cg.FAMILY_MODEL[family].get("catalog_family", family)]), path + ) self.assertEqual( path.read_text(), generated, diff --git a/tools/oo_codegen/codegen.py b/tools/oo_codegen/codegen.py index 01d09378..750204fc 100644 --- a/tools/oo_codegen/codegen.py +++ b/tools/oo_codegen/codegen.py @@ -161,6 +161,12 @@ class Method: # the faithful-mixin emitter to detect the trailing `distance` arg and # the restriction `*_stbox` border flag). params: dict[str, list[str]] = field(default_factory=dict) + # backing C function name -> its IDL parameter names (a trailing + # `spheroid` flag is passed as True, the default of the SQL function) + pnames: dict[str, list[str]] = field(default_factory=dict) + # backing C function name -> the SQL type of the first operand of each + # SQL signature it backs + sqlfirst: dict[str, set[str]] = field(default_factory=dict) @dataclass @@ -211,6 +217,8 @@ def collect(idl: dict) -> tuple[dict[str, dict[str, Method]], Stats]: meth.overloads.setdefault(token or fam, cname) meth.c_names.append(cname) meth.params[cname] = [p.get("canonical", p.get("cType", "")) for p in entry["params"]] + meth.pnames[cname] = [p.get("name", "") for p in entry["params"]] + meth.sqlfirst[cname] = {sig["args"][0] for sig in entry.get("sqlSignatures") or [] if sig.get("args")} st.emitted_overloads += 1 st.by_family[fam] += 1 return fams, st @@ -323,6 +331,50 @@ def collect(idl: dict) -> tuple[dict[str, dict[str, Method]], Stats]: }, "stbox_lazy": "from ...boxes import STBox", }, + # The temporal points take one mixin per class, each holding the + # overloads whose SQL signatures take the class's SQL type as first + # operand: tgeompoint and tgeogpoint declare different relationships, + # and neither declares the containment and covering a surface answers. + "geompoint": { + "catalog_family": "geo", + "sql_types": ("tgeompoint",), + "mixin_class": "TGeomPointRegularMixin", + "base_class": "TGeomPoint", + "base_import": "import shapely.geometry as shp\n\nfrom ...collections import FloatSpan", + "temporal_class": "TGeomPoint", + "temporal_import": "from ..tpoint import TGeomPoint", + "temporal_token": ("tpoint", "tgeo"), + "tokens": { + "geo": ("shpb.BaseGeometry", "geo_to_gserialized($o, False)"), + "geom": ("shpb.BaseGeometry", "geo_to_gserialized($o, False)"), + # atValue and minusValue take a geometry(Point) + "value": ("shp.Point", "geo_to_gserialized($o, False)"), + "elevation": ("FloatSpan", "$o._inner"), + "tgeo": ("TGeomPoint", "$o._inner"), + "stbox": ("STBox", "$o._inner"), + }, + "stbox_lazy": "from ...boxes import STBox", + }, + "geogpoint": { + "catalog_family": "geo", + "sql_types": ("tgeogpoint",), + "mixin_class": "TGeogPointRegularMixin", + "base_class": "TGeogPoint", + "base_import": "import shapely.geometry as shp\n\nfrom ...collections import FloatSpan", + "temporal_class": "TGeogPoint", + "temporal_import": "from ..tpoint import TGeogPoint", + "temporal_token": ("tpoint", "tgeo"), + "tokens": { + "geo": ("shpb.BaseGeometry", "geo_to_gserialized($o, True)"), + "geom": ("shpb.BaseGeometry", "geo_to_gserialized($o, True)"), + # atValue and minusValue take a geometry(Point) + "value": ("shp.Point", "geo_to_gserialized($o, True)"), + "elevation": ("FloatSpan", "$o._inner"), + "tgeo": ("TGeogPoint", "$o._inner"), + "stbox": ("STBox", "$o._inner"), + }, + "stbox_lazy": "from ...boxes import STBox", + }, } # Result post-processing, derived verbatim from the hand-written oracle. @@ -363,10 +415,12 @@ def collect(idl: dict) -> tuple[dict[str, dict[str, Method]], Stats]: } _ORDER = [ + "value", "geo", "geom", "point", "tpoint", + "tgeo", "trgeo", "cbuffer", "tcbuffer", @@ -377,7 +431,6 @@ def collect(idl: dict) -> tuple[dict[str, dict[str, Method]], Stats]: "tpose", "rgeometry", "trgeometry", - "value", "values", "elevation", "stbox", @@ -391,22 +444,25 @@ def collect(idl: dict) -> tuple[dict[str, dict[str, Method]], Stats]: _OO_TO_CPREFIX = {oo: pfx for pfx, (oo, _k) in MEMBER_SPEC.items()} -def _faithful_overloads(oo_name: str, m: Method, ttok: str) -> dict[str, str]: +def _faithful_overloads( + oo_name: str, m: Method, ttoks: tuple[str, ...], sql_types: tuple[str, ...] | None = None +) -> dict[str, str]: """Rebuild the dispatch table for a temporal-type mixin: keep only the overloads whose C name is ``__`` (or, for restriction, ``_(at|minus)_``) and key on ````. This discards reversed-operand / base-class forms so the generated method calls exactly the backing the hand-written method called.""" out: dict[str, str] = {} - if oo_name in ("at", "minus"): - pat = re.compile(rf"^{re.escape(ttok)}_{oo_name}_(.+)$") - else: - cpre = _OO_TO_CPREFIX[oo_name] - pat = re.compile(rf"^{re.escape(cpre)}_{re.escape(ttok)}_(.+)$") - for cn in m.c_names: - mm = pat.match(cn) - if mm: - out.setdefault(mm.group(1), cn) + for ttok in ttoks: + if oo_name in ("at", "minus"): + pat = re.compile(rf"^{re.escape(ttok)}_{oo_name}_(.+)$") + else: + cpre = _OO_TO_CPREFIX[oo_name] + pat = re.compile(rf"^{re.escape(cpre)}_{re.escape(ttok)}_(.+)$") + for cn in m.c_names: + mm = pat.match(cn) + if mm and (sql_types is None or m.sqlfirst.get(cn, set()) & set(sql_types)): + out.setdefault(mm.group(1), cn) return out @@ -495,7 +551,9 @@ def emit_faithful_mixin(family: str, methods: dict[str, Method]) -> str: (so a coverage gap is loud, never silent).""" model = FAMILY_MODEL[family] tokens = model["tokens"] - ttok = model["temporal_token"] + ttoks = model["temporal_token"] + if isinstance(ttoks, str): + ttoks = (ttoks,) out = [ _MIXIN_HEADER.format( family=family, @@ -509,7 +567,7 @@ def emit_faithful_mixin(family: str, methods: dict[str, Method]) -> str: # Faithful dispatch tables (temporal operand first); reversed/base forms # discarded. Any arg token without a model mapping is loud, not silent. - faithful = {oo: _faithful_overloads(oo, m, ttok) for oo, m in methods.items()} + faithful = {oo: _faithful_overloads(oo, m, ttoks, model.get("sql_types")) for oo, m in methods.items()} unmodelled = sorted({tok for tbl in faithful.values() for tok in tbl if tok not in tokens}) if unmodelled: raise SystemExit( @@ -553,6 +611,8 @@ def emit_faithful_mixin(family: str, methods: dict[str, Method]) -> str: call_args.append("distance") elif oo_name in ("at", "minus") and tok == "stbox" and nparams == 3: call_args.append("True") + if m.pnames.get(cfn, [])[-1:] == ["spheroid"]: + call_args.append("True") kw = "if" if i == 0 else "elif" body.append( f" {kw} isinstance(other, {pytype}):\n result = {cfn}({', '.join(call_args)})\n" @@ -615,14 +675,15 @@ def main() -> int: if args.mixin not in FAMILY_MODEL: raise SystemExit(f"--mixin {args.mixin!r}: no FAMILY_MODEL (modelled: {sorted(FAMILY_MODEL)})") out = Path(args.mixin_out) if args.mixin_out else mixin_path(args.mixin) - src = format_mixin(emit_faithful_mixin(args.mixin, fams[args.mixin]), out) + methods = fams[FAMILY_MODEL[args.mixin].get("catalog_family", args.mixin)] + src = format_mixin(emit_faithful_mixin(args.mixin, methods), out) if args.mixin_out: Path(args.mixin_out).parent.mkdir(parents=True, exist_ok=True) Path(args.mixin_out).write_text(src) print( f"[oo-codegen] wrote faithful mixin " f"{FAMILY_MODEL[args.mixin]['mixin_class']} -> " - f"{args.mixin_out} ({len(fams[args.mixin])} methods)" + f"{args.mixin_out} ({len(methods)} methods)" ) else: print(src)