1 # SPDX-License-Identifier: GPL-3.0-or-later
3 # This file is part of Nominatim. (https://nominatim.org)
5 # Copyright (C) 2023 by the Nominatim developer community.
6 # For a full list of authors see the git log.
8 Custom types for SQLAlchemy.
10 from typing import Callable, Any, cast
13 import sqlalchemy as sa
14 from sqlalchemy import types
16 from nominatim.typing import SaColumn, SaBind
20 class Geometry(types.UserDefinedType): # type: ignore[type-arg]
21 """ Simplified type decorator for PostGIS geometry. This type
22 only supports geometries in 4326 projection.
26 def __init__(self, subtype: str = 'Geometry'):
27 self.subtype = subtype
30 def get_col_spec(self) -> str:
31 return f'GEOMETRY({self.subtype}, 4326)'
34 def bind_processor(self, dialect: 'sa.Dialect') -> Callable[[Any], str]:
35 def process(value: Any) -> str:
36 if isinstance(value, str):
39 return cast(str, value.to_wkt())
43 def result_processor(self, dialect: 'sa.Dialect', coltype: object) -> Callable[[Any], str]:
44 def process(value: Any) -> str:
45 assert isinstance(value, str)
50 def bind_expression(self, bindvalue: SaBind) -> SaColumn:
51 return sa.func.ST_GeomFromText(bindvalue, sa.text('4326'), type_=self)
54 class comparator_factory(types.UserDefinedType.Comparator): # type: ignore[type-arg]
56 def intersects(self, other: SaColumn) -> 'sa.Operators':
57 return self.op('&&')(other)
59 def is_line_like(self) -> SaColumn:
60 return sa.func.ST_GeometryType(self, type_=sa.String).in_(('ST_LineString',
61 'ST_MultiLineString'))
63 def is_area(self) -> SaColumn:
64 return sa.func.ST_GeometryType(self, type_=sa.String).in_(('ST_Polygon',
68 def ST_DWithin(self, other: SaColumn, distance: SaColumn) -> SaColumn:
69 return sa.func.ST_DWithin(self, other, distance, type_=sa.Boolean)
72 def ST_DWithin_no_index(self, other: SaColumn, distance: SaColumn) -> SaColumn:
73 return sa.func._ST_DWithin(self, other, distance, type_=sa.Boolean)
76 def ST_Intersects_no_index(self, other: SaColumn) -> SaColumn:
77 return sa.func._ST_Intersects(self, other, type_=sa.Boolean)
80 def ST_Distance(self, other: SaColumn) -> SaColumn:
81 return sa.func.ST_Distance(self, other, type_=sa.Float)
84 def ST_Contains(self, other: SaColumn) -> SaColumn:
85 return sa.func.ST_Contains(self, other, type_=sa.Boolean)
88 def ST_CoveredBy(self, other: SaColumn) -> SaColumn:
89 return sa.func.ST_CoveredBy(self, other, type_=sa.Boolean)
92 def ST_ClosestPoint(self, other: SaColumn) -> SaColumn:
93 return sa.func.ST_ClosestPoint(self, other, type_=Geometry)
96 def ST_Buffer(self, other: SaColumn) -> SaColumn:
97 return sa.func.ST_Buffer(self, other, type_=Geometry)
100 def ST_Expand(self, other: SaColumn) -> SaColumn:
101 return sa.func.ST_Expand(self, other, type_=Geometry)
104 def ST_Collect(self) -> SaColumn:
105 return sa.func.ST_Collect(self, type_=Geometry)
108 def ST_Centroid(self) -> SaColumn:
109 return sa.func.ST_Centroid(self, type_=Geometry)
112 def ST_LineInterpolatePoint(self, other: SaColumn) -> SaColumn:
113 return sa.func.ST_LineInterpolatePoint(self, other, type_=Geometry)
116 def ST_LineLocatePoint(self, other: SaColumn) -> SaColumn:
117 return sa.func.ST_LineLocatePoint(self, other, type_=sa.Float)